Skip to content

Commit

Permalink
feat: Use external patch for Docker provider custom image (#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
jimmidyson authored Sep 26, 2023
1 parent 90a8ae1 commit a1b356b
Show file tree
Hide file tree
Showing 18 changed files with 623 additions and 91 deletions.
2 changes: 1 addition & 1 deletion api/v1alpha1/clusterconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (Image) VariableSchema() clusterv1.VariableSchema {
"tag": {
Description: "Image tag to use.",
Type: "string",
Pattern: patterns.Anchored(patterns.Tag),
Pattern: patterns.Anchored(patterns.ImageTag),
},
},
},
Expand Down
26 changes: 26 additions & 0 deletions api/v1alpha1/docker_clusterconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
package v1alpha1

import (
"maps"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"

"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/openapi/patterns"
)

//+kubebuilder:object:root=true
Expand All @@ -21,11 +25,21 @@ type DockerClusterConfig struct {
// DockerClusterConfigSpec defines the desired state of DockerClusterConfig.
type DockerClusterConfigSpec struct {
GenericClusterConfig `json:",inline"`

//+optional
CustomImage *OCIImage `json:"customImage,omitempty"`
}

func (DockerClusterConfigSpec) VariableSchema() clusterv1.VariableSchema {
clusterConfigProps := GenericClusterConfig{}.VariableSchema().OpenAPIV3Schema.Properties

maps.Copy(
clusterConfigProps,
map[string]clusterv1.JSONSchemaProps{
"customImage": OCIImage("").VariableSchema().OpenAPIV3Schema,
},
)

return clusterv1.VariableSchema{
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
Description: "Docker cluster configuration",
Expand All @@ -35,6 +49,18 @@ func (DockerClusterConfigSpec) VariableSchema() clusterv1.VariableSchema {
}
}

type OCIImage string

func (OCIImage) VariableSchema() clusterv1.VariableSchema {
return clusterv1.VariableSchema{
OpenAPIV3Schema: clusterv1.JSONSchemaProps{
Description: "Custom OCI image for control plane and worker nodes.",
Type: "string",
Pattern: patterns.Anchored(patterns.ImageReference),
},
}
}

func init() {
SchemeBuilder.Register(&DockerClusterConfig{})
}
5 changes: 5 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.

9 changes: 7 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"flag"
"fmt"
"os"
"slices"

"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
Expand All @@ -31,6 +30,7 @@ import (
awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/region"
dockerclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/docker/clusterconfig"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/docker/mutation/customimage"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/lifecycle/cni/calico"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/lifecycle/nfd"
"github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/lifecycle/servicelbgc"
Expand Down Expand Up @@ -151,7 +151,12 @@ func main() {

// dockerMetaPatchHandlers combines all Docker patch and variable handlers under a single handler.
// It allows to specify configuration under a single variable.
dockerMetaPatchHandlers := slices.Clone(genericMetaPatchHandlers)
dockerMetaPatchHandlers := append(
[]mutation.MetaMutater{
customimage.NewMetaPatch(),
},
genericMetaPatchHandlers...,
)
dockerMetaHandlers := []handlers.Named{
dockerclusterconfig.NewVariable(),
mutation.NewMetaGeneratePatchesHandler(
Expand Down
14 changes: 9 additions & 5 deletions common/pkg/capi/clustertopology/handlers/mutation/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers"
"github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/decoder"
)

type MetaMutater interface {
Expand All @@ -38,12 +39,15 @@ func NewMetaGeneratePatchesHandler(name string, m ...MetaMutater) handlers.Named
scheme := runtime.NewScheme()
_ = bootstrapv1.AddToScheme(scheme)
_ = controlplanev1.AddToScheme(scheme)

capiDecoder := serializer.NewCodecFactory(scheme).UniversalDecoder(
controlplanev1.GroupVersion,
bootstrapv1.GroupVersion,
)

return metaGeneratePatches{
name: name,
decoder: serializer.NewCodecFactory(scheme).UniversalDecoder(
controlplanev1.GroupVersion,
bootstrapv1.GroupVersion,
),
name: name,
decoder: decoder.NewDecoderWithUnstructuredFallback(capiDecoder),
mutaters: m,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ func (h *testHandler) Mutate(
}

return patches.Generate(
obj, machineVars(), &holderRef, selectors.AllWorkersSelector(), logr.Discard(),
obj,
machineVars(),
&holderRef,
selectors.WorkersKubeadmConfigTemplateSelector(),
logr.Discard(),
func(obj *bootstrapv1.KubeadmConfigTemplate) error {
obj.Spec.Template.Spec.PostKubeadmCommands = append(
obj.Spec.Template.Spec.PostKubeadmCommands,
Expand Down
46 changes: 35 additions & 11 deletions common/pkg/capi/clustertopology/patches/selectors/selectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,70 @@ func ControlPlane() clusterv1.PatchSelector {
}
}

func DefaultWorkerSelector() clusterv1.PatchSelector {
func WorkersKubeadmConfigTemplateSelector() clusterv1.PatchSelector {
return clusterv1.PatchSelector{
APIVersion: bootstrapv1.GroupVersion.String(),
Kind: "KubeadmConfigTemplate",
MatchResources: clusterv1.PatchSelectorMatch{
MachineDeploymentClass: &clusterv1.PatchSelectorMatchMachineDeploymentClass{
Names: []string{
"default-worker",
"*",
},
},
},
}
}

func AllWorkersSelector() clusterv1.PatchSelector {
// InfrastructureCluster selector matches against infrastructure clusters.
// Passing in the API version (not the API group) is required because different providers could support different API
// versions. This also allows for a patch to select multiple infrastructure versions for the same provider.
func InfrastructureCluster(capiInfrastructureAPIVersion, kind string) clusterv1.PatchSelector {
return clusterv1.PatchSelector{
APIVersion: bootstrapv1.GroupVersion.String(),
Kind: "KubeadmConfigTemplate",
APIVersion: schema.GroupVersion{
Group: "infrastructure.cluster.x-k8s.io",
Version: capiInfrastructureAPIVersion,
}.String(),
Kind: kind,
MatchResources: clusterv1.PatchSelectorMatch{
InfrastructureCluster: true,
},
}
}

// InfrastructureWorkerMachineTemplates selector matches against infrastructure machines.
// Passing in the API version (not the API group) is required because different providers could support different API
// versions. This also allows for a patch to select multiple infrastructure versions for the same provider.
func InfrastructureWorkerMachineTemplates(
capiInfrastructureAPIVersion, kind string,
) clusterv1.PatchSelector {
return clusterv1.PatchSelector{
APIVersion: schema.GroupVersion{
Group: "infrastructure.cluster.x-k8s.io",
Version: capiInfrastructureAPIVersion,
}.String(),
Kind: kind,
MatchResources: clusterv1.PatchSelectorMatch{
MachineDeploymentClass: &clusterv1.PatchSelectorMatchMachineDeploymentClass{
Names: []string{
"*",
},
Names: []string{"*"},
},
},
}
}

// InfrastructureCluster selector matches against infrastructure clusters.
// InfrastructureControlPlaneMachines selector matches against infrastructure control-plane machines.
// Passing in the API version (not the API group) is required because different providers could support different API
// versions. This also allows for a oatch to select multiple infrastructure versions for the same provider.
func InfrastructureCluster(capiInfrastructureAPIVersion, kind string) clusterv1.PatchSelector {
func InfrastructureControlPlaneMachines(
capiInfrastructureAPIVersion, kind string,
) clusterv1.PatchSelector {
return clusterv1.PatchSelector{
APIVersion: schema.GroupVersion{
Group: "infrastructure.cluster.x-k8s.io",
Version: capiInfrastructureAPIVersion,
}.String(),
Kind: kind,
MatchResources: clusterv1.PatchSelectorMatch{
InfrastructureCluster: true,
ControlPlane: true,
},
}
}
32 changes: 32 additions & 0 deletions common/pkg/capi/decoder/unstructured_fallback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2023 D2iQ, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package decoder

import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)

type unstructructuredFallback struct {
defaultDecoder runtime.Decoder
}

var _ runtime.Decoder = unstructructuredFallback{}

func (u unstructructuredFallback) Decode(
data []byte, defaults *schema.GroupVersionKind, into runtime.Object,
) (runtime.Object, *schema.GroupVersionKind, error) {
if obj, gvk, err := u.defaultDecoder.Decode(data, defaults, into); err == nil {
return obj, gvk, nil
}

return unstructured.UnstructuredJSONScheme.Decode(data, defaults, into)
}

func NewDecoderWithUnstructuredFallback(decoder runtime.Decoder) runtime.Decoder {
return unstructructuredFallback{
defaultDecoder: decoder,
}
}
7 changes: 5 additions & 2 deletions common/pkg/openapi/patterns/distribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ const (
ImageRepository = `(` + HostAndOptionalPort + `/)?` + PathComponent + `(/` + PathComponent + `)*`

// See: https://github.com/distribution/reference/blob/v0.5.0/regexp.go#L68
Tag = `[\w][\w.-]{0,127}`
ImageTag = `[\w][\w.-]{0,127}`

// See: https://github.com/distribution/reference/blob/v0.5.0/regexp.go#L81
Digest = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][0-9A-Fa-f]{32,}`
ImageDigest = `[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][0-9A-Fa-f]{32,}`

// See: https://github.com/distribution/reference/blob/v0.5.0/regexp.go#L136C2-L136C14
ImageReference = ImageRepository + `(:` + ImageTag + `)?` + `(@` + ImageDigest + `)?`
)
21 changes: 19 additions & 2 deletions common/pkg/testutils/capitest/request/items.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package request

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/uuid"
Expand Down Expand Up @@ -38,8 +39,8 @@ func NewKubeadmConfigTemplateRequestItem(uid types.UID) runtimehooksv1.GenerateP
return NewRequestItem(
&bootstrapv1.KubeadmConfigTemplate{
TypeMeta: metav1.TypeMeta{
Kind: "KubeadmConfigTemplate",
APIVersion: bootstrapv1.GroupVersion.String(),
Kind: "KubeadmConfigTemplate",
},
Spec: bootstrapv1.KubeadmConfigTemplateSpec{
Template: bootstrapv1.KubeadmConfigTemplateResource{
Expand All @@ -63,8 +64,8 @@ func NewKubeadmControlPlaneTemplateRequestItem(
return NewRequestItem(
&controlplanev1.KubeadmControlPlaneTemplate{
TypeMeta: metav1.TypeMeta{
Kind: "KubeadmControlPlaneTemplate",
APIVersion: controlplanev1.GroupVersion.String(),
Kind: "KubeadmControlPlaneTemplate",
},
},
&runtimehooksv1.HolderReference{
Expand All @@ -74,3 +75,19 @@ func NewKubeadmControlPlaneTemplateRequestItem(
uid,
)
}

func NewUnstructuredRequestItem(
apiVersion, kind string,
holderRef *runtimehooksv1.HolderReference,
uid types.UID,
) runtimehooksv1.GeneratePatchesRequestItem {
obj := &unstructured.Unstructured{}
obj.SetAPIVersion(apiVersion)
obj.SetKind(kind)

return NewRequestItem(
obj,
holderRef,
uid,
)
}
24 changes: 0 additions & 24 deletions examples/capi-quick-start/docker-cluster-class.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,6 @@ spec:
discoverVariablesExtension: dockerclusterconfigvars.capi-runtime-extensions
generateExtension: dockerclusterconfigpatch.capi-runtime-extensions
name: cluster-config
- definitions:
- jsonPatches:
- op: add
path: /spec/template/spec/customImage
valueFrom:
template: ghcr.io/mesosphere/kind-node:{{ .builtin.machineDeployment.version }}
selector:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachineTemplate
matchResources:
machineDeploymentClass:
names:
- '*'
- jsonPatches:
- op: add
path: /spec/template/spec/customImage
valueFrom:
template: ghcr.io/mesosphere/kind-node:{{ .builtin.controlPlane.version }}
selector:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachineTemplate
matchResources:
controlPlane: true
name: customKinDNodeImageForWorkers
workers:
machineDeployments:
- class: default-worker
Expand Down
35 changes: 4 additions & 31 deletions hack/examples/bases/docker/kustomization.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -53,36 +53,9 @@ patches:
- op: "add"
path: "/spec/patches"
value:
- name: customKinDNodeImageForWorkers
definitions:
- selector:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachineTemplate
matchResources:
machineDeploymentClass:
names:
- "*"
jsonPatches:
- op: add
path: /spec/template/spec/customImage
valueFrom:
template: ghcr.io/mesosphere/kind-node:{{ .builtin.machineDeployment.version }}
- selector:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachineTemplate
matchResources:
controlPlane: true
jsonPatches:
- op: add
path: /spec/template/spec/customImage
valueFrom:
template: ghcr.io/mesosphere/kind-node:{{ .builtin.controlPlane.version }}
- op: "add"
path: "/spec/patches/0"
value:
name: "cluster-config"
external:
generateExtension: "dockerclusterconfigpatch.capi-runtime-extensions"
discoverVariablesExtension: "dockerclusterconfigvars.capi-runtime-extensions"
- name: "cluster-config"
external:
generateExtension: "dockerclusterconfigpatch.capi-runtime-extensions"
discoverVariablesExtension: "dockerclusterconfigvars.capi-runtime-extensions"
- op: "remove"
path: "/spec/variables"
Loading

0 comments on commit a1b356b

Please sign in to comment.