diff --git a/builtin/core/roles/certs/init/tasks/main.yaml b/builtin/core/roles/certs/init/tasks/main.yaml index ab7b02a1..7a8266eb 100644 --- a/builtin/core/roles/certs/init/tasks/main.yaml +++ b/builtin/core/roles/certs/init/tasks/main.yaml @@ -84,6 +84,9 @@ {{- if .image_registry.ha_vip | empty | not -}} {{- $ips = append $ips .image_registry.ha_vip -}} {{- end -}} + {{- if .image_registry.auth.registry | empty | not -}} + {{- $ips = append $ips .image_registry.auth.registry -}} + {{- end -}} {{- range .groups.image_registry | default list -}} {{- $internalIPv4 := index $.hostvars . "internal_ipv4" | default "" -}} {{- $internalIPv6 := index $.hostvars . "internal_ipv6" | default "" -}} diff --git a/builtin/core/roles/defaults/vars/10-download.yaml b/builtin/core/roles/defaults/vars/10-download.yaml index 05b48fcd..2fa33d36 100644 --- a/builtin/core/roles/defaults/vars/10-download.yaml +++ b/builtin/core/roles/defaults/vars/10-download.yaml @@ -221,4 +221,4 @@ download: kubeovn: https://kubeovn.github.io/kube-ovn/kube-ovn-{{ .cni.kubeovn_version }}.tgz hybridnet: https://github.com/alibaba/hybridnet/releases/download/helm-chart-{{ .cni.hybridnet_version }}/hybridnet-{{ .cni.hybridnet_version }}.tgz nfs_provisioner: https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/releases/download/nfs-subdir-external-provisioner-4.0.18/nfs-subdir-external-provisioner-{{ .storage_class.nfs_provisioner_version }}.tgz - download_image: true + download_image: false diff --git a/builtin/core/roles/download/tasks/images.yaml b/builtin/core/roles/download/tasks/images.yaml index 46064b3f..33a232e6 100644 --- a/builtin/core/roles/download/tasks/images.yaml +++ b/builtin/core/roles/download/tasks/images.yaml @@ -1,9 +1,7 @@ - name: Image | Download container images image: pull: - auths: - - username: "{{ .image_registry.auth.username }}" - password: "{{ .image_registry.auth.password }}" + auths: "{{ .cri.registry.auths | toJson }}" images_dir: >- {{ .binary_dir }}/images/ manifests: "{{ .image_manifests | toJson }}" diff --git a/builtin/core/roles/image-registry/tasks/main.yaml b/builtin/core/roles/image-registry/tasks/main.yaml index ad5b93d0..47cd9d65 100644 --- a/builtin/core/roles/image-registry/tasks/main.yaml +++ b/builtin/core/roles/image-registry/tasks/main.yaml @@ -1,8 +1,8 @@ --- - name: ImageRegistry | Synchronize images to remote host + run_once: true when: - - .image_manifests | default list | empty | not - - .download.download_image + - printf "%s/images/" .binary_dir | fileExist copy: src: >- {{ .binary_dir }}/images/ diff --git a/cmd/kk/app/options/builtin/artifact.go b/cmd/kk/app/options/builtin/artifact.go index 38aa4e02..730ebd9a 100644 --- a/cmd/kk/app/options/builtin/artifact.go +++ b/cmd/kk/app/options/builtin/artifact.go @@ -21,6 +21,7 @@ package builtin import ( "fmt" + "strings" "github.com/cockroachdb/errors" kkcorev1 "github.com/kubesphere/kubekey/api/core/v1" @@ -82,6 +83,9 @@ func (o *ArtifactExportOptions) Complete(cmd *cobra.Command, args []string) (*kk Playbook: o.Playbook, SkipTags: []string{"certs"}, } + + o.CommonOptions.Set = setDefaultDownload(o.CommonOptions.Set) + if err := o.CommonOptions.Complete(playbook); err != nil { return nil, err } @@ -152,9 +156,19 @@ func (o *ArtifactImagesOptions) Complete(cmd *cobra.Command, args []string) (*kk Tags: tags, } + o.CommonOptions.Set = setDefaultDownload(o.CommonOptions.Set) if err := o.CommonOptions.Complete(playbook); err != nil { return nil, errors.WithStack(err) } return playbook, nil } + +func setDefaultDownload(set []string) []string { + for _, s := range set { + if strings.Contains(s, "download.download_image=") { + return set + } + } + return append(set, "download.download_image=true") +} diff --git a/pkg/modules/image.go b/pkg/modules/image.go index 03da4423..8621975a 100644 --- a/pkg/modules/image.go +++ b/pkg/modules/image.go @@ -21,6 +21,7 @@ import ( "context" "crypto/tls" "encoding/json" + "fmt" "io" "io/fs" "net/http" @@ -109,6 +110,8 @@ Return Values: - On failure: Returns error message in stderr */ +const defaultRegistry = "docker.io" + // imageArgs holds the configuration for image operations type imageArgs struct { pull *imagePullArgs @@ -133,9 +136,9 @@ type imagePullAuth struct { // pull retrieves images from a remote registry and stores them locally func (i imagePullArgs) pull(ctx context.Context, platform string) error { for _, img := range i.manifests { - src, err := remote.NewRepository(img) + src, err := remote.NewRepository(normalizeImageNameSimple(img)) if err != nil { - return errors.Wrapf(err, "failed to get remote image %p", img) + return errors.Wrapf(err, "failed to get remote image %s", img) } src.Client = &auth.Client{ Client: &http.Client{ @@ -294,11 +297,9 @@ func newImageArgs(_ context.Context, raw runtime.RawExtension, vars map[string]a ipl := &imagePullArgs{} ipl.manifests, _ = variable.StringSliceVar(vars, pull, "manifests") ipl.auths = make([]imagePullAuth, 0) - varAuths := make([]imagePullAuth, 0) - _ = variable.AnyVar(vars, vars, &varAuths, "cri", "registry", "auths") pullAuths := make([]imagePullAuth, 0) _ = variable.AnyVar(vars, pull, &pullAuths, "auths") - for _, a := range append(varAuths, pullAuths...) { + for _, a := range pullAuths { a.Repo, _ = tmpl.ParseFunc(vars, a.Repo, func(b []byte) string { return string(b) }) a.Username, _ = tmpl.ParseFunc(vars, a.Username, func(b []byte) string { return string(b) }) a.Password, _ = tmpl.ParseFunc(vars, a.Password, func(b []byte) string { return string(b) }) @@ -687,6 +688,30 @@ func (i imageTransport) get(request *http.Request) *http.Response { return responseNotAllowed } +func normalizeImageNameSimple(image string) string { + parts := strings.Split(image, "/") + + switch len(parts) { + case 1: + // image like: ubuntu -> docker.io/library/ubuntu + return fmt.Sprintf("%s/library/%s", defaultRegistry, image) + case 2: + // image like: project/xx or registry/project + firstPart := parts[0] + if firstPart == "localhost" || (strings.Contains(firstPart, ".") || strings.Contains(firstPart, ":")) { + return image + } + return fmt.Sprintf("%s/%s", defaultRegistry, image) + default: + // image like: registry/project/xx/sub + firstPart := parts[0] + if firstPart == "localhost" || (strings.Contains(firstPart, ".") || strings.Contains(firstPart, ":")) { + return image + } + return fmt.Sprintf("%s/%s", defaultRegistry, image) + } +} + func init() { utilruntime.Must(RegisterModule("image", ModuleImage)) } diff --git a/pkg/variable/helper.go b/pkg/variable/helper.go index bcfac0d7..be2bafb1 100644 --- a/pkg/variable/helper.go +++ b/pkg/variable/helper.go @@ -352,15 +352,22 @@ func AnyVar(ctx, args map[string]any, dest any, keys ...string) error { if !found { return errors.Errorf("cannot find variable %q", strings.Join(keys, ".")) } - valBytes, err := json.Marshal(val) - if err != nil { - return errors.Wrapf(err, "failed to marshal variable %q", strings.Join(keys, ".")) + var valBytes []byte + switch valv := val.(type) { + case string: + valBytes = []byte(valv) + case []any: + valBytes, err = json.Marshal(valv) + if err != nil { + return errors.Wrapf(err, "failed to marshal variable %q", strings.Join(keys, ".")) + } } - valBytes, err = tmpl.Parse(ctx, string(valBytes)) + + valBytesAfterTmpl, err := tmpl.Parse(ctx, string(valBytes)) if err != nil { return err } - err = json.Unmarshal(valBytes, dest) + err = json.Unmarshal(valBytesAfterTmpl, dest) if err != nil { return errors.Wrapf(err, "failed to unmarshal variable %q to dest", strings.Join(keys, ".")) }