diff --git a/bootstrap/eks/api/v1alpha3/conversion.go b/bootstrap/eks/api/v1alpha3/conversion.go index c66add2c41..ad6021e4f0 100644 --- a/bootstrap/eks/api/v1alpha3/conversion.go +++ b/bootstrap/eks/api/v1alpha3/conversion.go @@ -17,22 +17,52 @@ limitations under the License. package v1alpha3 import ( - "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1beta1" + apiconversion "k8s.io/apimachinery/pkg/conversion" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" + + "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1beta1" ) // ConvertTo converts the v1alpha3 EKSConfig receiver to a v1beta1 EKSConfig. func (r *EKSConfig) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v1beta1.EKSConfig) - return Convert_v1alpha3_EKSConfig_To_v1beta1_EKSConfig(r, dst, nil) + if err := Convert_v1alpha3_EKSConfig_To_v1beta1_EKSConfig(r, dst, nil); err != nil { + return err + } + + restored := &v1beta1.EKSConfig{} + if ok, err := utilconversion.UnmarshalData(r, restored); err != nil || !ok { + return err + } + + dst.Spec.ContainerRuntime = restored.Spec.ContainerRuntime + dst.Spec.DNSClusterIP = restored.Spec.DNSClusterIP + dst.Spec.DockerConfigJSON = restored.Spec.DockerConfigJSON + dst.Spec.APIRetryAttempts = restored.Spec.APIRetryAttempts + if restored.Spec.PauseContainer != nil { + dst.Spec.PauseContainer.AccountNumber = restored.Spec.PauseContainer.AccountNumber + dst.Spec.PauseContainer.Version = restored.Spec.PauseContainer.Version + } + dst.Spec.UseMaxPods = restored.Spec.UseMaxPods + + return nil } // ConvertFrom converts the v1beta1 EKSConfig receiver to a v1alpha3 EKSConfig. func (r *EKSConfig) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v1beta1.EKSConfig) - return Convert_v1beta1_EKSConfig_To_v1alpha3_EKSConfig(src, r, nil) + if err := Convert_v1beta1_EKSConfig_To_v1alpha3_EKSConfig(src, r, nil); err != nil { + return err + } + + if err := utilconversion.MarshalData(src, r); err != nil { + return err + } + + return nil } // ConvertTo converts the v1alpha3 EKSConfigList receiver to a v1beta1 EKSConfigList. @@ -76,3 +106,9 @@ func (r *EKSConfigTemplateList) ConvertFrom(srcRaw conversion.Hub) error { return Convert_v1beta1_EKSConfigTemplateList_To_v1alpha3_EKSConfigTemplateList(src, r, nil) } + +func Convert_v1beta1_EKSConfigSpec_To_v1alpha3_EKSConfigSpec(in *v1beta1.EKSConfigSpec, out *EKSConfigSpec, s apiconversion.Scope) error { + out.KubeletExtraArgs = in.KubeletExtraArgs + + return nil +} diff --git a/bootstrap/eks/api/v1alpha3/zz_generated.conversion.go b/bootstrap/eks/api/v1alpha3/zz_generated.conversion.go index 251f37309e..08c2db670d 100644 --- a/bootstrap/eks/api/v1alpha3/zz_generated.conversion.go +++ b/bootstrap/eks/api/v1alpha3/zz_generated.conversion.go @@ -63,11 +63,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.EKSConfigSpec)(nil), (*EKSConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_EKSConfigSpec_To_v1alpha3_EKSConfigSpec(a.(*v1beta1.EKSConfigSpec), b.(*EKSConfigSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*EKSConfigStatus)(nil), (*v1beta1.EKSConfigStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha3_EKSConfigStatus_To_v1beta1_EKSConfigStatus(a.(*EKSConfigStatus), b.(*v1beta1.EKSConfigStatus), scope) }); err != nil { @@ -118,6 +113,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.EKSConfigSpec)(nil), (*EKSConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_EKSConfigSpec_To_v1alpha3_EKSConfigSpec(a.(*v1beta1.EKSConfigSpec), b.(*EKSConfigSpec), scope) + }); err != nil { + return err + } return nil } @@ -207,14 +207,15 @@ func Convert_v1alpha3_EKSConfigSpec_To_v1beta1_EKSConfigSpec(in *EKSConfigSpec, func autoConvert_v1beta1_EKSConfigSpec_To_v1alpha3_EKSConfigSpec(in *v1beta1.EKSConfigSpec, out *EKSConfigSpec, s conversion.Scope) error { out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.ContainerRuntime requires manual conversion: does not exist in peer-type + // WARNING: in.DNSClusterIP requires manual conversion: does not exist in peer-type + // WARNING: in.DockerConfigJSON requires manual conversion: does not exist in peer-type + // WARNING: in.APIRetryAttempts requires manual conversion: does not exist in peer-type + // WARNING: in.PauseContainer requires manual conversion: does not exist in peer-type + // WARNING: in.UseMaxPods requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_EKSConfigSpec_To_v1alpha3_EKSConfigSpec is an autogenerated conversion function. -func Convert_v1beta1_EKSConfigSpec_To_v1alpha3_EKSConfigSpec(in *v1beta1.EKSConfigSpec, out *EKSConfigSpec, s conversion.Scope) error { - return autoConvert_v1beta1_EKSConfigSpec_To_v1alpha3_EKSConfigSpec(in, out, s) -} - func autoConvert_v1alpha3_EKSConfigStatus_To_v1beta1_EKSConfigStatus(in *EKSConfigStatus, out *v1beta1.EKSConfigStatus, s conversion.Scope) error { out.Ready = in.Ready out.DataSecretName = (*string)(unsafe.Pointer(in.DataSecretName)) @@ -293,7 +294,17 @@ func Convert_v1beta1_EKSConfigTemplate_To_v1alpha3_EKSConfigTemplate(in *v1beta1 func autoConvert_v1alpha3_EKSConfigTemplateList_To_v1beta1_EKSConfigTemplateList(in *EKSConfigTemplateList, out *v1beta1.EKSConfigTemplateList, s conversion.Scope) error { out.ListMeta = in.ListMeta - out.Items = *(*[]v1beta1.EKSConfigTemplate)(unsafe.Pointer(&in.Items)) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta1.EKSConfigTemplate, len(*in)) + for i := range *in { + if err := Convert_v1alpha3_EKSConfigTemplate_To_v1beta1_EKSConfigTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } return nil } @@ -304,7 +315,17 @@ func Convert_v1alpha3_EKSConfigTemplateList_To_v1beta1_EKSConfigTemplateList(in func autoConvert_v1beta1_EKSConfigTemplateList_To_v1alpha3_EKSConfigTemplateList(in *v1beta1.EKSConfigTemplateList, out *EKSConfigTemplateList, s conversion.Scope) error { out.ListMeta = in.ListMeta - out.Items = *(*[]EKSConfigTemplate)(unsafe.Pointer(&in.Items)) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EKSConfigTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta1_EKSConfigTemplate_To_v1alpha3_EKSConfigTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } return nil } diff --git a/bootstrap/eks/api/v1alpha4/conversion.go b/bootstrap/eks/api/v1alpha4/conversion.go index 69c1b74cfb..8a29a012be 100644 --- a/bootstrap/eks/api/v1alpha4/conversion.go +++ b/bootstrap/eks/api/v1alpha4/conversion.go @@ -17,22 +17,51 @@ limitations under the License. package v1alpha4 import ( - "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1beta1" + apiconversion "k8s.io/apimachinery/pkg/conversion" + utilconversion "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" + + "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1beta1" ) // ConvertTo converts the v1alpha4 EKSConfig receiver to a v1beta1 EKSConfig. func (r *EKSConfig) ConvertTo(dstRaw conversion.Hub) error { dst := dstRaw.(*v1beta1.EKSConfig) - - return Convert_v1alpha4_EKSConfig_To_v1beta1_EKSConfig(r, dst, nil) + if err := Convert_v1alpha4_EKSConfig_To_v1beta1_EKSConfig(r, dst, nil); err != nil { + return err + } + + restored := &v1beta1.EKSConfig{} + if ok, err := utilconversion.UnmarshalData(r, restored); err != nil || !ok { + return err + } + + dst.Spec.ContainerRuntime = restored.Spec.ContainerRuntime + dst.Spec.DNSClusterIP = restored.Spec.DNSClusterIP + dst.Spec.DockerConfigJSON = restored.Spec.DockerConfigJSON + dst.Spec.APIRetryAttempts = restored.Spec.APIRetryAttempts + if restored.Spec.PauseContainer != nil { + dst.Spec.PauseContainer.AccountNumber = restored.Spec.PauseContainer.AccountNumber + dst.Spec.PauseContainer.Version = restored.Spec.PauseContainer.Version + } + dst.Spec.UseMaxPods = restored.Spec.UseMaxPods + + return nil } // ConvertFrom converts the v1beta1 EKSConfig receiver to a v1alpha4 EKSConfig. func (r *EKSConfig) ConvertFrom(srcRaw conversion.Hub) error { src := srcRaw.(*v1beta1.EKSConfig) - return Convert_v1beta1_EKSConfig_To_v1alpha4_EKSConfig(src, r, nil) + if err := Convert_v1beta1_EKSConfig_To_v1alpha4_EKSConfig(src, r, nil); err != nil { + return err + } + + if err := utilconversion.MarshalData(src, r); err != nil { + return err + } + + return nil } // ConvertTo converts the v1alpha4 EKSConfigList receiver to a v1beta1 EKSConfigList. @@ -76,3 +105,9 @@ func (r *EKSConfigTemplateList) ConvertFrom(srcRaw conversion.Hub) error { return Convert_v1beta1_EKSConfigTemplateList_To_v1alpha4_EKSConfigTemplateList(src, r, nil) } + +func Convert_v1beta1_EKSConfigSpec_To_v1alpha4_EKSConfigSpec(in *v1beta1.EKSConfigSpec, out *EKSConfigSpec, s apiconversion.Scope) error { + out.KubeletExtraArgs = in.KubeletExtraArgs + + return nil +} diff --git a/bootstrap/eks/api/v1alpha4/zz_generated.conversion.go b/bootstrap/eks/api/v1alpha4/zz_generated.conversion.go index 158abf7a44..224a6e3afd 100644 --- a/bootstrap/eks/api/v1alpha4/zz_generated.conversion.go +++ b/bootstrap/eks/api/v1alpha4/zz_generated.conversion.go @@ -63,11 +63,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.EKSConfigSpec)(nil), (*EKSConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_EKSConfigSpec_To_v1alpha4_EKSConfigSpec(a.(*v1beta1.EKSConfigSpec), b.(*EKSConfigSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*EKSConfigStatus)(nil), (*v1beta1.EKSConfigStatus)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha4_EKSConfigStatus_To_v1beta1_EKSConfigStatus(a.(*EKSConfigStatus), b.(*v1beta1.EKSConfigStatus), scope) }); err != nil { @@ -118,6 +113,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.EKSConfigSpec)(nil), (*EKSConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_EKSConfigSpec_To_v1alpha4_EKSConfigSpec(a.(*v1beta1.EKSConfigSpec), b.(*EKSConfigSpec), scope) + }); err != nil { + return err + } return nil } @@ -207,14 +207,15 @@ func Convert_v1alpha4_EKSConfigSpec_To_v1beta1_EKSConfigSpec(in *EKSConfigSpec, func autoConvert_v1beta1_EKSConfigSpec_To_v1alpha4_EKSConfigSpec(in *v1beta1.EKSConfigSpec, out *EKSConfigSpec, s conversion.Scope) error { out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs)) + // WARNING: in.ContainerRuntime requires manual conversion: does not exist in peer-type + // WARNING: in.DNSClusterIP requires manual conversion: does not exist in peer-type + // WARNING: in.DockerConfigJSON requires manual conversion: does not exist in peer-type + // WARNING: in.APIRetryAttempts requires manual conversion: does not exist in peer-type + // WARNING: in.PauseContainer requires manual conversion: does not exist in peer-type + // WARNING: in.UseMaxPods requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_EKSConfigSpec_To_v1alpha4_EKSConfigSpec is an autogenerated conversion function. -func Convert_v1beta1_EKSConfigSpec_To_v1alpha4_EKSConfigSpec(in *v1beta1.EKSConfigSpec, out *EKSConfigSpec, s conversion.Scope) error { - return autoConvert_v1beta1_EKSConfigSpec_To_v1alpha4_EKSConfigSpec(in, out, s) -} - func autoConvert_v1alpha4_EKSConfigStatus_To_v1beta1_EKSConfigStatus(in *EKSConfigStatus, out *v1beta1.EKSConfigStatus, s conversion.Scope) error { out.Ready = in.Ready out.DataSecretName = (*string)(unsafe.Pointer(in.DataSecretName)) @@ -293,7 +294,17 @@ func Convert_v1beta1_EKSConfigTemplate_To_v1alpha4_EKSConfigTemplate(in *v1beta1 func autoConvert_v1alpha4_EKSConfigTemplateList_To_v1beta1_EKSConfigTemplateList(in *EKSConfigTemplateList, out *v1beta1.EKSConfigTemplateList, s conversion.Scope) error { out.ListMeta = in.ListMeta - out.Items = *(*[]v1beta1.EKSConfigTemplate)(unsafe.Pointer(&in.Items)) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta1.EKSConfigTemplate, len(*in)) + for i := range *in { + if err := Convert_v1alpha4_EKSConfigTemplate_To_v1beta1_EKSConfigTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } return nil } @@ -304,7 +315,17 @@ func Convert_v1alpha4_EKSConfigTemplateList_To_v1beta1_EKSConfigTemplateList(in func autoConvert_v1beta1_EKSConfigTemplateList_To_v1alpha4_EKSConfigTemplateList(in *v1beta1.EKSConfigTemplateList, out *EKSConfigTemplateList, s conversion.Scope) error { out.ListMeta = in.ListMeta - out.Items = *(*[]EKSConfigTemplate)(unsafe.Pointer(&in.Items)) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EKSConfigTemplate, len(*in)) + for i := range *in { + if err := Convert_v1beta1_EKSConfigTemplate_To_v1alpha4_EKSConfigTemplate(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } return nil } diff --git a/bootstrap/eks/api/v1beta1/eksconfig_types.go b/bootstrap/eks/api/v1beta1/eksconfig_types.go index fc7a5fbfe6..0221f99d88 100644 --- a/bootstrap/eks/api/v1beta1/eksconfig_types.go +++ b/bootstrap/eks/api/v1beta1/eksconfig_types.go @@ -26,6 +26,39 @@ type EKSConfigSpec struct { // KubeletExtraArgs passes the specified kubelet args into the Amazon EKS machine bootstrap script // +optional KubeletExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"` + // ContainerRuntime specify the container runtime to use when bootstrapping EKS. + // +optional + ContainerRuntime *string `json:"containerRuntime,omitempty"` + // DNSClusterIP overrides the IP address to use for DNS queries within the cluster. + // +optional + DNSClusterIP *string `json:"dnsClusterIP,omitempty"` + // DockerConfigJson is used for the contents of the /etc/docker/daemon.json file. Useful if you want a custom config differing from the default one in the AMI. + // This is expected to be a json string. + // +optional + DockerConfigJSON *string `json:"dockerConfigJson,omitempty"` + // APIRetryAttempts is the number of retry attempts for AWS API call. + // +optional + APIRetryAttempts *int `json:"apiRetryAttempts,omitempty"` + // PauseContainer allows customization of the pause container to use. + // +optional + PauseContainer *PauseContainer `json:"pauseContainer,omitempty"` + // UseMaxPods sets --max-pods for the kubelet when true. + // +optional + UseMaxPods *bool `json:"useMaxPods,omitempty"` + + // TODO(richardcase): this can be uncommented when we get to the ipv6/dual-stack implementation + // ServiceIPV6Cidr is the ipv6 cidr range of the cluster. If this is specified then + // the ip family will be set to ipv6. + // +optional + // ServiceIPV6Cidr *string `json:"serviceIPV6Cidr,omitempty"` +} + +// PauseContainer contains details of pause container. +type PauseContainer struct { + // AccountNumber is the AWS account number to pull the pause container from. + AccountNumber string `json:"accountNumber"` + // Version is the tag of the pause container to use. + Version string `json:"version"` } // EKSConfigStatus defines the observed state of the Amazon EKS Bootstrap Configuration. diff --git a/bootstrap/eks/api/v1beta1/zz_generated.deepcopy.go b/bootstrap/eks/api/v1beta1/zz_generated.deepcopy.go index 40798ad2d5..c05693098f 100644 --- a/bootstrap/eks/api/v1beta1/zz_generated.deepcopy.go +++ b/bootstrap/eks/api/v1beta1/zz_generated.deepcopy.go @@ -95,6 +95,36 @@ func (in *EKSConfigSpec) DeepCopyInto(out *EKSConfigSpec) { (*out)[key] = val } } + if in.ContainerRuntime != nil { + in, out := &in.ContainerRuntime, &out.ContainerRuntime + *out = new(string) + **out = **in + } + if in.DNSClusterIP != nil { + in, out := &in.DNSClusterIP, &out.DNSClusterIP + *out = new(string) + **out = **in + } + if in.DockerConfigJSON != nil { + in, out := &in.DockerConfigJSON, &out.DockerConfigJSON + *out = new(string) + **out = **in + } + if in.APIRetryAttempts != nil { + in, out := &in.APIRetryAttempts, &out.APIRetryAttempts + *out = new(int) + **out = **in + } + if in.PauseContainer != nil { + in, out := &in.PauseContainer, &out.PauseContainer + *out = new(PauseContainer) + **out = **in + } + if in.UseMaxPods != nil { + in, out := &in.UseMaxPods, &out.UseMaxPods + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EKSConfigSpec. @@ -223,3 +253,18 @@ func (in *EKSConfigTemplateSpec) DeepCopy() *EKSConfigTemplateSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PauseContainer) DeepCopyInto(out *PauseContainer) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PauseContainer. +func (in *PauseContainer) DeepCopy() *PauseContainer { + if in == nil { + return nil + } + out := new(PauseContainer) + in.DeepCopyInto(out) + return out +} diff --git a/bootstrap/eks/controllers/eksconfig_controller.go b/bootstrap/eks/controllers/eksconfig_controller.go index 45b5504ddb..03acbf7295 100644 --- a/bootstrap/eks/controllers/eksconfig_controller.go +++ b/bootstrap/eks/controllers/eksconfig_controller.go @@ -27,9 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/utils/pointer" - eksbootstrapv1 "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1beta1" - "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/internal/userdata" - ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" bsutil "sigs.k8s.io/cluster-api/bootstrap/util" expclusterv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" @@ -44,6 +42,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/source" + + eksbootstrapv1 "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/api/v1beta1" + "sigs.k8s.io/cluster-api-provider-aws/bootstrap/eks/internal/userdata" + ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1" ) // EKSConfigReconciler reconciles a EKSConfig object. @@ -185,13 +187,28 @@ func (r *EKSConfigReconciler) joinWorker(ctx context.Context, cluster *clusterv1 log.Info("Generating userdata") - // generate userdata - userDataScript, err := userdata.NewNode(&userdata.NodeInput{ + nodeInput := &userdata.NodeInput{ // AWSManagedControlPlane webhooks default and validate EKSClusterName - ClusterName: controlPlane.Spec.EKSClusterName, - + ClusterName: controlPlane.Spec.EKSClusterName, KubeletExtraArgs: config.Spec.KubeletExtraArgs, - }) + ContainerRuntime: config.Spec.ContainerRuntime, + DNSClusterIP: config.Spec.DNSClusterIP, + DockerConfigJSON: config.Spec.DockerConfigJSON, + APIRetryAttempts: config.Spec.APIRetryAttempts, + UseMaxPods: config.Spec.UseMaxPods, + } + if config.Spec.PauseContainer != nil { + nodeInput.PauseContainerAccount = &config.Spec.PauseContainer.AccountNumber + nodeInput.PauseContainerVersion = &config.Spec.PauseContainer.Version + } + // TODO(richardcase): uncomment when we support ipv6 / dual stack + /*if config.Spec.ServiceIPV6Cidr != nil && *config.Spec.ServiceIPV6Cidr != "" { + nodeInput.ServiceIPV6Cidr = config.Spec.ServiceIPV6Cidr + nodeInput.IPFamily = pointer.String("ipv6") + }*/ + + // generate userdata + userDataScript, err := userdata.NewNode(nodeInput) if err != nil { log.Error(err, "Failed to create a worker join configuration") conditions.MarkFalse(config, eksbootstrapv1.DataSecretAvailableCondition, eksbootstrapv1.DataSecretGenerationFailedReason, clusterv1.ConditionSeverityWarning, "") diff --git a/bootstrap/eks/internal/userdata/kubelet_args.go b/bootstrap/eks/internal/userdata/kubelet_args.go index 7a4613e3d1..7c7233f78c 100644 --- a/bootstrap/eks/internal/userdata/kubelet_args.go +++ b/bootstrap/eks/internal/userdata/kubelet_args.go @@ -17,8 +17,17 @@ limitations under the License. package userdata const argsTemplate = `{{- define "args" -}} -{{- if . }} --kubelet-extra-args '{{ template "kubeletArgsTemplate" . }}' +{{- if .KubeletExtraArgs }} --kubelet-extra-args '{{ template "kubeletArgsTemplate" .KubeletExtraArgs }}' {{- end -}} +{{- if .ContainerRuntime }} --container-runtime {{.ContainerRuntime}}{{- end -}} +{{- if .IPFamily }} --ip-family {{.IPFamily}}{{- end -}} +{{- if .ServiceIPV6Cidr }} --service-ipv6-cidr {{.ServiceIPV6Cidr}}{{- end -}} +{{- if .UseMaxPods }} --use-max-pods {{.UseMaxPods}}{{- end -}} +{{- if .APIRetryAttempts }} --aws-api-retry-attempts {{.APIRetryAttempts}}{{- end -}} +{{- if .PauseContainerAccount }} --pause-container-account {{.PauseContainerAccount}}{{- end -}} +{{- if .PauseContainerVersion }} --pause-container-version {{.PauseContainerVersion}}{{- end -}} +{{- if .DNSClusterIP }} --dns-cluster-ip {{.DNSClusterIP}}{{- end -}} +{{- if .DockerConfigJSON }} --docker-config-json {{ .DockerConfigJSONEscaped }}{{- end -}} {{- end -}}` const kubeletArgsTemplate = `{{- define "kubeletArgsTemplate" -}} diff --git a/bootstrap/eks/internal/userdata/node.go b/bootstrap/eks/internal/userdata/node.go index 8d3a76dcfc..f7ac0d5a8f 100644 --- a/bootstrap/eks/internal/userdata/node.go +++ b/bootstrap/eks/internal/userdata/node.go @@ -20,18 +20,39 @@ import ( "bytes" "fmt" "text/template" + + "github.com/alessio/shellescape" ) const ( nodeUserData = `#!/bin/bash -/etc/eks/bootstrap.sh {{.ClusterName}} {{- template "args" .KubeletExtraArgs }} +/etc/eks/bootstrap.sh {{.ClusterName}} {{- template "args" . }} ` ) // NodeInput defines the context to generate a node user data. type NodeInput struct { - ClusterName string - KubeletExtraArgs map[string]string + ClusterName string + KubeletExtraArgs map[string]string + ContainerRuntime *string + DNSClusterIP *string + DockerConfigJSON *string + APIRetryAttempts *int + PauseContainerAccount *string + PauseContainerVersion *string + UseMaxPods *bool + // NOTE: currently the IPFamily/ServiceIPV6Cidr isn't exposed to the user. + // TODO (richardcase): remove the above comment when IPV6 / dual stack is implemented. + IPFamily *string + ServiceIPV6Cidr *string +} + +func (ni *NodeInput) DockerConfigJSONEscaped() string { + if ni.DockerConfigJSON == nil || len(*ni.DockerConfigJSON) == 0 { + return "''" + } + + return shellescape.Quote(*ni.DockerConfigJSON) } // NewNode returns the user data string to be used on a node instance. diff --git a/bootstrap/eks/internal/userdata/node_test.go b/bootstrap/eks/internal/userdata/node_test.go index 4cfaa120dd..a769cf3342 100644 --- a/bootstrap/eks/internal/userdata/node_test.go +++ b/bootstrap/eks/internal/userdata/node_test.go @@ -21,6 +21,8 @@ import ( . "github.com/onsi/gomega" "github.com/onsi/gomega/format" + + "k8s.io/utils/pointer" ) func TestNewNode(t *testing.T) { @@ -30,6 +32,7 @@ func TestNewNode(t *testing.T) { type args struct { input *NodeInput } + tests := []struct { name string args args @@ -37,7 +40,7 @@ func TestNewNode(t *testing.T) { expectErr bool }{ { - name: "success case", + name: "only cluster name", args: args{ input: &NodeInput{ ClusterName: "test-cluster", @@ -49,22 +52,124 @@ func TestNewNode(t *testing.T) { expectErr: false, }, { - name: "with extra args", + name: "sample-with-values", args: args{ input: &NodeInput{ ClusterName: "test-cluster", KubeletExtraArgs: map[string]string{ - "foo": "bar", - "pizza-topping": "pepperoni", + "node-labels": "node-role.undistro.io/infra=true", + "register-with-taints": "dedicated=infra:NoSchedule", }, }, }, expectedBytes: []byte(`#!/bin/bash -/etc/eks/bootstrap.sh test-cluster --kubelet-extra-args '--foo=bar --pizza-topping=pepperoni' +/etc/eks/bootstrap.sh test-cluster --kubelet-extra-args '--node-labels=node-role.undistro.io/infra=true --register-with-taints=dedicated=infra:NoSchedule' +`), + }, + { + name: "with container runtime", + args: args{ + input: &NodeInput{ + ClusterName: "test-cluster", + ContainerRuntime: pointer.String("containerd"), + }, + }, + expectedBytes: []byte(`#!/bin/bash +/etc/eks/bootstrap.sh test-cluster --container-runtime containerd +`), + }, + { + name: "with kubelet extra args and container runtime", + args: args{ + input: &NodeInput{ + ClusterName: "test-cluster", + KubeletExtraArgs: map[string]string{ + "node-labels": "node-role.undistro.io/infra=true", + "register-with-taints": "dedicated=infra:NoSchedule", + }, + ContainerRuntime: pointer.String("containerd"), + }, + }, + expectedBytes: []byte(`#!/bin/bash +/etc/eks/bootstrap.sh test-cluster --kubelet-extra-args '--node-labels=node-role.undistro.io/infra=true --register-with-taints=dedicated=infra:NoSchedule' --container-runtime containerd +`), + }, + { + name: "with ipv6", + args: args{ + input: &NodeInput{ + ClusterName: "test-cluster", + ServiceIPV6Cidr: pointer.String("fe80:0000:0000:0000:0204:61ff:fe9d:f156/24"), + IPFamily: pointer.String("ipv6"), + }, + }, + expectedBytes: []byte(`#!/bin/bash +/etc/eks/bootstrap.sh test-cluster --ip-family ipv6 --service-ipv6-cidr fe80:0000:0000:0000:0204:61ff:fe9d:f156/24 +`), + }, + { + name: "without max pods", + args: args{ + input: &NodeInput{ + ClusterName: "test-cluster", + UseMaxPods: pointer.Bool(false), + }, + }, + expectedBytes: []byte(`#!/bin/bash +/etc/eks/bootstrap.sh test-cluster --use-max-pods false +`), + }, + { + name: "with api retry attempts", + args: args{ + input: &NodeInput{ + ClusterName: "test-cluster", + APIRetryAttempts: pointer.Int(5), + }, + }, + expectedBytes: []byte(`#!/bin/bash +/etc/eks/bootstrap.sh test-cluster --aws-api-retry-attempts 5 +`), + }, + { + name: "with pause container", + args: args{ + input: &NodeInput{ + ClusterName: "test-cluster", + PauseContainerAccount: pointer.String("12345678"), + PauseContainerVersion: pointer.String("v1"), + }, + }, + expectedBytes: []byte(`#!/bin/bash +/etc/eks/bootstrap.sh test-cluster --pause-container-account 12345678 --pause-container-version v1 +`), + }, + { + name: "with dns cluster ip", + args: args{ + input: &NodeInput{ + ClusterName: "test-cluster", + DNSClusterIP: pointer.String("192.168.0.1"), + }, + }, + expectedBytes: []byte(`#!/bin/bash +/etc/eks/bootstrap.sh test-cluster --dns-cluster-ip 192.168.0.1 +`), + }, + { + name: "with docker json", + args: args{ + input: &NodeInput{ + ClusterName: "test-cluster", + DockerConfigJSON: pointer.String("{\"debug\":true}"), + }, + }, + expectedBytes: []byte(`#!/bin/bash +/etc/eks/bootstrap.sh test-cluster --docker-config-json '{"debug":true}' `), - expectErr: false, }, } + for _, testcase := range tests { t.Run(testcase.name, func(t *testing.T) { bytes, err := NewNode(testcase.args.input) diff --git a/config/crd/bases/bootstrap.cluster.x-k8s.io_eksconfigs.yaml b/config/crd/bases/bootstrap.cluster.x-k8s.io_eksconfigs.yaml index 5b5d379eb1..239714544f 100644 --- a/config/crd/bases/bootstrap.cluster.x-k8s.io_eksconfigs.yaml +++ b/config/crd/bases/bootstrap.cluster.x-k8s.io_eksconfigs.yaml @@ -264,12 +264,47 @@ spec: description: EKSConfigSpec defines the desired state of Amazon EKS Bootstrap Configuration. properties: + apiRetryAttempts: + description: APIRetryAttempts is the number of retry attempts for + AWS API call. + type: integer + containerRuntime: + description: ContainerRuntime specify the container runtime to use + when bootstrapping EKS. + type: string + dnsClusterIP: + description: DNSClusterIP overrides the IP address to use for DNS + queries within the cluster. + type: string + dockerConfigJson: + description: DockerConfigJson is used for the contents of the /etc/docker/daemon.json + file. Useful if you want a custom config differing from the default + one in the AMI. This is expected to be a json string. + type: string kubeletExtraArgs: additionalProperties: type: string description: KubeletExtraArgs passes the specified kubelet args into the Amazon EKS machine bootstrap script type: object + pauseContainer: + description: PauseContainer allows customization of the pause container + to use. + properties: + accountNumber: + description: AccountNumber is the AWS account number to pull the + pause container from. + type: string + version: + description: Version is the tag of the pause container to use. + type: string + required: + - accountNumber + - version + type: object + useMaxPods: + description: UseMaxPods sets --max-pods for the kubelet when true. + type: boolean type: object status: description: EKSConfigStatus defines the observed state of the Amazon diff --git a/config/crd/bases/bootstrap.cluster.x-k8s.io_eksconfigtemplates.yaml b/config/crd/bases/bootstrap.cluster.x-k8s.io_eksconfigtemplates.yaml index 3ee3f2768b..345bdc072d 100644 --- a/config/crd/bases/bootstrap.cluster.x-k8s.io_eksconfigtemplates.yaml +++ b/config/crd/bases/bootstrap.cluster.x-k8s.io_eksconfigtemplates.yaml @@ -128,12 +128,50 @@ spec: description: EKSConfigSpec defines the desired state of Amazon EKS Bootstrap Configuration. properties: + apiRetryAttempts: + description: APIRetryAttempts is the number of retry attempts + for AWS API call. + type: integer + containerRuntime: + description: ContainerRuntime specify the container runtime + to use when bootstrapping EKS. + type: string + dnsClusterIP: + description: DNSClusterIP overrides the IP address to use + for DNS queries within the cluster. + type: string + dockerConfigJson: + description: DockerConfigJson is used for the contents of + the /etc/docker/daemon.json file. Useful if you want a custom + config differing from the default one in the AMI. This is + expected to be a json string. + type: string kubeletExtraArgs: additionalProperties: type: string description: KubeletExtraArgs passes the specified kubelet args into the Amazon EKS machine bootstrap script type: object + pauseContainer: + description: PauseContainer allows customization of the pause + container to use. + properties: + accountNumber: + description: AccountNumber is the AWS account number to + pull the pause container from. + type: string + version: + description: Version is the tag of the pause container + to use. + type: string + required: + - accountNumber + - version + type: object + useMaxPods: + description: UseMaxPods sets --max-pods for the kubelet when + true. + type: boolean type: object type: object required: diff --git a/go.mod b/go.mod index 82c582a720..b7c99d10cb 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ replace sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.0.1-0.202111111752 // github.com/go-logr/logr on v0.4.x // k8s.io/klog/v2 on v2.10.x require ( + github.com/alessio/shellescape v1.4.1 github.com/apparentlymart/go-cidr v1.1.0 github.com/aws/amazon-vpc-cni-k8s v1.9.3 github.com/aws/aws-lambda-go v1.27.0 @@ -51,7 +52,6 @@ require ( github.com/BurntSushi/toml v0.3.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect - github.com/alessio/shellescape v1.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/containerd/containerd v1.5.2 // indirect