Skip to content

Commit

Permalink
feat: global image registry mirror variable
Browse files Browse the repository at this point in the history
  • Loading branch information
supershal committed Jan 23, 2024
1 parent 176d403 commit 4dd5b1f
Show file tree
Hide file tree
Showing 19 changed files with 389 additions and 307 deletions.
52 changes: 21 additions & 31 deletions api/v1alpha1/clusterconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ type GenericClusterConfig struct {
// +optional
ImageRegistries ImageRegistries `json:"imageRegistries,omitempty"`

// +optional
GlobalImageRegistryMirror *GlobalImageRegistryMirror `json:"globalImageRegistryMirror,omitempty"`

// +optional
Addons *Addons `json:"addons,omitempty"`
}
Expand All @@ -108,7 +111,8 @@ func (s GenericClusterConfig) VariableSchema() clusterv1.VariableSchema { //noli
"",
).VariableSchema().
OpenAPIV3Schema,
"imageRegistries": ImageRegistries{}.VariableSchema().OpenAPIV3Schema,
"imageRegistries": ImageRegistries{}.VariableSchema().OpenAPIV3Schema,
"globalImageRegistryMirror": GlobalImageRegistryMirror{}.VariableSchema().OpenAPIV3Schema,
},
},
}
Expand Down Expand Up @@ -240,7 +244,7 @@ func (ExtraAPIServerCertSANs) VariableSchema() clusterv1.VariableSchema {

type ImageCredentials struct {
// The Secret containing the registry credentials and CA certificate
// The Secret should have keys 'username', 'password' and 'caCert'
// The Secret should have keys 'username', 'password' and 'ca.crt'
// This credentials Secret is not required for some registries, e.g. ECR.
// +optional
SecretRef *corev1.ObjectReference `json:"secretRef,omitempty"`
Expand All @@ -253,7 +257,7 @@ func (ImageCredentials) VariableSchema() clusterv1.VariableSchema {
Properties: map[string]clusterv1.JSONSchemaProps{
"secretRef": {
Description: "The Secret containing the registry credentials. " +
"The Secret should have keys 'username', 'password'. " +
"The Secret should have keys 'username', 'password' and 'ca.crt' " +
"This credentials Secret is not required for some registries, e.g. ECR.",
Type: "object",
Properties: map[string]clusterv1.JSONSchemaProps{
Expand All @@ -274,37 +278,28 @@ func (ImageCredentials) VariableSchema() clusterv1.VariableSchema {
}
}

type RegistryMirror struct {
// The secret containing CA certificate for the registry mirror.
// The secret should have 'ca.crt' key
// GlobalImageRegistryMirror sets default mirror configuration for all the image registries.
type GlobalImageRegistryMirror struct {
// Registry URL.
URL string `json:"url"`

// Credentials and CA certificate for the image registry mirror
// +optional
SecretRef *corev1.ObjectReference `json:"secretRef,omitempty"`
Credentials *ImageCredentials `json:"credentials,omitempty"`
}

func (RegistryMirror) VariableSchema() clusterv1.VariableSchema {
func (GlobalImageRegistryMirror) VariableSchema() clusterv1.VariableSchema {
return clusterv1.VariableSchema{
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
Type: "object",
Properties: map[string]clusterv1.JSONSchemaProps{
"secretRef": {
Description: "The Secret containing the registry CA certificate. " +
"The Secret should have keys 'ca.crt'. " +
"This credentials Secret is not required for public registries.",
Type: "object",
Properties: map[string]clusterv1.JSONSchemaProps{
"name": {
Description: "The name of the Secret containing the registry CA certificate.",
Type: "string",
},
"namespace": {
Description: "The namespace of the Secret containing the registry CA certificate. " +
"Defaults to the namespace of the KubeadmControlPlaneTemplate and KubeadmConfigTemplate" +
" that reference this variable.",
Type: "string",
},
},
"url": {
Description: "Registry mirror URL.",
Type: "string",
},
"credentials": ImageCredentials{}.VariableSchema().OpenAPIV3Schema,
},
Required: []string{"url"},
},
}
}
Expand All @@ -313,13 +308,9 @@ type ImageRegistry struct {
// Registry URL.
URL string `json:"url"`

// Credentials for the image registry
// Credentials and CA certificate for the image registry
// +optional
Credentials *ImageCredentials `json:"credentials,omitempty"`

// Use this registry as a mirror
// +optional
Mirror *RegistryMirror `json:"mirror,omitempty"`
}

func (ImageRegistry) VariableSchema() clusterv1.VariableSchema {
Expand All @@ -332,7 +323,6 @@ func (ImageRegistry) VariableSchema() clusterv1.VariableSchema {
Type: "string",
},
"credentials": ImageCredentials{}.VariableSchema().OpenAPIV3Schema,
"mirror": RegistryMirror{}.VariableSchema().OpenAPIV3Schema,
},
Required: []string{"url"},
},
Expand Down
50 changes: 25 additions & 25 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions docs/content/customization/generic/global-mirror.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
+++
title = "Global Image Registry Mirror"
+++

Add containerd image registry mirror configuration to all Nodes in the cluster.

When the `globalImageRegistryMirror` variable is set, `files` with configurations for
[Containerd default mirror](https://github.com/containerd/containerd/blob/main/docs/hosts.md#setup-default-mirror-for-all-registries) will be added.

This customization will be available when the
[provider-specific cluster configuration patch]({{< ref "..">}}) is included in the `ClusterClass`.

## Example

To provide image registry mirror with CA certificate, specify the following configuration:

If your registry mirror requires self signed CA certifate, create a Kubernetes Secret with keys for `ca.crt`:

```shell
kubectl create secret generic my-mirror-ca-cert-secret \
--from-file=ca.crt=registry-ca.crt
```

```yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: <NAME>
spec:
topology:
variables:
- name: clusterConfig
value:
globalImageRegistryMirror:
url: https://my-mirror.io
credentials:
secretRef:
name: my-mirror-ca-cert-secret
```
Applying this configuration will result in following new files on the
`KubeadmControlPlaneTemplate` and `KubeadmConfigTemplate`

- `/etc/containerd/certs.d/_default/hosts.toml`
- `/etc/certs/mirror.pem`

To use a public hosted image registry (ex. ECR) as mirror, specify the following configuration:

```yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: <NAME>
spec:
topology:
variables:
- name: clusterConfig
value:
globalImageRegistryMirror:
url: https://123456789.dkr.ecr.us-east-1.amazonaws.com
```

Applying this configuration will result in following new files on the
`KubeadmControlPlaneTemplate` and `KubeadmConfigTemplate`

- `/etc/containerd/certs.d/_default/hosts.toml`
6 changes: 4 additions & 2 deletions pkg/handlers/aws/mutation/metapatch_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import (
imageregistrycredentialstests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/imageregistries/credentials/tests"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/kubernetesimagerepository"
kubernetesimagerepositorytests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/kubernetesimagerepository/tests"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/mirrors"
globalimageregistrymirrortests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/mirrors/tests"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/workerconfig"
)

Expand Down Expand Up @@ -156,12 +158,12 @@ func TestGeneratePatches(t *testing.T) {
imageregistries.VariableName,
)

imageregistrycredentialstests.TestGenerateMirrorPatches(
globalimageregistrymirrortests.TestGeneratePatches(
t,
metaPatchGeneratorFunc(mgr),
mgr.GetClient(),
clusterconfig.MetaVariableName,
imageregistries.VariableName,
mirrors.GlobalMirrorVariableName,
)

amitests.TestControlPlaneGeneratePatches(
Expand Down
6 changes: 4 additions & 2 deletions pkg/handlers/docker/mutation/metapatch_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
imageregistrycredentialstests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/imageregistries/credentials/tests"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/kubernetesimagerepository"
kubernetesimagerepositorytests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/kubernetesimagerepository/tests"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/mirrors"
globalimageregistrymirrortests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/mirrors/tests"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/workerconfig"
)

Expand Down Expand Up @@ -113,11 +115,11 @@ func TestGeneratePatches(t *testing.T) {
imageregistries.VariableName,
)

imageregistrycredentialstests.TestGenerateMirrorPatches(
globalimageregistrymirrortests.TestGeneratePatches(
t,
metaPatchGeneratorFunc(mgr),
mgr.GetClient(),
clusterconfig.MetaVariableName,
imageregistries.VariableName,
mirrors.GlobalMirrorVariableName,
)
}
2 changes: 2 additions & 0 deletions pkg/handlers/generic/mutation/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/httpproxy"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/imageregistries/credentials"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/kubernetesimagerepository"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/mirrors"
)

// MetaMutators returns all generic patch handlers.
Expand All @@ -25,6 +26,7 @@ func MetaMutators(mgr manager.Manager) []mutation.MetaMutator {
httpproxy.NewPatch(mgr.GetClient()),
kubernetesimagerepository.NewPatch(),
credentials.NewPatch(mgr.GetClient()),
mirrors.NewPatch(mgr.GetClient()),
calico.NewPatch(),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,7 @@ func (c providerConfig) isCredentialsEmpty() bool {
c.Password == ""
}

func templateFilesForImageCredentialProviderConfigs(
config providerConfig,
mirror *mirrorConfig,
) ([]cabpkv1.File, error) {
func templateFilesForImageCredentialProviderConfigs(config providerConfig) ([]cabpkv1.File, error) {
var files []cabpkv1.File

kubeletCredentialProviderConfigFile, err := templateKubeletCredentialProviderConfig()
Expand All @@ -68,7 +65,6 @@ func templateFilesForImageCredentialProviderConfigs(

kubeletDynamicCredentialProviderConfigFile, err := templateDynamicCredentialProviderConfig(
config,
mirror,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -104,7 +100,6 @@ func templateKubeletCredentialProviderConfig() (*cabpkv1.File, error) {

func templateDynamicCredentialProviderConfig(
config providerConfig,
mirror *mirrorConfig,
) (*cabpkv1.File, error) {
registryURL, err := url.ParseRequestURI(config.URL)
if err != nil {
Expand Down Expand Up @@ -142,13 +137,11 @@ func templateDynamicCredentialProviderConfig(
ProviderBinary string
ProviderArgs []string
ProviderAPIVersion string
Mirror *mirrorConfig
}{
RegistryHost: registryHostWithPath,
ProviderBinary: providerBinary,
ProviderArgs: providerArgs,
ProviderAPIVersion: providerAPIVersion,
Mirror: mirror,
}

return fileFromTemplate(t, templateInput, kubeletDynamicCredentialProviderConfigOnRemote)
Expand Down
Loading

0 comments on commit 4dd5b1f

Please sign in to comment.