diff --git a/charts/core/Chart.yaml b/charts/core/Chart.yaml index fafdb66..57495fe 100644 --- a/charts/core/Chart.yaml +++ b/charts/core/Chart.yaml @@ -4,8 +4,8 @@ description: A Helm chart for deploying Unikorn Core type: application -version: v0.1.79 -appVersion: v0.1.79 +version: v0.1.80 +appVersion: v0.1.80 icon: https://assets.unikorn-cloud.org/images/logos/dark-on-light/icon.svg diff --git a/charts/core/crds/unikorn-cloud.org_helmapplications.yaml b/charts/core/crds/unikorn-cloud.org_helmapplications.yaml index 50c5298..37f80b5 100644 --- a/charts/core/crds/unikorn-cloud.org_helmapplications.yaml +++ b/charts/core/crds/unikorn-cloud.org_helmapplications.yaml @@ -92,29 +92,8 @@ spec: constraints: description: |- Constraints is a set of versioning constraints that must be met - by a SAT solver, the set is composed as a logical AND so all - constraints must be met. - items: - properties: - operator: - description: Operator defines the constraint operation. - enum: - - Equal - - GreaterThan - - LessThan - - GreaterThanOrEqual - - LessThanOrEqual - type: string - version: - description: Version is the version the operator - compares against. - pattern: ^v?[0-9]+(\.[0-9]+)?(\.[0-9]+)?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?$ - type: string - required: - - operator - - version - type: object - type: array + by a SAT solver. + type: string name: description: Name of the application to depend on. minLength: 1 diff --git a/pkg/apis/unikorn/v1alpha1/helmapplication_types.go b/pkg/apis/unikorn/v1alpha1/helmapplication_types.go index 09cb0d5..0c669d1 100644 --- a/pkg/apis/unikorn/v1alpha1/helmapplication_types.go +++ b/pkg/apis/unikorn/v1alpha1/helmapplication_types.go @@ -121,27 +121,8 @@ type HelmApplicationDependency struct { // +kubebuilder:validation:MinLength=1 Name string `json:"name"` // Constraints is a set of versioning constraints that must be met - // by a SAT solver, the set is composed as a logical AND so all - // constraints must be met. - Constraints []DependencyConstraint `json:"constraints,omitempty"` -} - -// +kubebuilder:validation:Enum=Equal;GreaterThan;LessThan;GreaterThanOrEqual;LessThanOrEqual -type DependencyConstraintOperator string - -const ( - Equal DependencyConstraintOperator = "Equal" - GreaterThan DependencyConstraintOperator = "GreaterThan" - LessThan DependencyConstraintOperator = "LessThan" - GreaterThanOrEqual DependencyConstraintOperator = "GreaterThanOrEqual" - LessThanOrEqual DependencyConstraintOperator = "LessThanOrEqual" -) - -type DependencyConstraint struct { - // Operator defines the constraint operation. - Operator DependencyConstraintOperator `json:"operator"` - // Version is the version the operator compares against. - Version SemanticVersion `json:"version"` + // by a SAT solver. + Constraints *SemanticVersionConstraints `json:"constraints,omitempty"` } type HelmApplicationRecommendation struct { diff --git a/pkg/apis/unikorn/v1alpha1/types.go b/pkg/apis/unikorn/v1alpha1/types.go index 0a1d44d..603d1ed 100644 --- a/pkg/apis/unikorn/v1alpha1/types.go +++ b/pkg/apis/unikorn/v1alpha1/types.go @@ -51,32 +51,68 @@ func (v SemanticVersion) Equal(o *SemanticVersion) bool { return v.Version.Equal(&o.Version) } -func (v SemanticVersion) GreaterThan(o *SemanticVersion) bool { - return v.Version.GreaterThan(&o.Version) +func (v *SemanticVersion) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &v.Version) } -func (v SemanticVersion) GreaterThanEqual(o *SemanticVersion) bool { - return v.Version.GreaterThanEqual(&o.Version) +func (v SemanticVersion) MarshalJSON() ([]byte, error) { + return json.Marshal(v.Original()) } -func (v SemanticVersion) LessThan(o *SemanticVersion) bool { - return v.Version.LessThan(&o.Version) +func (v SemanticVersion) ToUnstructured() interface{} { + return v.Original() } -func (v SemanticVersion) LessThanEqual(o *SemanticVersion) bool { - return v.Version.LessThanEqual(&o.Version) +// SemanticVersionConstraints allows an arbitrary semantic version to be constrained. +// +kubebuilder:validation:Type=string +// +kubebuilder:object:generate=false +type SemanticVersionConstraints struct { + semver.Constraints } -func (v *SemanticVersion) UnmarshalJSON(b []byte) error { - return json.Unmarshal(b, &v.Version) +func (c SemanticVersionConstraints) Check(v *SemanticVersion) bool { + return c.Constraints.Check(&v.Version) } -func (v SemanticVersion) MarshalJSON() ([]byte, error) { - return json.Marshal(v.Original()) +func (c SemanticVersionConstraints) String() string { + return c.Constraints.String() } -func (v SemanticVersion) ToUnstructured() interface{} { - return v.Original() +func (c *SemanticVersionConstraints) UnmarshalJSON(b []byte) error { + var s string + + if err := json.Unmarshal(b, &s); err != nil { + return err + } + + constraints, err := semver.NewConstraint(s) + if err != nil { + return err + } + + c.Constraints = *constraints + + return nil +} + +func (c SemanticVersionConstraints) MarshalJSON() ([]byte, error) { + return json.Marshal(c.Constraints.String()) +} + +func (c SemanticVersionConstraints) ToUnstructured() interface{} { + return c.Constraints.String() +} + +func (c *SemanticVersionConstraints) DeepCopyInto(out *SemanticVersionConstraints) { + t, _ := c.MarshalText() + _ = out.Constraints.UnmarshalText(t) +} + +func (c *SemanticVersionConstraints) DeepCopy() *SemanticVersionConstraints { + out := &SemanticVersionConstraints{} + c.DeepCopyInto(out) + + return out } // +kubebuilder:validation:Type=string diff --git a/pkg/apis/unikorn/v1alpha1/types_test.go b/pkg/apis/unikorn/v1alpha1/types_test.go index 2c5086c..3b04904 100644 --- a/pkg/apis/unikorn/v1alpha1/types_test.go +++ b/pkg/apis/unikorn/v1alpha1/types_test.go @@ -50,23 +50,58 @@ var ( func TestSemanticVersionCanonical(t *testing.T) { t.Parallel() + jsonSemver := `"1.2.3-foo+bar"` + out := &v1alpha1.SemanticVersion{} - require.NoError(t, out.UnmarshalJSON([]byte(`"1.2.3-foo+bar"`))) + require.NoError(t, out.UnmarshalJSON([]byte(jsonSemver))) require.EqualValues(t, 1, out.Major()) require.EqualValues(t, 2, out.Minor()) require.EqualValues(t, 3, out.Patch()) + + marshalled, err := out.MarshalJSON() + require.NoError(t, err) + require.Equal(t, jsonSemver, string(marshalled)) } func TestSemanticVersion(t *testing.T) { t.Parallel() + jsonSemver := `"v1.2.3-foo+bar"` + out := &v1alpha1.SemanticVersion{} - require.NoError(t, out.UnmarshalJSON([]byte(`"v1.2.3-foo+bar"`))) + require.NoError(t, out.UnmarshalJSON([]byte(jsonSemver))) require.EqualValues(t, 1, out.Major()) require.EqualValues(t, 2, out.Minor()) require.EqualValues(t, 3, out.Patch()) + + marshalled, err := out.MarshalJSON() + require.NoError(t, err) + require.Equal(t, jsonSemver, string(marshalled)) +} + +func TestConstraints(t *testing.T) { + t.Parallel() + + good := &v1alpha1.SemanticVersion{} + require.NoError(t, good.UnmarshalJSON([]byte(`"1.5.0"`))) + + bad := &v1alpha1.SemanticVersion{} + require.NoError(t, bad.UnmarshalJSON([]byte(`"3.0.0"`))) + + jsonConstraints := `">= 1.0.0, < 2.0.0"` + + out := &v1alpha1.SemanticVersionConstraints{} + + require.NoError(t, out.UnmarshalJSON([]byte(jsonConstraints))) + require.True(t, out.Check(good)) + require.False(t, out.Check(bad)) + + // NOTE: This emits UTF8, which isn't the same as the input. + // We could do some text transformation I guess... + _, err := out.MarshalJSON() + require.NoError(t, err) } func TestIPv4AddressUnmarshal(t *testing.T) { diff --git a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go index 1e036eb..56082fa 100644 --- a/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/unikorn/v1alpha1/zz_generated.deepcopy.go @@ -97,23 +97,6 @@ func (in *Condition) DeepCopy() *Condition { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DependencyConstraint) DeepCopyInto(out *DependencyConstraint) { - *out = *in - out.Version = in.Version - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependencyConstraint. -func (in *DependencyConstraint) DeepCopy() *DependencyConstraint { - if in == nil { - return nil - } - out := new(DependencyConstraint) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HelmApplication) DeepCopyInto(out *HelmApplication) { *out = *in @@ -147,8 +130,7 @@ func (in *HelmApplicationDependency) DeepCopyInto(out *HelmApplicationDependency *out = *in if in.Constraints != nil { in, out := &in.Constraints, &out.Constraints - *out = make([]DependencyConstraint, len(*in)) - copy(*out, *in) + *out = (*in).DeepCopy() } return }