From 349bf97cf3aba491f782db694cfbdbd1a4802048 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Tue, 17 Oct 2023 07:26:12 -0700 Subject: [PATCH] feat: add VPC ID and Subnet IDs patch (#220) Fixes https://github.com/d2iq-labs/capi-runtime-extensions/issues/201 Fixes https://github.com/d2iq-labs/capi-runtime-extensions/issues/200 Depends on https://github.com/d2iq-labs/capi-runtime-extensions/pull/219 Tested manually (at-least that the values are set in `AWSCluster`) ``` apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: name: spec: topology: variables: - name: clusterConfig value: aws: network: vpc: id: vpc-1234567890 subnets: - id: subnet-1 - id: subnet-2 - id: subnet-3 ``` This is what the `AWSCluster` looked like: ``` spec: network: subnets: - id: subnet-1 isPublic: false - id: subnet-2 isPublic: false - id: subnet-3 isPublic: false vpc: availabilityZoneSelection: Ordered availabilityZoneUsageLimit: 3 id: vpc-1234567890 region: us-west-2 ``` --- api/v1alpha1/aws_clusterconfig_types.go | 83 +++++++++++- api/v1alpha1/clusterconfig_types.go | 2 +- api/v1alpha1/constants.go | 2 + api/v1alpha1/node_types.go | 2 +- api/v1alpha1/zz_generated.deepcopy.go | 79 ++++++++++++ docs/content/customization/aws/network.md | 65 ++++++++++ pkg/handlers/aws/clusterconfig/variables.go | 3 - .../aws/mutation/ami/inject_control_plane.go | 6 +- .../aws/mutation/ami/inject_worker.go | 4 +- .../aws/mutation/cni/calico/inject.go | 1 - .../inject_control_plane.go | 4 +- .../iaminstanceprofile/inject_worker.go | 4 +- .../instancetype/inject_control_plane.go | 4 +- .../mutation/instancetype/inject_worker.go | 4 +- .../aws/mutation/metapatch_handler.go | 4 +- .../aws/mutation/metapatch_handler_test.go | 26 ++-- pkg/handlers/aws/mutation/network/inject.go | 120 ++++++++++++++++++ .../network/tests/generate_patches.go | 104 +++++++++++++++ .../aws/mutation/network/variables_test.go | 68 ++++++++++ pkg/handlers/aws/mutation/region/inject.go | 4 +- pkg/handlers/aws/workerconfig/variables.go | 3 - pkg/handlers/generic/mutation/etcd/inject.go | 1 - .../mutation/extraapiservercertsans/inject.go | 1 - .../imageregistries/credentials/inject.go | 1 - .../kubernetesimagerepository/inject.go | 1 - 25 files changed, 550 insertions(+), 46 deletions(-) create mode 100644 docs/content/customization/aws/network.md create mode 100644 pkg/handlers/aws/mutation/network/inject.go create mode 100644 pkg/handlers/aws/mutation/network/tests/generate_patches.go create mode 100644 pkg/handlers/aws/mutation/network/variables_test.go diff --git a/api/v1alpha1/aws_clusterconfig_types.go b/api/v1alpha1/aws_clusterconfig_types.go index 7cbfa4553..e0e113b60 100644 --- a/api/v1alpha1/aws_clusterconfig_types.go +++ b/api/v1alpha1/aws_clusterconfig_types.go @@ -11,6 +11,8 @@ type AWSSpec struct { // AWS region to create cluster in. // +optional Region *Region `json:"region,omitempty"` + // +optional + Network *AWSNetwork `json:"network,omitempty"` } func (AWSSpec) VariableSchema() clusterv1.VariableSchema { @@ -19,7 +21,8 @@ func (AWSSpec) VariableSchema() clusterv1.VariableSchema { Description: "AWS cluster configuration", Type: "object", Properties: map[string]clusterv1.JSONSchemaProps{ - "region": Region("").VariableSchema().OpenAPIV3Schema, + "region": Region("").VariableSchema().OpenAPIV3Schema, + "network": AWSNetwork{}.VariableSchema().OpenAPIV3Schema, }, }, } @@ -30,8 +33,84 @@ type Region string func (Region) VariableSchema() clusterv1.VariableSchema { return clusterv1.VariableSchema{ OpenAPIV3Schema: clusterv1.JSONSchemaProps{ - Type: "string", Description: "AWS region to create cluster in", + Type: "string", + }, + } +} + +type AWSNetwork struct { + // +optional + VPC *VPC `json:"vpc,omitempty"` + + // +optional + Subnets Subnets `json:"subnets,omitempty"` +} + +func (AWSNetwork) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "AWS network configuration", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "vpc": VPC{}.VariableSchema().OpenAPIV3Schema, + "subnets": Subnets{}.VariableSchema().OpenAPIV3Schema, + }, + }, + } +} + +type VPC struct { + // ID is the vpc-id of the VPC this provider should use to create resources. + ID string `json:"id,omitempty"` +} + +func (VPC) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "AWS VPC configuration", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "id": { + Description: "Existing VPC ID to use for the cluster", + Type: "string", + }, + }, + }, + } +} + +type Subnets []SubnetSpec + +func (Subnets) VariableSchema() clusterv1.VariableSchema { + resourceSchema := SubnetSpec{}.VariableSchema().OpenAPIV3Schema + + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "AWS Subnet configurations", + Type: "array", + Items: &resourceSchema, + }, + } +} + +// SubnetSpec configures an AWS Subnet. +type SubnetSpec struct { + // ID defines a unique identifier to reference this resource. + ID string `json:"id"` +} + +func (SubnetSpec) VariableSchema() clusterv1.VariableSchema { + return clusterv1.VariableSchema{ + OpenAPIV3Schema: clusterv1.JSONSchemaProps{ + Description: "An AWS Subnet configuration", + Type: "object", + Properties: map[string]clusterv1.JSONSchemaProps{ + "id": { + Description: "Existing Subnet ID to use for the cluster", + Type: "string", + }, + }, }, } } diff --git a/api/v1alpha1/clusterconfig_types.go b/api/v1alpha1/clusterconfig_types.go index 6af15ad05..482ecd0c7 100644 --- a/api/v1alpha1/clusterconfig_types.go +++ b/api/v1alpha1/clusterconfig_types.go @@ -51,7 +51,7 @@ func (s ClusterConfigSpec) VariableSchema() clusterv1.VariableSchema { //nolint: maps.Copy( clusterConfigProps.OpenAPIV3Schema.Properties, map[string]clusterv1.JSONSchemaProps{ - "aws": AWSSpec{}.VariableSchema().OpenAPIV3Schema, + AWSVariableName: AWSSpec{}.VariableSchema().OpenAPIV3Schema, "controlPlane": NodeConfigSpec{ AWS: &AWSNodeSpec{}, }.VariableSchema().OpenAPIV3Schema, diff --git a/api/v1alpha1/constants.go b/api/v1alpha1/constants.go index 70e6a22de..f05b39324 100644 --- a/api/v1alpha1/constants.go +++ b/api/v1alpha1/constants.go @@ -6,4 +6,6 @@ package v1alpha1 const ( // CNIVariableName is the external patch variable name. CNIVariableName = "cni" + // AWSVariableName is the AWS config patch variable name. + AWSVariableName = "aws" ) diff --git a/api/v1alpha1/node_types.go b/api/v1alpha1/node_types.go index 441da042a..b9b678866 100644 --- a/api/v1alpha1/node_types.go +++ b/api/v1alpha1/node_types.go @@ -39,7 +39,7 @@ func (s NodeConfigSpec) VariableSchema() clusterv1.VariableSchema { maps.Copy( nodeConfigProps.OpenAPIV3Schema.Properties, map[string]clusterv1.JSONSchemaProps{ - "aws": AWSNodeSpec{}.VariableSchema().OpenAPIV3Schema, + AWSVariableName: AWSNodeSpec{}.VariableSchema().OpenAPIV3Schema, }, ) case s.Docker != nil: diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 102f6a8a0..e34069e87 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -47,6 +47,31 @@ func (in *AMISpec) DeepCopy() *AMISpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSNetwork) DeepCopyInto(out *AWSNetwork) { + *out = *in + if in.VPC != nil { + in, out := &in.VPC, &out.VPC + *out = new(VPC) + **out = **in + } + if in.Subnets != nil { + in, out := &in.Subnets, &out.Subnets + *out = make(Subnets, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSNetwork. +func (in *AWSNetwork) DeepCopy() *AWSNetwork { + if in == nil { + return nil + } + out := new(AWSNetwork) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AWSNodeSpec) DeepCopyInto(out *AWSNodeSpec) { *out = *in @@ -85,6 +110,11 @@ func (in *AWSSpec) DeepCopyInto(out *AWSSpec) { *out = new(Region) **out = **in } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(AWSNetwork) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSSpec. @@ -556,3 +586,52 @@ func (in *ObjectMeta) DeepCopy() *ObjectMeta { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SubnetSpec) DeepCopyInto(out *SubnetSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetSpec. +func (in *SubnetSpec) DeepCopy() *SubnetSpec { + if in == nil { + return nil + } + out := new(SubnetSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in Subnets) DeepCopyInto(out *Subnets) { + { + in := &in + *out = make(Subnets, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subnets. +func (in Subnets) DeepCopy() Subnets { + if in == nil { + return nil + } + out := new(Subnets) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VPC) DeepCopyInto(out *VPC) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VPC. +func (in *VPC) DeepCopy() *VPC { + if in == nil { + return nil + } + out := new(VPC) + in.DeepCopyInto(out) + return out +} diff --git a/docs/content/customization/aws/network.md b/docs/content/customization/aws/network.md new file mode 100644 index 000000000..f5114035f --- /dev/null +++ b/docs/content/customization/aws/network.md @@ -0,0 +1,65 @@ ++++ +title = "Network" ++++ + +The network customization allows the user to specify existing infrastructure to use for the cluster. + +This customization will be available when the +[provider-specific cluster configuration patch]({{< ref "..">}}) is included in the `ClusterClass`. + +## Example + +To specify existing AWS VPC, use the following configuration: + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + aws: + network: + vpc: + id: vpc-1234567890 +``` + +To also specify existing AWS Subnets, use the following configuration: + +```yaml +apiVersion: cluster.x-k8s.io/v1beta1 +kind: Cluster +metadata: + name: +spec: + topology: + variables: + - name: clusterConfig + value: + aws: + network: + vpc: + id: vpc-1234567890 + subnets: + - id: subnet-1 + - id: subnet-2 + - id: subnet-3 +``` + +Applying this configuration will result in the following value being set: + +- `AWSClusterTemplate`: + + - ```yaml + spec: + network: + subnets: + - id: subnet-1 + - id: subnet-2 + - id: subnet-3 + vpc: + id: vpc-1234567890 + ``` diff --git a/pkg/handlers/aws/clusterconfig/variables.go b/pkg/handlers/aws/clusterconfig/variables.go index 0f1ddada9..8c0ef8b48 100644 --- a/pkg/handlers/aws/clusterconfig/variables.go +++ b/pkg/handlers/aws/clusterconfig/variables.go @@ -23,9 +23,6 @@ var ( const ( // HandlerNameVariable is the name of the variable handler. HandlerNameVariable = "AWSClusterConfigVars" - - // AWSVariableName is the AWS config patch variable name. - AWSVariableName = "aws" ) func NewVariable() *awsClusterConfigVariableHandler { diff --git a/pkg/handlers/aws/mutation/ami/inject_control_plane.go b/pkg/handlers/aws/mutation/ami/inject_control_plane.go index 837fd8113..89361400a 100644 --- a/pkg/handlers/aws/mutation/ami/inject_control_plane.go +++ b/pkg/handlers/aws/mutation/ami/inject_control_plane.go @@ -4,11 +4,9 @@ package ami import ( - _ "embed" - + "github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors" capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" - awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig" ) @@ -17,7 +15,7 @@ func NewControlPlanePatch() *awsAMISpecPatchHandler { clusterconfig.MetaVariableName, []string{ clusterconfig.MetaControlPlaneConfigName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, VariableName, }, selectors.InfrastructureControlPlaneMachines( diff --git a/pkg/handlers/aws/mutation/ami/inject_worker.go b/pkg/handlers/aws/mutation/ami/inject_worker.go index f587e6fd3..c2fc9b6d2 100644 --- a/pkg/handlers/aws/mutation/ami/inject_worker.go +++ b/pkg/handlers/aws/mutation/ami/inject_worker.go @@ -3,9 +3,9 @@ package ami import ( + "github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors" capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" - awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/workerconfig" ) @@ -13,7 +13,7 @@ func NewWorkerPatch() *awsAMISpecPatchHandler { return newAWSAMISpecPatchHandler( workerconfig.MetaVariableName, []string{ - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, VariableName, }, selectors.InfrastructureWorkerMachineTemplates( diff --git a/pkg/handlers/aws/mutation/cni/calico/inject.go b/pkg/handlers/aws/mutation/cni/calico/inject.go index e961a386d..45c68ce79 100644 --- a/pkg/handlers/aws/mutation/cni/calico/inject.go +++ b/pkg/handlers/aws/mutation/cni/calico/inject.go @@ -5,7 +5,6 @@ package calico import ( "context" - _ "embed" "slices" "github.com/go-logr/logr" diff --git a/pkg/handlers/aws/mutation/iaminstanceprofile/inject_control_plane.go b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_control_plane.go index af67dff9b..625e7744b 100644 --- a/pkg/handlers/aws/mutation/iaminstanceprofile/inject_control_plane.go +++ b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_control_plane.go @@ -5,7 +5,6 @@ package iaminstanceprofile import ( "context" - _ "embed" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -18,7 +17,6 @@ import ( "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables" capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" - awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig" ) @@ -36,7 +34,7 @@ func NewControlPlanePatch() *awsIAMInstanceProfileControlPlanePatchHandler { return newAWSIAMInstanceProfileControlPlanePatchHandler( clusterconfig.MetaVariableName, clusterconfig.MetaControlPlaneConfigName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, VariableName, ) } diff --git a/pkg/handlers/aws/mutation/iaminstanceprofile/inject_worker.go b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_worker.go index 9529e507f..675974a70 100644 --- a/pkg/handlers/aws/mutation/iaminstanceprofile/inject_worker.go +++ b/pkg/handlers/aws/mutation/iaminstanceprofile/inject_worker.go @@ -5,7 +5,6 @@ package iaminstanceprofile import ( "context" - _ "embed" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -18,7 +17,6 @@ import ( "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables" capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" - awsworkerconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/workerconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/workerconfig" ) @@ -30,7 +28,7 @@ type awsIAMInstanceProfileWorkerPatchHandler struct { func NewWorkerPatch() *awsIAMInstanceProfileWorkerPatchHandler { return newAWSIAMInstanceProfileWorkerPatchHandler( workerconfig.MetaVariableName, - awsworkerconfig.AWSVariableName, + v1alpha1.AWSVariableName, VariableName, ) } diff --git a/pkg/handlers/aws/mutation/instancetype/inject_control_plane.go b/pkg/handlers/aws/mutation/instancetype/inject_control_plane.go index 21cc77998..ee5423d8c 100644 --- a/pkg/handlers/aws/mutation/instancetype/inject_control_plane.go +++ b/pkg/handlers/aws/mutation/instancetype/inject_control_plane.go @@ -5,7 +5,6 @@ package instancetype import ( "context" - _ "embed" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -18,7 +17,6 @@ import ( "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables" capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" - awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig" ) @@ -36,7 +34,7 @@ func NewControlPlanePatch() *awsInstanceTypeControlPlanePatchHandler { return newAWSInstanceTypeControlPlanePatchHandler( clusterconfig.MetaVariableName, clusterconfig.MetaControlPlaneConfigName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, VariableName, ) } diff --git a/pkg/handlers/aws/mutation/instancetype/inject_worker.go b/pkg/handlers/aws/mutation/instancetype/inject_worker.go index 3cf6d5d1c..09dbe5ccd 100644 --- a/pkg/handlers/aws/mutation/instancetype/inject_worker.go +++ b/pkg/handlers/aws/mutation/instancetype/inject_worker.go @@ -5,7 +5,6 @@ package instancetype import ( "context" - _ "embed" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -18,7 +17,6 @@ import ( "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables" capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" - awsworkerconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/workerconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/workerconfig" ) @@ -30,7 +28,7 @@ type awsInstanceTypeWorkerPatchHandler struct { func NewWorkerPatch() *awsInstanceTypeWorkerPatchHandler { return newAWSInstanceTypeWorkerPatchHandler( workerconfig.MetaVariableName, - awsworkerconfig.AWSVariableName, + v1alpha1.AWSVariableName, VariableName, ) } diff --git a/pkg/handlers/aws/mutation/metapatch_handler.go b/pkg/handlers/aws/mutation/metapatch_handler.go index 999f45412..a7c2ffc4c 100644 --- a/pkg/handlers/aws/mutation/metapatch_handler.go +++ b/pkg/handlers/aws/mutation/metapatch_handler.go @@ -12,6 +12,7 @@ import ( "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/cni/calico" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/iaminstanceprofile" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/instancetype" + "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/network" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/region" genericmutation "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation" ) @@ -20,8 +21,9 @@ import ( func MetaPatchHandler(mgr manager.Manager) handlers.Named { patchHandlers := append( []mutation.MetaMutator{ - region.NewPatch(), calico.NewPatch(), + region.NewPatch(), + network.NewPatch(), iaminstanceprofile.NewControlPlanePatch(), instancetype.NewControlPlanePatch(), ami.NewControlPlanePatch(), diff --git a/pkg/handlers/aws/mutation/metapatch_handler_test.go b/pkg/handlers/aws/mutation/metapatch_handler_test.go index 1de87ae5f..586c2f9dd 100644 --- a/pkg/handlers/aws/mutation/metapatch_handler_test.go +++ b/pkg/handlers/aws/mutation/metapatch_handler_test.go @@ -13,7 +13,6 @@ import ( "github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation" - awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/ami" amitests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/ami/tests" calicotests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/cni/calico/tests" @@ -21,9 +20,10 @@ import ( iaminstanceprofiletests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/iaminstanceprofile/tests" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/instancetype" instancetypetests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/instancetype/tests" + "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/network" + networktests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/network/tests" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/region" regiontests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/mutation/region/tests" - awsworkerconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/workerconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig" auditpolicytests "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/auditpolicy/tests" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/mutation/etcd" @@ -68,7 +68,7 @@ func TestGeneratePatches(t *testing.T) { t, metaPatchGeneratorFunc(mgr), clusterconfig.MetaVariableName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, region.VariableName, ) @@ -77,7 +77,7 @@ func TestGeneratePatches(t *testing.T) { metaPatchGeneratorFunc(mgr), clusterconfig.MetaVariableName, clusterconfig.MetaControlPlaneConfigName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, iaminstanceprofile.VariableName, ) @@ -85,7 +85,7 @@ func TestGeneratePatches(t *testing.T) { t, workerPatchGeneratorFunc(), workerconfig.MetaVariableName, - awsworkerconfig.AWSVariableName, + v1alpha1.AWSVariableName, iaminstanceprofile.VariableName, ) @@ -94,7 +94,7 @@ func TestGeneratePatches(t *testing.T) { metaPatchGeneratorFunc(mgr), clusterconfig.MetaVariableName, clusterconfig.MetaControlPlaneConfigName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, instancetype.VariableName, ) @@ -102,7 +102,7 @@ func TestGeneratePatches(t *testing.T) { t, workerPatchGeneratorFunc(), workerconfig.MetaVariableName, - awsworkerconfig.AWSVariableName, + v1alpha1.AWSVariableName, instancetype.VariableName, ) @@ -161,7 +161,7 @@ func TestGeneratePatches(t *testing.T) { metaPatchGeneratorFunc(mgr), clusterconfig.MetaVariableName, clusterconfig.MetaControlPlaneConfigName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, ami.VariableName, ) @@ -169,7 +169,15 @@ func TestGeneratePatches(t *testing.T) { t, workerPatchGeneratorFunc(), workerconfig.MetaVariableName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, ami.VariableName, ) + + networktests.TestGeneratePatches( + t, + metaPatchGeneratorFunc(mgr), + clusterconfig.MetaVariableName, + v1alpha1.AWSVariableName, + network.VariableName, + ) } diff --git a/pkg/handlers/aws/mutation/network/inject.go b/pkg/handlers/aws/mutation/network/inject.go new file mode 100644 index 000000000..73ec5016b --- /dev/null +++ b/pkg/handlers/aws/mutation/network/inject.go @@ -0,0 +1,120 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package network + +import ( + "context" + + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1" + "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches" + "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors" + "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables" + capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" + "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig" +) + +const ( + // VariableName is the external patch variable name. + VariableName = "network" +) + +type awsNetworkPatchHandler struct { + variableName string + variableFieldPath []string +} + +func NewPatch() *awsNetworkPatchHandler { + return newAWSPatchPatchHandler( + clusterconfig.MetaVariableName, + v1alpha1.AWSVariableName, + VariableName, + ) +} + +func newAWSPatchPatchHandler( + variableName string, + variableFieldPath ...string, +) *awsNetworkPatchHandler { + return &awsNetworkPatchHandler{ + variableName: variableName, + variableFieldPath: variableFieldPath, + } +} + +func (h *awsNetworkPatchHandler) Mutate( + ctx context.Context, + obj *unstructured.Unstructured, + vars map[string]apiextensionsv1.JSON, + holderRef runtimehooksv1.HolderReference, + _ client.ObjectKey, +) error { + log := ctrl.LoggerFrom(ctx).WithValues( + "holderRef", holderRef, + ) + + networkVar, found, err := variables.Get[v1alpha1.AWSNetwork]( + vars, + h.variableName, + h.variableFieldPath..., + ) + if err != nil { + return err + } + if !found { + log.V(5).Info("AWS Network variable not defined") + return nil + } + + log = log.WithValues( + "variableName", + h.variableName, + "variableFieldPath", + h.variableFieldPath, + "variableValue", + networkVar, + ) + + return patches.MutateIfApplicable( + obj, + vars, + &holderRef, + selectors.InfrastructureCluster(capav1.GroupVersion.Version, "AWSClusterTemplate"), + log, + func(obj *capav1.AWSClusterTemplate) error { + log.WithValues( + "patchedObjectKind", obj.GetObjectKind().GroupVersionKind().String(), + "patchedObjectName", client.ObjectKeyFromObject(obj), + ).Info("setting Network in AWSCluster spec") + + if networkVar.VPC != nil && + networkVar.VPC.ID != "" { + obj.Spec.Template.Spec.NetworkSpec.VPC = capav1.VPCSpec{ + ID: networkVar.VPC.ID, + } + } + + if networkVar.Subnets != nil && + len(networkVar.Subnets) > 0 { + subnets := make([]capav1.SubnetSpec, 0) + for _, subnet := range networkVar.Subnets { + if subnet.ID == "" { + continue + } + subnets = append(subnets, capav1.SubnetSpec{ + ID: subnet.ID, + }) + } + obj.Spec.Template.Spec.NetworkSpec.Subnets = subnets + } + + return nil + }, + ) +} diff --git a/pkg/handlers/aws/mutation/network/tests/generate_patches.go b/pkg/handlers/aws/mutation/network/tests/generate_patches.go new file mode 100644 index 000000000..c1bd1cc22 --- /dev/null +++ b/pkg/handlers/aws/mutation/network/tests/generate_patches.go @@ -0,0 +1,104 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package tests + +import ( + "testing" + + "github.com/onsi/gomega" + runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" + + "github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1" + "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/handlers/mutation" + "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest" + "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest/request" +) + +func TestGeneratePatches( + t *testing.T, + generatorFunc func() mutation.GeneratePatches, + variableName string, + variablePath ...string, +) { + t.Helper() + + capitest.ValidateGeneratePatches( + t, + generatorFunc, + capitest.PatchTestDef{ + Name: "unset variable", + }, + capitest.PatchTestDef{ + Name: "VPC ID set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + variableName, + v1alpha1.AWSNetwork{ + VPC: &v1alpha1.VPC{ + ID: "vpc-1234", + }, + }, + variablePath..., + ), + }, + RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/network/vpc/id", + ValueMatcher: gomega.Equal("vpc-1234"), + }}, + }, + capitest.PatchTestDef{ + Name: "Subnet IDs set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + variableName, + v1alpha1.AWSNetwork{ + Subnets: v1alpha1.Subnets{ + {ID: "subnet-1"}, + {ID: "subnet-2"}, + {ID: "subnet-3"}, + }, + }, + variablePath..., + ), + }, + RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/network/subnets", + ValueMatcher: gomega.HaveLen(3), + }}, + }, + capitest.PatchTestDef{ + Name: "both VPC ID and Subnet IDs set", + Vars: []runtimehooksv1.Variable{ + capitest.VariableWithValue( + variableName, + v1alpha1.AWSNetwork{ + VPC: &v1alpha1.VPC{ + ID: "vpc-1234", + }, + Subnets: v1alpha1.Subnets{ + {ID: "subnet-1"}, + {ID: "subnet-2"}, + {ID: "subnet-3"}, + }, + }, + variablePath..., + ), + }, + RequestItem: request.NewAWSClusterTemplateRequestItem("1234"), + ExpectedPatchMatchers: []capitest.JSONPatchMatcher{{ + Operation: "add", + Path: "/spec/template/spec/network/vpc/id", + ValueMatcher: gomega.Equal("vpc-1234"), + }, { + Operation: "add", + Path: "/spec/template/spec/network/subnets", + ValueMatcher: gomega.HaveLen(3), + }}, + }, + ) +} diff --git a/pkg/handlers/aws/mutation/network/variables_test.go b/pkg/handlers/aws/mutation/network/variables_test.go new file mode 100644 index 000000000..8d3cf1754 --- /dev/null +++ b/pkg/handlers/aws/mutation/network/variables_test.go @@ -0,0 +1,68 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package network + +import ( + "testing" + + "k8s.io/utils/ptr" + + "github.com/d2iq-labs/capi-runtime-extensions/api/v1alpha1" + "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/testutils/capitest" + awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig" + "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig" +) + +func TestVariableValidation(t *testing.T) { + capitest.ValidateDiscoverVariables( + t, + clusterconfig.MetaVariableName, + ptr.To(v1alpha1.ClusterConfigSpec{AWS: &v1alpha1.AWSSpec{}}.VariableSchema()), + true, + awsclusterconfig.NewVariable, + capitest.VariableTestDef{ + Name: "specified VPC ID", + Vals: v1alpha1.ClusterConfigSpec{ + AWS: &v1alpha1.AWSSpec{ + Network: &v1alpha1.AWSNetwork{ + VPC: &v1alpha1.VPC{ + ID: "vpc-1234", + }, + }, + }, + }, + }, + capitest.VariableTestDef{ + Name: "specified subnet IDs", + Vals: v1alpha1.ClusterConfigSpec{ + AWS: &v1alpha1.AWSSpec{ + Network: &v1alpha1.AWSNetwork{ + Subnets: v1alpha1.Subnets{ + {ID: "subnet-1"}, + {ID: "subnet-2"}, + {ID: "subnet-3"}, + }, + }, + }, + }, + }, + capitest.VariableTestDef{ + Name: "specified both VPC ID and subnet IDs", + Vals: v1alpha1.ClusterConfigSpec{ + AWS: &v1alpha1.AWSSpec{ + Network: &v1alpha1.AWSNetwork{ + VPC: &v1alpha1.VPC{ + ID: "vpc-1234", + }, + Subnets: v1alpha1.Subnets{ + {ID: "subnet-1"}, + {ID: "subnet-2"}, + {ID: "subnet-3"}, + }, + }, + }, + }, + }, + ) +} diff --git a/pkg/handlers/aws/mutation/region/inject.go b/pkg/handlers/aws/mutation/region/inject.go index 5809dc233..ced6d07e8 100644 --- a/pkg/handlers/aws/mutation/region/inject.go +++ b/pkg/handlers/aws/mutation/region/inject.go @@ -5,7 +5,6 @@ package region import ( "context" - _ "embed" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -18,7 +17,6 @@ import ( "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/patches/selectors" "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/capi/clustertopology/variables" capav1 "github.com/d2iq-labs/capi-runtime-extensions/common/pkg/external/sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2" - awsclusterconfig "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/aws/clusterconfig" "github.com/d2iq-labs/capi-runtime-extensions/pkg/handlers/generic/clusterconfig" ) @@ -35,7 +33,7 @@ type awsRegionPatchHandler struct { func NewPatch() *awsRegionPatchHandler { return newAWSRegionPatchHandler( clusterconfig.MetaVariableName, - awsclusterconfig.AWSVariableName, + v1alpha1.AWSVariableName, VariableName, ) } diff --git a/pkg/handlers/aws/workerconfig/variables.go b/pkg/handlers/aws/workerconfig/variables.go index 65f9a2006..b60776f76 100644 --- a/pkg/handlers/aws/workerconfig/variables.go +++ b/pkg/handlers/aws/workerconfig/variables.go @@ -23,9 +23,6 @@ var ( const ( // HandlerNameVariable is the name of the variable handler. HandlerNameVariable = "AWSWorkerConfigVars" - - // AWSVariableName is the AWS config patch variable name. - AWSVariableName = "aws" ) func NewVariable() *awsWorkerConfigVariableHandler { diff --git a/pkg/handlers/generic/mutation/etcd/inject.go b/pkg/handlers/generic/mutation/etcd/inject.go index c81011449..82d92feda 100644 --- a/pkg/handlers/generic/mutation/etcd/inject.go +++ b/pkg/handlers/generic/mutation/etcd/inject.go @@ -5,7 +5,6 @@ package etcd import ( "context" - _ "embed" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" diff --git a/pkg/handlers/generic/mutation/extraapiservercertsans/inject.go b/pkg/handlers/generic/mutation/extraapiservercertsans/inject.go index b0c1aaed9..ebcf18f6b 100644 --- a/pkg/handlers/generic/mutation/extraapiservercertsans/inject.go +++ b/pkg/handlers/generic/mutation/extraapiservercertsans/inject.go @@ -5,7 +5,6 @@ package extraapiservercertsans import ( "context" - _ "embed" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" diff --git a/pkg/handlers/generic/mutation/imageregistries/credentials/inject.go b/pkg/handlers/generic/mutation/imageregistries/credentials/inject.go index 84df0c2ac..a1ac10c1e 100644 --- a/pkg/handlers/generic/mutation/imageregistries/credentials/inject.go +++ b/pkg/handlers/generic/mutation/imageregistries/credentials/inject.go @@ -5,7 +5,6 @@ package credentials import ( "context" - _ "embed" "fmt" corev1 "k8s.io/api/core/v1" diff --git a/pkg/handlers/generic/mutation/kubernetesimagerepository/inject.go b/pkg/handlers/generic/mutation/kubernetesimagerepository/inject.go index 0e509bf06..b32431fb3 100644 --- a/pkg/handlers/generic/mutation/kubernetesimagerepository/inject.go +++ b/pkg/handlers/generic/mutation/kubernetesimagerepository/inject.go @@ -5,7 +5,6 @@ package kubernetesimagerepository import ( "context" - _ "embed" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"