diff --git a/api/v1alpha1/configuredresource_types.go b/api/v1alpha1/configuredresource_types.go index 5a70bcf..18248aa 100644 --- a/api/v1alpha1/configuredresource_types.go +++ b/api/v1alpha1/configuredresource_types.go @@ -85,6 +85,28 @@ func (in *ConfiguredResource) SetTarget(v *ConfigurationReference) { v.DeepCopyInto(&in.Spec.Target) } +func (in *ConfiguredResource) GetOCIArtifact() *OCIArtifactInfo { + return in.Status.OCIArtifact +} + +// GetOCIRepository returns the name of the OCI repository of the OCI artifact in which the component +// descriptors are stored. +func (in *ConfiguredResource) GetOCIRepository() string { + return in.Status.OCIArtifact.Repository +} + +// GetManifestDigest returns the manifest digest of the OCI artifact, in which the component descriptors +// are stored. +func (in *ConfiguredResource) GetManifestDigest() string { + return in.Status.OCIArtifact.Digest +} + +// GetBlobDigest returns the blob digest of the OCI artifact, in which the component descriptors +// are stored. +func (in *ConfiguredResource) GetBlobDigest() string { + return in.Status.OCIArtifact.Blob.Digest +} + // ConfiguredResourceStatus defines the observed state of ConfiguredResource. type ConfiguredResourceStatus struct { ObservedGeneration int64 `json:"observedGeneration,omitempty"` diff --git a/internal/controller/configuration/configuration_controller.go b/internal/controller/configuration/configuration_controller.go index cb6f0ba..c2887d6 100644 --- a/internal/controller/configuration/configuration_controller.go +++ b/internal/controller/configuration/configuration_controller.go @@ -25,11 +25,9 @@ import ( "github.com/fluxcd/pkg/runtime/patch" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/predicate" - corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" "github.com/open-component-model/ocm-k8s-toolkit/api/v1alpha1" @@ -50,8 +48,6 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.ConfiguredResource{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})). - // Update when the owned artifact containing the configured data changes - Owns(&v1alpha1.Snapshot{}). // Update when a resource specified as target changes Watches(&v1alpha1.Resource{}, onTargetChange). Watches(&v1alpha1.LocalizedResource{}, onTargetChange). @@ -159,21 +155,32 @@ func (r *Reconciler) reconcileExists(ctx context.Context, configuration *v1alpha // Check if a snapshot of the configuration resource already exists and if it holds the same calculated combinedDigest // from above logger.V(1).Info("verifying configuration", "combinedDigest", combinedDigest, "revision", revision) - // TODO: Replace ValidateSnapshotForOwner - hasValidSnapshot, err := ociartifact.ValidateSnapshotForOwner( - ctx, - r.Client, - configuration, - combinedDigest, - ) - if err != nil { - return ctrl.Result{}, fmt.Errorf("failed to check if snapshot is valid: %w", err) + + hasValidArtifact := false + + if configuration.GetOCIArtifact() != nil { + ociRepository, err := r.Registry.NewRepository(ctx, configuration.GetOCIRepository()) + if err != nil { + status.MarkNotReady(r.EventRecorder, configuration, v1alpha1.CreateOCIRepositoryFailedReason, err.Error()) + + return ctrl.Result{}, err + } + + exists, err := ociRepository.ExistsArtifact(ctx, configuration.GetManifestDigest()) + if err != nil { + status.MarkNotReady(r.EventRecorder, configuration, v1alpha1.OCIRepositoryExistsFailedReason, err.Error()) + + return ctrl.Result{}, err + } + if exists { + hasValidArtifact = combinedDigest == configuration.GetBlobDigest() + } } - // If no valid snapshot is present (because it never existed or is just not valid), we will configure the target, - // create a snapshot and return. + // If no valid OCI artifact is present (because it never existed or is just not valid), we will configure the target, + // create an OCI artifact and return. //nolint:nestif // Ignore as it is not that complex. - if !hasValidSnapshot { + if !hasValidArtifact { logger.V(1).Info("configuring", "combinedDigest", combinedDigest, "revision", revision) basePath, err := os.MkdirTemp("", "configured-") if err != nil { @@ -227,37 +234,18 @@ func (r *Reconciler) reconcileExists(ctx context.Context, configuration *v1alpha } // We use the combinedDigest calculated above for the blob-info combinedDigest, so we can compare for any changes - snapshotCR := ociartifact.Create( - configuration, - repositoryName, - manifestDigest.String(), - &v1alpha1.BlobInfo{ + configuration.Status.OCIArtifact = &v1alpha1.OCIArtifactInfo{ + Repository: repositoryName, + Digest: manifestDigest.String(), + Blob: &v1alpha1.BlobInfo{ Digest: combinedDigest, Tag: tag, Size: int64(len(dataTGZ)), }, - ) - - if _, err = controllerutil.CreateOrUpdate(ctx, r.GetClient(), snapshotCR, func() error { - if snapshotCR.ObjectMeta.CreationTimestamp.IsZero() { - if err := controllerutil.SetControllerReference(configuration, snapshotCR, r.GetScheme()); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - } - - configuration.Status.SnapshotRef = corev1.LocalObjectReference{ - Name: snapshotCR.GetName(), - } - - return nil - }); err != nil { - status.MarkNotReady(r.EventRecorder, configuration, v1alpha1.CreateSnapshotFailedReason, err.Error()) - - return ctrl.Result{}, err } } - logger.Info("configuration successful", "snapshot", configuration.Status.SnapshotRef) + logger.Info("configuration successful", "OCIArtifact", configuration.GetOCIArtifact()) status.MarkReady(r.EventRecorder, configuration, "configured successfully") return ctrl.Result{RequeueAfter: configuration.Spec.Interval.Duration}, nil diff --git a/internal/controller/configuration/configuration_controller_test.go b/internal/controller/configuration/configuration_controller_test.go index 62349fb..20fb08e 100644 --- a/internal/controller/configuration/configuration_controller_test.go +++ b/internal/controller/configuration/configuration_controller_test.go @@ -2,6 +2,7 @@ package configuration import ( "context" + "fmt" "os" "path/filepath" "time" @@ -15,7 +16,6 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" corev1 "k8s.io/api/core/v1" @@ -146,28 +146,30 @@ var _ = Describe("ConfiguredResource Controller", func() { } Expect(k8sClient.Create(ctx, configuredResource)).To(Succeed()) - Eventually(func(ctx context.Context) bool { + Eventually(func(ctx context.Context) error { err := k8sClient.Get(ctx, client.ObjectKeyFromObject(configuredResource), configuredResource) if err != nil { - return false + return err } - return conditions.IsReady(configuredResource) && configuredResource.GetSnapshotName() != "" - }, "15s").WithContext(ctx).Should(BeTrue()) - - snapshotResource := &v1alpha1.Snapshot{} - Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: configuredResource.GetNamespace(), Name: configuredResource.GetSnapshotName()}, snapshotResource)).To(Succeed()) + if !conditions.IsReady(configuredResource) { + return fmt.Errorf("resource not ready") + } + if configuredResource.GetOCIArtifact() == nil { + return fmt.Errorf("OCI artifact not present") + } + return nil + }, "15s").WithContext(ctx).Should(Succeed()) - snapshotRepository, err := registry.NewRepository(ctx, snapshotResource.Spec.Repository) + ociRepository, err := registry.NewRepository(ctx, configuredResource.GetOCIRepository()) Expect(err).NotTo(HaveOccurred()) - snapshotResourceContent, err := snapshotRepository.FetchArtifact(ctx, snapshotResource.GetDigest()) + resourceContent, err := ociRepository.FetchArtifact(ctx, configuredResource.GetManifestDigest()) Expect(err).NotTo(HaveOccurred()) - dataExtracted, err := compression.ExtractDataFromTGZ(snapshotResourceContent) + dataExtracted, err := compression.ExtractDataFromTGZ(resourceContent) Expect(err).NotTo(HaveOccurred()) Expect(dataExtracted).To(MatchYAML(fileContentAfterConfiguration)) By("delete resources manually") Expect(k8sClient.Delete(ctx, configuredResource)).To(Succeed()) - Expect(k8sClient.Delete(ctx, snapshotResource)).To(Succeed()) Eventually(func(ctx context.Context) bool { err := k8sClient.Get(ctx, client.ObjectKeyFromObject(configuredResource), configuredResource) return errors.IsNotFound(err) @@ -180,14 +182,8 @@ var _ = Describe("ConfiguredResource Controller", func() { }, "15s").WithContext(ctx).Should(BeTrue()) Expect(k8sClient.Delete(ctx, targetResource)).To(Succeed()) - snapshotTargetResource := &v1alpha1.Snapshot{} - Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: targetResource.GetNamespace(), Name: targetResource.GetSnapshotName()}, snapshotTargetResource)).To(Succeed()) - Expect(k8sClient.Delete(ctx, snapshotTargetResource)).To(Succeed()) Expect(k8sClient.Delete(ctx, component)).To(Succeed()) - snapshotComponent := &v1alpha1.Snapshot{} - Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: component.GetNamespace(), Name: component.GetSnapshotName()}, snapshotComponent)).To(Succeed()) - Expect(k8sClient.Delete(ctx, snapshotComponent)).To(Succeed()) }) })