Skip to content

Commit

Permalink
feat: support configuring CoreDNS image (#950)
Browse files Browse the repository at this point in the history
**What problem does this PR solve?**:
New API to support setting CoreDNS image repository and image tag.
```
spec:
  topology:
    variables:
      - name: clusterConfig
        value:
          dns:
            coreDNS:
              image:
                repository: my-registry.io/my-org/my-repo
                tag: "v1.11.3_custom.0"
```

**Which issue(s) this PR fixes**:
Fixes #

**How Has This Been Tested?**:
<!--
Please describe the tests that you ran to verify your changes.
Provide output from the tests and any manual steps needed to replicate
the tests.
-->
1. New unit tests.
2. Brought up a Nutanix cluster with and verified the expected image was
set in the CoreDNS Deployment.
```
        dns:
          coreDNS:
            image:
              repository: docker.io/dkoshkin
              tag: "v1.11.3"
```

**Special notes for your reviewer**:
<!--
Use this to provide any additional information to the reviewers.
This may include:
- Best way to review the PR.
- Where the author wants the most review attention on.
- etc.
-->
  • Loading branch information
dkoshkin authored Oct 28, 2024
1 parent c6307ea commit b5d23a0
Show file tree
Hide file tree
Showing 13 changed files with 554 additions and 1 deletion.
18 changes: 17 additions & 1 deletion api/v1alpha1/clusterconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,11 @@ type GenericClusterConfigSpec struct {
// +kubebuilder:validation:Optional
Users []User `json:"users,omitempty"`

// +optional
// +kubebuilder:validation:Optional
EncryptionAtRest *EncryptionAtRest `json:"encryptionAtRest,omitempty"`

// +kubebuilder:validation:Optional
DNS *DNS `json:"dns,omitempty"`
}

type Image struct {
Expand Down Expand Up @@ -310,6 +313,19 @@ type AESConfiguration struct{}

type SecretboxConfiguration struct{}

// DNS defines the DNS configuration for the cluster.
type DNS struct {
// CoreDNS defines the CoreDNS configuration for the cluster.
// +kubebuilder:validation:Optional
CoreDNS *CoreDNS `json:"coreDNS,omitempty"`
}

type CoreDNS struct {
// Image required for overriding Kubernetes DNS image details.
// +kubebuilder:validation:Optional
Image *Image `json:"image,omitempty"`
}

//nolint:gochecknoinits // Idiomatic to use init functions to register APIs with scheme.
func init() {
SchemeBuilder.Register(
Expand Down
3 changes: 3 additions & 0 deletions api/v1alpha1/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,8 @@ const (
// ImageRegistriesVariableName is the image registries patch variable name.
ImageRegistriesVariableName = "imageRegistries"

// DNSVariableName is the DNS external patch variable name.
DNSVariableName = "dns"

ClusterUUIDAnnotationKey = APIGroup + "/cluster-uuid"
)
20 changes: 20 additions & 0 deletions api/v1alpha1/crds/caren.nutanix.com_awsclusterconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,26 @@ spec:
type: object
type: array
type: object
dns:
description: DNS defines the DNS configuration for the cluster.
properties:
coreDNS:
description: CoreDNS defines the CoreDNS configuration for the cluster.
properties:
image:
description: Image required for overriding Kubernetes DNS image details.
properties:
repository:
description: Repository is used to override the image repository to pull from.
pattern: ^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*|\[(?:[a-fA-F0-9:]+)\])(:[0-9]+)?/)?[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*(/[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*)*$
type: string
tag:
description: Tag is used to override the default image tag.
pattern: ^[\w][\w.-]{0,127}$
type: string
type: object
type: object
type: object
encryptionAtRest:
description: |-
EncryptionAtRest defines the configuration to enable encryption at REST
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha1/crds/caren.nutanix.com_dockerclusterconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,26 @@ spec:
type: object
type: array
type: object
dns:
description: DNS defines the DNS configuration for the cluster.
properties:
coreDNS:
description: CoreDNS defines the CoreDNS configuration for the cluster.
properties:
image:
description: Image required for overriding Kubernetes DNS image details.
properties:
repository:
description: Repository is used to override the image repository to pull from.
pattern: ^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*|\[(?:[a-fA-F0-9:]+)\])(:[0-9]+)?/)?[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*(/[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*)*$
type: string
tag:
description: Tag is used to override the default image tag.
pattern: ^[\w][\w.-]{0,127}$
type: string
type: object
type: object
type: object
docker:
type: object
encryptionAtRest:
Expand Down
24 changes: 24 additions & 0 deletions api/v1alpha1/crds/caren.nutanix.com_genericclusterconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,30 @@ spec:
spec:
description: GenericClusterConfigSpec defines the desired state of GenericClusterConfig.
properties:
dns:
description: DNS defines the DNS configuration for the cluster.
properties:
coreDNS:
description: CoreDNS defines the CoreDNS configuration for the
cluster.
properties:
image:
description: Image required for overriding Kubernetes DNS
image details.
properties:
repository:
description: Repository is used to override the image
repository to pull from.
pattern: ^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*|\[(?:[a-fA-F0-9:]+)\])(:[0-9]+)?/)?[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*(/[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*)*$
type: string
tag:
description: Tag is used to override the default image
tag.
pattern: ^[\w][\w.-]{0,127}$
type: string
type: object
type: object
type: object
encryptionAtRest:
description: |-
EncryptionAtRest defines the configuration to enable encryption at REST
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha1/crds/caren.nutanix.com_nutanixclusterconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,26 @@ spec:
type: object
type: array
type: object
dns:
description: DNS defines the DNS configuration for the cluster.
properties:
coreDNS:
description: CoreDNS defines the CoreDNS configuration for the cluster.
properties:
image:
description: Image required for overriding Kubernetes DNS image details.
properties:
repository:
description: Repository is used to override the image repository to pull from.
pattern: ^((?:[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*|\[(?:[a-fA-F0-9:]+)\])(:[0-9]+)?/)?[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*(/[a-z0-9]+((?:[._]|__|[-]+)[a-z0-9]+)*)*$
type: string
tag:
description: Tag is used to override the default image tag.
pattern: ^[\w][\w.-]{0,127}$
type: string
type: object
type: object
type: object
encryptionAtRest:
description: |-
EncryptionAtRest defines the configuration to enable encryption at REST
Expand Down
45 changes: 45 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

50 changes: 50 additions & 0 deletions docs/content/customization/generic/dns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
+++
title = "etcd"
+++

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

The DNS configuration can then be manipulated via the cluster variables.
If the `dns` property is not specified, then the customization will be skipped.

## CoreDNS

The CoreDNS configuration can then be manipulated via the cluster variables.
If the `dns.coreDNS` property is not specified, then the customization will be skipped.

### Example

To change the repository and tag for the container image for the CoreDNS pod, specify the following configuration:

> Note do not include "coredns" in the repository, kubeadm already appends it.
```yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: <NAME>
spec:
topology:
variables:
- name: clusterConfig
value:
dns:
coreDNS:
image:
repository: my-registry.io/my-org/my-repo
tag: "v1.11.3_custom.0"
```
Applying this configuration will result in the following value being set:
- `KubeadmControlPlaneTemplate`:

- ```yaml
spec:
kubeadmConfigSpec:
clusterConfiguration:
dns:
imageRepository: "my-registry.io/my-org/my-repo"
imageTag: "v1.11.3_custom.0"
```
2 changes: 2 additions & 0 deletions docs/content/customization/generic/etcd.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ the customization will be skipped.

To change the repository and tag for the container image for the etcd pod, specify the following configuration:

> Note do not include "etcd" in the repository, kubeadm already appends it.
```yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
Expand Down
114 changes: 114 additions & 0 deletions pkg/handlers/generic/mutation/coredns/inject.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2023 Nutanix. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package coredns

import (
"context"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"

"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/mutation"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/patches/selectors"
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
)

const (
// VariableName is the external patch variable name.
VariableName = "coreDNS"
)

type coreDNSPatchHandler struct {
variableName string
variableFieldPath []string
}

func NewPatch() *coreDNSPatchHandler {
return newKubernetesDNSPatchHandlerPatchHandler(
v1alpha1.ClusterConfigVariableName, v1alpha1.DNSVariableName, VariableName,
)
}

func newKubernetesDNSPatchHandlerPatchHandler(
variableName string,
variableFieldPath ...string,
) *coreDNSPatchHandler {
return &coreDNSPatchHandler{
variableName: variableName,
variableFieldPath: variableFieldPath,
}
}

func (h *coreDNSPatchHandler) Mutate(
ctx context.Context,
obj *unstructured.Unstructured,
vars map[string]apiextensionsv1.JSON,
holderRef runtimehooksv1.HolderReference,
_ ctrlclient.ObjectKey,
_ mutation.ClusterGetter,
) error {
log := ctrl.LoggerFrom(ctx).WithValues(
"holderRef", holderRef,
)

coreDNSVar, err := variables.Get[v1alpha1.CoreDNS](
vars,
h.variableName,
h.variableFieldPath...,
)
if err != nil {
if variables.IsNotFoundError(err) {
log.V(5).Info("coreDNSVar variable not defined")
return nil
}
return err
}

log = log.WithValues(
"variableName",
h.variableName,
"variableFieldPath",
h.variableFieldPath,
"variableValue",
coreDNSVar,
)

return patches.MutateIfApplicable(
obj, vars, &holderRef, selectors.ControlPlane(), log,
func(obj *controlplanev1.KubeadmControlPlaneTemplate) error {
log.WithValues(
"patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(),
"patchedObjectName", ctrlclient.ObjectKeyFromObject(obj),
).Info("setting CoreDNS version if needed")

if obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration == nil {
obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration = &bootstrapv1.ClusterConfiguration{}
}

if coreDNSVar.Image == nil {
return nil
}

dns := obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.DNS

if coreDNSVar.Image.Tag != "" {
dns.ImageTag = coreDNSVar.Image.Tag
}

if coreDNSVar.Image.Repository != "" {
dns.ImageRepository = coreDNSVar.Image.Repository
}

obj.Spec.Template.Spec.KubeadmConfigSpec.ClusterConfiguration.DNS = dns

return nil
})
}
Loading

0 comments on commit b5d23a0

Please sign in to comment.