From 658329d9d1d9ec43d4b059295c0add920e8bc692 Mon Sep 17 00:00:00 2001 From: Derek Brown <6845676+DerekTBrown@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:57:51 -0800 Subject: [PATCH] [feat] add pull-through registry support --- pkg/api/types.go | 16 +++++++++++++ pkg/cluster/admin_docker_desktop.go | 3 +++ pkg/cluster/admin_k3d.go | 3 +++ pkg/cluster/admin_kind.go | 20 ++++++++++++++++ pkg/cluster/admin_kind_test.go | 36 +++++++++++++++++++++++++++++ pkg/cluster/admin_minikube.go | 3 +++ 6 files changed, 81 insertions(+) diff --git a/pkg/api/types.go b/pkg/api/types.go index 708e9f2..95cf181 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -16,6 +16,17 @@ type TypeMeta struct { APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` } +// PullThroughRegistry contains configuration for pull-through registries +type PullThroughRegistry struct { + // The FQDN of the registry (i.e. docker.io) + RegistryFQDN string `json:"registryFQDN,omitempty" yaml:"registryFQDN,omitempty"` + + // The RemoteURL of the registry (i.e. https://registry-1.docker.io) + RemoteURL string `json:"remoteURL,omitempty" yaml:"remoteURL,omitempty"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` + Password string `json:"password,omitempty" yaml:"password,omitempty"` +} + // Cluster contains cluster configuration. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type Cluster struct { @@ -42,6 +53,11 @@ type Cluster struct { // Not supported on all cluster products. Registry string `json:"registry,omitempty" yaml:"registry,omitempty"` + // A list of pull-through registries to configure on the cluster. + // + // Not supported on all cluster products. + PullThroughRegistries []PullThroughRegistry `json:"pullThroughRegistries,omitempty" yaml:"pullThroughRegistries,omitempty"` + // The desired version of Kubernetes to run. // // Examples: diff --git a/pkg/cluster/admin_docker_desktop.go b/pkg/cluster/admin_docker_desktop.go index 3603ff1..f57b9a0 100644 --- a/pkg/cluster/admin_docker_desktop.go +++ b/pkg/cluster/admin_docker_desktop.go @@ -27,6 +27,9 @@ func (a *dockerDesktopAdmin) Create(ctx context.Context, desired *api.Cluster, r if registry != nil { return fmt.Errorf("ctlptl currently does not support connecting a registry to docker-desktop") } + if len(desired.PullThroughRegistries) > 0 { + return fmt.Errorf("ctlptl currently does not support connecting pull-through registries to minikube") + } isLocalDockerDesktop := docker.IsLocalDockerDesktop(a.host, a.os) if !isLocalDockerDesktop { diff --git a/pkg/cluster/admin_k3d.go b/pkg/cluster/admin_k3d.go index 631abed..b746263 100644 --- a/pkg/cluster/admin_k3d.go +++ b/pkg/cluster/admin_k3d.go @@ -53,6 +53,9 @@ func (a *k3dAdmin) Create(ctx context.Context, desired *api.Cluster, registry *a if registry != nil { klog.V(3).Infof("Initializing cluster with registry config:\n%+v\n---\n", registry) } + if len(desired.PullThroughRegistries) > 0 { + return fmt.Errorf("ctlptl currently does not support connecting pull-through registries to minikube") + } k3dV, err := a.version(ctx) if err != nil { diff --git a/pkg/cluster/admin_kind.go b/pkg/cluster/admin_kind.go index 951fc20..14aa0f3 100644 --- a/pkg/cluster/admin_kind.go +++ b/pkg/cluster/admin_kind.go @@ -84,6 +84,26 @@ func (a *kindAdmin) kindClusterConfig(desired *api.Cluster, registry *api.Regist kindConfig.ContainerdConfigPatches = append(kindConfig.ContainerdConfigPatches, patch) } } + + for _, reg := range desired.PullThroughRegistries { + // Add the registry to the list of mirrors. + patch := fmt.Sprintf(`[plugins."io.containerd.grpc.v1.cri".registry.mirrors."%s"] + endpoint = ["http://%s"] +`, reg.RegistryFQDN, reg.RemoteURL) + kindConfig.ContainerdConfigPatches = append(kindConfig.ContainerdConfigPatches, patch) + + // Specify the auth for the registry, if provided. + if reg.Username != "" || reg.Password != "" { + passwordValue := os.ExpandEnv(reg.Password) + + patch := fmt.Sprintf(`[plugins."io.containerd.grpc.v1.cri".registry.configs."%s".auth] + username = "%s" + password = "%s" +`, reg.RegistryFQDN, reg.Username, passwordValue) + kindConfig.ContainerdConfigPatches = append(kindConfig.ContainerdConfigPatches, patch) + } + } + return kindConfig } diff --git a/pkg/cluster/admin_kind_test.go b/pkg/cluster/admin_kind_test.go index fdecd58..420befd 100644 --- a/pkg/cluster/admin_kind_test.go +++ b/pkg/cluster/admin_kind_test.go @@ -82,3 +82,39 @@ kind-control-plane2 "kind-control-plane2", }, nodeExec) } + +func TestKindClusterConfigWithPullThroughRegistries(t *testing.T) { + iostreams := genericclioptions.IOStreams{ + In: os.Stdin, + Out: os.Stdout, + ErrOut: os.Stderr, + } + runner := exec.NewFakeCmdRunner(func(argv []string) string { + return "" + }) + a := newKindAdmin(iostreams, runner, &fakeDockerClient{}) + + desired := &api.Cluster{ + PullThroughRegistries: []api.PullThroughRegistry{ + { + RegistryFQDN: "example.com", + RemoteURL: "example.com:5000", + Username: "user", + Password: "pass", + }, + }, + } + + kindConfig := a.kindClusterConfig(desired, nil, containerdRegistryV2) + + expectedMirror := `[plugins."io.containerd.grpc.v1.cri".registry.mirrors."example.com"] + endpoint = ["http://example.com:5000"] +` + expectedAuth := `[plugins."io.containerd.grpc.v1.cri".registry.configs."example.com".auth] + username = "user" + password = "pass" +` + + assert.Contains(t, kindConfig.ContainerdConfigPatches, expectedMirror) + assert.Contains(t, kindConfig.ContainerdConfigPatches, expectedAuth) +} diff --git a/pkg/cluster/admin_minikube.go b/pkg/cluster/admin_minikube.go index ec84335..03a72a9 100644 --- a/pkg/cluster/admin_minikube.go +++ b/pkg/cluster/admin_minikube.go @@ -83,6 +83,9 @@ func (a *minikubeAdmin) Create(ctx context.Context, desired *api.Cluster, regist if registry != nil { klog.V(3).Infof("Initializing cluster with registry config:\n%+v\n---\n", registry) } + if len(desired.PullThroughRegistries) > 0 { + return fmt.Errorf("ctlptl currently does not support connecting pull-through registries to minikube") + } v, err := a.version(ctx) if err != nil {