Skip to content

Commit 7b18170

Browse files
address review feedback
Signed-off-by: Rashmi Gottipati <rgottipa@redhat.com>
1 parent a97f95a commit 7b18170

18 files changed

Lines changed: 156 additions & 99 deletions

api/v1/clusterextension_types.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -467,16 +467,27 @@ type BundleMetadata struct {
467467
// +kubebuilder:validation:XValidation:rule="self.matches(\"^([0-9]+)(\\\\.[0-9]+)?(\\\\.[0-9]+)?(-([-0-9A-Za-z]+(\\\\.[-0-9A-Za-z]+)*))?(\\\\+([-0-9A-Za-z]+(-\\\\.[-0-9A-Za-z]+)*))?\")",message="version must be well-formed semver"
468468
Version string `json:"version"`
469469

470-
// release is an optional field that references the release value for this bundle.
471-
// The release follows pre-release/build metadata syntax as defined in https://semver.org/,
472-
// consisting of dot-separated identifiers where numeric identifiers must not have leading zeros.
470+
// release is an optional field that identifies a specific release of this bundle's version.
471+
// A release represents a re-publication of the same version, typically used to deliver
472+
// packaging or metadata changes without changing the version number. When multiple
473+
// releases exist for the same version, higher releases are preferred. An unset release
474+
// is less preferred than all other release values.
475+
//
476+
// The value consists of dot-separated identifiers, where each identifier is either a
477+
// numeric value (without leading zeros) or an alphanumeric string (e.g., "2", "1.el9",
478+
// "3.alpha.1"). Releases are compared identifier by identifier: numeric identifiers are
479+
// compared as integers, alphanumeric identifiers are compared lexically, and numeric
480+
// identifiers always sort before alphanumeric identifiers.
481+
//
473482
// For bundles with explicit pkg.Release metadata, this field contains that release value.
474-
// For registry+v1 bundles, this field contains the release extracted from version's build metadata (e.g., '2' from '1.0.0+2').
475-
// This field may be omitted if there is no explicit release and the version contains no parseable build metadata.
483+
// For registry+v1 bundles lacking an explicit release value, this field contains the release
484+
// extracted from version's build metadata (e.g., '2' from '1.0.0+2').
485+
// This field is omitted when the bundle's release value is unset.
476486
//
477487
// +optional
478-
// +kubebuilder:validation:XValidation:rule="self.matches(\"^$|^(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\\\\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*$\")",message="release must be empty or well-formed pre-release/build metadata (dot-separated identifiers, numeric parts without leading zeros)"
479-
Release string `json:"release,omitempty"`
488+
// <opcon:experimental>
489+
// +kubebuilder:validation:XValidation:rule="self.matches(\"^$|^(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\\\\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*$\")",message="release must be empty or consist of dot-separated identifiers (numeric without leading zeros, or alphanumeric)"
490+
Release *string `json:"release,omitempty"`
480491
}
481492

482493
// RevisionStatus defines the observed state of a ClusterObjectSet.

api/v1/zz_generated.deepcopy.go

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

applyconfigurations/api/v1/bundlemetadata.go

Lines changed: 17 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/api-reference/olmv1-api-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ _Appears in:_
6767
| --- | --- | --- | --- |
6868
| `name` _string_ | name is required and follows the DNS subdomain standard as defined in [RFC 1123].<br />It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.),<br />start and end with an alphanumeric character, and be no longer than 253 characters. | | Required: \{\} <br /> |
6969
| `version` _string_ | version is required and references the version that this bundle represents.<br />It follows the semantic versioning standard as defined in https://semver.org/. | | Required: \{\} <br /> |
70-
| `release` _string_ | release is an optional field that references the release value for this bundle.<br />The release follows pre-release/build metadata syntax as defined in https://semver.org/,<br />consisting of dot-separated identifiers where numeric identifiers must not have leading zeros.<br />For bundles with explicit pkg.Release metadata, this field contains that release value.<br />For registry+v1 bundles, this field contains the release extracted from version's build metadata (e.g., '2' from '1.0.0+2').<br />This field may be omitted if there is no explicit release and the version contains no parseable build metadata. | | Optional: \{\} <br /> |
70+
| `release` _string_ | release is an optional field that identifies a specific release of this bundle's version.<br />A release represents a re-publication of the same version, typically used to deliver<br />packaging or metadata changes without changing the version number. When multiple<br />releases exist for the same version, higher releases are preferred. An unset release<br />is less preferred than all other release values.<br />The value consists of dot-separated identifiers, where each identifier is either a<br />numeric value (without leading zeros) or an alphanumeric string (e.g., "2", "1.el9",<br />"3.alpha.1"). Releases are compared identifier by identifier: numeric identifiers are<br />compared as integers, alphanumeric identifiers are compared lexically, and numeric<br />identifiers always sort before alphanumeric identifiers.<br />For bundles with explicit pkg.Release metadata, this field contains that release value.<br />For registry+v1 bundles lacking an explicit release value, this field contains the release<br />extracted from version's build metadata (e.g., '2' from '1.0.0+2').<br />This field is omitted when the bundle's release value is unset.<br /><opcon:experimental> | | Optional: \{\} <br /> |
7171

7272

7373
#### CRDUpgradeSafetyEnforcement

helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensions.yaml

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -690,17 +690,26 @@ spec:
690690
rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$")
691691
release:
692692
description: |-
693-
release is an optional field that references the release value for this bundle.
694-
The release follows pre-release/build metadata syntax as defined in https://semver.org/,
695-
consisting of dot-separated identifiers where numeric identifiers must not have leading zeros.
693+
release is an optional field that identifies a specific release of this bundle's version.
694+
A release represents a re-publication of the same version, typically used to deliver
695+
packaging or metadata changes without changing the version number. When multiple
696+
releases exist for the same version, higher releases are preferred. An unset release
697+
is less preferred than all other release values.
698+
699+
The value consists of dot-separated identifiers, where each identifier is either a
700+
numeric value (without leading zeros) or an alphanumeric string (e.g., "2", "1.el9",
701+
"3.alpha.1"). Releases are compared identifier by identifier: numeric identifiers are
702+
compared as integers, alphanumeric identifiers are compared lexically, and numeric
703+
identifiers always sort before alphanumeric identifiers.
704+
696705
For bundles with explicit pkg.Release metadata, this field contains that release value.
697-
For registry+v1 bundles, this field contains the release extracted from version's build metadata (e.g., '2' from '1.0.0+2').
698-
This field may be omitted if there is no explicit release and the version contains no parseable build metadata.
706+
For registry+v1 bundles lacking an explicit release value, this field contains the release
707+
extracted from version's build metadata (e.g., '2' from '1.0.0+2').
708+
This field is omitted when the bundle's release value is unset.
699709
type: string
700710
x-kubernetes-validations:
701-
- message: release must be empty or well-formed pre-release/build
702-
metadata (dot-separated identifiers, numeric parts without
703-
leading zeros)
711+
- message: release must be empty or consist of dot-separated
712+
identifiers (numeric without leading zeros, or alphanumeric)
704713
rule: self.matches("^$|^(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*$")
705714
version:
706715
description: |-

helm/olmv1/base/operator-controller/crd/standard/olm.operatorframework.io_clusterextensions.yaml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -556,20 +556,6 @@ spec:
556556
hyphens (-) or periods (.), start and end with an alphanumeric
557557
character, and be no longer than 253 characters
558558
rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$")
559-
release:
560-
description: |-
561-
release is an optional field that references the release value for this bundle.
562-
The release follows pre-release/build metadata syntax as defined in https://semver.org/,
563-
consisting of dot-separated identifiers where numeric identifiers must not have leading zeros.
564-
For bundles with explicit pkg.Release metadata, this field contains that release value.
565-
For registry+v1 bundles, this field contains the release extracted from version's build metadata (e.g., '2' from '1.0.0+2').
566-
This field may be omitted if there is no explicit release and the version contains no parseable build metadata.
567-
type: string
568-
x-kubernetes-validations:
569-
- message: release must be empty or well-formed pre-release/build
570-
metadata (dot-separated identifiers, numeric parts without
571-
leading zeros)
572-
rule: self.matches("^$|^(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*$")
573559
version:
574560
description: |-
575561
version is required and references the version that this bundle represents.

internal/operator-controller/bundle/versionrelease.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ func (vr *VersionRelease) AsLegacyRegistryV1Version() bsemver.Version {
8585

8686
type Release []bsemver.PRVersion
8787

88+
// String returns the string representation of the release.
89+
// Returns an empty string if the release is nil or empty.
90+
func (r Release) String() string {
91+
if len(r) == 0 {
92+
return ""
93+
}
94+
parts := make([]string, len(r))
95+
for i, pr := range r {
96+
parts[i] = pr.String()
97+
}
98+
return strings.Join(parts, ".")
99+
}
100+
88101
// Compare compares two Release values. It returns:
89102
//
90103
// -1 if r < other

internal/operator-controller/bundleutil/bundle.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package bundleutil
33
import (
44
"encoding/json"
55
"fmt"
6-
"strings"
76

87
bsemver "github.com/blang/semver/v4"
98

@@ -13,7 +12,6 @@ import (
1312
ocv1 "github.com/operator-framework/operator-controller/api/v1"
1413
"github.com/operator-framework/operator-controller/internal/operator-controller/bundle"
1514
"github.com/operator-framework/operator-controller/internal/operator-controller/features"
16-
slicesutil "github.com/operator-framework/operator-controller/internal/shared/util/slices"
1715
)
1816

1917
func GetVersionAndRelease(b declcfg.Bundle) (*bundle.VersionRelease, error) {
@@ -52,6 +50,11 @@ func parseVersionRelease(pkgData json.RawMessage) (*bundle.VersionRelease, error
5250
}
5351

5452
// Fall back to legacy registry+v1 behavior (release in build metadata)
53+
//
54+
// TODO: For now, we assume that all bundles are registry+v1 bundles.
55+
// In the future, for supporting other bundle formats, we should not
56+
// use the legacy registry+v1 mechanism (i.e. using build metadata in
57+
// the version) to determine the bundle's release.
5558
vr, err := bundle.NewLegacyRegistryV1VersionRelease(pkg.Version)
5659
if err != nil {
5760
return nil, err
@@ -65,9 +68,9 @@ func MetadataFor(bundleName string, vr bundle.VersionRelease) ocv1.BundleMetadat
6568
Name: bundleName,
6669
Version: vr.Version.String(),
6770
}
68-
if len(vr.Release) > 0 {
69-
parts := slicesutil.Map(vr.Release, func(pr bsemver.PRVersion) string { return pr.String() })
70-
bm.Release = strings.Join(parts, ".")
71+
if vr.Release != nil {
72+
relStr := vr.Release.String()
73+
bm.Release = &relStr
7174
}
7275
return bm
7376
}

internal/operator-controller/bundleutil/bundle_test.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ func TestMetadataFor(t *testing.T) {
213213
result := bundleutil.MetadataFor("test-bundle", vr)
214214
require.Equal(t, "test-bundle", result.Name)
215215
require.Equal(t, "1.0.0", result.Version)
216-
require.Equal(t, "2", result.Release)
216+
require.NotNil(t, result.Release)
217+
require.Equal(t, "2", *result.Release)
217218
})
218219

219220
t.Run("without release", func(t *testing.T) {
@@ -224,6 +225,18 @@ func TestMetadataFor(t *testing.T) {
224225
result := bundleutil.MetadataFor("test-bundle", vr)
225226
require.Equal(t, "test-bundle", result.Name)
226227
require.Equal(t, "1.0.0", result.Version)
227-
require.Empty(t, result.Release)
228+
require.Nil(t, result.Release)
229+
})
230+
231+
t.Run("with explicit empty release", func(t *testing.T) {
232+
vr := bundle.VersionRelease{
233+
Version: bsemver.MustParse("1.0.0"),
234+
Release: bundle.Release([]bsemver.PRVersion{}),
235+
}
236+
result := bundleutil.MetadataFor("test-bundle", vr)
237+
require.Equal(t, "test-bundle", result.Name)
238+
require.Equal(t, "1.0.0", result.Version)
239+
require.NotNil(t, result.Release)
240+
require.Empty(t, *result.Release)
228241
})
229242
}

internal/operator-controller/catalogmetadata/filter/successors.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import (
1414

1515
func SuccessorsOf(installedBundle ocv1.BundleMetadata, channels ...declcfg.Channel) (filter.Predicate[declcfg.Bundle], error) {
1616
// Construct VersionRelease from BundleMetadata.
17-
// If the Release field is populated, parse version and release separately.
18-
// Otherwise, parse release from version build metadata (registry+v1 legacy format).
17+
// If the Release field is not nil (even if empty string), use it as the explicit release.
18+
// If the Release field is nil, parse release from version build metadata (registry+v1 legacy format).
1919
var installedVersionRelease *bundle.VersionRelease
2020
var err error
2121

22-
if installedBundle.Release != "" {
23-
// Bundle has explicit release field - parse version and release from separate fields.
22+
if installedBundle.Release != nil {
23+
// Bundle has explicit release field (or explicitly empty) - parse version and release from separate fields.
2424
// Note: We can't use NewLegacyRegistryV1VersionRelease here because the version might
2525
// already contain build metadata (e.g., "1.0.0+git.abc"), which serves its proper
2626
// semver purpose when using explicit pkg.Release. Concatenating would create invalid
@@ -29,9 +29,9 @@ func SuccessorsOf(installedBundle ocv1.BundleMetadata, channels ...declcfg.Chann
2929
if err != nil {
3030
return nil, fmt.Errorf("failed to parse installed bundle version %q: %w", installedBundle.Version, err)
3131
}
32-
release, err := bundle.NewRelease(installedBundle.Release)
32+
release, err := bundle.NewRelease(*installedBundle.Release)
3333
if err != nil {
34-
return nil, fmt.Errorf("failed to parse installed bundle release %q: %w", installedBundle.Release, err)
34+
return nil, fmt.Errorf("failed to parse installed bundle release %q: %w", *installedBundle.Release, err)
3535
}
3636
installedVersionRelease = &bundle.VersionRelease{
3737
Version: version,

0 commit comments

Comments
 (0)