diff --git a/api/v1alpha1/conditions_consts.go b/api/v1alpha1/conditions_consts.go index 0835ddf2..60ae8798 100644 --- a/api/v1alpha1/conditions_consts.go +++ b/api/v1alpha1/conditions_consts.go @@ -39,6 +39,6 @@ const ( // CheckLatestUpdateAvailableReason is a reason for a False condition, due to update being available. CheckLatestUpdateAvailableReason = "UpdateAvailable" - // CheckLatestProviderUnknown is a reason for an Unknown condition, due to provider not being available. - CheckLatestProviderUnknown = "ProviderUnknown" + // CheckLatestProviderUnknownReason is a reason for an Unknown condition, due to provider not being available. + CheckLatestProviderUnknownReason = "ProviderUnknown" ) diff --git a/internal/sync/provider_sync.go b/internal/sync/provider_sync.go index 7699984a..616a952b 100644 --- a/internal/sync/provider_sync.go +++ b/internal/sync/provider_sync.go @@ -176,7 +176,7 @@ func (s *ProviderSync) updateLatestVersion(ctx context.Context) error { switch { case !knownProvider: - conditions.MarkUnknown(s.Source, turtlesv1.CheckLatestVersionTime, turtlesv1.CheckLatestProviderUnknown, "Provider is unknown") + conditions.MarkUnknown(s.Source, turtlesv1.CheckLatestVersionTime, turtlesv1.CheckLatestProviderUnknownReason, "Provider is unknown") case s.Source.Spec.Version != "" && latest: conditions.MarkTrue(s.Source, turtlesv1.CheckLatestVersionTime) case s.Source.Spec.Version != "" && !latest: @@ -192,7 +192,7 @@ func (s *ProviderSync) updateLatestVersion(ctx context.Context) error { updatedMessage := fmt.Sprintf("Updated to latest %s version", providerVersion) if lastCheck == nil || lastCheck.Message != updatedMessage { - log.Info(fmt.Sprintf("Version %s is beyound current latest, updated to %s", cmp.Or(s.Source.Spec.Version, "latest"), providerVersion)) + log.Info(fmt.Sprintf("Version %s is beyond current latest, updated to %s", cmp.Or(s.Source.Spec.Version, "latest"), providerVersion)) lastCheck = conditions.TrueCondition(turtlesv1.CheckLatestVersionTime) lastCheck.Message = updatedMessage diff --git a/internal/sync/provider_sync_test.go b/internal/sync/provider_sync_test.go index 9cfd03db..eb2c0343 100644 --- a/internal/sync/provider_sync_test.go +++ b/internal/sync/provider_sync_test.go @@ -17,6 +17,7 @@ limitations under the License. package sync_test import ( + "os" "time" . "github.com/onsi/ginkgo/v2" @@ -40,12 +41,17 @@ var _ = Describe("Provider sync", func() { ns *corev1.Namespace otherNs *corev1.Namespace capiProvider *turtlesv1.CAPIProvider + customCAPIProvider *turtlesv1.CAPIProvider + unknownCAPIProvider *turtlesv1.CAPIProvider capiProviderAzure *turtlesv1.CAPIProvider capiProviderDuplicate *turtlesv1.CAPIProvider infrastructure *operatorv1.InfrastructureProvider + customInfrastructure *operatorv1.InfrastructureProvider + unknownInfrastructure *operatorv1.InfrastructureProvider infrastructureStatusOutdated operatorv1.ProviderStatus infrastructureDuplicate *operatorv1.InfrastructureProvider infrastructureAzure *operatorv1.InfrastructureProvider + clusterctlconfig *turtlesv1.ClusterctlConfig ) BeforeEach(func() { @@ -70,6 +76,14 @@ var _ = Describe("Provider sync", func() { capiProviderAzure.Spec.Name = "azure" capiProviderAzure.Name = "azure" + customCAPIProvider = capiProvider.DeepCopy() + customCAPIProvider.Name = "custom-provider" + customCAPIProvider.Spec.Name = "custom-provider" + + unknownCAPIProvider = capiProvider.DeepCopy() + unknownCAPIProvider.Name = "unknown-provider" + unknownCAPIProvider.Spec.Name = "unknown-provider" + capiProviderDuplicate = capiProvider.DeepCopy() capiProviderDuplicate.Namespace = otherNs.Name @@ -78,6 +92,16 @@ var _ = Describe("Provider sync", func() { Namespace: ns.Name, }} + customInfrastructure = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{ + Name: string(customCAPIProvider.Spec.Name), + Namespace: ns.Name, + }} + + unknownInfrastructure = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{ + Name: string(unknownCAPIProvider.Spec.Name), + Namespace: ns.Name, + }} + infrastructureAzure = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{ Name: string(capiProviderAzure.Spec.Name), Namespace: ns.Name, @@ -104,16 +128,129 @@ var _ = Describe("Provider sync", func() { }, } + clusterctlconfig = &turtlesv1.ClusterctlConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: turtlesv1.ClusterctlConfigName, + Namespace: ns.Name, + }, + Spec: turtlesv1.ClusterctlConfigSpec{ + Providers: turtlesv1.ProviderList{{ + Name: "custom-provider", + URL: "https://github.com/org/repo/releases/v1.2.3/components.yaml", + Type: "InfrastructureProvider", + }}, + }, + } + + os.Setenv("POD_NAMESPACE", ns.Name) + Expect(testEnv.Client.Create(ctx, capiProvider)).To(Succeed()) Expect(testEnv.Client.Create(ctx, capiProviderDuplicate)).To(Succeed()) Expect(testEnv.Client.Create(ctx, capiProviderAzure)).To(Succeed()) + Expect(testEnv.Client.Create(ctx, customCAPIProvider)).To(Succeed()) + Expect(testEnv.Client.Create(ctx, unknownCAPIProvider)).To(Succeed()) + Expect(testEnv.Client.Create(ctx, clusterctlconfig)).To(Succeed()) }) AfterEach(func() { testEnv.Cleanup(ctx, ns, otherNs) }) - It("Should sync spec down", func() { + It("Should sync spec down and set version to latest", func() { + s := sync.NewProviderSync(testEnv, capiProvider.DeepCopy()) + + expected := capiProvider.DeepCopy() + expected.Spec.Version = "v1.7.3" + + Eventually(func(g Gomega) { + g.Expect(s.Get(ctx)).To(Succeed()) + g.Expect(s.Sync(ctx)).To(Succeed()) + var err error = nil + s.Apply(ctx, &err) + g.Expect(err).To(Succeed()) + }).Should(Succeed()) + + Eventually(Object(infrastructure)).Should( + HaveField("Spec.ProviderSpec", Equal(expected.Spec.ProviderSpec))) + }) + + It("Should create unknown provider to clusterctl override with unchanged 'latest' version", func() { + s := sync.NewProviderSync(testEnv, unknownCAPIProvider.DeepCopy()) + + expected := unknownCAPIProvider.DeepCopy() + + Eventually(func(g Gomega) { + g.Expect(s.Get(ctx)).To(Succeed()) + g.Expect(s.Sync(ctx)).To(Succeed()) + var err error = nil + s.Apply(ctx, &err) + g.Expect(err).To(Succeed()) + g.Expect(conditions.IsUnknown(expected, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) + }).Should(Succeed()) + + Eventually(Object(unknownInfrastructure)).Should( + HaveField("Spec.ProviderSpec", Equal(expected.Spec.ProviderSpec))) + }) + + It("Should create unknown provider to clusterctl override with unchanged specific version", func() { + expected := unknownCAPIProvider.DeepCopy() + expected.Spec.Version = "v1.0.0" + s := sync.NewProviderSync(testEnv, expected) + + Eventually(func(g Gomega) { + g.Expect(s.Get(ctx)).To(Succeed()) + g.Expect(s.Sync(ctx)).To(Succeed()) + var err error = nil + s.Apply(ctx, &err) + g.Expect(err).To(Succeed()) + g.Expect(conditions.IsTrue(expected, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) + g.Expect(conditions.IsUnknown(expected, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) + }).Should(Succeed()) + + Eventually(Object(unknownInfrastructure)).Should( + HaveField("Spec.ProviderSpec", Equal(expected.Spec.ProviderSpec))) + }) + + It("Should set custom provider version to latest according to clusterctlconfig override", func() { + s := sync.NewProviderSync(testEnv, customCAPIProvider.DeepCopy()) + + expected := customCAPIProvider.DeepCopy() + expected.Spec.Version = "v1.2.3" + + Eventually(func(g Gomega) { + g.Expect(s.Get(ctx)).To(Succeed()) + g.Expect(s.Sync(ctx)).To(Succeed()) + var err error = nil + s.Apply(ctx, &err) + g.Expect(err).To(Succeed()) + }).Should(Succeed()) + + Eventually(Object(customInfrastructure)).Should( + HaveField("Spec.ProviderSpec", Equal(expected.Spec.ProviderSpec))) + }) + + It("Should not change custom provider version even if it is in the clusterctlconfig override", func() { + expected := customCAPIProvider.DeepCopy() + expected.Spec.Version = "v1.0.0" + s := sync.NewProviderSync(testEnv, expected) + + Eventually(func(g Gomega) { + g.Expect(s.Get(ctx)).To(Succeed()) + g.Expect(s.Sync(ctx)).To(Succeed()) + var err error = nil + s.Apply(ctx, &err) + g.Expect(err).To(Succeed()) + g.Expect(conditions.IsTrue(expected, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue()) + g.Expect(conditions.IsFalse(expected, turtlesv1.CheckLatestVersionTime)).To(BeTrue()) + }).Should(Succeed()) + + Eventually(Object(customInfrastructure)).Should( + HaveField("Spec.ProviderSpec", Equal(expected.Spec.ProviderSpec))) + Consistently(Object(customInfrastructure)).Should( + HaveField("Spec.ProviderSpec", Equal(expected.Spec.ProviderSpec))) + }) + + It("Should sync spec down and set version to latest", func() { s := sync.NewProviderSync(testEnv, capiProvider.DeepCopy()) expected := capiProvider.DeepCopy() diff --git a/internal/test/helpers/envtest.go b/internal/test/helpers/envtest.go index 96250a25..c1a628fe 100644 --- a/internal/test/helpers/envtest.go +++ b/internal/test/helpers/envtest.go @@ -47,6 +47,8 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" logf "sigs.k8s.io/cluster-api/cmd/clusterctl/log" + + turtlesv1 "github.com/rancher/turtles/api/v1alpha1" ) var root string @@ -63,6 +65,7 @@ func init() { utilruntime.Must(apiextensionsv1.AddToScheme(clientgoscheme.Scheme)) utilruntime.Must(admissionv1.AddToScheme(clientgoscheme.Scheme)) utilruntime.Must(clusterv1.AddToScheme(clientgoscheme.Scheme)) + utilruntime.Must(turtlesv1.AddToScheme(clientgoscheme.Scheme)) // Get the root of the current file to use in CRD paths. _, filename, _, _ := goruntime.Caller(0) //nolint @@ -168,6 +171,13 @@ func (t *TestEnvironmentConfiguration) Build() (*TestEnvironment, error) { Metrics: server.Options{ BindAddress: "0", }, + Client: client.Options{ + Cache: &client.CacheOptions{ + DisableFor: []client.Object{ + &turtlesv1.ClusterctlConfig{}, + }, + }, + }, } mgr, err := ctrl.NewManager(t.env.Config, options) diff --git a/main.go b/main.go index b73ce835..c91f8115 100644 --- a/main.go +++ b/main.go @@ -150,6 +150,7 @@ func main() { DisableFor: []client.Object{ &corev1.ConfigMap{}, &corev1.Secret{}, + &turtlesv1.ClusterctlConfig{}, }, }, },