diff --git a/VERSION b/VERSION index 02fbef3..1afd364 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.1.16-dev \ No newline at end of file +v0.1.17 \ No newline at end of file diff --git a/api/crds/manifests/core.orchestrate.cloud.sap_controlplanes.yaml b/api/crds/manifests/core.orchestrate.cloud.sap_controlplanes.yaml index 1a11aa6..b019ac8 100644 --- a/api/crds/manifests/core.orchestrate.cloud.sap_controlplanes.yaml +++ b/api/crds/manifests/core.orchestrate.cloud.sap_controlplanes.yaml @@ -68,6 +68,10 @@ spec: repository: description: Repository is the URL to a Helm repository type: string + url: + description: URL is the URL to an OCI registry where the Helm + chart is stored. + type: string version: description: Version of the Helm chart, latest version if not set @@ -97,6 +101,10 @@ spec: repository: description: Repository is the URL to a Helm repository type: string + url: + description: URL is the URL to an OCI registry where the Helm + chart is stored. + type: string version: description: Version of the Helm chart, latest version if not set @@ -141,6 +149,10 @@ spec: repository: description: Repository is the URL to a Helm repository type: string + url: + description: URL is the URL to an OCI registry where the Helm + chart is stored. + type: string version: description: Version of the Helm chart, latest version if not set @@ -259,6 +271,10 @@ spec: repository: description: Repository is the URL to a Helm repository type: string + url: + description: URL is the URL to an OCI registry where the Helm + chart is stored. + type: string version: description: Version of the Helm chart, latest version if not set @@ -288,6 +304,10 @@ spec: repository: description: Repository is the URL to a Helm repository type: string + url: + description: URL is the URL to an OCI registry where the Helm + chart is stored. + type: string version: description: Version of the Helm chart, latest version if not set @@ -317,6 +337,10 @@ spec: repository: description: Repository is the URL to a Helm repository type: string + url: + description: URL is the URL to an OCI registry where the Helm + chart is stored. + type: string version: description: Version of the Helm chart, latest version if not set diff --git a/api/crds/manifests/core.orchestrate.cloud.sap_releasechannels.yaml b/api/crds/manifests/core.orchestrate.cloud.sap_releasechannels.yaml index e659698..af0d7f3 100644 --- a/api/crds/manifests/core.orchestrate.cloud.sap_releasechannels.yaml +++ b/api/crds/manifests/core.orchestrate.cloud.sap_releasechannels.yaml @@ -125,6 +125,10 @@ spec: description: if it's a helm chart, this specifies the helm repo type: string + ociUrl: + description: if the Helm chart is stored in an OCI registry, + this specifies the OCI URL + type: string version: description: The version number for that ComponentVersion type: string diff --git a/api/v1beta1/controlplane_types.go b/api/v1beta1/controlplane_types.go index e5f2cd1..a0896d1 100644 --- a/api/v1beta1/controlplane_types.go +++ b/api/v1beta1/controlplane_types.go @@ -89,6 +89,9 @@ type ChartSpec struct { // Repository is the URL to a Helm repository Repository string `json:"repository,omitempty"` + // URL is the URL to an OCI registry where the Helm chart is stored. + URL string `json:"url,omitempty"` + // Name of the Helm chart Name string `json:"name,omitempty"` diff --git a/api/v1beta1/releasechannel_types.go b/api/v1beta1/releasechannel_types.go index 117054a..b9497e7 100644 --- a/api/v1beta1/releasechannel_types.go +++ b/api/v1beta1/releasechannel_types.go @@ -71,6 +71,8 @@ type ComponentVersion struct { HelmRepo string `json:"helmRepo,omitempty"` // if it's a helm chart, this specifies the chart name HelmChart string `json:"helmChart,omitempty"` + // if the Helm chart is stored in an OCI registry, this specifies the OCI URL + OCIURL string `json:"ociUrl,omitempty"` } // ReleaseChannel is the Schema for the ReleaseChannel API diff --git a/charts/control-plane-operator/Chart.yaml b/charts/control-plane-operator/Chart.yaml index 28a4073..187cf3f 100644 --- a/charts/control-plane-operator/Chart.yaml +++ b/charts/control-plane-operator/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: control-plane-operator description: A Helm chart for the Cloud Orchestration Control Plane Operator type: application -version: v0.1.16 -appVersion: v0.1.16 +version: v0.1.17 +appVersion: v0.1.17 home: https://github.com/openmcp-project/control-plane-operator sources: - https://github.com/openmcp-project/control-plane-operator diff --git a/charts/control-plane-operator/values.yaml b/charts/control-plane-operator/values.yaml index 7f3ddfa..b05d3a8 100644 --- a/charts/control-plane-operator/values.yaml +++ b/charts/control-plane-operator/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/openmcp-project/images/control-plane-operator pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. - tag: v0.1.16 + tag: v0.1.17 imagePullSecrets: [] nameOverride: "" diff --git a/pkg/juggler/fluxcd/flux_sources.go b/pkg/juggler/fluxcd/flux_sources.go index dce229b..dd317b6 100644 --- a/pkg/juggler/fluxcd/flux_sources.go +++ b/pkg/juggler/fluxcd/flux_sources.go @@ -163,3 +163,75 @@ func (g *GitRepositoryAdapter) ApplyDefaults() { g.Source.Spec.Interval = metav1.Duration{Duration: 1 * time.Hour} } + +// +// ----------------------------------- +// + +var _ SourceAdapter = &OCIRepositoryAdapter{} + +// OCIRepositoryAdapter implements SourceAdapter +type OCIRepositoryAdapter struct { + Source *sourcev1.OCIRepository +} + +// ApplyDefaults implements SourceAdapter. +func (o *OCIRepositoryAdapter) ApplyDefaults() { + // This usually does nothing but we can keep it here in case Flux resources will have + // a defaulting func in the future. + scheme.Default(o.Source) + + o.Source.Spec.Interval = metav1.Duration{Duration: 1 * time.Hour} +} + +// Empty implements SourceAdapter. +func (o *OCIRepositoryAdapter) Empty() SourceAdapter { + return &OCIRepositoryAdapter{&sourcev1.OCIRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: o.Source.Name, + Namespace: o.Source.Namespace, + }, + }} +} + +// GetHealthiness implements SourceAdapter. +func (o *OCIRepositoryAdapter) GetHealthiness() juggler.ResourceHealthiness { + cond := apimeta.FindStatusCondition(o.Source.Status.Conditions, fluxmeta.ReadyCondition) + if cond == nil { + return juggler.ResourceHealthiness{ + Healthy: false, + Message: msgReadyNotPresent, + } + } + return juggler.ResourceHealthiness{ + Healthy: cond.Status == metav1.ConditionTrue, + Message: cond.Message, + } +} + +// GetObject implements SourceAdapter. +func (o *OCIRepositoryAdapter) GetObject() client.Object { + return o.Source +} + +// GetObjectKey implements SourceAdapter. +func (o *OCIRepositoryAdapter) GetObjectKey() client.ObjectKey { + return client.ObjectKey{ + Namespace: o.Source.Namespace, + Name: o.Source.Name, + } +} + +// Reconcile implements SourceAdapter. +func (o *OCIRepositoryAdapter) Reconcile(desired FluxResource) error { + desiredAdapter, ok := desired.(*OCIRepositoryAdapter) + if !ok { + return errNotAGitRepositoryAdapter + } + + preserved := o.Source.Spec.DeepCopy() + o.Source.Spec = desiredAdapter.Source.Spec + // Give suspension precedence + o.Source.Spec.Suspend = preserved.Suspend + return nil +} diff --git a/pkg/juggler/fluxcd/flux_sources_test.go b/pkg/juggler/fluxcd/flux_sources_test.go index 9bd42bb..15120ac 100644 --- a/pkg/juggler/fluxcd/flux_sources_test.go +++ b/pkg/juggler/fluxcd/flux_sources_test.go @@ -193,3 +193,94 @@ func TestGitRepositoryAdapter_GetHealthiness(t *testing.T) { }) } } + +func TestOCIRepositoryAdapter_GetHealthiness(t *testing.T) { + tests := []struct { + name string + adapter OCIRepositoryAdapter + expected juggler.ResourceHealthiness + }{ + { + name: "OCIRepositoryAdapter - Status Condition nil - Ready condition not present", + adapter: OCIRepositoryAdapter{ + Source: &sourcev1.OCIRepository{ + Status: sourcev1.OCIRepositoryStatus{ + Conditions: nil, + }, + }, + }, + expected: juggler.ResourceHealthiness{ + Healthy: false, + Message: msgReadyNotPresent, + }, + }, + { + name: "OCIRepositoryAdapter - Status Condition Ready not found", + adapter: OCIRepositoryAdapter{ + Source: &sourcev1.OCIRepository{ + Status: sourcev1.OCIRepositoryStatus{ + Conditions: []metav1.Condition{ + { + Type: "NotReady", // can not be found + Status: metav1.ConditionTrue, + Message: "The release is ready", + }, + }, + }, + }, + }, + expected: juggler.ResourceHealthiness{ + Healthy: false, + Message: msgReadyNotPresent, + }, + }, + { + name: "OCIRepositoryAdapter - Status Condition Ready = True", + adapter: OCIRepositoryAdapter{ + Source: &sourcev1.OCIRepository{ + Status: sourcev1.OCIRepositoryStatus{ + Conditions: []metav1.Condition{ + { + Type: fluxmeta.ReadyCondition, + Status: metav1.ConditionTrue, + Message: "The release is ready", + }, + }, + }, + }, + }, + expected: juggler.ResourceHealthiness{ + Healthy: true, + Message: "The release is ready", + }, + }, + { + name: "OCIRepositoryAdapter - Status Condition Ready = False", + adapter: OCIRepositoryAdapter{ + Source: &sourcev1.OCIRepository{ + Status: sourcev1.OCIRepositoryStatus{ + Conditions: []metav1.Condition{ + { + Type: fluxmeta.ReadyCondition, + Status: metav1.ConditionFalse, + Message: "The release is not ready", + }, + }, + }, + }, + }, + expected: juggler.ResourceHealthiness{ + Healthy: false, + Message: "The release is not ready", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := tt.adapter.GetHealthiness() + if !assert.Equal(t, tt.expected, actual) { + t.Errorf("OCIRepositoryAdapter.GetHealthiness() = %v, want %v", actual, tt.expected) + } + }) + } +}