diff --git a/charts/karmada/_crds/patches/webhook_in_clusterresourcebindings.yaml b/charts/karmada/_crds/patches/webhook_in_clusterresourcebindings.yaml index 0cbf4d43e..9b5a7d81f 100644 --- a/charts/karmada/_crds/patches/webhook_in_clusterresourcebindings.yaml +++ b/charts/karmada/_crds/patches/webhook_in_clusterresourcebindings.yaml @@ -9,6 +9,6 @@ spec: strategy: Webhook webhook: clientConfig: - url: https://karmada-webhook.karmada-system.svc:443/convert + url: "https://{{name}}.{{namespace}}.svc:443/convert" caBundle: "{{caBundle}}" conversionReviewVersions: ["v1"] diff --git a/charts/karmada/_crds/patches/webhook_in_resourcebindings.yaml b/charts/karmada/_crds/patches/webhook_in_resourcebindings.yaml index 0dd0f8937..ce48fd825 100644 --- a/charts/karmada/_crds/patches/webhook_in_resourcebindings.yaml +++ b/charts/karmada/_crds/patches/webhook_in_resourcebindings.yaml @@ -9,6 +9,6 @@ spec: strategy: Webhook webhook: clientConfig: - url: https://karmada-webhook.karmada-system.svc:443/convert + url: "https://{{name}}.{{namespace}}.svc:443/convert" caBundle: "{{caBundle}}" conversionReviewVersions: ["v1"] diff --git a/hack/deploy-karmada.sh b/hack/deploy-karmada.sh index e8c5ca40c..5607f54c6 100755 --- a/hack/deploy-karmada.sh +++ b/hack/deploy-karmada.sh @@ -302,7 +302,11 @@ TEMP_PATH_CRDS=$(mktemp -d) trap '{ rm -rf ${TEMP_PATH_CRDS}; }' EXIT cp -rf "${REPO_ROOT}"/charts/karmada/_crds "${TEMP_PATH_CRDS}" util::fill_cabundle "${ROOT_CA_FILE}" "${TEMP_PATH_CRDS}/_crds/patches/webhook_in_resourcebindings.yaml" +sed -i'' -e "s/{{name}}/karmada-webhook/g" "${TEMP_PATH_CRDS}/_crds/patches/webhook_in_resourcebindings.yaml" +sed -i'' -e "s/{{namespace}}/karmada-system/g" "${TEMP_PATH_CRDS}/_crds/patches/webhook_in_resourcebindings.yaml" util::fill_cabundle "${ROOT_CA_FILE}" "${TEMP_PATH_CRDS}/_crds/patches/webhook_in_clusterresourcebindings.yaml" +sed -i'' -e "s/{{name}}/karmada-webhook/g" "${TEMP_PATH_CRDS}/_crds/patches/webhook_in_clusterresourcebindings.yaml" +sed -i'' -e "s/{{namespace}}/karmada-system/g" "${TEMP_PATH_CRDS}/_crds/patches/webhook_in_clusterresourcebindings.yaml" installCRDs "karmada-apiserver" "${TEMP_PATH_CRDS}" # render the caBundle in these apiservice with root ca, then karmada-apiserver can use caBundle to verify corresponding AA's server-cert diff --git a/operator/pkg/tasks/init/karmadaresource.go b/operator/pkg/tasks/init/karmadaresource.go index f0c6dd5e0..11339e508 100644 --- a/operator/pkg/tasks/init/karmadaresource.go +++ b/operator/pkg/tasks/init/karmadaresource.go @@ -122,7 +122,7 @@ func runCrds(r workflow.RunData) error { } caBase64 := base64.StdEncoding.EncodeToString(cert.CertData()) - if err := patchCrds(crdsClient, crdsPatchPath, caBase64); err != nil { + if err := patchCrds(data, crdsClient, crdsPatchPath, caBase64); err != nil { return fmt.Errorf("failed to patch karmada crds, err: %w", err) } @@ -149,14 +149,28 @@ func createCrds(crdsClient *crdsclient.Clientset, crdsPath string) error { return nil } -func patchCrds(crdsClient *crdsclient.Clientset, patchPath string, caBundle string) error { +func patchCrds(data InitData, crdsClient *crdsclient.Clientset, patchPath string, caBundle string) error { for _, file := range util.ListFileWithSuffix(patchPath, ".yaml") { - reg, err := regexp.Compile("{{caBundle}}") + caBundleReg, err := regexp.Compile("{{caBundle}}") if err != nil { return err } - crdBytes, err := util.ReplaceYamlForReg(file.AbsPath, caBundle, reg) + nameReg, err := regexp.Compile("{{name}}") + if err != nil { + return err + } + + namespaceReg, err := regexp.Compile("{{namespace}}") + if err != nil { + return err + } + + crdBytes, err := util.ReplaceYamlForRegs(file.AbsPath, map[*regexp.Regexp]string{ + caBundleReg: caBundle, + nameReg: util.KarmadaWebhookName(data.GetName()), + namespaceReg: data.GetNamespace(), + }) if err != nil { return err } diff --git a/operator/pkg/util/util.go b/operator/pkg/util/util.go index aa7205e6f..eb2658a09 100644 --- a/operator/pkg/util/util.go +++ b/operator/pkg/util/util.go @@ -230,15 +230,19 @@ func ReadYamlFile(path string) ([]byte, error) { return yaml.YAMLToJSON(data) } -// ReplaceYamlForReg replace content of yaml file with a Regexp -func ReplaceYamlForReg(path, destResource string, reg *regexp.Regexp) ([]byte, error) { +// ReplaceYamlForRegs replace content of yaml file with Regexps +func ReplaceYamlForRegs(path string, replacements map[*regexp.Regexp]string) ([]byte, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } - repl := reg.ReplaceAllString(string(data), destResource) - return yaml.YAMLToJSON([]byte(repl)) + src := string(data) + for reg, dest := range replacements { + src = reg.ReplaceAllString(src, dest) + } + + return yaml.YAMLToJSON([]byte(src)) } // ContainAllTasks checks if all tasks in the subset are present in the tasks slice. diff --git a/operator/pkg/util/util_test.go b/operator/pkg/util/util_test.go index a9fe592cc..b1512c1ad 100644 --- a/operator/pkg/util/util_test.go +++ b/operator/pkg/util/util_test.go @@ -26,11 +26,14 @@ import ( "net/http" "os" "path/filepath" + "regexp" "strings" "testing" "time" + "github.com/stretchr/testify/assert" "k8s.io/utils/ptr" + "sigs.k8s.io/yaml" ) // mockReader is a simple io.Reader that returns an error after being called. @@ -383,3 +386,84 @@ func verifyValidTarGzipped(tarFile, regularFile string, targetPath *string) erro return nil } + +func TestReplaceYamlForRegs(t *testing.T) { + tests := []struct { + name string + content string + replacements map[*regexp.Regexp]string + expectedContent string + }{ + { + name: "simple replacement", + content: ` + url: "https://{{name}}.{{namespace}}.svc:443/convert" + caBundle: "{{caBundle}}" +`, + replacements: map[*regexp.Regexp]string{ + regexp.MustCompile("{{caBundle}}"): "testCaBundle", + regexp.MustCompile("{{name}}"): "testName", + regexp.MustCompile("{{namespace}}"): "testNamespace", + }, + expectedContent: ` + url: "https://testName.testNamespace.svc:443/convert" + caBundle: "testCaBundle" +`, + }, + { + name: "partial replacement", + content: ` + url: "https://{{name}}.{{namespace}}.svc:443/convert" + caBundle: "{{caBundle}}" +`, + replacements: map[*regexp.Regexp]string{ + regexp.MustCompile("{{caBundle}}"): "testCaBundle", + regexp.MustCompile("{{namespace}}"): "testNamespace", + }, + expectedContent: ` + url: "https://{{name}}.testNamespace.svc:443/convert" + caBundle: "testCaBundle" +`, + }, + { + name: "redundant replacement", + content: ` + url: "https://{{name}}.{{namespace}}.svc:443/convert" + caBundle: "{{caBundle}}" +`, + replacements: map[*regexp.Regexp]string{ + regexp.MustCompile("{{caBundle}}"): "testCaBundle", + regexp.MustCompile("{{name}}"): "testName", + regexp.MustCompile("{{namespace}}"): "testNamespace", + regexp.MustCompile("{{foo}}"): "foo", + }, + expectedContent: ` + url: "https://testName.testNamespace.svc:443/convert" + caBundle: "testCaBundle" +`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpFile, err := os.CreateTemp("", "example") + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + defer os.Remove(tmpFile.Name()) + + if _, err := tmpFile.Write([]byte(tt.content)); err != nil { + t.Fatalf("failed to write temp file: %v", err) + } + if err := tmpFile.Close(); err != nil { + t.Fatalf("failed to close temp file: %v", err) + } + + result, err := ReplaceYamlForRegs(tmpFile.Name(), tt.replacements) + expectedJSON, expectedErr := yaml.YAMLToJSON([]byte(tt.expectedContent)) + + assert.Equal(t, result, expectedJSON) + assert.Equal(t, err, expectedErr) + }) + } +} diff --git a/pkg/karmadactl/cmdinit/karmada/deploy.go b/pkg/karmadactl/cmdinit/karmada/deploy.go index 987fdc4f7..508dbd91c 100644 --- a/pkg/karmadactl/cmdinit/karmada/deploy.go +++ b/pkg/karmadactl/cmdinit/karmada/deploy.go @@ -98,7 +98,7 @@ func InitKarmadaResources(dir, caBase64, systemNamespace string) error { if path.Ext(v) != ".yaml" { continue } - if err := patchCRDs(crdClient, caBase64, v); err != nil { + if err := patchCRDs(crdClient, caBase64, systemNamespace, v); err != nil { return err } } @@ -173,7 +173,7 @@ func createExtraResources(clientSet *kubernetes.Clientset, dir string) error { return nil } -func crdPatchesResources(filename, caBundle string) ([]byte, error) { +func crdPatchesResources(filename, caBundle, systemNamespace string) ([]byte, error) { data, err := os.ReadFile(filename) if err != nil { return nil, err @@ -185,6 +185,18 @@ func crdPatchesResources(filename, caBundle string) ([]byte, error) { } repl := re.ReplaceAllString(string(data), caBundle) + re, err = regexp.Compile("{{name}}") + if err != nil { + return nil, err + } + repl = re.ReplaceAllString(repl, names.KarmadaWebhookComponentName) + + re, err = regexp.Compile("{{namespace}}") + if err != nil { + return nil, err + } + repl = re.ReplaceAllString(repl, systemNamespace) + return []byte(repl), nil } @@ -220,8 +232,8 @@ func createCRDs(crdClient clientset.Interface, filename string) error { } // patchCRDs patch crd resource -func patchCRDs(crdClient clientset.Interface, caBundle, filename string) error { - data, err := crdPatchesResources(filename, caBundle) +func patchCRDs(crdClient clientset.Interface, caBundle, systemNamespace, filename string) error { + data, err := crdPatchesResources(filename, caBundle, systemNamespace) if err != nil { return err } diff --git a/pkg/karmadactl/cmdinit/karmada/deploy_test.go b/pkg/karmadactl/cmdinit/karmada/deploy_test.go new file mode 100644 index 000000000..87488d927 --- /dev/null +++ b/pkg/karmadactl/cmdinit/karmada/deploy_test.go @@ -0,0 +1,68 @@ +/* +Copyright 2025 The Karmada Authors. + +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 karmada + +import ( + "os" + "testing" + + "github.com/karmada-io/karmada/pkg/util/names" +) + +func TestCrdPatchesResources(t *testing.T) { + tests := []struct { + name string + content string + caBundle string + systemNs string + expectedContent string + }{ + { + name: "simple replacement", + content: "caBundle: {{caBundle}}\nname: {{name}}\nnamespace: {{namespace}}", + caBundle: "testCaBundle", + systemNs: "testNamespace", + expectedContent: "caBundle: testCaBundle\nname: " + names.KarmadaWebhookComponentName + "\nnamespace: testNamespace", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpFile, err := os.CreateTemp("", "example") + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + defer os.Remove(tmpFile.Name()) + + if _, err := tmpFile.Write([]byte(tt.content)); err != nil { + t.Fatalf("failed to write temp file: %v", err) + } + if err := tmpFile.Close(); err != nil { + t.Fatalf("failed to close temp file: %v", err) + } + + result, err := crdPatchesResources(tmpFile.Name(), tt.caBundle, tt.systemNs) + if err != nil { + t.Errorf("crdPatchesResources() error = %v", err) + } + + if string(result) != tt.expectedContent { + t.Errorf("crdPatchesResources() = %v, want %v", string(result), tt.expectedContent) + } + }) + } +}