diff --git a/cmd/manager/main.go b/cmd/manager/main.go
index 417d7057b04..f9ffea1f7f2 100644
--- a/cmd/manager/main.go
+++ b/cmd/manager/main.go
@@ -286,9 +286,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/controllers/apps/component_controller_test.go b/controllers/apps/component_controller_test.go
index 31a82de54f5..b45a0133dfd 100644
--- a/controllers/apps/component_controller_test.go
+++ b/controllers/apps/component_controller_test.go
@@ -2099,4 +2099,92 @@ var _ = Describe("Component Controller", func() {
testImageUnchangedAfterNewReleasePublished(release)
})
})
+
+ Context("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 *kbappsv1.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 := kbappsv1.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 *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 *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 *kbappsv1.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())
+ })
+ })
})
diff --git a/controllers/apps/suite_test.go b/controllers/apps/suite_test.go
index 17992859076..40e69fc601a 100644
--- a/controllers/apps/suite_test.go
+++ b/controllers/apps/suite_test.go
@@ -179,7 +179,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/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go
index 50d17998e07..8d6c284a5a4 100644
--- a/controllers/apps/transformer_component_workload.go
+++ b/controllers/apps/transformer_component_workload.go
@@ -145,6 +145,16 @@ func (t *componentWorkloadTransformer) reconcileWorkload(synthesizedComp *compon
protoITS.Spec.Template.Labels = intctrlutil.MergeMetadataMaps(runningITS.Spec.Template.Labels, synthesizedComp.DynamicLabels)
}
+ // 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 {
+ protoITS.Spec.Template.Spec.Containers[i].Image = intctrlutil.ReplaceImageRegistry(container.Image)
+ }
+ for i, container := range protoITS.Spec.Template.Spec.InitContainers {
+ protoITS.Spec.Template.Spec.InitContainers[i].Image = intctrlutil.ReplaceImageRegistry(container.Image)
+ }
+
buildInstanceSetPlacementAnnotation(comp, protoITS)
// build configuration template annotations to workload
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/go.mod b/go.mod
index 92b562bb37c..d91a822c579 100644
--- a/go.mod
+++ b/go.mod
@@ -14,6 +14,7 @@ require (
github.com/charmbracelet/keygen v0.5.1
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
@@ -104,7 +105,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.5.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 7de2e547ae1..850f8b206fe 100644
--- a/go.sum
+++ b/go.sum
@@ -192,8 +192,8 @@ 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=
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..d1e90fab2ed
--- /dev/null
+++ b/pkg/controllerutil/image_util.go
@@ -0,0 +1,202 @@
+/*
+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 (
+ // Import these crypto algorithm so that the image parser can work with digest
+ _ "crypto/sha256"
+ _ "crypto/sha512"
+ "fmt"
+ "strings"
+ "sync"
+
+ "github.com/distribution/reference"
+ "sigs.k8s.io/controller-runtime/pkg/log"
+
+ "github.com/apecloud/kubeblocks/pkg/constant"
+ viper "github.com/apecloud/kubeblocks/pkg/viperx"
+)
+
+var imageLogger = log.Log.WithName("ImageUtil")
+
+type RegistryConfig struct {
+ 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
+ NamespaceMapping map[string]string `mapstructure:"namespaceMapping"`
+}
+
+type RegistriesConfig struct {
+ DefaultRegistry string `mapstructure:"defaultRegistry"`
+ DefaultNamespace string `mapstructure:"defaultNamespace"`
+ RegistryConfig []RegistryConfig `mapstructure:"registryConfig"`
+}
+
+// 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()
+
+ // this will return a copy of the pointer
+ return registriesConfig
+}
+
+func ReloadRegistryConfig() {
+ registriesConfigMutex.Lock()
+ defer registriesConfigMutex.Unlock()
+
+ registriesConfig = &RegistriesConfig{}
+ if err := viper.UnmarshalKey(constant.CfgRegistries, ®istriesConfig); err != nil {
+ panic(err)
+ }
+
+ for _, registry := range registriesConfig.RegistryConfig {
+ if len(registry.From) == 0 {
+ panic("from can't be empty")
+ }
+
+ if len(registry.To) == 0 {
+ panic("to can't be empty")
+ }
+ }
+
+ // 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)))
+
+ imageLogger.Info("registriesConfig reloaded", "registriesConfig", registriesConfig)
+}
+
+// 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.
+func parseImageName(image string) (
+ host string, namespace string, repository string, remainder string, err error,
+) {
+ named, err := reference.ParseNormalizedNamed(image)
+ if err != nil {
+ imageLogger.Error(err, "parse image failed, the image remains unchanged", "image", image)
+ 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 {
+ 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 image
+ }
+ registriesConfigCopy := GetRegistriesConfig()
+
+ chooseRegistry := func() string {
+ if registriesConfigCopy.DefaultRegistry != "" {
+ return registriesConfigCopy.DefaultRegistry
+ } else {
+ return registry
+ }
+ }
+
+ chooseNamespace := func() *string {
+ if registriesConfigCopy.DefaultNamespace != "" {
+ return ®istriesConfigCopy.DefaultNamespace
+ } else {
+ return &namespace
+ }
+ }
+
+ var dstRegistry string
+ var dstNamespace *string
+ for _, registryMapping := range registriesConfigCopy.RegistryConfig {
+ if registryMapping.From == registry {
+ dstRegistry = registryMapping.To
+
+ for orig, new := range registryMapping.NamespaceMapping {
+ if namespace == orig {
+ dstNamespace = &new
+ break
+ }
+ }
+
+ if dstNamespace == nil {
+ if registryMapping.RegistryDefaultNamespace != "" {
+ dstNamespace = ®istryMapping.RegistryDefaultNamespace
+ } else {
+ dstNamespace = &namespace
+ }
+ }
+
+ break
+ }
+ }
+
+ // no match in registriesConf.Registries
+ if dstRegistry == "" {
+ dstRegistry = chooseRegistry()
+ }
+
+ if dstNamespace == nil {
+ dstNamespace = chooseNamespace()
+ }
+
+ if *dstNamespace == "" {
+ return fmt.Sprintf("%v/%v%v", dstRegistry, repository, remainder)
+ }
+ 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
new file mode 100644
index 00000000000..2f3ea8c1801
--- /dev/null
+++ b/pkg/controllerutil/image_util_test.go
@@ -0,0 +1,162 @@
+/*
+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 (
+ "fmt"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+)
+
+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", ""},
+ {
+ "registry.k8s.io/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07",
+ "registry.k8s.io", "", "pause", ":latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07"},
+ }
+
+ AfterEach(func() {
+ // reset config
+ registriesConfig = &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]))
+ }
+
+ _, _, _, _, err := parseImageName("/invalid")
+ Expect(err).To(HaveOccurred())
+ })
+
+ It("only expands image when config does not exist", func() {
+ for _, image := range imageList {
+ newImage := ReplaceImageRegistry(image[0])
+ 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() {
+ registriesConfig = &RegistriesConfig{
+ DefaultRegistry: "foo.bar",
+ }
+ for _, image := range imageList {
+ newImage := ReplaceImageRegistry(image[0])
+ 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])))
+ }
+ }
+
+ registriesConfig = &RegistriesConfig{
+ DefaultRegistry: "foo.bar",
+ DefaultNamespace: "test",
+ }
+ for _, image := range imageList {
+ newImage := ReplaceImageRegistry(image[0])
+ Expect(newImage).To(Equal(fmt.Sprintf("%v/%v/%v%v", "foo.bar", "test", image[3], image[4])))
+ }
+ })
+
+ It("replaces image when registry/namespace config exists", func() {
+ registriesConfig = &RegistriesConfig{
+ DefaultRegistry: "foo.bar",
+ DefaultNamespace: "default",
+ RegistryConfig: []RegistryConfig{
+ {
+ From: "docker.io",
+ To: "bar.io",
+ NamespaceMapping: map[string]string{
+ "library": "foo",
+ "apecloud": "",
+ },
+ },
+ {
+ From: "foo.io",
+ To: "foo.bar",
+ NamespaceMapping: map[string]string{
+ "a/b": "foo",
+ },
+ },
+ {
+ From: "registry.k8s.io",
+ To: "k8s.bar",
+ NamespaceMapping: map[string]string{
+ "": "k8s",
+ },
+ },
+ },
+ }
+ expectedImage := []string{
+ "bar.io/foo/busybox",
+ "bar.io/busybox:1.28",
+ "foo.bar/foo/busybox",
+ "k8s.bar/k8s/pause:latest@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07",
+ }
+ for i, image := range imageList {
+ newImage := ReplaceImageRegistry(image[0])
+ 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 := ReplaceImageRegistry(image[0])
+ Expect(newImage).To(Equal(expectedImage[i]))
+ }
+ })
+})
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,
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()