diff --git a/pkg/registry/csv.go b/pkg/registry/csv.go index fec3d2907..26766f6b6 100644 --- a/pkg/registry/csv.go +++ b/pkg/registry/csv.go @@ -187,6 +187,7 @@ func (csv *ClusterServiceVersion) GetVersion() (string, error) { // GetRelease returns the release of the CSV // // If not defined, the function returns an empty string. +// The release field can be either a string or a number. func (csv *ClusterServiceVersion) GetRelease() (string, error) { var objmap map[string]*json.RawMessage if err := json.Unmarshal(csv.Spec, &objmap); err != nil { @@ -198,12 +199,24 @@ func (csv *ClusterServiceVersion) GetRelease() (string, error) { return "", nil } + // Try to unmarshal as string first (the expected type) var r string - if err := json.Unmarshal(*rawValue, &r); err != nil { - return "", err + if err := json.Unmarshal(*rawValue, &r); err == nil { + return r, nil + } + + // If string unmarshal fails, try as a number and convert to string + var num float64 + if err := json.Unmarshal(*rawValue, &num); err == nil { + // Format as integer if it's a whole number, otherwise as float + if num == float64(int64(num)) { + return fmt.Sprintf("%d", int64(num)), nil + } + return fmt.Sprintf("%g", num), nil } - return r, nil + // If both attempts fail, return an error + return "", fmt.Errorf("release field must be a string or number") } // GetSkipRange returns the skiprange of the CSV diff --git a/pkg/registry/csv_test.go b/pkg/registry/csv_test.go index 59a64cc8a..b0553eef4 100644 --- a/pkg/registry/csv_test.go +++ b/pkg/registry/csv_test.go @@ -465,6 +465,101 @@ func TestClusterServiceVersion_GetVersion(t *testing.T) { } } +func TestClusterServiceVersion_GetRelease(t *testing.T) { + type fields struct { + TypeMeta metav1.TypeMeta + ObjectMeta metav1.ObjectMeta + Spec json.RawMessage + } + tests := []struct { + name string + fields fields + want string + wantErr bool + }{ + { + name: "string release", + fields: fields{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: json.RawMessage(`{"release": "1.0.0"}`), + }, + want: "1.0.0", + }, + { + name: "integer numeric release", + fields: fields{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: json.RawMessage(`{"release": 20250104}`), + }, + want: "20250104", + }, + { + name: "float numeric release", + fields: fields{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: json.RawMessage(`{"release": 0.20250104}`), + }, + want: "0.20250104", + }, + { + name: "large integer release", + fields: fields{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: json.RawMessage(`{"release": 20250104235959}`), + }, + want: "20250104235959", + }, + { + name: "no release field", + fields: fields{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: json.RawMessage(`{"other": "field"}`), + }, + want: "", + }, + { + name: "null release", + fields: fields{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: json.RawMessage(`{"release": null}`), + }, + want: "", + }, + { + name: "invalid release type", + fields: fields{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: json.RawMessage(`{"release": true}`), + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + csv := &ClusterServiceVersion{ + TypeMeta: tt.fields.TypeMeta, + ObjectMeta: tt.fields.ObjectMeta, + Spec: tt.fields.Spec, + } + got, err := csv.GetRelease() + if (err != nil) != tt.wantErr { + t.Errorf("GetRelease() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GetRelease() got = %v, want %v", got, tt.want) + } + }) + } +} + func TestClusterServiceVersion_GetRelatedImages(t *testing.T) { type fields struct { TypeMeta metav1.TypeMeta