diff --git a/api/v1alpha2/awsmachine_conversion.go b/api/v1alpha2/awsmachine_conversion.go index 389e96c649..dc974e7d16 100644 --- a/api/v1alpha2/awsmachine_conversion.go +++ b/api/v1alpha2/awsmachine_conversion.go @@ -50,6 +50,10 @@ func restoreAWSMachineSpec(restored *infrav1alpha3.AWSMachineSpec, dst *infrav1a restored.RootVolume.DeepCopyInto(dst.RootVolume) } + if restored.EtcdVolume != nil { + restored.EtcdVolume.DeepCopyInto(dst.EtcdVolume) + } + // override the SSHKeyName conversion if we are roundtripping from v1alpha3 and the v1alpha3 value is nil if dst.SSHKeyName != nil && *dst.SSHKeyName == "" && restored.SSHKeyName == nil { dst.SSHKeyName = nil diff --git a/api/v1alpha2/zz_generated.conversion.go b/api/v1alpha2/zz_generated.conversion.go index a2195724e9..c8a84b6995 100644 --- a/api/v1alpha2/zz_generated.conversion.go +++ b/api/v1alpha2/zz_generated.conversion.go @@ -602,6 +602,7 @@ func autoConvert_v1alpha3_AWSMachineSpec_To_v1alpha2_AWSMachineSpec(in *v1alpha3 return err } // WARNING: in.RootVolume requires manual conversion: does not exist in peer-type + // WARNING: in.EtcdVolume requires manual conversion: does not exist in peer-type out.NetworkInterfaces = *(*[]string)(unsafe.Pointer(&in.NetworkInterfaces)) // WARNING: in.UncompressedUserData requires manual conversion: does not exist in peer-type // WARNING: in.CloudInit requires manual conversion: inconvertible types (sigs.k8s.io/cluster-api-provider-aws/api/v1alpha3.CloudInit vs *sigs.k8s.io/cluster-api-provider-aws/api/v1alpha2.CloudInit) @@ -1005,6 +1006,7 @@ func autoConvert_v1alpha3_Instance_To_v1alpha2_Instance(in *v1alpha3.Instance, o out.ENASupport = (*bool)(unsafe.Pointer(in.ENASupport)) out.EBSOptimized = (*bool)(unsafe.Pointer(in.EBSOptimized)) // WARNING: in.RootVolume requires manual conversion: does not exist in peer-type + // WARNING: in.EtcdVolume requires manual conversion: does not exist in peer-type out.NetworkInterfaces = *(*[]string)(unsafe.Pointer(&in.NetworkInterfaces)) out.Tags = *(*map[string]string)(unsafe.Pointer(&in.Tags)) return nil diff --git a/api/v1alpha3/awsmachine_types.go b/api/v1alpha3/awsmachine_types.go index 6d0d3c87bb..3632aec841 100644 --- a/api/v1alpha3/awsmachine_types.go +++ b/api/v1alpha3/awsmachine_types.go @@ -88,6 +88,10 @@ type AWSMachineSpec struct { // +optional RootVolume *RootVolume `json:"rootVolume,omitempty"` + // EtcdVolume encapsulates the configuration options for the etcd data-store volume + // +optional + EtcdVolume *EtcdVolume `json:"etcdVolume,omitempty"` + // NetworkInterfaces is a list of ENIs to associate with the instance. // A maximum of 2 may be specified. // +optional diff --git a/api/v1alpha3/types.go b/api/v1alpha3/types.go index 171dcc08d7..1d6b02216a 100644 --- a/api/v1alpha3/types.go +++ b/api/v1alpha3/types.go @@ -542,6 +542,10 @@ type Instance struct { // +optional RootVolume *RootVolume `json:"rootVolume,omitempty"` + // Configuration options for the etcd storage volume. + // +optional + EtcdVolume *EtcdVolume `json:"etcdVolume,omitempty"` + // Specifies ENIs attached to instance NetworkInterfaces []string `json:"networkInterfaces,omitempty"` @@ -574,3 +578,29 @@ type RootVolume struct { // +optional EncryptionKey string `json:"encryptionKey,omitempty"` } + +// EtcdVolume encapsulates the configuration options for the etcd volume +type EtcdVolume struct { + // Size specifies size (in Gi) of the root storage device. + // Must be greater than the image root snapshot size or 8 (whichever is greater). + // +kubebuilder:validation:Minimum=8 + Size int64 `json:"size"` + + // Type is the type of the root volume (e.g. gp2, io1, etc...). + // +optional + Type string `json:"type,omitempty"` + + // IOPS is the number of IOPS requested for the disk. Not applicable to all types. + // +optional + IOPS int64 `json:"iops,omitempty"` + + // Encrypted is whether the volume should be encrypted or not. + // +optional + Encrypted bool `json:"encrypted,omitempty"` + + // EncryptionKey is the KMS key to use to encrypt the volume. Can be either a KMS key ID or ARN. + // If Encrypted is set and this is omitted, the default AWS key will be used. + // The key must already exist and be accessible by the controller. + // +optional + EncryptionKey string `json:"encryptionKey,omitempty"` +} diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index bac011e646..63172532e8 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -276,6 +276,11 @@ func (in *AWSMachineSpec) DeepCopyInto(out *AWSMachineSpec) { *out = new(RootVolume) **out = **in } + if in.EtcdVolume != nil { + in, out := &in.EtcdVolume, &out.EtcdVolume + *out = new(EtcdVolume) + **out = **in + } if in.NetworkInterfaces != nil { in, out := &in.NetworkInterfaces, &out.NetworkInterfaces *out = make([]string, len(*in)) @@ -617,6 +622,21 @@ func (in *CloudInit) DeepCopy() *CloudInit { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EtcdVolume) DeepCopyInto(out *EtcdVolume) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EtcdVolume. +func (in *EtcdVolume) DeepCopy() *EtcdVolume { + if in == nil { + return nil + } + out := new(EtcdVolume) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Filter) DeepCopyInto(out *Filter) { *out = *in @@ -735,6 +755,11 @@ func (in *Instance) DeepCopyInto(out *Instance) { *out = new(RootVolume) **out = **in } + if in.EtcdVolume != nil { + in, out := &in.EtcdVolume, &out.EtcdVolume + *out = new(EtcdVolume) + **out = **in + } if in.NetworkInterfaces != nil { in, out := &in.NetworkInterfaces, &out.NetworkInterfaces *out = make([]string, len(*in)) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml index 69d4226dbd..de591c6879 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsclusters.yaml @@ -632,6 +632,39 @@ spec: description: Specifies whether enhanced networking with ENA is enabled. type: boolean + etcdVolume: + description: Configuration options for the etcd storage volume. + properties: + encrypted: + description: Encrypted is whether the volume should be encrypted + or not. + type: boolean + encryptionKey: + description: EncryptionKey is the KMS key to use to encrypt + the volume. Can be either a KMS key ID or ARN. If Encrypted + is set and this is omitted, the default AWS key will be + used. The key must already exist and be accessible by the + controller. + type: string + iops: + description: IOPS is the number of IOPS requested for the + disk. Not applicable to all types. + format: int64 + type: integer + size: + description: Size specifies size (in Gi) of the root storage + device. Must be greater than the image root snapshot size + or 8 (whichever is greater). + format: int64 + minimum: 8 + type: integer + type: + description: Type is the type of the root volume (e.g. gp2, + io1, etc...). + type: string + required: + - size + type: object iamProfile: description: The name of the IAM instance profile associated with the instance, if applicable. 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 656cc106ec..ddfc97fd01 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachines.yaml @@ -419,6 +419,39 @@ spec: as a node against the workload cluster. type: string type: object + etcdVolume: + description: EtcdVolume encapsulates the configuration options for + the etcd data-store volume + properties: + encrypted: + description: Encrypted is whether the volume should be encrypted + or not. + type: boolean + encryptionKey: + description: EncryptionKey is the KMS key to use to encrypt the + volume. Can be either a KMS key ID or ARN. If Encrypted is set + and this is omitted, the default AWS key will be used. The key + must already exist and be accessible by the controller. + type: string + iops: + description: IOPS is the number of IOPS requested for the disk. + Not applicable to all types. + format: int64 + type: integer + size: + description: Size specifies size (in Gi) of the root storage device. + Must be greater than the image root snapshot size or 8 (whichever + is greater). + format: int64 + minimum: 8 + type: integer + type: + description: Type is the type of the root volume (e.g. gp2, io1, + etc...). + type: string + required: + - size + type: object failureDomain: description: FailureDomain is the failure domain unique identifier this Machine should be attached to, as defined in Cluster API. For 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 2184470b7a..ace9e040fc 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_awsmachinetemplates.yaml @@ -378,6 +378,40 @@ spec: machine registers as a node against the workload cluster. type: string type: object + etcdVolume: + description: EtcdVolume encapsulates the configuration options + for the etcd data-store volume + properties: + encrypted: + description: Encrypted is whether the volume should be + encrypted or not. + type: boolean + encryptionKey: + description: EncryptionKey is the KMS key to use to encrypt + the volume. Can be either a KMS key ID or ARN. If Encrypted + is set and this is omitted, the default AWS key will + be used. The key must already exist and be accessible + by the controller. + type: string + iops: + description: IOPS is the number of IOPS requested for + the disk. Not applicable to all types. + format: int64 + type: integer + size: + description: Size specifies size (in Gi) of the root storage + device. Must be greater than the image root snapshot + size or 8 (whichever is greater). + format: int64 + minimum: 8 + type: integer + type: + description: Type is the type of the root volume (e.g. + gp2, io1, etc...). + type: string + required: + - size + type: object failureDomain: description: FailureDomain is the failure domain unique identifier this Machine should be attached to, as defined in Cluster diff --git a/pkg/cloud/services/ec2/instances.go b/pkg/cloud/services/ec2/instances.go index 45ab426b99..867555970a 100644 --- a/pkg/cloud/services/ec2/instances.go +++ b/pkg/cloud/services/ec2/instances.go @@ -108,6 +108,7 @@ func (s *Service) CreateInstance(scope *scope.MachineScope, userData []byte) (*i Type: scope.AWSMachine.Spec.InstanceType, IAMProfile: scope.AWSMachine.Spec.IAMInstanceProfile, RootVolume: scope.AWSMachine.Spec.RootVolume, + EtcdVolume: scope.AWSMachine.Spec.EtcdVolume, NetworkInterfaces: scope.AWSMachine.Spec.NetworkInterfaces, } @@ -395,6 +396,37 @@ func (s *Service) runInstance(role string, i *infrav1.Instance) (*infrav1.Instan } } + if i.EtcdVolume != nil { + + etcdDeviceName := aws.String("/dev/sdb") + + ebsEtcdDevice := &ec2.EbsBlockDevice{ + DeleteOnTermination: aws.Bool(true), + VolumeSize: aws.Int64(i.EtcdVolume.Size), + Encrypted: aws.Bool(i.DeepCopy().EtcdVolume.Encrypted), + } + + if i.EtcdVolume.IOPS != 0 { + ebsEtcdDevice.Iops = aws.Int64(i.EtcdVolume.IOPS) + } + + if i.EtcdVolume.EncryptionKey != "" { + ebsEtcdDevice.Encrypted = aws.Bool(true) + ebsEtcdDevice.KmsKeyId = aws.String(i.EtcdVolume.EncryptionKey) + } + + if i.EtcdVolume.Type != "" { + ebsEtcdDevice.VolumeType = aws.String(i.EtcdVolume.Type) + } + + input.BlockDeviceMappings = append(input.BlockDeviceMappings, []*ec2.BlockDeviceMapping{ + { + DeviceName: etcdDeviceName, + Ebs: ebsEtcdDevice, + }, + }...) + } + if len(i.Tags) > 0 { spec := &ec2.TagSpecification{ResourceType: aws.String(ec2.ResourceTypeInstance)} for key, value := range i.Tags { diff --git a/templates/cluster-template.yaml b/templates/cluster-template.yaml index 0bafd4746e..bf69f761c6 100644 --- a/templates/cluster-template.yaml +++ b/templates/cluster-template.yaml @@ -23,6 +23,8 @@ metadata: spec: region: "${AWS_REGION}" sshKeyName: "${AWS_SSH_KEY_NAME}" + bastion: + enabled: true --- kind: KubeadmControlPlane apiVersion: controlplane.cluster.x-k8s.io/v1alpha3 @@ -35,6 +37,18 @@ spec: apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 name: "${CLUSTER_NAME}-control-plane" kubeadmConfigSpec: + preKubeadmCommands: + - if [ -d "/var/lib/etcd/lost+found" ]; then rm -Rf /var/lib/etcd/lost+found; fi + diskSetup: + filesystems: + - device: /dev/nvme1n1p1 + filesystem: ext4 + label: etcd_disk + partitions: + - device: /dev/nvme1n1 + layout: true + overwrite: false + tableType: mbr initConfiguration: nodeRegistration: name: '{{ ds.meta_data.local_hostname }}' @@ -106,6 +120,16 @@ metadata: spec: template: spec: + diskSetup: + filesystems: + - device: /dev/nvme1n1p1 + filesystem: ext4 + label: etcd_disk + partitions: + - device: /dev/nvme1n1 + layout: true + overwrite: false + tableType: mbr joinConfiguration: nodeRegistration: name: '{{ ds.meta_data.local_hostname }}'