diff --git a/charts/rancher-turtles/templates/azure-rbac.yaml b/charts/rancher-turtles/templates/azure-rbac.yaml new file mode 100644 index 00000000..4cd993c7 --- /dev/null +++ b/charts/rancher-turtles/templates/azure-rbac.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: caprke2-azure-aggregated-role + labels: + cluster.x-k8s.io/aggregate-to-capz-manager: "true" +rules: +- apiGroups: + - bootstrap.cluster.x-k8s.io + resources: + - rke2configs + verbs: + - create + - update + - delete + - get + - list + - patch + - watch \ No newline at end of file diff --git a/charts/rancher-turtles/templates/clusterctl-config.yaml b/charts/rancher-turtles/templates/clusterctl-config.yaml index 2c48e1b8..e930eddd 100644 --- a/charts/rancher-turtles/templates/clusterctl-config.yaml +++ b/charts/rancher-turtles/templates/clusterctl-config.yaml @@ -17,7 +17,7 @@ data: url: "https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/v2.3.5/infrastructure-components.yaml" type: "InfrastructureProvider" - name: "azure" - url: "https://github.com/kubernetes-sigs/cluster-api-provider-azure/releases/v1.13.2/infrastructure-components.yaml" + url: "https://github.com/kubernetes-sigs/cluster-api-provider-azure/releases/v1.16.0/infrastructure-components.yaml" type: "InfrastructureProvider" - name: "docker" url: "https://github.com/kubernetes-sigs/cluster-api/releases/v1.4.6/infrastructure-components-development.yaml" diff --git a/internal/controllers/capiprovider_controller.go b/internal/controllers/capiprovider_controller.go index 2d7c8090..12418c3c 100644 --- a/internal/controllers/capiprovider_controller.go +++ b/internal/controllers/capiprovider_controller.go @@ -17,6 +17,7 @@ limitations under the License. package controllers import ( + "cmp" "context" corev1 "k8s.io/api/core/v1" @@ -69,11 +70,17 @@ func (r *CAPIProviderReconciler) reconcileNormal(ctx context.Context, capiProvid func (r *CAPIProviderReconciler) sync(ctx context.Context, capiProvider *turtlesv1.CAPIProvider) (_ ctrl.Result, err error) { s := sync.NewList( - sync.NewProviderSync(r.Client, capiProvider), sync.NewSecretSync(r.Client, capiProvider), sync.NewSecretMapperSync(ctx, r.Client, capiProvider), ) + switch cmp.Or(capiProvider.Spec.Name, capiProvider.GetName()) { + case "azure": + s = append(s, sync.NewAzureProviderSync(r.Client, capiProvider)) + default: + s = append(s, sync.NewProviderSync(r.Client, capiProvider)) + } + defer r.patchStatus(ctx, capiProvider, &err) if err := s.Sync(ctx); client.IgnoreNotFound(err) != nil { diff --git a/internal/sync/azure_provider_sync.go b/internal/sync/azure_provider_sync.go new file mode 100644 index 00000000..1b85779b --- /dev/null +++ b/internal/sync/azure_provider_sync.go @@ -0,0 +1,74 @@ +/* +Copyright © 2023 - 2024 SUSE LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sync + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha2" + + turtlesv1 "github.com/rancher/turtles/api/v1alpha1" + "github.com/rancher/turtles/internal/api" +) + +// NewAzureProviderSync creates a new mirror object. +func NewAzureProviderSync(cl client.Client, capiProvider *turtlesv1.CAPIProvider) Sync { + template := ProviderSync{}.Template(capiProvider) + + destination, ok := template.(api.Provider) + if !ok || destination == nil { + return nil + } + + spec := capiProvider.GetSpec() + if spec.Deployment == nil { + spec.Deployment = &operatorv1.DeploymentSpec{} + } + + var container *operatorv1.ContainerSpec + + for i := range spec.Deployment.Containers { + if spec.Deployment.Containers[i].Name == "manager" { + container = &spec.Deployment.Containers[i] + break + } + } + + if container != nil { + if len(container.Args) == 0 { + container.Args = map[string]string{ + "--bootstrap-config-gvk": "RKE2Config.v1beta1.bootstrap.cluster.x-k8s.io", + } + } else if _, found := container.Args["--bootstrap-config-gvk"]; !found { + container.Args["--bootstrap-config-gvk"] = "RKE2Config.v1beta1.bootstrap.cluster.x-k8s.io" + } + } else { + spec.Deployment.Containers = append(spec.Deployment.Containers, operatorv1.ContainerSpec{ + Name: "manager", + Args: map[string]string{ + "--bootstrap-config-gvk": "RKE2Config.v1beta1.bootstrap.cluster.x-k8s.io", + }, + }) + } + + capiProvider.SetSpec(spec) + + return &ProviderSync{ + DefaultSynchronizer: NewDefaultSynchronizer(cl, capiProvider, template), + Destination: destination, + } +} diff --git a/internal/sync/provider_sync_test.go b/internal/sync/provider_sync_test.go index 237e0cdf..0bf9876c 100644 --- a/internal/sync/provider_sync_test.go +++ b/internal/sync/provider_sync_test.go @@ -40,10 +40,12 @@ var _ = Describe("Provider sync", func() { ns *corev1.Namespace otherNs *corev1.Namespace capiProvider *turtlesv1.CAPIProvider + capiProviderAzure *turtlesv1.CAPIProvider capiProviderDuplicate *turtlesv1.CAPIProvider infrastructure *operatorv1.InfrastructureProvider infrastructureStatusOutdated operatorv1.ProviderStatus infrastructureDuplicate *operatorv1.InfrastructureProvider + infrastructureAzure *operatorv1.InfrastructureProvider ) BeforeEach(func() { @@ -64,6 +66,10 @@ var _ = Describe("Provider sync", func() { Type: turtlesv1.Infrastructure, }} + capiProviderAzure = capiProvider.DeepCopy() + capiProviderAzure.Spec.Name = "azure" + capiProviderAzure.Name = "azure" + capiProviderDuplicate = capiProvider.DeepCopy() capiProviderDuplicate.Namespace = otherNs.Name @@ -72,6 +78,11 @@ var _ = Describe("Provider sync", func() { Namespace: ns.Name, }} + infrastructureAzure = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{ + Name: string(capiProviderAzure.Spec.Name), + Namespace: ns.Name, + }} + infrastructureDuplicate = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{ Name: string(capiProvider.Spec.Name), Namespace: otherNs.Name, @@ -87,6 +98,7 @@ var _ = Describe("Provider sync", func() { Expect(testEnv.Client.Create(ctx, capiProvider)).To(Succeed()) Expect(testEnv.Client.Create(ctx, capiProviderDuplicate)).To(Succeed()) + Expect(testEnv.Client.Create(ctx, capiProviderAzure)).To(Succeed()) }) AfterEach(func() { @@ -107,6 +119,29 @@ var _ = Describe("Provider sync", func() { HaveField("Spec.ProviderSpec", Equal(capiProvider.Spec.ProviderSpec))) }) + It("Should sync azure spec", func() { + s := sync.NewAzureProviderSync(testEnv, capiProviderAzure) + + Eventually(s.Get(ctx)).Should(Succeed()) + Expect(s.Sync(ctx)).To(Succeed()) + + var err error + s.Apply(ctx, &err) + Expect(err).To(Succeed()) + + capiProviderAzure.Spec.Deployment = &operatorv1.DeploymentSpec{ + Containers: []operatorv1.ContainerSpec{{ + Name: "manager", + Args: map[string]string{ + "--bootstrap-config-gvk": "RKE2Config.v1beta1.bootstrap.cluster.x-k8s.io", + }, + }}, + } + + Eventually(Object(infrastructureAzure)).Should( + HaveField("Spec.ProviderSpec", Equal(capiProviderAzure.Spec.ProviderSpec))) + }) + It("Should sync status up and set provisioning state", func() { Expect(testEnv.Client.Create(ctx, infrastructure.DeepCopy())).To(Succeed()) Eventually(UpdateStatus(infrastructure, func() {