diff --git a/api/v1beta1/conversion.go b/api/v1beta1/conversion.go index f18fdc678b..2b124f027a 100644 --- a/api/v1beta1/conversion.go +++ b/api/v1beta1/conversion.go @@ -98,3 +98,7 @@ func Convert_v1beta2_NetworkSpec_To_v1beta1_NetworkSpec(in *v1beta2.NetworkSpec, func Convert_v1beta2_S3Bucket_To_v1beta1_S3Bucket(in *v1beta2.S3Bucket, out *S3Bucket, s conversion.Scope) error { return autoConvert_v1beta2_S3Bucket_To_v1beta1_S3Bucket(in, out, s) } + +func Convert_v1beta2_Ignition_To_v1beta1_Ignition(in *v1beta2.Ignition, out *Ignition, s conversion.Scope) error { + return autoConvert_v1beta2_Ignition_To_v1beta1_Ignition(in, out, s) +} diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go index 347e67fd35..dea14de263 100644 --- a/api/v1beta1/zz_generated.conversion.go +++ b/api/v1beta1/zz_generated.conversion.go @@ -445,11 +445,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta2.Ignition)(nil), (*Ignition)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta2_Ignition_To_v1beta1_Ignition(a.(*v1beta2.Ignition), b.(*Ignition), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*IngressRule)(nil), (*v1beta2.IngressRule)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_IngressRule_To_v1beta2_IngressRule(a.(*IngressRule), b.(*v1beta2.IngressRule), scope) }); err != nil { @@ -560,6 +555,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta2.Ignition)(nil), (*Ignition)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_Ignition_To_v1beta1_Ignition(a.(*v1beta2.Ignition), b.(*Ignition), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1beta2.IngressRule)(nil), (*IngressRule)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_IngressRule_To_v1beta1_IngressRule(a.(*v1beta2.IngressRule), b.(*IngressRule), scope) }); err != nil { @@ -1359,7 +1359,15 @@ func autoConvert_v1beta1_AWSMachineSpec_To_v1beta2_AWSMachineSpec(in *AWSMachine if err := Convert_v1beta1_CloudInit_To_v1beta2_CloudInit(&in.CloudInit, &out.CloudInit, s); err != nil { return err } - out.Ignition = (*v1beta2.Ignition)(unsafe.Pointer(in.Ignition)) + if in.Ignition != nil { + in, out := &in.Ignition, &out.Ignition + *out = new(v1beta2.Ignition) + if err := Convert_v1beta1_Ignition_To_v1beta2_Ignition(*in, *out, s); err != nil { + return err + } + } else { + out.Ignition = nil + } out.SpotMarketOptions = (*v1beta2.SpotMarketOptions)(unsafe.Pointer(in.SpotMarketOptions)) out.Tenancy = in.Tenancy return nil @@ -1407,7 +1415,15 @@ func autoConvert_v1beta2_AWSMachineSpec_To_v1beta1_AWSMachineSpec(in *v1beta2.AW if err := Convert_v1beta2_CloudInit_To_v1beta1_CloudInit(&in.CloudInit, &out.CloudInit, s); err != nil { return err } - out.Ignition = (*Ignition)(unsafe.Pointer(in.Ignition)) + if in.Ignition != nil { + in, out := &in.Ignition, &out.Ignition + *out = new(Ignition) + if err := Convert_v1beta2_Ignition_To_v1beta1_Ignition(*in, *out, s); err != nil { + return err + } + } else { + out.Ignition = nil + } out.SpotMarketOptions = (*SpotMarketOptions)(unsafe.Pointer(in.SpotMarketOptions)) // WARNING: in.PlacementGroupName requires manual conversion: does not exist in peer-type out.Tenancy = in.Tenancy @@ -1918,14 +1934,11 @@ func Convert_v1beta1_Ignition_To_v1beta2_Ignition(in *Ignition, out *v1beta2.Ign func autoConvert_v1beta2_Ignition_To_v1beta1_Ignition(in *v1beta2.Ignition, out *Ignition, s conversion.Scope) error { out.Version = in.Version + // WARNING: in.Proxy requires manual conversion: does not exist in peer-type + // WARNING: in.TLS requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta2_Ignition_To_v1beta1_Ignition is an autogenerated conversion function. -func Convert_v1beta2_Ignition_To_v1beta1_Ignition(in *v1beta2.Ignition, out *Ignition, s conversion.Scope) error { - return autoConvert_v1beta2_Ignition_To_v1beta1_Ignition(in, out, s) -} - func autoConvert_v1beta1_IngressRule_To_v1beta2_IngressRule(in *IngressRule, out *v1beta2.IngressRule, s conversion.Scope) error { out.Description = in.Description out.Protocol = v1beta2.SecurityGroupProtocol(in.Protocol) diff --git a/api/v1beta2/awsmachine_types.go b/api/v1beta2/awsmachine_types.go index 83fed36893..35e2923021 100644 --- a/api/v1beta2/awsmachine_types.go +++ b/api/v1beta2/awsmachine_types.go @@ -197,6 +197,39 @@ type Ignition struct { // +kubebuilder:default="2.3" // +kubebuilder:validation:Enum="2.3";"3.0";"3.1";"3.2";"3.3";"3.4" Version string `json:"version,omitempty"` + + // IgnitionProxy defines proxy settings for Ignition. + // Only valid for Ignition versions 3.4 and above. + // +optional + Proxy *IgnitionProxy `json:"proxy,omitempty"` + + // TLS defines TLS settings for Ignition. + // Only valid for Ignition versions 3.4 and above. + // +optional + TLS *IgnitionTLS `json:"tls,omitempty"` +} + +// IgnitionTLS defines TLS settings for Ignition. +type IgnitionTLS struct { + // CASources defines the list of certificate authorities to use for Ignition. + // THe value is the certificate bundle (in PEM format). The bundle can contain multiple concatenated certificates. + // Supported schemes are http, https, tftp, s3, arn, gs, and `data` (RFC 2397) URL scheme. + // + // +optional + CASources []string `json:"certificateAuthorities,omitempty"` +} + +// IgnitionProxy defines proxy settings for Ignition. +type IgnitionProxy struct { + // HTTPProxy is the HTTP proxy to use for Ignition. + // +optional + HTTPProxy *string `json:"httpProxy,omitempty"` + // HTTPSProxy is the HTTPS proxy to use for Ignition. + // +optional + HTTPSProxy *string `json:"httpsProxy,omitempty"` + // NoProxy is the list of domains to not proxy for Ignition. + // +optional + NoProxy []string `json:"noProxy,omitempty"` } // AWSMachineStatus defines the observed state of AWSMachine. diff --git a/api/v1beta2/awsmachine_webhook.go b/api/v1beta2/awsmachine_webhook.go index cbf728fda4..f9a0a1ac60 100644 --- a/api/v1beta2/awsmachine_webhook.go +++ b/api/v1beta2/awsmachine_webhook.go @@ -171,6 +171,15 @@ func (r *AWSMachine) validateIgnitionAndCloudInit() field.ErrorList { allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "cloudInit"), "cannot be set if spec.ignition is set")) } + if r.ignitionEnabled() && (r.Spec.Ignition.Version == "2.3" || r.Spec.Ignition.Version == "3.0") { + if r.Spec.Ignition.Proxy != nil { + allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "ignition", "proxy"), "cannot be set if spec.ignition.version is 2.3 or 3.0")) + } + if r.Spec.Ignition.TLS != nil { + allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "ignition", "tls"), "cannot be set if spec.ignition.version is 2.3 or 3.0")) + } + } + return allErrs } diff --git a/api/v1beta2/awsmachine_webhook_test.go b/api/v1beta2/awsmachine_webhook_test.go index df8f876627..ce519f91f0 100644 --- a/api/v1beta2/awsmachine_webhook_test.go +++ b/api/v1beta2/awsmachine_webhook_test.go @@ -24,8 +24,10 @@ import ( "github.com/aws/aws-sdk-go/aws" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/component-base/featuregate/testing" "k8s.io/utils/ptr" + "sigs.k8s.io/cluster-api-provider-aws/v2/feature" utildefaulting "sigs.k8s.io/cluster-api/util/defaulting" ) @@ -248,9 +250,44 @@ func TestAWSMachineCreate(t *testing.T) { }, wantErr: true, }, + { + name: "ignition proxy and TLS can be from version 3.1", + machine: &AWSMachine{ + Spec: AWSMachineSpec{ + InstanceType: "test", + Ignition: &Ignition{ + Version: "3.1", + Proxy: &IgnitionProxy{ + HTTPProxy: "http://proxy.example.com:3128", + }, + TLS: &IgnitionTLS{ + CASources: []string{"test"}, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "cannot use ignition proxy with version 2.3", + machine: &AWSMachine{ + Spec: AWSMachineSpec{ + InstanceType: "test", + Ignition: &Ignition{ + Version: "2.3.0", + Proxy: &IgnitionProxy{ + HTTPProxy: "http://proxy.example.com:3128", + }, + }, + }, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, feature.BootstrapFormatIgnition, true)() + machine := tt.machine.DeepCopy() machine.ObjectMeta = metav1.ObjectMeta{ GenerateName: "machine-", diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index cd5ba0a0f9..56b7fa576f 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -731,7 +731,7 @@ func (in *AWSMachineSpec) DeepCopyInto(out *AWSMachineSpec) { if in.Ignition != nil { in, out := &in.Ignition, &out.Ignition *out = new(Ignition) - **out = **in + (*in).DeepCopyInto(*out) } if in.SpotMarketOptions != nil { in, out := &in.SpotMarketOptions, &out.SpotMarketOptions @@ -1315,6 +1315,16 @@ func (in *IPv6) DeepCopy() *IPv6 { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Ignition) DeepCopyInto(out *Ignition) { *out = *in + if in.Proxy != nil { + in, out := &in.Proxy, &out.Proxy + *out = new(IgnitionProxy) + (*in).DeepCopyInto(*out) + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(IgnitionTLS) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Ignition. @@ -1327,6 +1337,46 @@ func (in *Ignition) DeepCopy() *Ignition { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IgnitionProxy) DeepCopyInto(out *IgnitionProxy) { + *out = *in + if in.NoProxy != nil { + in, out := &in.NoProxy, &out.NoProxy + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnitionProxy. +func (in *IgnitionProxy) DeepCopy() *IgnitionProxy { + if in == nil { + return nil + } + out := new(IgnitionProxy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IgnitionTLS) DeepCopyInto(out *IgnitionTLS) { + *out = *in + if in.CASources != nil { + in, out := &in.CASources, &out.CASources + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IgnitionTLS. +func (in *IgnitionTLS) DeepCopy() *IgnitionTLS { + if in == nil { + return nil + } + out := new(IgnitionTLS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressRule) DeepCopyInto(out *IngressRule) { *out = *in diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml index 10665df729..2f42a3ba3d 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml @@ -632,6 +632,37 @@ spec: description: Ignition defined options related to the bootstrapping systems where Ignition is used. properties: + proxy: + description: IgnitionProxy defines proxy settings for Ignition. + Only valid for Ignition versions 3.4 and above. + properties: + httpProxy: + description: HTTPProxy is the HTTP proxy to use for Ignition. + type: string + httpsProxy: + description: HTTPSProxy is the HTTPS proxy to use for Ignition. + type: string + noProxy: + description: NoProxy is the list of domains to not proxy for + Ignition. + items: + type: string + type: array + type: object + tls: + description: TLS defines TLS settings for Ignition. Only valid + for Ignition versions 3.4 and above. + properties: + certificateAuthorities: + description: CASources defines the list of certificate authorities + to use for Ignition. THe value is the certificate bundle + (in PEM format). The bundle can contain multiple concatenated + certificates. Supported schemes are http, https, tftp, s3, + arn, gs, and `data` (RFC 2397) URL scheme. + items: + type: string + type: array + type: object version: default: "2.3" description: Version defines which version of Ignition will be diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml index aaaf3b378b..b68da8725d 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml @@ -578,6 +578,40 @@ spec: description: Ignition defined options related to the bootstrapping systems where Ignition is used. properties: + proxy: + description: IgnitionProxy defines proxy settings for + Ignition. Only valid for Ignition versions 3.4 and above. + properties: + httpProxy: + description: HTTPProxy is the HTTP proxy to use for + Ignition. + type: string + httpsProxy: + description: HTTPSProxy is the HTTPS proxy to use + for Ignition. + type: string + noProxy: + description: NoProxy is the list of domains to not + proxy for Ignition. + items: + type: string + type: array + type: object + tls: + description: TLS defines TLS settings for Ignition. Only + valid for Ignition versions 3.4 and above. + properties: + certificateAuthorities: + description: CASources defines the list of certificate + authorities to use for Ignition. THe value is the + certificate bundle (in PEM format). The bundle can + contain multiple concatenated certificates. Supported + schemes are http, https, tftp, s3, arn, gs, and + `data` (RFC 2397) URL scheme. + items: + type: string + type: array + type: object version: default: "2.3" description: Version defines which version of Ignition diff --git a/controllers/awsmachine_controller.go b/controllers/awsmachine_controller.go index 20d3832c0d..be8c2523c9 100644 --- a/controllers/awsmachine_controller.go +++ b/controllers/awsmachine_controller.go @@ -787,6 +787,25 @@ func (r *AWSMachineReconciler) ignitionUserData(scope *scope.MachineScope, objec }, } + if scope.AWSMachine.Spec.Ignition.Proxy != nil { + ignData.Ignition.Proxy = ignV3Types.Proxy{ + HTTPProxy: scope.AWSMachine.Spec.Ignition.Proxy.HTTPProxy, + HTTPSProxy: scope.AWSMachine.Spec.Ignition.Proxy.HTTPSProxy, + } + for _, noProxy := range scope.AWSMachine.Spec.Ignition.Proxy.NoProxy { + ignData.Ignition.Proxy.NoProxy = append(ignData.Ignition.Proxy.NoProxy, ignV3Types.NoProxyItem(noProxy)) + } + } + + if scope.AWSMachine.Spec.Ignition.TLS != nil { + for _, cert := range scope.AWSMachine.Spec.Ignition.TLS.CASources { + ignData.Ignition.Security.TLS.CertificateAuthorities = append( + ignData.Ignition.Security.TLS.CertificateAuthorities, + ignV3Types.Resource{Source: aws.String(cert)}, + ) + } + } + return json.Marshal(ignData) default: return nil, errors.Errorf("unsupported ignition version %q", ignVersion)