diff --git a/cmd/clusterctl/client/repository/components.go b/cmd/clusterctl/client/repository/components.go index 92efe7a9a1de..807a17e1c720 100644 --- a/cmd/clusterctl/client/repository/components.go +++ b/cmd/clusterctl/client/repository/components.go @@ -42,16 +42,12 @@ const ( clusterRoleKind = "ClusterRole" clusterRoleBindingKind = "ClusterRoleBinding" roleBindingKind = "RoleBinding" + certificateKind = "Certificate" mutatingWebhookConfigurationKind = "MutatingWebhookConfiguration" validatingWebhookConfigurationKind = "ValidatingWebhookConfiguration" customResourceDefinitionKind = "CustomResourceDefinition" ) -const ( - // WebhookNamespaceName is the namespace used to deploy Cluster API webhooks. - WebhookNamespaceName = "capi-webhook-system" -) - // Components wraps a YAML file that defines the provider components // to be installed in a management cluster (CRD, Controller, RBAC etc.) // It is important to notice that clusterctl applies a set of processing steps to the “raw” component YAML read @@ -313,6 +309,8 @@ func fixTargetNamespace(objs []unstructured.Unstructured, targetNamespace string o.SetName(targetNamespace) } + originalNamespace := o.GetNamespace() + // if the object is namespaced, set the namespace name if util.IsResourceNamespaced(o.GetKind()) { o.SetNamespace(targetNamespace) @@ -325,6 +323,14 @@ func fixTargetNamespace(objs []unstructured.Unstructured, targetNamespace string return nil, err } } + + if o.GetKind() == certificateKind { + var err error + o, err = fixCertificate(o, originalNamespace, targetNamespace) + if err != nil { + return nil, err + } + } objs[i] = o } return objs, nil @@ -413,6 +419,7 @@ func fixValidatingWebhookNamespaceReferences(o unstructured.Unstructured, target } return o, errors.Errorf("failed to patch %s ValidatingWebhookConfiguration", version) } + func fixCRDWebhookNamespaceReference(o unstructured.Unstructured, targetNamespace string) (unstructured.Unstructured, error) { version := o.GroupVersionKind().Version switch version { @@ -439,6 +446,33 @@ func fixCRDWebhookNamespaceReference(o unstructured.Unstructured, targetNamespac return o, errors.Errorf("failed to patch %s CustomResourceDefinition", version) } +// fixCertificate fixes the dnsNames of cert-manager Certificates. The DNS names contain the dns names of the provider +// services (including the namespace) and thus have to be modified to use the target namespace instead. +func fixCertificate(o unstructured.Unstructured, originalNamespace, targetNamespace string) (unstructured.Unstructured, error) { + dnsNames, ok, err := unstructured.NestedStringSlice(o.UnstructuredContent(), "spec", "dnsNames") + if err != nil { + return o, errors.Wrapf(err, "failed to get .spec.dnsNames from Certificate %s/%s", o.GetNamespace(), o.GetName()) + } + // Return if we don't find .spec.dnsNames. + if !ok { + return o, nil + } + + // Iterate through dnsNames and adjust the namespace. + // The dnsNames slice usually looks like this: + // - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + // - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + for i, dnsName := range dnsNames { + dnsNames[i] = strings.Replace(dnsName, fmt.Sprintf(".%s.", originalNamespace), fmt.Sprintf(".%s.", targetNamespace), 1) + } + + if err := unstructured.SetNestedStringSlice(o.UnstructuredContent(), dnsNames, "spec", "dnsNames"); err != nil { + return o, errors.Wrapf(err, "failed to set .spec.dnsNames to Certificate %s/%s", o.GetNamespace(), o.GetName()) + } + + return o, nil +} + // fixRBAC ensures all the ClusterRole and ClusterRoleBinding have the name prefixed with the namespace name and that // all the clusterRole/clusterRoleBinding namespaced subjects refers to targetNamespace. func fixRBAC(objs []unstructured.Unstructured, targetNamespace string) ([]unstructured.Unstructured, error) { diff --git a/cmd/clusterctl/client/repository/components_test.go b/cmd/clusterctl/client/repository/components_test.go index 50fc48a5f716..840e835acf10 100644 --- a/cmd/clusterctl/client/repository/components_test.go +++ b/cmd/clusterctl/client/repository/components_test.go @@ -523,6 +523,58 @@ func Test_fixTargetNamespace(t *testing.T) { }, }, }, + { + name: "fix cert-manager Certificate", + objs: []unstructured.Unstructured{ + { + Object: map[string]interface{}{ + "apiVersion": "cert-manager.io/v1", + "kind": "Certificate", + "metadata": map[string]interface{}{ + "name": "capi-serving-cert", + "namespace": "capi-system", + }, + "spec": map[string]interface{}{ + "dnsNames": []interface{}{ + "capi-webhook-service.capi-system.svc", + "capi-webhook-service.capi-system.svc.cluster.local", + "random-other-dns-name", + }, + "issuerRef": map[string]interface{}{ + "kind": "Issuer", + "name": "capi-selfsigned-issuer", + }, + "secretName": "capi-webhook-service-cert", + }, + }, + }, + }, + targetNamespace: "bar", + want: []unstructured.Unstructured{ + { + Object: map[string]interface{}{ + "apiVersion": "cert-manager.io/v1", + "kind": "Certificate", + "metadata": map[string]interface{}{ + "name": "capi-serving-cert", + "namespace": "bar", + }, + "spec": map[string]interface{}{ + "dnsNames": []interface{}{ + "capi-webhook-service.bar.svc", + "capi-webhook-service.bar.svc.cluster.local", + "random-other-dns-name", + }, + "issuerRef": map[string]interface{}{ + "kind": "Issuer", + "name": "capi-selfsigned-issuer", + }, + "secretName": "capi-webhook-service-cert", + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {