// Copyright 2022 Google 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 main import ( "context" "flag" "fmt" "io/ioutil" "log" "os" "path" "regexp" "strings" "sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/addon/pkg/loaders" "github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/k8s" "github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/test/util/paths" "github.com/GoogleCloudPlatform/k8s-config-connector/operator/scripts/utils" ) const ( dirMode = os.FileMode(0755) //drwxr-x--- fileMode = os.FileMode(0644) // -rw-r--r-- gcsPathTmpl = "gs://cnrm/%v/release-bundle.tar.gz" baseDir = "scripts/update-kcc-manifest" channelDir = "channels/packages/configconnector" managerPatch = "manager_sidecar_patch.yaml" recorderPatch = "recorder_sidecar_patch.yaml" finalizerPatch = "finalizer_patch.yaml" autopilotChannelDir = "autopilot-channels/packages/configconnector" autopilotRecorderPatch = "recorder_remove_hostport_patch.yaml" ) var ( version string ) // Download the latest KCC manifest, kustomize and upload it to the stable channel // Usage: go run scripts/update-kcc-manifest/main.go --version latest // For debug/testing you can run // `go run scripts/update-kcc-manifest/main.go --version local` // This will attempt to read a release-bundle.tar.gz from root generated by // `./operator/scripts/release.sh -d` func main() { ctx := context.TODO() flag.StringVar(&version, "version", "latest", "Version of the KCC core to download.") flag.Parse() // download the KCC manifest operatorSrcRoot := paths.GetOperatorSrcRootOrLogFatal() log.Printf("Operator source root is set to %s.\r\n", operatorSrcRoot) outputDir := path.Join(operatorSrcRoot, baseDir, "kcc") if err := os.Mkdir(outputDir, dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir %v: %v", outputDir, err) } if version == "local" { tarballPath := path.Join("..", "release-bundle.tar.gz") log.Printf("Extracting bundle resource from local %s to %s.\r\n", tarballPath, outputDir) if err := utils.ExtractTarball(tarballPath, outputDir); err != nil { log.Fatalf("error extracting tarball: %v", err) } } else { gcsPath := fmt.Sprintf(gcsPathTmpl, version) log.Printf("GCS Path is set to %s.\r\n", gcsPath) if err := utils.DownloadAndExtractTarballAt(gcsPath, outputDir); err != nil { log.Fatalf("error downloading and extracting the tarball %v: %v", gcsPath, err) } } kustomizeBuild(operatorSrcRoot) // swap container registry wiSystemManifest := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-workload-identity", "0-cnrm-system.yaml") gcpSystemManifest := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-gcp-identity", "0-cnrm-system.yaml") namespacedSystemManifest := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-namespaced", "0-cnrm-system.yaml") autopilotWiSystemManifest := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-autopilot-workload-identity", "0-cnrm-system.yaml") autopilotGcpSystemManifest := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-autopilot-gcp-identity", "0-cnrm-system.yaml") autopilotNamespacedSystemManifest := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-autopilot-namespaced", "0-cnrm-system.yaml") pnc := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-namespaced", "per-namespace-components.yaml") manifests := []string{wiSystemManifest, gcpSystemManifest, namespacedSystemManifest, pnc} for _, manifest := range manifests { if err := swapContainerRegistry(manifest); err != nil { log.Fatalf("error swapping container registry: %v", err) } } autopilotPnc := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-autopilot-namespaced", "per-namespace-components.yaml") manifests = []string{autopilotWiSystemManifest, autopilotGcpSystemManifest, autopilotNamespacedSystemManifest, autopilotPnc} for _, manifest := range manifests { if err := swapContainerRegistry(manifest); err != nil { log.Fatalf("error swapping container registry: %v", err) } } // upload the new manifest under channels dir manifestFile := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-namespaced", "0-cnrm-system.yaml") version, err := extractVersionFromManifest(manifestFile) if err != nil { log.Fatalf("error extracting version from manifest %v: %v", manifestFile, err) } manifestDir := path.Join(operatorSrcRoot, channelDir, version) if err := os.Mkdir(manifestDir, dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir %v: %v", manifestDir, err) } autopilotManifestDir := path.Join(operatorSrcRoot, autopilotChannelDir, version) if err := os.Mkdir(autopilotManifestDir, dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir %v: %v", autopilotManifestDir, err) } // copy crds.yaml crds := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-namespaced", "crds.yaml") destCRDs := path.Join(manifestDir, "crds.yaml") if err := utils.Copy(crds, destCRDs); err != nil { log.Fatalf("error copying %v to %v: %v", crds, destCRDs, err) } destCRDs = path.Join(autopilotManifestDir, "crds.yaml") if err := utils.Copy(crds, destCRDs); err != nil { log.Fatalf("error copying %v to %v: %v", crds, destCRDs, err) } // create the cluster dir if err := os.Mkdir(path.Join(manifestDir, "cluster"), dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir: %v", err) } if err := os.Mkdir(path.Join(autopilotManifestDir, "cluster"), dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir: %v", err) } // copy install-bundle-workload-identity/0-cnrm-system.yaml if err := os.Mkdir(path.Join(manifestDir, "cluster", "workload-identity"), dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir: %v", err) } destWiSystemManifest := path.Join(manifestDir, "cluster", "workload-identity", "0-cnrm-system.yaml") if err := utils.Copy(wiSystemManifest, destWiSystemManifest); err != nil { log.Fatalf("error copying %v to %v: %v", wiSystemManifest, destWiSystemManifest, err) } if err := os.Mkdir(path.Join(autopilotManifestDir, "cluster", "workload-identity"), dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir: %v", err) } destWiSystemManifest = path.Join(autopilotManifestDir, "cluster", "workload-identity", "0-cnrm-system.yaml") if err := utils.Copy(autopilotWiSystemManifest, destWiSystemManifest); err != nil { log.Fatalf("error copying %v to %v: %v", wiSystemManifest, destWiSystemManifest, err) } // copy install-bundle-gcp-identity/0-cnrm-system.yaml if err := os.Mkdir(path.Join(manifestDir, "cluster", "gcp-identity"), dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir: %v", err) } destGcpSystemManifest := path.Join(manifestDir, "cluster", "gcp-identity", "0-cnrm-system.yaml") if err := utils.Copy(gcpSystemManifest, destGcpSystemManifest); err != nil { log.Fatalf("error copying %v to %v: %v", wiSystemManifest, destWiSystemManifest, err) } if err := os.Mkdir(path.Join(autopilotManifestDir, "cluster", "gcp-identity"), dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir: %v", err) } destGcpSystemManifest = path.Join(autopilotManifestDir, "cluster", "gcp-identity", "0-cnrm-system.yaml") if err := utils.Copy(autopilotGcpSystemManifest, destGcpSystemManifest); err != nil { log.Fatalf("error copying %v to %v: %v", wiSystemManifest, destWiSystemManifest, err) } // copy install-bundle-namespaced/0-cnrm-system.yaml and install-bundle-namespaced/per-namespace-components.yaml namespacedDir := path.Join(manifestDir, "namespaced") if err := os.Mkdir(namespacedDir, dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir %v: %v", namespacedDir, err) } destNamespacedSystemManifest := path.Join(manifestDir, "namespaced", "0-cnrm-system.yaml") if err := utils.Copy(namespacedSystemManifest, destNamespacedSystemManifest); err != nil { log.Fatalf("error copying %v to %v: %v", namespacedSystemManifest, destNamespacedSystemManifest, err) } destPnc := path.Join(manifestDir, "namespaced", "per-namespace-components.yaml") if err := utils.Copy(pnc, destPnc); err != nil { log.Fatalf("error copying %v to %v: %v", pnc, destPnc, err) } namespacedDir = path.Join(autopilotManifestDir, "namespaced") if err := os.Mkdir(namespacedDir, dirMode); err != nil && !os.IsExist(err) { log.Fatalf("error creating dir %v: %v", namespacedDir, err) } destNamespacedSystemManifest = path.Join(autopilotManifestDir, "namespaced", "0-cnrm-system.yaml") if err := utils.Copy(autopilotNamespacedSystemManifest, destNamespacedSystemManifest); err != nil { log.Fatalf("error copying %v to %v: %v", autopilotNamespacedSystemManifest, destNamespacedSystemManifest, err) } destPnc = path.Join(autopilotManifestDir, "namespaced", "per-namespace-components.yaml") if err := utils.Copy(autopilotPnc, destPnc); err != nil { log.Fatalf("error copying %v to %v: %v", autopilotPnc, destPnc, err) } if err := os.RemoveAll(outputDir); err != nil { log.Fatalf("error deleting dir %v: %v", outputDir, err) } // update the operator version for default kustomization kustomizationFilePath := path.Join(operatorSrcRoot, "config", "default", "kustomization.yaml") b, err := ioutil.ReadFile(kustomizationFilePath) if err != nil { log.Fatalf("error reading %v: %v", kustomizationFilePath, err) } kustomization := string(b) m := regexp.MustCompile("cnrm.cloud.google.com/operator-version: (\".*\")") kustomization = m.ReplaceAllString(kustomization, fmt.Sprintf("cnrm.cloud.google.com/operator-version: \"%v\"", version)) if err := ioutil.WriteFile(kustomizationFilePath, []byte(kustomization), fileMode); err != nil { log.Fatalf("error updating file %v", kustomizationFilePath) } log.Printf("successfully updated the version annotation in %v for default kustomization\n", kustomizationFilePath) // update the operator version for autopilot kustomization kustomizationFilePath = path.Join(operatorSrcRoot, "config", "autopilot", "kustomization.yaml") b, err = ioutil.ReadFile(kustomizationFilePath) if err != nil { log.Fatalf("error reading %v: %v", kustomizationFilePath, err) } kustomization = string(b) m = regexp.MustCompile("cnrm.cloud.google.com/operator-version: (\".*\")") kustomization = m.ReplaceAllString(kustomization, fmt.Sprintf("cnrm.cloud.google.com/operator-version: \"%v\"", version)) if err := ioutil.WriteFile(kustomizationFilePath, []byte(kustomization), fileMode); err != nil { log.Fatalf("error updating file %v", kustomizationFilePath) } log.Printf("successfully updated the version annotation in %v for autopilot kustomization\n", kustomizationFilePath) //remove the stale manifest r := loaders.NewFSRepository(path.Join(operatorSrcRoot, loaders.FlagChannel)) channel, err := r.LoadChannel(ctx, k8s.StableChannel) if err != nil { log.Fatalf("error loading %v channel: %v", k8s.StableChannel, err) } currentVersion, err := channel.Latest(ctx, "configconnector") if err != nil { log.Fatalf("error resolving the current version: %v", err) } if currentVersion.Version == version { log.Printf("the current KCC version is the same as the latest version %v\n", version) return } stableFilePath := path.Join(operatorSrcRoot, "channels", "stable") b, err = ioutil.ReadFile(stableFilePath) if err != nil { log.Fatalf("error reading %v: %v", stableFilePath, err) } stable := string(b) stable = strings.ReplaceAll(stable, fmt.Sprintf("- version: %v", currentVersion.Version), fmt.Sprintf("- version: %v", version)) if err := ioutil.WriteFile(stableFilePath, []byte(stable), fileMode); err != nil { log.Fatalf("error updating file %v", stableFilePath) } stableFilePath = path.Join(operatorSrcRoot, "autopilot-channels", "stable") b, err = ioutil.ReadFile(stableFilePath) if err != nil { log.Fatalf("error reading %v: %v", stableFilePath, err) } stable = string(b) stable = strings.ReplaceAll(stable, fmt.Sprintf("- version: %v", currentVersion.Version), fmt.Sprintf("- version: %v", version)) if err := ioutil.WriteFile(stableFilePath, []byte(stable), fileMode); err != nil { log.Fatalf("error updating file %v", stableFilePath) } staleManifestDir := path.Join(operatorSrcRoot, "channels", "packages", "configconnector", currentVersion.Version) log.Printf("removing stale manifest %v", staleManifestDir) if err := os.RemoveAll(staleManifestDir); err != nil { log.Fatalf("error deleting dir %v: %v", staleManifestDir, err) } staleManifestDir = path.Join(operatorSrcRoot, "autopilot-channels", "packages", "configconnector", currentVersion.Version) log.Printf("removing stale manifest %v", staleManifestDir) if err := os.RemoveAll(staleManifestDir); err != nil { log.Fatalf("error deleting dir %v: %v", staleManifestDir, err) } } func kustomizeBuild(operatorSrcRoot string) { // workload-identity cluster mode buildPath := path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-workload-identity") if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, "kustomizations", "kustomization_workload-identity.yaml"), path.Join(buildPath, "kustomization.yaml")); err != nil { log.Fatalf("error copying kustomization: %v", err) } if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, managerPatch), path.Join(buildPath, managerPatch)); err != nil { log.Fatalf("error copying %v: %v", managerPatch, err) } if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, recorderPatch), path.Join(buildPath, recorderPatch)); err != nil { log.Fatalf("error copying %v: %v", recorderPatch, err) } output := path.Join(buildPath, "0-cnrm-system.yaml") if err := utils.KustomizeBuild(buildPath, output); err != nil { log.Fatalf("error running kustomize build: %v", err) } // autopilot workload-identity cluster mode buildPath = path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-autopilot-workload-identity") if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, "kustomizations", "kustomization_autopilot_workload-identity.yaml"), path.Join(buildPath, "kustomization.yaml")); err != nil { log.Fatalf("error copying kustomization: %v", err) } if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, autopilotRecorderPatch), path.Join(buildPath, autopilotRecorderPatch)); err != nil { log.Fatalf("error copying %v: %v", autopilotRecorderPatch, err) } output = path.Join(buildPath, "0-cnrm-system.yaml") if err := utils.KustomizeBuild(buildPath, output); err != nil { log.Fatalf("error running kustomize build: %v", err) } // autopilot gcp-identity cluster mode buildPath = path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-autopilot-gcp-identity") if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, "kustomizations", "kustomization_autopilot_gcp-identity.yaml"), path.Join(buildPath, "kustomization.yaml")); err != nil { log.Fatalf("error copying kustomization: %v", err) } if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, autopilotRecorderPatch), path.Join(buildPath, autopilotRecorderPatch)); err != nil { log.Fatalf("error copying %v: %v", autopilotRecorderPatch, err) } output = path.Join(buildPath, "0-cnrm-system.yaml") if err := utils.KustomizeBuild(buildPath, output); err != nil { log.Fatalf("error running kustomize build: %v", err) } // namespaced mode buildPath = path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-namespaced") buildNamespacedMode(operatorSrcRoot, buildPath, output, false) // autpilot namespaced mode buildPath = path.Join(operatorSrcRoot, baseDir, "kcc", "install-bundle-autopilot-namespaced") buildNamespacedMode(operatorSrcRoot, buildPath, output, true) } func buildNamespacedMode(operatorSrcRoot, buildPath, output string, autopilot bool) { if !autopilot { if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, managerPatch), path.Join(buildPath, managerPatch)); err != nil { log.Fatalf("error copying %v: %v", managerPatch, err) } if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, recorderPatch), path.Join(buildPath, recorderPatch)); err != nil { log.Fatalf("error copying %v: %v", recorderPatch, err) } } else { if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, autopilotRecorderPatch), path.Join(buildPath, autopilotRecorderPatch)); err != nil { log.Fatalf("error copying %v: %v", autopilotRecorderPatch, err) } } if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, finalizerPatch), path.Join(buildPath, finalizerPatch)); err != nil { log.Fatalf("error copying %v: %v", finalizerPatch, err) } if autopilot { if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, "kustomizations", "kustomization_autopilot_namespaced_0-cnrm-system.yaml"), path.Join(buildPath, "kustomization.yaml")); err != nil { log.Fatalf("error copying kustomization: %v", err) } } else { if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, "kustomizations", "kustomization_namespaced_0-cnrm-system.yaml"), path.Join(buildPath, "kustomization.yaml")); err != nil { log.Fatalf("error copying kustomization: %v", err) } } output = path.Join(buildPath, "0-cnrm-system.yaml") if err := utils.KustomizeBuild(buildPath, output); err != nil { log.Fatalf("error running kustomize build: %v", err) } if autopilot { if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, "kustomizations", "kustomization_autopilot_namespaced_per-namespace-components.yaml"), path.Join(buildPath, "kustomization.yaml")); err != nil { log.Fatalf("error copying kustomization: %v", err) } } else { if err := utils.Copy(path.Join(operatorSrcRoot, baseDir, "kustomizations", "kustomization_namespaced_per-namespace-components.yaml"), path.Join(buildPath, "kustomization.yaml")); err != nil { log.Fatalf("error copying kustomization: %v", err) } } output = path.Join(buildPath, "per-namespace-components.yaml") if err := utils.KustomizeBuild(buildPath, output); err != nil { log.Fatalf("error running kustomize build: %v", err) } } // This step can be removed once we switch KCC core to also use gcr.io/gke-release container registry func swapContainerRegistry(manifestPath string) error { content, err := ioutil.ReadFile(manifestPath) if err != nil { return fmt.Errorf("error reading manifestPath: %v", err) } manifest := string(content) updatedManifest := strings.ReplaceAll(manifest, "gcr.io/cnrm-eap/", "gcr.io/gke-release/cnrm/") fileMode := os.FileMode(0644) // -rw-r--r-- return ioutil.WriteFile(manifestPath, []byte(updatedManifest), fileMode) } func extractVersionFromManifest(filePath string) (string, error) { objs, err := utils.ReadFileToUnstructs(filePath) if err != nil { return "", fmt.Errorf("error reading file %v and converting to unstructs: %v", filePath, err) } for _, obj := range objs { if obj.GetKind() == "Namespace" && obj.GetName() == k8s.CNRMSystemNamespace { for key, val := range obj.GetAnnotations() { if key == k8s.VersionAnnotation { return val, nil } } } } return "", fmt.Errorf("couldn't extract the version from the manifest %v", filePath) }