From 5da51667056a2eafc6a3eddf52faa1e33bca1fad Mon Sep 17 00:00:00 2001 From: Danil-Grigorev Date: Mon, 26 Aug 2024 13:30:09 +0200 Subject: [PATCH] Make latest check optional behind followLatest field Signed-off-by: Danil-Grigorev --- api/v1alpha1/capiprovider_types.go | 6 ++ .../templates/addon-provider-fleet.yaml | 1 + .../templates/core-provider.yaml | 1 + .../templates/rke2-bootstrap.yaml | 1 + .../templates/rke2-controlplane.yaml | 1 + .../turtles-capi.cattle.io_capiproviders.yaml | 6 ++ internal/sync/provider_sync.go | 2 +- internal/sync/provider_sync_test.go | 84 ++++++++++++------- 8 files changed, 70 insertions(+), 32 deletions(-) diff --git a/api/v1alpha1/capiprovider_types.go b/api/v1alpha1/capiprovider_types.go index 8896fadd..46aa74bf 100644 --- a/api/v1alpha1/capiprovider_types.go +++ b/api/v1alpha1/capiprovider_types.go @@ -60,6 +60,12 @@ type CAPIProviderSpec struct { // +kubebuilder:example={CLUSTER_TOPOLOGY:"true",EXP_CLUSTER_RESOURCE_SET:"true",EXP_MACHINE_POOL: "true"} Variables map[string]string `json:"variables,omitempty"` + // FollowLatest allows to track current latest version, and update provider accordingly to keep + // manifests in sync with new releases. Maximum version of the released components is limited by the provider + // override in the clusterctl.yaml data within clusterctl-config `ConfigMap`, or the fetchConfig.url. + // +optional + FollowLatest bool `json:"followLatest,omitempty"` + // ProviderSpec is the spec of the underlying CAPI Provider resource. operatorv1.ProviderSpec `json:",inline"` } diff --git a/charts/rancher-turtles/templates/addon-provider-fleet.yaml b/charts/rancher-turtles/templates/addon-provider-fleet.yaml index 211fcd7e..15bac8b7 100644 --- a/charts/rancher-turtles/templates/addon-provider-fleet.yaml +++ b/charts/rancher-turtles/templates/addon-provider-fleet.yaml @@ -10,6 +10,7 @@ metadata: "helm.sh/hook-weight": "2" spec: type: addon + followLatest: true additionalManifests: name: fleet-addon-config namespace: '{{ .Values.rancherTurtles.namespace }}' diff --git a/charts/rancher-turtles/templates/core-provider.yaml b/charts/rancher-turtles/templates/core-provider.yaml index fdb1eee5..5292631e 100644 --- a/charts/rancher-turtles/templates/core-provider.yaml +++ b/charts/rancher-turtles/templates/core-provider.yaml @@ -21,6 +21,7 @@ metadata: "helm.sh/hook-weight": "2" spec: name: cluster-api + followLatest: true type: core version: {{ index .Values "cluster-api-operator" "cluster-api" "version" }} additionalManifests: diff --git a/charts/rancher-turtles/templates/rke2-bootstrap.yaml b/charts/rancher-turtles/templates/rke2-bootstrap.yaml index 69b5f89f..3405e70c 100644 --- a/charts/rancher-turtles/templates/rke2-bootstrap.yaml +++ b/charts/rancher-turtles/templates/rke2-bootstrap.yaml @@ -22,6 +22,7 @@ metadata: spec: name: rke2 type: bootstrap + followLatest: true {{- if index .Values "cluster-api-operator" "cluster-api" "rke2" "version" }} version: {{ index .Values "cluster-api-operator" "cluster-api" "rke2" "version" }} {{- end }} diff --git a/charts/rancher-turtles/templates/rke2-controlplane.yaml b/charts/rancher-turtles/templates/rke2-controlplane.yaml index d9c6b0fa..8ca72da7 100644 --- a/charts/rancher-turtles/templates/rke2-controlplane.yaml +++ b/charts/rancher-turtles/templates/rke2-controlplane.yaml @@ -22,6 +22,7 @@ metadata: spec: name: rke2 type: controlPlane + followLatest: true {{- if index .Values "cluster-api-operator" "cluster-api" "rke2" "version" }} version: {{ index .Values "cluster-api-operator" "cluster-api" "rke2" "version" }} {{- end }} diff --git a/config/crd/bases/turtles-capi.cattle.io_capiproviders.yaml b/config/crd/bases/turtles-capi.cattle.io_capiproviders.yaml index c0d44cc8..eb337489 100644 --- a/config/crd/bases/turtles-capi.cattle.io_capiproviders.yaml +++ b/config/crd/bases/turtles-capi.cattle.io_capiproviders.yaml @@ -2769,6 +2769,12 @@ spec: desired version of the release from GitHub. type: string type: object + followLatest: + description: |- + FollowLatest allows to track current latest version, and update provider accordingly to keep + manifests in sync with new releases. Maximum version of the released components is limited by the provider + override in the clusterctl.yaml data within clusterctl-config `ConfigMap`, or the fetchConfig.url. + type: boolean manager: description: Manager defines the properties that can be enabled on the controller manager for the provider. diff --git a/internal/sync/provider_sync.go b/internal/sync/provider_sync.go index dad56cd1..7f7e834f 100644 --- a/internal/sync/provider_sync.go +++ b/internal/sync/provider_sync.go @@ -153,7 +153,7 @@ func (s *ProviderSync) rolloutInfrastructure() { func (s *ProviderSync) updateLatestVersion(ctx context.Context) error { // Skip for user specified versions - if s.Source.Spec.Version != "" { + if s.Source.Spec.Version != "" || !s.Source.Spec.FollowLatest { return nil } diff --git a/internal/sync/provider_sync_test.go b/internal/sync/provider_sync_test.go index 7b7ca647..3888957c 100644 --- a/internal/sync/provider_sync_test.go +++ b/internal/sync/provider_sync_test.go @@ -40,9 +40,11 @@ var _ = Describe("Provider sync", func() { ns *corev1.Namespace otherNs *corev1.Namespace capiProvider *turtlesv1.CAPIProvider + capiProviderFollowLatest *turtlesv1.CAPIProvider capiProviderAzure *turtlesv1.CAPIProvider capiProviderDuplicate *turtlesv1.CAPIProvider infrastructure *operatorv1.InfrastructureProvider + infrastructureFollowLatest *operatorv1.InfrastructureProvider infrastructureStatusOutdated operatorv1.ProviderStatus infrastructureDuplicate *operatorv1.InfrastructureProvider infrastructureAzure *operatorv1.InfrastructureProvider @@ -73,6 +75,10 @@ var _ = Describe("Provider sync", func() { capiProviderDuplicate = capiProvider.DeepCopy() capiProviderDuplicate.Namespace = otherNs.Name + capiProviderFollowLatest = capiProvider.DeepCopy() + capiProviderFollowLatest.Name = "latest" + capiProviderFollowLatest.Spec.FollowLatest = true + infrastructure = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{ Name: string(capiProvider.Spec.Name), Namespace: ns.Name, @@ -88,6 +94,11 @@ var _ = Describe("Provider sync", func() { Namespace: otherNs.Name, }} + infrastructureFollowLatest = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{ + Name: string(capiProviderFollowLatest.Spec.Name), + Namespace: otherNs.Name, + }} + infrastructureStatusOutdated = operatorv1.ProviderStatus{ Conditions: clusterv1.Conditions{ { @@ -103,6 +114,7 @@ var _ = Describe("Provider sync", func() { }, } + Expect(testEnv.Client.Create(ctx, capiProviderFollowLatest)).To(Succeed()) Expect(testEnv.Client.Create(ctx, capiProvider)).To(Succeed()) Expect(testEnv.Client.Create(ctx, capiProviderDuplicate)).To(Succeed()) Expect(testEnv.Client.Create(ctx, capiProviderAzure)).To(Succeed()) @@ -169,26 +181,25 @@ var _ = Describe("Provider sync", func() { g.Expect(s.Sync(ctx)).To(Succeed()) s.Apply(ctx, &err) g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) - g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) - g.Expect(capiProvider.Status.Conditions).To(HaveLen(2)) + g.Expect(capiProvider.Status.Conditions).To(HaveLen(1)) g.Expect(capiProvider).To(HaveField("Status.Phase", Equal(turtlesv1.Provisioning))) }).Should(Succeed()) }) It("Should update outdated condition, maintain last applied time and empty the hash annotation", func() { - capiProvider.Status.ProviderStatus = infrastructureStatusOutdated + capiProviderFollowLatest.Status.ProviderStatus = infrastructureStatusOutdated - appliedCondition := conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime) - lastVersionCheckCondition := conditions.Get(capiProvider, turtlesv1.CheckLatestVersionTime) + appliedCondition := conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime) + lastVersionCheckCondition := conditions.Get(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime) - Eventually(testEnv.Status().Update(ctx, capiProvider)).Should(Succeed()) + Eventually(testEnv.Status().Update(ctx, capiProviderFollowLatest)).Should(Succeed()) Eventually(func(g Gomega) { - g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProvider), capiProvider)).To(Succeed()) - g.Expect(conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime)).ToNot(BeNil()) - g.Expect(conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.Second()).To(Equal(appliedCondition.LastTransitionTime.Second())) + g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProviderFollowLatest), capiProviderFollowLatest)).To(Succeed()) + g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)).ToNot(BeNil()) + g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.Second()).To(Equal(appliedCondition.LastTransitionTime.Second())) }).Should(Succeed()) - s := sync.NewProviderSync(testEnv, capiProvider) + s := sync.NewProviderSync(testEnv, capiProviderFollowLatest) dest := &operatorv1.InfrastructureProvider{} Eventually(func(g Gomega) { @@ -200,48 +211,61 @@ var _ = Describe("Provider sync", func() { g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructure), dest)).To(Succeed()) g.Expect(dest.GetAnnotations()).To(HaveKeyWithValue(sync.AppliedSpecHashAnnotation, "")) g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructure), dest)).To(Succeed()) - g.Expect(capiProvider.Status.Conditions).To(HaveLen(2)) - g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) - g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) - g.Expect(conditions.Get(capiProvider, turtlesv1.CheckLatestVersionTime).LastTransitionTime.Equal( + g.Expect(capiProviderFollowLatest.Status.Conditions).To(HaveLen(2)) + g.Expect(conditions.IsTrue(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) + g.Expect(conditions.IsTrue(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) + g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime).LastTransitionTime.Equal( &lastVersionCheckCondition.LastTransitionTime)).To(BeTrue()) - g.Expect(conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.After( + g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.After( appliedCondition.LastTransitionTime.Time)).To(BeTrue()) }).Should(Succeed()) - Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProvider), capiProvider)).To(Succeed()) - condition := conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime) - lastVersionCheckCondition = conditions.Get(capiProvider, turtlesv1.CheckLatestVersionTime) + Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProviderFollowLatest), capiProviderFollowLatest)).To(Succeed()) + condition := conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime) + lastVersionCheckCondition = conditions.Get(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime) Consistently(func(g Gomega) { err = nil g.Expect(s.Get(ctx)).To(Succeed()) g.Expect(s.Sync(ctx)).To(Succeed()) s.Apply(ctx, &err) - g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProvider), capiProvider)).To(Succeed()) - g.Expect(conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(Equal(condition)) - g.Expect(conditions.Get(capiProvider, turtlesv1.CheckLatestVersionTime)).To(Equal(lastVersionCheckCondition)) + g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProviderFollowLatest), capiProviderFollowLatest)).To(Succeed()) + g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)).To(Equal(condition)) + g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime)).To(Equal(lastVersionCheckCondition)) }, 5*time.Second).Should(Succeed()) }) It("Should set the last applied version check condition and empty the version field", func() { - s := sync.NewProviderSync(testEnv, capiProvider) + s := sync.NewProviderSync(testEnv, capiProviderFollowLatest) - infrastructure.Spec.Version = "v1.2.3" + infrastructureFollowLatest.Spec.Version = "v1.2.3" - Expect(testEnv.Create(ctx, infrastructure)).To(Succeed()) + Expect(testEnv.Create(ctx, infrastructureFollowLatest)).To(Succeed()) Eventually(func(g Gomega) { err = nil g.Expect(s.Get(ctx)).To(Succeed()) g.Expect(s.Sync(ctx)).To(Succeed()) s.Apply(ctx, &err) - g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) - g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) - g.Expect(capiProvider.Status.Conditions).To(HaveLen(2)) + g.Expect(conditions.IsTrue(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) + g.Expect(conditions.IsTrue(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) + g.Expect(capiProviderFollowLatest.Status.Conditions).To(HaveLen(2)) }, 5*time.Second).Should(Succeed()) }) + It("Should not set the last applied version check, if the FollowLatest is not set", func() { + s := sync.NewProviderSync(testEnv, capiProvider) + + Consistently(func(g Gomega) { + err = nil + g.Expect(s.Get(ctx)).To(Succeed()) + g.Expect(s.Sync(ctx)).To(Succeed()) + s.Apply(ctx, &err) + g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) + g.Expect(capiProvider.Status.Conditions).To(HaveLen(1)) + }).Should(Succeed()) + }) + It("Should individually sync every provider", func() { Expect(testEnv.Client.Create(ctx, infrastructure.DeepCopy())).To(Succeed()) Eventually(UpdateStatus(infrastructure, func() { @@ -261,9 +285,8 @@ var _ = Describe("Provider sync", func() { g.Expect(s.Sync(ctx)).To(Succeed()) s.Apply(ctx, &err) g.Expect(err).ToNot(HaveOccurred()) - g.Expect(capiProvider.Status.Conditions).To(HaveLen(2)) + g.Expect(capiProvider.Status.Conditions).To(HaveLen(1)) g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) - g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructure), dest)).To(Succeed()) }).Should(Succeed()) @@ -278,9 +301,8 @@ var _ = Describe("Provider sync", func() { g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructureDuplicate), dest)).To(Succeed()) g.Expect(dest.GetAnnotations()).To(HaveKeyWithValue(sync.AppliedSpecHashAnnotation, "")) - g.Expect(capiProviderDuplicate.Status.Conditions).To(HaveLen(2)) + g.Expect(capiProviderDuplicate.Status.Conditions).To(HaveLen(1)) g.Expect(conditions.IsTrue(capiProviderDuplicate, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) - g.Expect(conditions.IsTrue(capiProviderDuplicate, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) g.Expect(conditions.Get(capiProviderDuplicate, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.Minute()).To(Equal(time.Now().UTC().Minute())) }).Should(Succeed())