From ab1a0d93fccb5fe9288ea508ad4380aef758bf5c Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Fri, 23 Aug 2024 10:59:09 +0800 Subject: [PATCH 01/19] utils to replace image --- .../apps/transformer_component_workload.go | 4 + go.mod | 2 +- go.sum | 2 + pkg/constant/viper_config.go | 2 + pkg/controllerutil/image_util.go | 150 ++++++++++++++++++ pkg/controllerutil/image_util_test.go | 65 ++++++++ pkg/viperx/viperx.go | 12 ++ 7 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 pkg/controllerutil/image_util.go create mode 100644 pkg/controllerutil/image_util_test.go diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index a039d4d6228..02500fdb463 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -145,6 +145,10 @@ func (t *componentWorkloadTransformer) reconcileWorkload(synthesizedComp *compon protoITS.Spec.Template.Labels = intctrlutil.MergeMetadataMaps(runningITS.Spec.Template.Labels, synthesizedComp.UserDefinedLabels) } + if runningITS == nil { + // + } + buildInstanceSetPlacementAnnotation(comp, protoITS) // build configuration template annotations to workload diff --git a/go.mod b/go.mod index ca7f8b9eb6c..57e3c62ed34 100644 --- a/go.mod +++ b/go.mod @@ -102,7 +102,7 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/distribution/reference v0.5.0 // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v25.0.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.0 // indirect diff --git a/go.sum b/go.sum index d68c0ea6b5c..1e2f3e3e8e2 100644 --- a/go.sum +++ b/go.sum @@ -192,6 +192,8 @@ github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aB github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU= github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= diff --git a/pkg/constant/viper_config.go b/pkg/constant/viper_config.go index d3b7fe6e1ab..a99fd6ccb3f 100644 --- a/pkg/constant/viper_config.go +++ b/pkg/constant/viper_config.go @@ -52,4 +52,6 @@ const ( CfgKBReconcileWorkers = "KUBEBLOCKS_RECONCILE_WORKERS" CfgClientQPS = "CLIENT_QPS" CfgClientBurst = "CLIENT_BURST" + + CfgRegistries = "registries" ) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go new file mode 100644 index 00000000000..c3d3c684feb --- /dev/null +++ b/pkg/controllerutil/image_util.go @@ -0,0 +1,150 @@ +package controllerutil + +import ( + // Import the crypto sha256 algorithm for the docker image parser to work + _ "crypto/sha256" + "fmt" + "strings" + + // Import the crypto/sha512 algorithm for the docker image parser to work with 384 and 512 sha hashes + _ "crypto/sha512" + + "github.com/distribution/reference" + + "github.com/apecloud/kubeblocks/pkg/constant" + viper "github.com/apecloud/kubeblocks/pkg/viperx" +) + +// Quote from https://docs.docker.com/reference/cli/docker/image/tag/ +// > While the OCI Distribution Specification supports more than two slash-separated components, +// > most registries only support two slash-separated components. +// > For Docker's public registry, the path format is as follows: +// > [NAMESPACE/]REPOSITORY: The first, optional component is typically a user's or an organization's +// namespace. The second, mandatory component is the repository name. When the namespace is +// not present, Docker uses `library` as the default namespace. +// +// So if there are more than two components, specify them both, or they won't be matched. +type registryNamespaceConfig struct { + From string + To string +} + +type registryConfig struct { + From string + To string + Namespaces []registryNamespaceConfig +} + +type registriesConfig struct { + DefaultRegistry string + DefaultNamespace string + Registries []registryConfig +} + +var registriesConf = registriesConfig{} + +func init() { + viper.UnmarshalKey(constant.CfgRegistries, ®istriesConf) + + // TODO: validate + for _, registry := range registriesConf.Registries { + if len(registry.From) == 0 { + panic("from can't be empty") + } + + for _, namespace := range registry.Namespaces { + if len(namespace.From) == 0 || len(namespace.To) == 0 { + panic("namespace can't be empty") + } + } + } +} + +// For a detailed explaination of an image's format, see: +// https://kubernetes.io/docs/concepts/containers/images/ + +// if registry is omitted, the default (docker hub) will be added. +// if namespace is omiited when using docker hub, the default namespace (library) will be added. +func parseImageName(image string) ( + host string, namespace string, repository string, remainder string, err error, +) { + named, err := reference.ParseNormalizedNamed(image) + if err != nil { + return + } + + tagged, ok := named.(reference.Tagged) + if ok { + remainder += ":" + tagged.Tag() + } + + digested, ok := named.(reference.Digested) + if ok { + remainder += "@" + digested.Digest().String() + } + + host = reference.Domain(named) + + pathSplit := strings.Split(reference.Path(named), "/") + if len(pathSplit) > 1 { + namespace = strings.Join(pathSplit[:len(pathSplit)-1], "/") + } + repository = pathSplit[len(pathSplit)-1] + + return +} + +func ReplaceImageRegistry(image string) (string, error) { + registry, namespace, repository, remainder, err := parseImageName(image) + if err != nil { + return "", err + } + + chooseRegistry := func() string { + if registriesConf.DefaultRegistry != "" { + return registriesConf.DefaultRegistry + } else { + return registry + } + } + + chooseNamespace := func() string { + if registriesConf.DefaultNamespace != "" { + return registriesConf.DefaultNamespace + } else { + return namespace + } + } + + var dstRegistry, dstNamespace string + for _, registryMapping := range registriesConf.Registries { + if registryMapping.From == registry { + if len(registryMapping.To) != 0 { + dstRegistry = registryMapping.To + } else { + dstRegistry = chooseRegistry() + } + + for _, namespaceConf := range registryMapping.Namespaces { + if namespace == namespaceConf.From { + dstNamespace = namespaceConf.To + break + } + } + + if dstNamespace == "" { + dstNamespace = namespace + } + + break + } + } + + // no match in registriesConf.Registries + if dstRegistry == "" { + dstRegistry = chooseRegistry() + dstNamespace = chooseNamespace() + } + + return fmt.Sprintf("%v/%v/%v%v", dstRegistry, dstNamespace, repository, remainder), nil +} diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go new file mode 100644 index 00000000000..14d62e17cf4 --- /dev/null +++ b/pkg/controllerutil/image_util_test.go @@ -0,0 +1,65 @@ +package controllerutil + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = FDescribe("image util test", func() { + imageList := [][]string{ + []string{"busybox", "docker.io", "library", "busybox", ""}, + []string{"apecloud/busybox:1.28", "docker.io", "apecloud", "busybox", ":1.28"}, + []string{"foo.io/a/b/busybox", "foo.io", "a/b", "busybox", ""}, + []string{ + "registry.k8s.io/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07", + "registry.k8s.io", "", "pause", ":latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07"}, + } + + AfterEach(func() { + // reset config + registriesConf = registriesConfig{} + }) + + It("parses image", func() { + for _, image := range imageList { + host, ns, repository, remainder, err := parseImageName(image[0]) + // fmt.Println(host, ns, repository, remainder) + Expect(err).NotTo(HaveOccurred()) + Expect(host).To(Equal(image[1])) + Expect(ns).To(Equal(image[2])) + Expect(repository).To(Equal(image[3])) + Expect(remainder).To(Equal(image[4])) + } + }) + + It("only expands image when config does not exist", func() { + for _, image := range imageList { + newImage, err := ReplaceImageRegistry(image[0]) + Expect(err).NotTo(HaveOccurred()) + Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", image[1], image[2], image[3], image[4]))) + } + }) + + It("replaces image when default config exists", func() { + registriesConf = registriesConfig{ + DefaultRegistry: "foo.bar", + } + for _, image := range imageList { + newImage, err := ReplaceImageRegistry(image[0]) + Expect(err).NotTo(HaveOccurred()) + Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", "foo.bar", image[2], image[3], image[4]))) + } + + registriesConf = registriesConfig{ + DefaultRegistry: "foo.bar", + DefaultNamespace: "test", + } + for _, image := range imageList { + newImage, err := ReplaceImageRegistry(image[0]) + Expect(err).NotTo(HaveOccurred()) + Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", "foo.bar", "test", image[3], image[4]))) + } + }) +}) diff --git a/pkg/viperx/viperx.go b/pkg/viperx/viperx.go index 620fc2ae4ba..113bc60582e 100644 --- a/pkg/viperx/viperx.go +++ b/pkg/viperx/viperx.go @@ -67,6 +67,18 @@ func GetDuration(key string) time.Duration { return rCall(key, viper.GetDuration) } +func Unmarshal(rawVal interface{}, opts ...viper.DecoderConfigOption) error { + lock.RLock() + defer lock.RUnlock() + return viper.Unmarshal(rawVal, opts...) +} + +func UnmarshalKey(key string, rawVal interface{}, opts ...viper.DecoderConfigOption) error { + lock.RLock() + defer lock.RUnlock() + return viper.UnmarshalKey(key, rawVal, opts...) +} + func AllSettings() map[string]interface{} { lock.RLock() defer lock.RUnlock() From 91df060458f74033437c9c1015764bf707283223 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Fri, 23 Aug 2024 17:51:54 +0800 Subject: [PATCH 02/19] add tests --- controllers/apps/component_controller_test.go | 88 +++++++++++++++++++ .../apps/transformer_component_workload.go | 11 ++- pkg/controllerutil/image_util.go | 35 +++++--- pkg/controllerutil/image_util_test.go | 55 ++++++++++-- 4 files changed, 168 insertions(+), 21 deletions(-) diff --git a/controllers/apps/component_controller_test.go b/controllers/apps/component_controller_test.go index 03015985cca..6b9f19fa876 100644 --- a/controllers/apps/component_controller_test.go +++ b/controllers/apps/component_controller_test.go @@ -2371,6 +2371,94 @@ var _ = Describe("Component Controller", func() { testImageUnchangedAfterNewReleasePublished(release) }) }) + + FContext("with registry replace enabled", func() { + registry := "foo.bar" + setRegistryConfig := func() { + viper.Set(constant.CfgRegistries, intctrlutil.RegistriesConfig{ + DefaultRegistry: registry, + }) + intctrlutil.ReloadRegistryConfig() + } + + BeforeEach(func() { + createAllDefinitionObjects() + }) + + AfterEach(func() { + viper.Set(constant.CfgRegistries, nil) + intctrlutil.ReloadRegistryConfig() + }) + + It("replaces image registry", func() { + setRegistryConfig() + + createClusterObj(defaultCompName, compDefName, nil) + + itsKey := compKey + Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) { + // check the image + c := its.Spec.Template.Spec.Containers[0] + g.Expect(c.Image).To(HavePrefix(registry)) + })).Should(Succeed()) + }) + + It("handles running its and upgrade", func() { + createClusterObj(defaultCompName, compDefName, nil) + itsKey := compKey + Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) { + // check the image + c := its.Spec.Template.Spec.Containers[0] + g.Expect(c.Image).To(Equal(compVerObj.Spec.Releases[0].Images[c.Name])) + })).Should(Succeed()) + + setRegistryConfig() + By("trigger component reconcile") + now := time.Now().Format(time.RFC3339) + Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *appsv1alpha1.Component) { + comp.Annotations["now"] = now + })()).Should(Succeed()) + + Consistently(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) { + // check the image + c := its.Spec.Template.Spec.Containers[0] + g.Expect(c.Image).NotTo(HavePrefix(registry)) + })).Should(Succeed()) + + By("replaces registry when upgrading") + release := appsv1alpha1.ComponentVersionRelease{ + Name: "8.0.31", + ServiceVersion: "8.0.31", + Images: map[string]string{ + testapps.DefaultMySQLContainerName: "docker.io/apecloud/mysql:8.0.31", + }, + } + + By("publish a new release") + compVerKey := client.ObjectKeyFromObject(compVerObj) + Expect(testapps.GetAndChangeObj(&testCtx, compVerKey, func(compVer *appsv1alpha1.ComponentVersion) { + compVer.Spec.Releases = append(compVer.Spec.Releases, release) + compVer.Spec.CompatibilityRules[0].Releases = append(compVer.Spec.CompatibilityRules[0].Releases, release.Name) + })()).Should(Succeed()) + + By("update serviceversion in cluster") + Expect(testapps.GetAndChangeObj(&testCtx, clusterKey, func(cluster *appsv1alpha1.Cluster) { + cluster.Spec.ComponentSpecs[0].ServiceVersion = "8.0.31" + })()).Should(Succeed()) + + By("trigger component reconcile") + now = time.Now().Format(time.RFC3339) + Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *appsv1alpha1.Component) { + comp.Annotations["now"] = now + })()).Should(Succeed()) + + Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) { + // check the image + c := its.Spec.Template.Spec.Containers[0] + g.Expect(c.Image).To(HavePrefix(registry)) + })).Should(Succeed()) + }) + }) }) func mockRestoreCompleted(ml client.MatchingLabels) { diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index 02500fdb463..ea4a5f640bb 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -145,8 +145,15 @@ func (t *componentWorkloadTransformer) reconcileWorkload(synthesizedComp *compon protoITS.Spec.Template.Labels = intctrlutil.MergeMetadataMaps(runningITS.Spec.Template.Labels, synthesizedComp.UserDefinedLabels) } - if runningITS == nil { - // + // if runningITS already exists, the image changes in protoITS will be + // rollback to the original image in `checkNRollbackProtoImages`. + // So changing registry configs won't affect existing clusters. + for i, container := range protoITS.Spec.Template.Spec.Containers { + newImage, err := intctrlutil.ReplaceImageRegistry(container.Image) + if err != nil { + return err + } + protoITS.Spec.Template.Spec.Containers[i].Image = newImage } buildInstanceSetPlacementAnnotation(comp, protoITS) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index c3d3c684feb..c514e657a15 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -24,30 +24,36 @@ import ( // not present, Docker uses `library` as the default namespace. // // So if there are more than two components, specify them both, or they won't be matched. -type registryNamespaceConfig struct { +type RegistryNamespaceConfig struct { From string To string } -type registryConfig struct { +type RegistryConfig struct { From string To string - Namespaces []registryNamespaceConfig + Namespaces []RegistryNamespaceConfig } -type registriesConfig struct { +type RegistriesConfig struct { DefaultRegistry string DefaultNamespace string - Registries []registryConfig + Registries []RegistryConfig } -var registriesConf = registriesConfig{} +var registriesConfig = RegistriesConfig{} func init() { - viper.UnmarshalKey(constant.CfgRegistries, ®istriesConf) + ReloadRegistryConfig() +} + +// TODO: this is needed in componnet controller test, is there a better way? +func ReloadRegistryConfig() { + registriesConfig = RegistriesConfig{} + viper.UnmarshalKey(constant.CfgRegistries, ®istriesConfig) // TODO: validate - for _, registry := range registriesConf.Registries { + for _, registry := range registriesConfig.Registries { if len(registry.From) == 0 { panic("from can't be empty") } @@ -101,23 +107,23 @@ func ReplaceImageRegistry(image string) (string, error) { } chooseRegistry := func() string { - if registriesConf.DefaultRegistry != "" { - return registriesConf.DefaultRegistry + if registriesConfig.DefaultRegistry != "" { + return registriesConfig.DefaultRegistry } else { return registry } } chooseNamespace := func() string { - if registriesConf.DefaultNamespace != "" { - return registriesConf.DefaultNamespace + if registriesConfig.DefaultNamespace != "" { + return registriesConfig.DefaultNamespace } else { return namespace } } var dstRegistry, dstNamespace string - for _, registryMapping := range registriesConf.Registries { + for _, registryMapping := range registriesConfig.Registries { if registryMapping.From == registry { if len(registryMapping.To) != 0 { dstRegistry = registryMapping.To @@ -146,5 +152,8 @@ func ReplaceImageRegistry(image string) (string, error) { dstNamespace = chooseNamespace() } + if dstNamespace == "" { + return fmt.Sprintf("%v/%v%v", dstRegistry, repository, remainder), nil + } return fmt.Sprintf("%v/%v/%v%v", dstRegistry, dstNamespace, repository, remainder), nil } diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go index 14d62e17cf4..6dc20495625 100644 --- a/pkg/controllerutil/image_util_test.go +++ b/pkg/controllerutil/image_util_test.go @@ -7,7 +7,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = FDescribe("image util test", func() { +var _ = Describe("image util test", func() { imageList := [][]string{ []string{"busybox", "docker.io", "library", "busybox", ""}, []string{"apecloud/busybox:1.28", "docker.io", "apecloud", "busybox", ":1.28"}, @@ -19,7 +19,7 @@ var _ = FDescribe("image util test", func() { AfterEach(func() { // reset config - registriesConf = registriesConfig{} + registriesConfig = RegistriesConfig{} }) It("parses image", func() { @@ -32,27 +32,38 @@ var _ = FDescribe("image util test", func() { Expect(repository).To(Equal(image[3])) Expect(remainder).To(Equal(image[4])) } + + _, _, _, _, err := parseImageName("/invalid") + Expect(err).To(HaveOccurred()) }) It("only expands image when config does not exist", func() { for _, image := range imageList { newImage, err := ReplaceImageRegistry(image[0]) Expect(err).NotTo(HaveOccurred()) - Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", image[1], image[2], image[3], image[4]))) + if image[2] == "" { + Expect(newImage).To(Equal(fmt.Sprintf("%v/%v%v", image[1], image[3], image[4]))) + } else { + Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", image[1], image[2], image[3], image[4]))) + } } }) It("replaces image when default config exists", func() { - registriesConf = registriesConfig{ + registriesConfig = RegistriesConfig{ DefaultRegistry: "foo.bar", } for _, image := range imageList { newImage, err := ReplaceImageRegistry(image[0]) Expect(err).NotTo(HaveOccurred()) - Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", "foo.bar", image[2], image[3], image[4]))) + if image[2] == "" { + Expect(newImage).To(Equal(fmt.Sprintf("%v/%v%v", "foo.bar", image[3], image[4]))) + } else { + Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", "foo.bar", image[2], image[3], image[4]))) + } } - registriesConf = registriesConfig{ + registriesConfig = RegistriesConfig{ DefaultRegistry: "foo.bar", DefaultNamespace: "test", } @@ -62,4 +73,36 @@ var _ = FDescribe("image util test", func() { Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", "foo.bar", "test", image[3], image[4]))) } }) + + It("replaces image when single registry config exists", func() { + registriesConfig = RegistriesConfig{ + DefaultRegistry: "foo.bar", + Registries: []RegistryConfig{ + { + From: "docker.io", + To: "bar.io", + Namespaces: []RegistryNamespaceConfig{ + {From: "library", To: "foo"}, + }, + }, + { + From: "foo.io", + Namespaces: []RegistryNamespaceConfig{ + {From: "a/b", To: "foo"}, + }, + }, + }, + } + expectedImage := []string{ + "bar.io/foo/busybox", + "bar.io/apecloud/busybox:1.28", + "foo.bar/foo/busybox", + "foo.bar/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07", + } + for i, image := range imageList { + newImage, err := ReplaceImageRegistry(image[0]) + Expect(err).NotTo(HaveOccurred()) + Expect(newImage).To(Equal(expectedImage[i])) + } + }) }) From 008e87d632ce77be2c9f08a4444166f5e261a486 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Fri, 23 Aug 2024 18:03:12 +0800 Subject: [PATCH 03/19] fix: license header --- controllers/apps/component_controller_test.go | 2 +- go.mod | 2 +- go.sum | 2 -- pkg/controllerutil/image_util.go | 19 +++++++++++++++++++ pkg/controllerutil/image_util_test.go | 19 +++++++++++++++++++ 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/controllers/apps/component_controller_test.go b/controllers/apps/component_controller_test.go index 6b9f19fa876..aa2005130e0 100644 --- a/controllers/apps/component_controller_test.go +++ b/controllers/apps/component_controller_test.go @@ -2372,7 +2372,7 @@ var _ = Describe("Component Controller", func() { }) }) - FContext("with registry replace enabled", func() { + Context("with registry replace enabled", func() { registry := "foo.bar" setRegistryConfig := func() { viper.Set(constant.CfgRegistries, intctrlutil.RegistriesConfig{ diff --git a/go.mod b/go.mod index 57e3c62ed34..51914f6c372 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/bhmj/jsonslice v1.1.2 github.com/clbanning/mxj/v2 v2.5.7 github.com/containers/common v0.55.4 + github.com/distribution/reference v0.6.0 github.com/docker/docker v25.0.6+incompatible github.com/evanphx/json-patch v5.6.0+incompatible github.com/fasthttp/router v1.4.20 @@ -102,7 +103,6 @@ require ( github.com/containerd/log v0.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v25.0.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.0 // indirect diff --git a/go.sum b/go.sum index 1e2f3e3e8e2..726405d7f3c 100644 --- a/go.sum +++ b/go.sum @@ -190,8 +190,6 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= -github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= -github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU= diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index c514e657a15..ac877caa45b 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -1,3 +1,22 @@ +/* +Copyright (C) 2022-2024 ApeCloud Co., Ltd + +This file is part of KubeBlocks project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + package controllerutil import ( diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go index 6dc20495625..fab3f38f2cb 100644 --- a/pkg/controllerutil/image_util_test.go +++ b/pkg/controllerutil/image_util_test.go @@ -1,3 +1,22 @@ +/* +Copyright (C) 2022-2024 ApeCloud Co., Ltd + +This file is part of KubeBlocks project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + package controllerutil import ( From 4ac55aa5d7a1841c96bdba6afb3dd7cf44915b07 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Fri, 23 Aug 2024 19:28:31 +0800 Subject: [PATCH 04/19] fix test --- controllers/apps/suite_test.go | 2 +- pkg/controllerutil/image_util.go | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/controllers/apps/suite_test.go b/controllers/apps/suite_test.go index 2d7b04cca72..d5ac76fcdba 100644 --- a/controllers/apps/suite_test.go +++ b/controllers/apps/suite_test.go @@ -169,7 +169,7 @@ var _ = BeforeSuite(func() { Expect(err).ToNot(HaveOccurred()) viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs") - viper.SetDefault(constant.KBToolsImage, "apecloud/kubeblocks-tools:latest") + viper.SetDefault(constant.KBToolsImage, "docker.io/apecloud/kubeblocks-tools:latest") viper.SetDefault("PROBE_SERVICE_PORT", 3501) viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info") viper.SetDefault("CM_NAMESPACE", "default") diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index ac877caa45b..d2934333feb 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -34,6 +34,8 @@ import ( viper "github.com/apecloud/kubeblocks/pkg/viperx" ) +// RegistryNamespaceConfig maps registry namespaces. +// // Quote from https://docs.docker.com/reference/cli/docker/image/tag/ // > While the OCI Distribution Specification supports more than two slash-separated components, // > most registries only support two slash-separated components. @@ -66,10 +68,12 @@ func init() { ReloadRegistryConfig() } -// TODO: this is needed in componnet controller test, is there a better way? func ReloadRegistryConfig() { + // TODO: this is needed in componnet controller test, is there a better way? registriesConfig = RegistriesConfig{} - viper.UnmarshalKey(constant.CfgRegistries, ®istriesConfig) + if err := viper.UnmarshalKey(constant.CfgRegistries, ®istriesConfig); err != nil { + panic(err) + } // TODO: validate for _, registry := range registriesConfig.Registries { @@ -85,8 +89,8 @@ func ReloadRegistryConfig() { } } -// For a detailed explaination of an image's format, see: -// https://kubernetes.io/docs/concepts/containers/images/ +// For a detailed explanation of an image's format, see: +// https://pkg.go.dev/github.com/distribution/reference // if registry is omitted, the default (docker hub) will be added. // if namespace is omiited when using docker hub, the default namespace (library) will be added. From e9e6a006f1e408ba5a3ea2cebf0587f97f3a901e Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Mon, 26 Aug 2024 10:42:01 +0800 Subject: [PATCH 05/19] add initcontainer --- controllers/apps/transformer_component_workload.go | 7 +++++++ pkg/controllerutil/image_util_test.go | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index ea4a5f640bb..7ae152e0fcb 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -155,6 +155,13 @@ func (t *componentWorkloadTransformer) reconcileWorkload(synthesizedComp *compon } protoITS.Spec.Template.Spec.Containers[i].Image = newImage } + for i, container := range protoITS.Spec.Template.Spec.InitContainers { + newImage, err := intctrlutil.ReplaceImageRegistry(container.Image) + if err != nil { + return err + } + protoITS.Spec.Template.Spec.InitContainers[i].Image = newImage + } buildInstanceSetPlacementAnnotation(comp, protoITS) diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go index fab3f38f2cb..2f882ceffcf 100644 --- a/pkg/controllerutil/image_util_test.go +++ b/pkg/controllerutil/image_util_test.go @@ -28,10 +28,10 @@ import ( var _ = Describe("image util test", func() { imageList := [][]string{ - []string{"busybox", "docker.io", "library", "busybox", ""}, - []string{"apecloud/busybox:1.28", "docker.io", "apecloud", "busybox", ":1.28"}, - []string{"foo.io/a/b/busybox", "foo.io", "a/b", "busybox", ""}, - []string{ + {"busybox", "docker.io", "library", "busybox", ""}, + {"apecloud/busybox:1.28", "docker.io", "apecloud", "busybox", ":1.28"}, + {"foo.io/a/b/busybox", "foo.io", "a/b", "busybox", ""}, + { "registry.k8s.io/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07", "registry.k8s.io", "", "pause", ":latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07"}, } From 16ac3087e286301c18ce1b6d1e39d36c7fa87c18 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Mon, 26 Aug 2024 10:59:30 +0800 Subject: [PATCH 06/19] comments --- pkg/controllerutil/image_util.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index d2934333feb..ad638e837ef 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -75,7 +75,6 @@ func ReloadRegistryConfig() { panic(err) } - // TODO: validate for _, registry := range registriesConfig.Registries { if len(registry.From) == 0 { panic("from can't be empty") From 2e4c7a5844ed49bda2ee8cf7a6b29e875446bb2f Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Sat, 7 Sep 2024 00:20:30 +0800 Subject: [PATCH 07/19] watch config change --- cmd/manager/main.go | 2 ++ pkg/controllerutil/image_util.go | 42 +++++++++++++++++---------- pkg/controllerutil/image_util_test.go | 8 ++--- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 2f57010096c..38b78941fa0 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -281,9 +281,11 @@ func main() { if err := viper.ReadInConfig(); err != nil { // Handle errors reading the config file setupLog.Info("unable to read in config, errors ignored") } + intctrlutil.ReloadRegistryConfig() setupLog.Info(fmt.Sprintf("config file: %s", viper.GetViper().ConfigFileUsed())) viper.OnConfigChange(func(e fsnotify.Event) { setupLog.Info(fmt.Sprintf("config file changed: %s", e.Name)) + intctrlutil.ReloadRegistryConfig() }) viper.WatchConfig() diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index ad638e837ef..7b10cae935c 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -22,11 +22,11 @@ package controllerutil import ( // Import the crypto sha256 algorithm for the docker image parser to work _ "crypto/sha256" - "fmt" - "strings" - + "sync" // Import the crypto/sha512 algorithm for the docker image parser to work with 384 and 512 sha hashes _ "crypto/sha512" + "fmt" + "strings" "github.com/distribution/reference" @@ -41,8 +41,8 @@ import ( // > most registries only support two slash-separated components. // > For Docker's public registry, the path format is as follows: // > [NAMESPACE/]REPOSITORY: The first, optional component is typically a user's or an organization's -// namespace. The second, mandatory component is the repository name. When the namespace is -// not present, Docker uses `library` as the default namespace. +// > namespace. The second, mandatory component is the repository name. When the namespace is +// > not present, Docker uses `library` as the default namespace. // // So if there are more than two components, specify them both, or they won't be matched. type RegistryNamespaceConfig struct { @@ -62,15 +62,24 @@ type RegistriesConfig struct { Registries []RegistryConfig } -var registriesConfig = RegistriesConfig{} +// this lock protects r/w to this variable itself, +// not the data it points to +var registriesConfigMutex sync.RWMutex +var registriesConfig = &RegistriesConfig{} + +func GetRegistriesConfig() *RegistriesConfig { + registriesConfigMutex.RLock() + defer registriesConfigMutex.RUnlock() -func init() { - ReloadRegistryConfig() + // this will return a copy of the pointer + return registriesConfig } func ReloadRegistryConfig() { - // TODO: this is needed in componnet controller test, is there a better way? - registriesConfig = RegistriesConfig{} + registriesConfigMutex.Lock() + defer registriesConfigMutex.Unlock() + + registriesConfig = &RegistriesConfig{} if err := viper.UnmarshalKey(constant.CfgRegistries, ®istriesConfig); err != nil { panic(err) } @@ -86,6 +95,8 @@ func ReloadRegistryConfig() { } } } + + fmt.Printf("registriesConfig is %v\n", registriesConfig) } // For a detailed explanation of an image's format, see: @@ -127,25 +138,26 @@ func ReplaceImageRegistry(image string) (string, error) { if err != nil { return "", err } + registriesConfigCopy := GetRegistriesConfig() chooseRegistry := func() string { - if registriesConfig.DefaultRegistry != "" { - return registriesConfig.DefaultRegistry + if registriesConfigCopy.DefaultRegistry != "" { + return registriesConfigCopy.DefaultRegistry } else { return registry } } chooseNamespace := func() string { - if registriesConfig.DefaultNamespace != "" { - return registriesConfig.DefaultNamespace + if registriesConfigCopy.DefaultNamespace != "" { + return registriesConfigCopy.DefaultNamespace } else { return namespace } } var dstRegistry, dstNamespace string - for _, registryMapping := range registriesConfig.Registries { + for _, registryMapping := range registriesConfigCopy.Registries { if registryMapping.From == registry { if len(registryMapping.To) != 0 { dstRegistry = registryMapping.To diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go index 2f882ceffcf..cbc913bb105 100644 --- a/pkg/controllerutil/image_util_test.go +++ b/pkg/controllerutil/image_util_test.go @@ -38,7 +38,7 @@ var _ = Describe("image util test", func() { AfterEach(func() { // reset config - registriesConfig = RegistriesConfig{} + registriesConfig = &RegistriesConfig{} }) It("parses image", func() { @@ -69,7 +69,7 @@ var _ = Describe("image util test", func() { }) It("replaces image when default config exists", func() { - registriesConfig = RegistriesConfig{ + registriesConfig = &RegistriesConfig{ DefaultRegistry: "foo.bar", } for _, image := range imageList { @@ -82,7 +82,7 @@ var _ = Describe("image util test", func() { } } - registriesConfig = RegistriesConfig{ + registriesConfig = &RegistriesConfig{ DefaultRegistry: "foo.bar", DefaultNamespace: "test", } @@ -94,7 +94,7 @@ var _ = Describe("image util test", func() { }) It("replaces image when single registry config exists", func() { - registriesConfig = RegistriesConfig{ + registriesConfig = &RegistriesConfig{ DefaultRegistry: "foo.bar", Registries: []RegistryConfig{ { From cc1b236775f992c08767d06e627bc6eebda6e667 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Mon, 9 Sep 2024 17:19:09 +0800 Subject: [PATCH 08/19] refine api --- .../apps/transformer_component_workload.go | 4 -- pkg/controllerutil/image_util.go | 56 ++++++++++--------- pkg/controllerutil/image_util_test.go | 50 +++++++++++++++-- 3 files changed, 76 insertions(+), 34 deletions(-) diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index 7ae152e0fcb..c5bc5c4f4b3 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -406,10 +406,6 @@ func checkNRollbackProtoImages(itsObj, itsProto *workloads.InstanceSet) { for i, cc := range [][]corev1.Container{itsObj.Spec.Template.Spec.InitContainers, itsObj.Spec.Template.Spec.Containers} { images[i] = make(map[string]string) for _, c := range cc { - // skip the kb-agent container - if component.IsKBAgentContainer(&c) { - continue - } images[i][c.Name] = c.Image } } diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index 7b10cae935c..bc05448180e 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -23,12 +23,14 @@ import ( // Import the crypto sha256 algorithm for the docker image parser to work _ "crypto/sha256" "sync" + // Import the crypto/sha512 algorithm for the docker image parser to work with 384 and 512 sha hashes _ "crypto/sha512" "fmt" "strings" "github.com/distribution/reference" + "sigs.k8s.io/controller-runtime/pkg/log" "github.com/apecloud/kubeblocks/pkg/constant" viper "github.com/apecloud/kubeblocks/pkg/viperx" @@ -51,15 +53,16 @@ type RegistryNamespaceConfig struct { } type RegistryConfig struct { - From string - To string - Namespaces []RegistryNamespaceConfig + From string + To string + RegistryDefaultNamespace string + Namespaces []RegistryNamespaceConfig } type RegistriesConfig struct { DefaultRegistry string DefaultNamespace string - Registries []RegistryConfig + RegistryConfig []RegistryConfig } // this lock protects r/w to this variable itself, @@ -84,19 +87,18 @@ func ReloadRegistryConfig() { panic(err) } - for _, registry := range registriesConfig.Registries { + for _, registry := range registriesConfig.RegistryConfig { if len(registry.From) == 0 { panic("from can't be empty") } - for _, namespace := range registry.Namespaces { - if len(namespace.From) == 0 || len(namespace.To) == 0 { - panic("namespace can't be empty") - } + if len(registry.To) == 0 { + panic("to can't be empty") } } - fmt.Printf("registriesConfig is %v\n", registriesConfig) + logger := log.Log + logger.Info("registriesConfig reloaded", "registriesConfig", registriesConfig) } // For a detailed explanation of an image's format, see: @@ -148,32 +150,33 @@ func ReplaceImageRegistry(image string) (string, error) { } } - chooseNamespace := func() string { + chooseNamespace := func() *string { if registriesConfigCopy.DefaultNamespace != "" { - return registriesConfigCopy.DefaultNamespace + return ®istriesConfigCopy.DefaultNamespace } else { - return namespace + return &namespace } } - var dstRegistry, dstNamespace string - for _, registryMapping := range registriesConfigCopy.Registries { + var dstRegistry string + var dstNamespace *string + for _, registryMapping := range registriesConfigCopy.RegistryConfig { if registryMapping.From == registry { - if len(registryMapping.To) != 0 { - dstRegistry = registryMapping.To - } else { - dstRegistry = chooseRegistry() - } + dstRegistry = registryMapping.To for _, namespaceConf := range registryMapping.Namespaces { if namespace == namespaceConf.From { - dstNamespace = namespaceConf.To + dstNamespace = &namespaceConf.To break } } - if dstNamespace == "" { - dstNamespace = namespace + if dstNamespace == nil { + if registryMapping.RegistryDefaultNamespace != "" { + dstNamespace = ®istryMapping.RegistryDefaultNamespace + } else { + dstNamespace = &namespace + } } break @@ -183,11 +186,14 @@ func ReplaceImageRegistry(image string) (string, error) { // no match in registriesConf.Registries if dstRegistry == "" { dstRegistry = chooseRegistry() + } + + if dstNamespace == nil { dstNamespace = chooseNamespace() } - if dstNamespace == "" { + if *dstNamespace == "" { return fmt.Sprintf("%v/%v%v", dstRegistry, repository, remainder), nil } - return fmt.Sprintf("%v/%v/%v%v", dstRegistry, dstNamespace, repository, remainder), nil + return fmt.Sprintf("%v/%v/%v%v", dstRegistry, *dstNamespace, repository, remainder), nil } diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go index cbc913bb105..fd014849fc8 100644 --- a/pkg/controllerutil/image_util_test.go +++ b/pkg/controllerutil/image_util_test.go @@ -28,6 +28,7 @@ import ( var _ = Describe("image util test", func() { imageList := [][]string{ + // original image name, registry, namespace, image name, tag and digest {"busybox", "docker.io", "library", "busybox", ""}, {"apecloud/busybox:1.28", "docker.io", "apecloud", "busybox", ":1.28"}, {"foo.io/a/b/busybox", "foo.io", "a/b", "busybox", ""}, @@ -93,30 +94,69 @@ var _ = Describe("image util test", func() { } }) - It("replaces image when single registry config exists", func() { + It("replaces image when registry/namespace config exists", func() { registriesConfig = &RegistriesConfig{ - DefaultRegistry: "foo.bar", - Registries: []RegistryConfig{ + DefaultRegistry: "foo.bar", + DefaultNamespace: "default", + RegistryConfig: []RegistryConfig{ { From: "docker.io", To: "bar.io", Namespaces: []RegistryNamespaceConfig{ {From: "library", To: "foo"}, + {From: "apecloud", To: ""}, }, }, { From: "foo.io", + To: "foo.bar", Namespaces: []RegistryNamespaceConfig{ {From: "a/b", To: "foo"}, }, }, + { + From: "registry.k8s.io", + To: "k8s.bar", + Namespaces: []RegistryNamespaceConfig{ + {From: "", To: "k8s"}, + }, + }, }, } expectedImage := []string{ "bar.io/foo/busybox", - "bar.io/apecloud/busybox:1.28", + "bar.io/busybox:1.28", "foo.bar/foo/busybox", - "foo.bar/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07", + "k8s.bar/k8s/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07", + } + for i, image := range imageList { + newImage, err := ReplaceImageRegistry(image[0]) + Expect(err).NotTo(HaveOccurred()) + Expect(newImage).To(Equal(expectedImage[i])) + } + }) + + It("replaces image w/ or w/o RegistryDefaultNamespace", func() { + registriesConfig = &RegistriesConfig{ + DefaultRegistry: "foo.bar", + DefaultNamespace: "default", + RegistryConfig: []RegistryConfig{ + { + From: "docker.io", + To: "bar.io", + RegistryDefaultNamespace: "docker", + }, + { + From: "foo.io", + To: "foo.bar", + }, + }, + } + expectedImage := []string{ + "bar.io/docker/busybox", + "bar.io/docker/busybox:1.28", + "foo.bar/a/b/busybox", + "foo.bar/default/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07", } for i, image := range imageList { newImage, err := ReplaceImageRegistry(image[0]) From afed11be8ae6463fe207d53a3f663ee3a2c20917 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Tue, 10 Sep 2024 11:04:56 +0800 Subject: [PATCH 09/19] import order --- pkg/controllerutil/image_util.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index bc05448180e..49592625819 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -20,14 +20,12 @@ along with this program. If not, see . package controllerutil import ( - // Import the crypto sha256 algorithm for the docker image parser to work + // Import the crypto/sha256 and crypto/sha512 algorithm for the docker image parser to work _ "crypto/sha256" - "sync" - - // Import the crypto/sha512 algorithm for the docker image parser to work with 384 and 512 sha hashes _ "crypto/sha512" "fmt" "strings" + "sync" "github.com/distribution/reference" "sigs.k8s.io/controller-runtime/pkg/log" From 08b87aec2b54cac145fece2790947fb599526b50 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Tue, 10 Sep 2024 15:34:02 +0800 Subject: [PATCH 10/19] review comments --- pkg/controllerutil/image_util.go | 54 +++++++++++++-------------- pkg/controllerutil/image_util_test.go | 14 +++---- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index 49592625819..6562084424d 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -20,7 +20,7 @@ along with this program. If not, see . package controllerutil import ( - // Import the crypto/sha256 and crypto/sha512 algorithm for the docker image parser to work + // Import these crypto algorithm so that the image parser can work with digest _ "crypto/sha256" _ "crypto/sha512" "fmt" @@ -34,33 +34,30 @@ import ( viper "github.com/apecloud/kubeblocks/pkg/viperx" ) -// RegistryNamespaceConfig maps registry namespaces. -// -// Quote from https://docs.docker.com/reference/cli/docker/image/tag/ -// > While the OCI Distribution Specification supports more than two slash-separated components, -// > most registries only support two slash-separated components. -// > For Docker's public registry, the path format is as follows: -// > [NAMESPACE/]REPOSITORY: The first, optional component is typically a user's or an organization's -// > namespace. The second, mandatory component is the repository name. When the namespace is -// > not present, Docker uses `library` as the default namespace. -// -// So if there are more than two components, specify them both, or they won't be matched. -type RegistryNamespaceConfig struct { - From string - To string -} - type RegistryConfig struct { - From string - To string - RegistryDefaultNamespace string - Namespaces []RegistryNamespaceConfig + From string `mapstructure:"from"` + To string `mapstructure:"to"` + RegistryDefaultNamespace string `mapstructure:"registryDefaultNamespace"` + + // Quote from https://docs.docker.com/reference/cli/docker/image/tag/ + // > While the OCI Distribution Specification supports more than two slash-separated components, + // > most registries only support two slash-separated components. + // > For Docker's public registry, the path format is as follows: + // > [NAMESPACE/]REPOSITORY: The first, optional component is typically a user's or an organization's + // > namespace. The second, mandatory component is the repository name. When the namespace is + // > not present, Docker uses `library` as the default namespace. + // + // So if there are more than two components (like `a/b` as a namespace), specify them both, + // or they won't be matched. Note empty namespace is legal too. + // + // key is the orignal namespace, value is the new namespace + Namespaces map[string]string } type RegistriesConfig struct { - DefaultRegistry string - DefaultNamespace string - RegistryConfig []RegistryConfig + DefaultRegistry string `mapstructure:"defaultRegistry"` + DefaultNamespace string `mapstructure:"defaultNamespace"` + RegistryConfig []RegistryConfig `mapstructure:"registryConfig"` } // this lock protects r/w to this variable itself, @@ -95,8 +92,7 @@ func ReloadRegistryConfig() { } } - logger := log.Log - logger.Info("registriesConfig reloaded", "registriesConfig", registriesConfig) + log.Log.Info("registriesConfig reloaded", "registriesConfig", registriesConfig) } // For a detailed explanation of an image's format, see: @@ -162,9 +158,9 @@ func ReplaceImageRegistry(image string) (string, error) { if registryMapping.From == registry { dstRegistry = registryMapping.To - for _, namespaceConf := range registryMapping.Namespaces { - if namespace == namespaceConf.From { - dstNamespace = &namespaceConf.To + for orig, new := range registryMapping.Namespaces { + if namespace == orig { + dstNamespace = &new break } } diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go index fd014849fc8..f940bfa1a6c 100644 --- a/pkg/controllerutil/image_util_test.go +++ b/pkg/controllerutil/image_util_test.go @@ -102,23 +102,23 @@ var _ = Describe("image util test", func() { { From: "docker.io", To: "bar.io", - Namespaces: []RegistryNamespaceConfig{ - {From: "library", To: "foo"}, - {From: "apecloud", To: ""}, + Namespaces: map[string]string{ + "library": "foo", + "apecloud": "", }, }, { From: "foo.io", To: "foo.bar", - Namespaces: []RegistryNamespaceConfig{ - {From: "a/b", To: "foo"}, + Namespaces: map[string]string{ + "a/b": "foo", }, }, { From: "registry.k8s.io", To: "k8s.bar", - Namespaces: []RegistryNamespaceConfig{ - {From: "", To: "k8s"}, + Namespaces: map[string]string{ + "": "k8s", }, }, }, From 1a2c615fde1ee5cd35091c01240a0b30e72830ca Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Tue, 10 Sep 2024 15:36:52 +0800 Subject: [PATCH 11/19] fix --- pkg/controllerutil/image_util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index 6562084424d..00c607b87ce 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -51,7 +51,7 @@ type RegistryConfig struct { // or they won't be matched. Note empty namespace is legal too. // // key is the orignal namespace, value is the new namespace - Namespaces map[string]string + Namespaces map[string]string `mapstructure:"namespaces"` } type RegistriesConfig struct { From 0d2f2dd8a4ce135811b7cbf92338a6614d021a95 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Tue, 10 Sep 2024 18:50:35 +0800 Subject: [PATCH 12/19] rename Namespaces to NamespaceMapping --- pkg/controllerutil/image_util.go | 4 ++-- pkg/controllerutil/image_util_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index 00c607b87ce..40d3ad66929 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -51,7 +51,7 @@ type RegistryConfig struct { // or they won't be matched. Note empty namespace is legal too. // // key is the orignal namespace, value is the new namespace - Namespaces map[string]string `mapstructure:"namespaces"` + NamespaceMapping map[string]string `mapstructure:"namespaceMapping"` } type RegistriesConfig struct { @@ -158,7 +158,7 @@ func ReplaceImageRegistry(image string) (string, error) { if registryMapping.From == registry { dstRegistry = registryMapping.To - for orig, new := range registryMapping.Namespaces { + for orig, new := range registryMapping.NamespaceMapping { if namespace == orig { dstNamespace = &new break diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go index f940bfa1a6c..99729187619 100644 --- a/pkg/controllerutil/image_util_test.go +++ b/pkg/controllerutil/image_util_test.go @@ -102,7 +102,7 @@ var _ = Describe("image util test", func() { { From: "docker.io", To: "bar.io", - Namespaces: map[string]string{ + NamespaceMapping: map[string]string{ "library": "foo", "apecloud": "", }, @@ -110,14 +110,14 @@ var _ = Describe("image util test", func() { { From: "foo.io", To: "foo.bar", - Namespaces: map[string]string{ + NamespaceMapping: map[string]string{ "a/b": "foo", }, }, { From: "registry.k8s.io", To: "k8s.bar", - Namespaces: map[string]string{ + NamespaceMapping: map[string]string{ "": "k8s", }, }, From 3e98dadafed6a6b0a38231dad6ea0b6625bbb5a5 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Thu, 12 Sep 2024 11:17:49 +0800 Subject: [PATCH 13/19] revert skipping kb-agent --- controllers/apps/transformer_component_workload.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index c5bc5c4f4b3..7ae152e0fcb 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -406,6 +406,10 @@ func checkNRollbackProtoImages(itsObj, itsProto *workloads.InstanceSet) { for i, cc := range [][]corev1.Container{itsObj.Spec.Template.Spec.InitContainers, itsObj.Spec.Template.Spec.Containers} { images[i] = make(map[string]string) for _, c := range cc { + // skip the kb-agent container + if component.IsKBAgentContainer(&c) { + continue + } images[i][c.Name] = c.Image } } From 981ec636b2852cf192207b0f39b4226cdca957cb Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Fri, 27 Sep 2024 16:08:02 +0800 Subject: [PATCH 14/19] simplify implementation --- controllers/apps/component_controller_test.go | 10 +++++----- .../apps/transformer_component_workload.go | 12 ++---------- pkg/controllerutil/image_util.go | 10 ++++++---- pkg/controllerutil/image_util_test.go | 15 +++++---------- 4 files changed, 18 insertions(+), 29 deletions(-) diff --git a/controllers/apps/component_controller_test.go b/controllers/apps/component_controller_test.go index 57017818261..b45a0133dfd 100644 --- a/controllers/apps/component_controller_test.go +++ b/controllers/apps/component_controller_test.go @@ -2143,7 +2143,7 @@ var _ = Describe("Component Controller", func() { setRegistryConfig() By("trigger component reconcile") now := time.Now().Format(time.RFC3339) - Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *appsv1alpha1.Component) { + Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *kbappsv1.Component) { comp.Annotations["now"] = now })()).Should(Succeed()) @@ -2154,7 +2154,7 @@ var _ = Describe("Component Controller", func() { })).Should(Succeed()) By("replaces registry when upgrading") - release := appsv1alpha1.ComponentVersionRelease{ + release := kbappsv1.ComponentVersionRelease{ Name: "8.0.31", ServiceVersion: "8.0.31", Images: map[string]string{ @@ -2164,19 +2164,19 @@ var _ = Describe("Component Controller", func() { By("publish a new release") compVerKey := client.ObjectKeyFromObject(compVerObj) - Expect(testapps.GetAndChangeObj(&testCtx, compVerKey, func(compVer *appsv1alpha1.ComponentVersion) { + Expect(testapps.GetAndChangeObj(&testCtx, compVerKey, func(compVer *kbappsv1.ComponentVersion) { compVer.Spec.Releases = append(compVer.Spec.Releases, release) compVer.Spec.CompatibilityRules[0].Releases = append(compVer.Spec.CompatibilityRules[0].Releases, release.Name) })()).Should(Succeed()) By("update serviceversion in cluster") - Expect(testapps.GetAndChangeObj(&testCtx, clusterKey, func(cluster *appsv1alpha1.Cluster) { + Expect(testapps.GetAndChangeObj(&testCtx, clusterKey, func(cluster *kbappsv1.Cluster) { cluster.Spec.ComponentSpecs[0].ServiceVersion = "8.0.31" })()).Should(Succeed()) By("trigger component reconcile") now = time.Now().Format(time.RFC3339) - Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *appsv1alpha1.Component) { + Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *kbappsv1.Component) { comp.Annotations["now"] = now })()).Should(Succeed()) diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index 2480f9d09fd..8d6c284a5a4 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -149,18 +149,10 @@ func (t *componentWorkloadTransformer) reconcileWorkload(synthesizedComp *compon // rollback to the original image in `checkNRollbackProtoImages`. // So changing registry configs won't affect existing clusters. for i, container := range protoITS.Spec.Template.Spec.Containers { - newImage, err := intctrlutil.ReplaceImageRegistry(container.Image) - if err != nil { - return err - } - protoITS.Spec.Template.Spec.Containers[i].Image = newImage + protoITS.Spec.Template.Spec.Containers[i].Image = intctrlutil.ReplaceImageRegistry(container.Image) } for i, container := range protoITS.Spec.Template.Spec.InitContainers { - newImage, err := intctrlutil.ReplaceImageRegistry(container.Image) - if err != nil { - return err - } - protoITS.Spec.Template.Spec.InitContainers[i].Image = newImage + protoITS.Spec.Template.Spec.InitContainers[i].Image = intctrlutil.ReplaceImageRegistry(container.Image) } buildInstanceSetPlacementAnnotation(comp, protoITS) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index 40d3ad66929..3199c060634 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -129,10 +129,12 @@ func parseImageName(image string) ( return } -func ReplaceImageRegistry(image string) (string, error) { +func ReplaceImageRegistry(image string) string { registry, namespace, repository, remainder, err := parseImageName(image) + // if parse has failed, return the original image. Since k8s will always error an invalid image, we + // don't need to deal with the error here if err != nil { - return "", err + return image } registriesConfigCopy := GetRegistriesConfig() @@ -187,7 +189,7 @@ func ReplaceImageRegistry(image string) (string, error) { } if *dstNamespace == "" { - return fmt.Sprintf("%v/%v%v", dstRegistry, repository, remainder), nil + return fmt.Sprintf("%v/%v%v", dstRegistry, repository, remainder) } - return fmt.Sprintf("%v/%v/%v%v", dstRegistry, *dstNamespace, repository, remainder), nil + return fmt.Sprintf("%v/%v/%v%v", dstRegistry, *dstNamespace, repository, remainder) } diff --git a/pkg/controllerutil/image_util_test.go b/pkg/controllerutil/image_util_test.go index 99729187619..2f3ea8c1801 100644 --- a/pkg/controllerutil/image_util_test.go +++ b/pkg/controllerutil/image_util_test.go @@ -59,8 +59,7 @@ var _ = Describe("image util test", func() { It("only expands image when config does not exist", func() { for _, image := range imageList { - newImage, err := ReplaceImageRegistry(image[0]) - Expect(err).NotTo(HaveOccurred()) + newImage := ReplaceImageRegistry(image[0]) if image[2] == "" { Expect(newImage).To(Equal(fmt.Sprintf("%v/%v%v", image[1], image[3], image[4]))) } else { @@ -74,8 +73,7 @@ var _ = Describe("image util test", func() { DefaultRegistry: "foo.bar", } for _, image := range imageList { - newImage, err := ReplaceImageRegistry(image[0]) - Expect(err).NotTo(HaveOccurred()) + newImage := ReplaceImageRegistry(image[0]) if image[2] == "" { Expect(newImage).To(Equal(fmt.Sprintf("%v/%v%v", "foo.bar", image[3], image[4]))) } else { @@ -88,8 +86,7 @@ var _ = Describe("image util test", func() { DefaultNamespace: "test", } for _, image := range imageList { - newImage, err := ReplaceImageRegistry(image[0]) - Expect(err).NotTo(HaveOccurred()) + newImage := ReplaceImageRegistry(image[0]) Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", "foo.bar", "test", image[3], image[4]))) } }) @@ -130,8 +127,7 @@ var _ = Describe("image util test", func() { "k8s.bar/k8s/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07", } for i, image := range imageList { - newImage, err := ReplaceImageRegistry(image[0]) - Expect(err).NotTo(HaveOccurred()) + newImage := ReplaceImageRegistry(image[0]) Expect(newImage).To(Equal(expectedImage[i])) } }) @@ -159,8 +155,7 @@ var _ = Describe("image util test", func() { "foo.bar/default/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07", } for i, image := range imageList { - newImage, err := ReplaceImageRegistry(image[0]) - Expect(err).NotTo(HaveOccurred()) + newImage := ReplaceImageRegistry(image[0]) Expect(newImage).To(Equal(expectedImage[i])) } }) From 4c75882944afa9775537fc5b2a72f284bb6b11e0 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Sun, 29 Sep 2024 16:34:54 +0800 Subject: [PATCH 15/19] replace images in other controllers --- controllers/extensions/addon_controller_stages.go | 2 +- pkg/controllerutil/image_util.go | 4 ++++ pkg/dataprotection/backup/deleter.go | 3 ++- pkg/dataprotection/backup/request.go | 7 ++++--- pkg/dataprotection/restore/builder.go | 4 +++- pkg/dataprotection/utils/backuprepo.go | 2 +- pkg/operations/switchover_util.go | 2 +- 7 files changed, 16 insertions(+), 8 deletions(-) diff --git a/controllers/extensions/addon_controller_stages.go b/controllers/extensions/addon_controller_stages.go index 9e37c95514a..8f455305685 100644 --- a/controllers/extensions/addon_controller_stages.go +++ b/controllers/extensions/addon_controller_stages.go @@ -514,7 +514,7 @@ func setInitContainer(addon *extensionsv1alpha1.Addon, helmJobPodSpec *corev1.Po } copyChartsContainer := corev1.Container{ Name: "copy-charts", - Image: addon.Spec.Helm.ChartsImage, + Image: intctrlutil.ReplaceImageRegistry(addon.Spec.Helm.ChartsImage), Command: []string{"sh", "-c", fmt.Sprintf("cp %s/* /mnt/charts", fromPath)}, VolumeMounts: []corev1.VolumeMount{ { diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index 3199c060634..efd154ca427 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -92,6 +92,10 @@ func ReloadRegistryConfig() { } } + // since the use of kb tools image is widespread, set viper value here so that we don't need + // to replace it every time + viper.Set(constant.KBToolsImage, ReplaceImageRegistry(viper.GetString(constant.KBToolsImage))) + log.Log.Info("registriesConfig reloaded", "registriesConfig", registriesConfig) } diff --git a/pkg/dataprotection/backup/deleter.go b/pkg/dataprotection/backup/deleter.go index f22eb310c07..545cbd9e42e 100644 --- a/pkg/dataprotection/backup/deleter.go +++ b/pkg/dataprotection/backup/deleter.go @@ -325,10 +325,11 @@ func (d *Deleter) doPreDeleteAction( if d.actionSet != nil { envVars = append(envVars, d.actionSet.Spec.Env...) } + image := common.Expand(preDeleteAction.Image, common.MappingFuncFor(utils.CovertEnvToMap(envVars))) container := corev1.Container{ Name: backup.Name, Command: preDeleteAction.Command, - Image: common.Expand(preDeleteAction.Image, common.MappingFuncFor(utils.CovertEnvToMap(envVars))), + Image: ctrlutil.ReplaceImageRegistry(image), Env: envVars, ImagePullPolicy: corev1.PullPolicy(viper.GetString(constant.KBImagePullPolicy)), SecurityContext: &corev1.SecurityContext{ diff --git a/pkg/dataprotection/backup/request.go b/pkg/dataprotection/backup/request.go index 47ed59fd25d..ca86bc9f4f1 100644 --- a/pkg/dataprotection/backup/request.go +++ b/pkg/dataprotection/backup/request.go @@ -405,10 +405,11 @@ func (r *Request) BuildJobActionPodSpec(targetPod *corev1.Pod, if err != nil { return nil, err } + // expand the image value with the env variables. + image := common.Expand(job.Image, common.MappingFuncFor(utils.CovertEnvToMap(env))) container := corev1.Container{ - Name: name, - // expand the image value with the env variables. - Image: common.Expand(job.Image, common.MappingFuncFor(utils.CovertEnvToMap(env))), + Name: name, + Image: intctrlutil.ReplaceImageRegistry(image), Command: job.Command, Env: env, EnvFrom: targetPod.Spec.Containers[0].EnvFrom, diff --git a/pkg/dataprotection/restore/builder.go b/pkg/dataprotection/restore/builder.go index 54f09d39343..4596f327d93 100644 --- a/pkg/dataprotection/restore/builder.go +++ b/pkg/dataprotection/restore/builder.go @@ -338,6 +338,8 @@ func (r *restoreJobBuilder) build() *batchv1.Job { // 2. set restore container r.specificVolumeMounts = append(r.specificVolumeMounts, r.commonVolumeMounts...) + // expand the image value with the env variables. + image := common.Expand(r.image, common.MappingFuncFor(utils.CovertEnvToMap(r.env))) container := corev1.Container{ Name: Restore, Resources: r.restore.Spec.ContainerResources, @@ -347,7 +349,7 @@ func (r *restoreJobBuilder) build() *batchv1.Job { Command: r.command, Args: r.args, // expand the image value with the env variables. - Image: common.Expand(r.image, common.MappingFuncFor(utils.CovertEnvToMap(r.env))), + Image: intctrlutil.ReplaceImageRegistry(image), ImagePullPolicy: corev1.PullIfNotPresent, } diff --git a/pkg/dataprotection/utils/backuprepo.go b/pkg/dataprotection/utils/backuprepo.go index da9efdd7bf2..a79b77336f1 100644 --- a/pkg/dataprotection/utils/backuprepo.go +++ b/pkg/dataprotection/utils/backuprepo.go @@ -148,7 +148,7 @@ func injectDatasafedInstaller(podSpec *corev1.PodSpec) { } initContainer := corev1.Container{ Name: "dp-copy-datasafed", - Image: datasafedImage, + Image: intctrlutil.ReplaceImageRegistry(datasafedImage), ImagePullPolicy: corev1.PullPolicy(viper.GetString(constant.KBImagePullPolicy)), Command: []string{"/bin/sh", "-c", fmt.Sprintf("/scripts/install-datasafed.sh %s", datasafedBinMountPath)}, VolumeMounts: []corev1.VolumeMount{sharedVolumeMount}, diff --git a/pkg/operations/switchover_util.go b/pkg/operations/switchover_util.go index 32297fb7aa4..b1ffd758d3d 100644 --- a/pkg/operations/switchover_util.go +++ b/pkg/operations/switchover_util.go @@ -212,7 +212,7 @@ func renderSwitchoverCmdJob(ctx context.Context, Containers: []corev1.Container{ { Name: KBSwitchoverJobContainerName, - Image: switchoverSpec.Exec.Image, + Image: intctrlutil.ReplaceImageRegistry(switchoverSpec.Exec.Image), ImagePullPolicy: corev1.PullIfNotPresent, Command: switchoverSpec.Exec.Command, Args: switchoverSpec.Exec.Args, From 815afa6929934944a5570973127edd15e0103253 Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Sun, 29 Sep 2024 16:43:35 +0800 Subject: [PATCH 16/19] improve log --- pkg/controllerutil/image_util.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index efd154ca427..d1e90fab2ed 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -34,6 +34,8 @@ import ( viper "github.com/apecloud/kubeblocks/pkg/viperx" ) +var imageLogger = log.Log.WithName("ImageUtil") + type RegistryConfig struct { From string `mapstructure:"from"` To string `mapstructure:"to"` @@ -96,7 +98,7 @@ func ReloadRegistryConfig() { // to replace it every time viper.Set(constant.KBToolsImage, ReplaceImageRegistry(viper.GetString(constant.KBToolsImage))) - log.Log.Info("registriesConfig reloaded", "registriesConfig", registriesConfig) + imageLogger.Info("registriesConfig reloaded", "registriesConfig", registriesConfig) } // For a detailed explanation of an image's format, see: @@ -109,6 +111,7 @@ func parseImageName(image string) ( ) { named, err := reference.ParseNormalizedNamed(image) if err != nil { + imageLogger.Error(err, "parse image failed, the image remains unchanged", "image", image) return } From 9cc02607ea9db47bcad3eb6c094e486b4eb6f18e Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Wed, 9 Oct 2024 16:39:43 +0800 Subject: [PATCH 17/19] load registry config for dataprotection process --- cmd/dataprotection/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/dataprotection/main.go b/cmd/dataprotection/main.go index ecdf6433e2f..f0e7a02213b 100644 --- a/cmd/dataprotection/main.go +++ b/cmd/dataprotection/main.go @@ -180,9 +180,11 @@ func main() { if err := viper.ReadInConfig(); err != nil { // Handle errors reading the config file setupLog.Info("unable to read in config, errors ignored") } + intctrlutil.ReloadRegistryConfig() setupLog.Info(fmt.Sprintf("config file: %s", viper.GetViper().ConfigFileUsed())) viper.OnConfigChange(func(e fsnotify.Event) { setupLog.Info(fmt.Sprintf("config file changed: %s", e.Name)) + intctrlutil.ReloadRegistryConfig() }) viper.WatchConfig() From 8cd721a7cc3125f10d3d4ae23f5e58a9864f117a Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Thu, 10 Oct 2024 15:08:18 +0800 Subject: [PATCH 18/19] fix a deadlock --- pkg/controllerutil/image_util.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/controllerutil/image_util.go b/pkg/controllerutil/image_util.go index d1e90fab2ed..30cc60b56a8 100644 --- a/pkg/controllerutil/image_util.go +++ b/pkg/controllerutil/image_util.go @@ -77,12 +77,11 @@ func GetRegistriesConfig() *RegistriesConfig { func ReloadRegistryConfig() { registriesConfigMutex.Lock() - defer registriesConfigMutex.Unlock() - registriesConfig = &RegistriesConfig{} if err := viper.UnmarshalKey(constant.CfgRegistries, ®istriesConfig); err != nil { panic(err) } + registriesConfigMutex.Unlock() for _, registry := range registriesConfig.RegistryConfig { if len(registry.From) == 0 { From 916749b60d8bb5caf2020bb00c2aea94587b526d Mon Sep 17 00:00:00 2001 From: Harold Cheng Date: Fri, 11 Oct 2024 15:29:11 +0800 Subject: [PATCH 19/19] fix comment --- pkg/dataprotection/restore/builder.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pkg/dataprotection/restore/builder.go b/pkg/dataprotection/restore/builder.go index 4596f327d93..788e4885f5a 100644 --- a/pkg/dataprotection/restore/builder.go +++ b/pkg/dataprotection/restore/builder.go @@ -341,14 +341,13 @@ func (r *restoreJobBuilder) build() *batchv1.Job { // expand the image value with the env variables. image := common.Expand(r.image, common.MappingFuncFor(utils.CovertEnvToMap(r.env))) container := corev1.Container{ - Name: Restore, - Resources: r.restore.Spec.ContainerResources, - Env: r.env, - EnvFrom: r.envFrom, - VolumeMounts: r.specificVolumeMounts, - Command: r.command, - Args: r.args, - // expand the image value with the env variables. + Name: Restore, + Resources: r.restore.Spec.ContainerResources, + Env: r.env, + EnvFrom: r.envFrom, + VolumeMounts: r.specificVolumeMounts, + Command: r.command, + Args: r.args, Image: intctrlutil.ReplaceImageRegistry(image), ImagePullPolicy: corev1.PullIfNotPresent, }