diff --git a/api/v1alpha2/azurecluster_conversion.go b/api/v1alpha2/azurecluster_conversion.go index 052d7590c64..736ffa3805f 100644 --- a/api/v1alpha2/azurecluster_conversion.go +++ b/api/v1alpha2/azurecluster_conversion.go @@ -59,6 +59,7 @@ func (src *AzureCluster) ConvertTo(dstRaw conversion.Hub) error { // nolint } dst.Status.FailureDomains = restored.Status.FailureDomains + dst.Status.Bastion.OSDisk.ManagedDisk.DiffDiskSettings = restored.Status.Bastion.OSDisk.ManagedDisk.DiffDiskSettings for _, restoredSubnet := range restored.Spec.NetworkSpec.Subnets { if restoredSubnet != nil { diff --git a/api/v1alpha2/azuremachine_conversion.go b/api/v1alpha2/azuremachine_conversion.go index ae2d44e2d15..4ee697ade4c 100644 --- a/api/v1alpha2/azuremachine_conversion.go +++ b/api/v1alpha2/azuremachine_conversion.go @@ -19,6 +19,7 @@ package v1alpha2 import ( apiconversion "k8s.io/apimachinery/pkg/conversion" infrav1alpha3 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" + v1alpha3 "sigs.k8s.io/cluster-api-provider-azure/api/v1alpha3" utilconversion "sigs.k8s.io/cluster-api/util/conversion" "sigs.k8s.io/controller-runtime/pkg/conversion" ) @@ -62,6 +63,7 @@ func restoreAzureMachineSpec(restored, dst *infrav1alpha3.AzureMachineSpec) { if len(restored.DataDisks) != 0 { dst.DataDisks = restored.DataDisks } + dst.OSDisk.ManagedDisk.DiffDiskSettings = restored.OSDisk.ManagedDisk.DiffDiskSettings } // ConvertFrom converts from the Hub version (v1alpha3) to this version. @@ -186,6 +188,18 @@ func Convert_v1alpha3_Image_To_v1alpha2_Image(in *infrav1alpha3.Image, out *Imag return nil } +// Convert_v1alpha2_ManagedDisk_To_v1alpha3_ManagedDisk converts between api versions +func Convert_v1alpha2_ManagedDisk_To_v1alpha3_ManagedDisk(in *ManagedDisk, out *v1alpha3.ManagedDisk, s apiconversion.Scope) error { + out.StorageAccountType = in.StorageAccountType + return nil +} + +// Convert_v1alpha3_ManagedDisk_To_v1alpha2_ManagedDisk converts between api versions +func Convert_v1alpha3_ManagedDisk_To_v1alpha2_ManagedDisk(in *v1alpha3.ManagedDisk, out *ManagedDisk, s apiconversion.Scope) error { + out.StorageAccountType = in.StorageAccountType + return nil +} + func isAzureMarketPlaceImage(in *Image) bool { if in.Publisher == nil || in.Offer == nil || in.SKU == nil || in.Version == nil { return false diff --git a/api/v1alpha2/zz_generated.conversion.go b/api/v1alpha2/zz_generated.conversion.go index 85b91581509..2065cdbd888 100644 --- a/api/v1alpha2/zz_generated.conversion.go +++ b/api/v1alpha2/zz_generated.conversion.go @@ -166,16 +166,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ManagedDisk)(nil), (*v1alpha3.ManagedDisk)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha2_ManagedDisk_To_v1alpha3_ManagedDisk(a.(*ManagedDisk), b.(*v1alpha3.ManagedDisk), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*v1alpha3.ManagedDisk)(nil), (*ManagedDisk)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha3_ManagedDisk_To_v1alpha2_ManagedDisk(a.(*v1alpha3.ManagedDisk), b.(*ManagedDisk), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1alpha3.Network)(nil), (*Network)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha3_Network_To_v1alpha2_Network(a.(*v1alpha3.Network), b.(*Network), scope) }); err != nil { @@ -251,6 +241,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*ManagedDisk)(nil), (*v1alpha3.ManagedDisk)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha2_ManagedDisk_To_v1alpha3_ManagedDisk(a.(*ManagedDisk), b.(*v1alpha3.ManagedDisk), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*NetworkSpec)(nil), (*v1alpha3.NetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha2_NetworkSpec_To_v1alpha3_NetworkSpec(a.(*NetworkSpec), b.(*v1alpha3.NetworkSpec), scope) }); err != nil { @@ -301,6 +296,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1alpha3.ManagedDisk)(nil), (*ManagedDisk)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha3_ManagedDisk_To_v1alpha2_ManagedDisk(a.(*v1alpha3.ManagedDisk), b.(*ManagedDisk), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1alpha3.NetworkSpec)(nil), (*NetworkSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha3_NetworkSpec_To_v1alpha2_NetworkSpec(a.(*v1alpha3.NetworkSpec), b.(*NetworkSpec), scope) }); err != nil { @@ -882,21 +882,12 @@ func autoConvert_v1alpha2_ManagedDisk_To_v1alpha3_ManagedDisk(in *ManagedDisk, o return nil } -// Convert_v1alpha2_ManagedDisk_To_v1alpha3_ManagedDisk is an autogenerated conversion function. -func Convert_v1alpha2_ManagedDisk_To_v1alpha3_ManagedDisk(in *ManagedDisk, out *v1alpha3.ManagedDisk, s conversion.Scope) error { - return autoConvert_v1alpha2_ManagedDisk_To_v1alpha3_ManagedDisk(in, out, s) -} - func autoConvert_v1alpha3_ManagedDisk_To_v1alpha2_ManagedDisk(in *v1alpha3.ManagedDisk, out *ManagedDisk, s conversion.Scope) error { + // WARNING: in.DiffDiskSettings requires manual conversion: does not exist in peer-type out.StorageAccountType = in.StorageAccountType return nil } -// Convert_v1alpha3_ManagedDisk_To_v1alpha2_ManagedDisk is an autogenerated conversion function. -func Convert_v1alpha3_ManagedDisk_To_v1alpha2_ManagedDisk(in *v1alpha3.ManagedDisk, out *ManagedDisk, s conversion.Scope) error { - return autoConvert_v1alpha3_ManagedDisk_To_v1alpha2_ManagedDisk(in, out, s) -} - func autoConvert_v1alpha2_Network_To_v1alpha3_Network(in *Network, out *v1alpha3.Network, s conversion.Scope) error { // WARNING: in.SecurityGroups requires manual conversion: does not exist in peer-type if err := Convert_v1alpha2_LoadBalancer_To_v1alpha3_LoadBalancer(&in.APIServerLB, &out.APIServerLB, s); err != nil { diff --git a/api/v1alpha3/azuremachine_validation.go b/api/v1alpha3/azuremachine_validation.go index db4b2c446cf..39850bc1344 100644 --- a/api/v1alpha3/azuremachine_validation.go +++ b/api/v1alpha3/azuremachine_validation.go @@ -19,6 +19,7 @@ package v1alpha3 import ( "encoding/base64" "fmt" + "strings" "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2020-06-01/compute" "golang.org/x/crypto/ssh" @@ -102,6 +103,61 @@ func ValidateOSDisk(osDisk OSDisk, fieldPath *field.Path) field.ErrorList { allErrs = append(allErrs, validateStorageAccountType(osDisk.ManagedDisk.StorageAccountType, fieldPath)...) + if errs := ValidateManagedDisk(osDisk.ManagedDisk, osDisk.ManagedDisk, fieldPath.Child("managedDisk")); len(errs) > 0 { + allErrs = append(allErrs, errs...) + } + + return allErrs +} + +// ValidateManagedDisk validates updates to the ManagedDisk field. +func ValidateManagedDisk(old, new ManagedDisk, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if old.StorageAccountType != new.StorageAccountType { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("storageAccountType"), new, "changing storage account type after machine creation is not allowed")) + } + + if errs := validateDiffDiskSettingsUpdate(old.DiffDiskSettings, new.DiffDiskSettings, fieldPath); len(errs) > 0 { + allErrs = append(allErrs, errs...) + } + + if new.DiffDiskSettings != nil && + strings.EqualFold(new.DiffDiskSettings.Option, string(compute.Local)) && + new.StorageAccountType != string(compute.StandardLRS) { + allErrs = append(allErrs, field.Invalid(fieldPath.Child("storageAccountType"), new, "storageAccountType must be 'Standard_LRS' if diffDiskSettings.option is set to 'local'")) + } + + return allErrs +} + +func validateDiffDiskSettingsUpdate(old, new *DiffDiskSettings, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + fldPath := fieldPath.Child("diffDiskSettings") + + if old == nil && new != nil { + allErrs = append(allErrs, field.Invalid(fldPath, new, fmt.Sprintf("enabling ephemeral os after machine creation is not allowed"))) + return allErrs + } + if old != nil && new == nil { + allErrs = append(allErrs, field.Invalid(fldPath, new, fmt.Sprintf("disabling ephemeral os after machine creation is not allowed"))) + return allErrs + } + + if old != nil && new != nil { + if old.Option != new.Option { + allErrs = append( + allErrs, + field.Invalid( + fldPath.Child("option"), + new, + fmt.Sprintf("changing ephemeral os settings after machine creation is not allowed"), + ), + ) + return allErrs + } + } + return allErrs } diff --git a/api/v1alpha3/azuremachine_validation_test.go b/api/v1alpha3/azuremachine_validation_test.go index 9adc1b1296f..e792181fd76 100644 --- a/api/v1alpha3/azuremachine_validation_test.go +++ b/api/v1alpha3/azuremachine_validation_test.go @@ -20,9 +20,12 @@ import ( "crypto/rand" "crypto/rsa" "encoding/base64" - "github.com/Azure/go-autorest/autorest/to" + "fmt" "testing" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2020-06-01/compute" + "github.com/Azure/go-autorest/autorest/to" + . "github.com/onsi/gomega" "golang.org/x/crypto/ssh" "k8s.io/apimachinery/pkg/util/validation/field" @@ -81,6 +84,20 @@ func TestAzureMachine_ValidateOSDisk(t *testing.T) { wantErr: false, osDisk: generateValidOSDisk(), }, + { + name: "valid ephemeral os disk spec", + wantErr: false, + osDisk: OSDisk{ + DiskSizeGB: 30, + OSType: "blah", + ManagedDisk: ManagedDisk{ + DiffDiskSettings: &DiffDiskSettings{ + Option: string(compute.Local), + }, + StorageAccountType: "Standard_LRS", + }, + }, + }, } testcases = append(testcases, generateNegativeTestCases()...) @@ -137,11 +154,21 @@ func generateNegativeTestCases() []osDiskTestInput { StorageAccountType: "invalid_type", }, }, + { + DiskSizeGB: 30, + OSType: "blah", + ManagedDisk: ManagedDisk{ + DiffDiskSettings: &DiffDiskSettings{ + Option: string(compute.Local), + }, + StorageAccountType: "Premium_LRS", + }, + }, } - for _, input := range invalidDiskSpecs { + for i, input := range invalidDiskSpecs { inputs = append(inputs, osDiskTestInput{ - name: testCaseName, + name: fmt.Sprintf("%s-%d", testCaseName, i), wantErr: true, osDisk: input, }) @@ -159,7 +186,6 @@ func generateValidOSDisk() OSDisk { }, } } - func TestAzureMachine_ValidateDataDisks(t *testing.T) { g := NewWithT(t) diff --git a/api/v1alpha3/azuremachine_webhook.go b/api/v1alpha3/azuremachine_webhook.go index 1dbad8c1999..94eacd3ea9a 100644 --- a/api/v1alpha3/azuremachine_webhook.go +++ b/api/v1alpha3/azuremachine_webhook.go @@ -71,10 +71,12 @@ func (m *AzureMachine) ValidateCreate() error { } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (m *AzureMachine) ValidateUpdate(old runtime.Object) error { +func (m *AzureMachine) ValidateUpdate(oldRaw runtime.Object) error { machinelog.Info("validate update", "name", m.Name) var allErrs field.ErrorList + old := oldRaw.(*AzureMachine) + if errs := ValidateSSHKey(m.Spec.SSHPublicKey, field.NewPath("sshPublicKey")); len(errs) > 0 { allErrs = append(allErrs, errs...) } @@ -87,6 +89,10 @@ func (m *AzureMachine) ValidateUpdate(old runtime.Object) error { allErrs = append(allErrs, errs...) } + if errs := ValidateManagedDisk(old.Spec.OSDisk.ManagedDisk, m.Spec.OSDisk.ManagedDisk, field.NewPath("osDisk").Child("managedDisk")); len(errs) > 0 { + allErrs = append(allErrs, errs...) + } + if len(allErrs) == 0 { return nil } diff --git a/api/v1alpha3/types.go b/api/v1alpha3/types.go index b8c9c7cb7d2..1842437a41c 100644 --- a/api/v1alpha3/types.go +++ b/api/v1alpha3/types.go @@ -326,7 +326,16 @@ type DataDisk struct { // ManagedDisk defines the managed disk options for a VM. type ManagedDisk struct { - StorageAccountType string `json:"storageAccountType"` + DiffDiskSettings *DiffDiskSettings `json:"diffDiskSettings,omitempty"` + StorageAccountType string `json:"storageAccountType"` +} + +// DiffDiskSettings describe ephemeral disk settings for the os disk. +type DiffDiskSettings struct { + // Option enables ephemeral OS when set to "Local" + // See https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ephemeral-os-disks for full details + // +kubebuilder:validation:Enum=Local + Option string `json:"option"` } // SubnetRole defines the unique role of a subnet. diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index 26e953fceee..a2dc043c46a 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -249,7 +249,7 @@ func (in *AzureMachineSpec) DeepCopyInto(out *AzureMachineSpec) { *out = make([]UserAssignedIdentity, len(*in)) copy(*out, *in) } - out.OSDisk = in.OSDisk + in.OSDisk.DeepCopyInto(&out.OSDisk) if in.DataDisks != nil { in, out := &in.DataDisks, &out.DataDisks *out = make([]DataDisk, len(*in)) @@ -515,6 +515,21 @@ func (in *DataDisk) DeepCopy() *DataDisk { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiffDiskSettings) DeepCopyInto(out *DiffDiskSettings) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiffDiskSettings. +func (in *DiffDiskSettings) DeepCopy() *DiffDiskSettings { + if in == nil { + return nil + } + out := new(DiffDiskSettings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FrontendIPConfig) DeepCopyInto(out *FrontendIPConfig) { *out = *in @@ -647,6 +662,11 @@ func (in *LoadBalancer) DeepCopy() *LoadBalancer { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ManagedDisk) DeepCopyInto(out *ManagedDisk) { *out = *in + if in.DiffDiskSettings != nil { + in, out := &in.DiffDiskSettings, &out.DiffDiskSettings + *out = new(DiffDiskSettings) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedDisk. @@ -706,7 +726,7 @@ func (in *NetworkSpec) DeepCopy() *NetworkSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OSDisk) DeepCopyInto(out *OSDisk) { *out = *in - out.ManagedDisk = in.ManagedDisk + in.ManagedDisk.DeepCopyInto(&out.ManagedDisk) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OSDisk. @@ -884,7 +904,7 @@ func (in *UserAssignedIdentity) DeepCopy() *UserAssignedIdentity { func (in *VM) DeepCopyInto(out *VM) { *out = *in in.Image.DeepCopyInto(&out.Image) - out.OSDisk = in.OSDisk + in.OSDisk.DeepCopyInto(&out.OSDisk) if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make(Tags, len(*in)) diff --git a/cloud/services/resourceskus/client.go b/cloud/services/resourceskus/client.go index 42f4466adae..7b7649dcd00 100644 --- a/cloud/services/resourceskus/client.go +++ b/cloud/services/resourceskus/client.go @@ -31,6 +31,7 @@ import ( type Client interface { List(context.Context, string) ([]compute.ResourceSku, error) HasAcceleratedNetworking(context.Context, string) (bool, error) + HasEphemeralOSDiskSupport(context.Context, string) (bool, error) } // AzureClient contains the Azure go-sdk Client @@ -98,3 +99,29 @@ func (ac *AzureClient) HasAcceleratedNetworking(ctx context.Context, name string } return false, nil } + +// HasEphemeralOSDiskSupport returns whether the given compute SKU supports ephemeral os. +func (ac *AzureClient) HasEphemeralOSDiskSupport(ctx context.Context, name string) (bool, error) { + if name == "" { + return false, nil + } + skus, err := ac.List(ctx, "") // "filter" argument only works for location, so filter in code + if err != nil { + return false, err + } + for _, sku := range skus { + if sku.Name != nil && *sku.Name == name { + if sku.Capabilities != nil { + for _, c := range *sku.Capabilities { + if c.Name != nil && *c.Name == "EphemeralOSDiskSupported" { + if c.Value != nil && strings.EqualFold(*c.Value, "True") { + return true, nil + } + } + } + } + break + } + } + return false, nil +} diff --git a/cloud/services/resourceskus/mock_resourceskus/resourceskus_mock.go b/cloud/services/resourceskus/mock_resourceskus/resourceskus_mock.go index ef4a7fc3fe6..9c1257fe2d2 100644 --- a/cloud/services/resourceskus/mock_resourceskus/resourceskus_mock.go +++ b/cloud/services/resourceskus/mock_resourceskus/resourceskus_mock.go @@ -79,3 +79,18 @@ func (mr *MockClientMockRecorder) HasAcceleratedNetworking(arg0, arg1 interface{ mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasAcceleratedNetworking", reflect.TypeOf((*MockClient)(nil).HasAcceleratedNetworking), arg0, arg1) } + +// HasEphemeralOSDiskSupport mocks base method. +func (m *MockClient) HasEphemeralOSDiskSupport(arg0 context.Context, arg1 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HasEphemeralOSDiskSupport", arg0, arg1) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HasEphemeralOSDiskSupport indicates an expected call of HasEphemeralOSDiskSupport. +func (mr *MockClientMockRecorder) HasEphemeralOSDiskSupport(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasEphemeralOSDiskSupport", reflect.TypeOf((*MockClient)(nil).HasEphemeralOSDiskSupport), arg0, arg1) +} diff --git a/cloud/services/scalesets/vmss.go b/cloud/services/scalesets/vmss.go index 3c5dab910fc..b635674a8af 100644 --- a/cloud/services/scalesets/vmss.go +++ b/cloud/services/scalesets/vmss.go @@ -75,7 +75,7 @@ func (s *Service) Reconcile(ctx context.Context, spec interface{}) error { return errors.New("invalid VMSS specification") } - storageProfile, err := generateStorageProfile(*vmssSpec) + storageProfile, err := s.generateStorageProfile(ctx, *vmssSpec) if err != nil { return err } @@ -221,7 +221,7 @@ func (s *Service) Delete(ctx context.Context, spec interface{}) error { } // generateStorageProfile generates a pointer to a compute.VirtualMachineScaleSetStorageProfile which can utilized for VM creation. -func generateStorageProfile(vmssSpec Spec) (*compute.VirtualMachineScaleSetStorageProfile, error) { +func (s *Service) generateStorageProfile(ctx context.Context, vmssSpec Spec) (*compute.VirtualMachineScaleSetStorageProfile, error) { storageProfile := &compute.VirtualMachineScaleSetStorageProfile{ OsDisk: &compute.VirtualMachineScaleSetOSDisk{ OsType: compute.OperatingSystemTypes(vmssSpec.OSDisk.OSType), @@ -233,6 +233,22 @@ func generateStorageProfile(vmssSpec Spec) (*compute.VirtualMachineScaleSetStora }, } + // enable ephemeral OS + if vmssSpec.OSDisk.ManagedDisk.DiffDiskSettings != nil { + hasEpehemeralOS, err := s.ResourceSkusClient.HasEphemeralOSDiskSupport(ctx, vmssSpec.Sku) + if err != nil { + return nil, errors.Wrap(err, "failed to get ephemeral os capability for vm") + } + + if !hasEpehemeralOS { + return nil, fmt.Errorf("vm size %s does not support ephemeral os. select a different vm size or disable ephemeral os", vmssSpec.Sku) + } + + storageProfile.OsDisk.DiffDiskSettings = &compute.DiffDiskSettings{ + Option: compute.DiffDiskOptions(vmssSpec.OSDisk.ManagedDisk.DiffDiskSettings.Option), + } + } + dataDisks := []compute.VirtualMachineScaleSetDataDisk{} for _, disk := range vmssSpec.DataDisks { dataDisks = append(dataDisks, compute.VirtualMachineScaleSetDataDisk{ diff --git a/cloud/services/scalesets/vmss_test.go b/cloud/services/scalesets/vmss_test.go index 87e1c49a598..33a303ac40f 100644 --- a/cloud/services/scalesets/vmss_test.go +++ b/cloud/services/scalesets/vmss_test.go @@ -292,7 +292,7 @@ func TestService_Reconcile(t *testing.T) { lbMock := mock_publicloadbalancers.NewMockClient(mockCtrl) svc.PublicLoadBalancersClient = lbMock - storageProfile, err := generateStorageProfile(*spec) + storageProfile, err := svc.generateStorageProfile(ctx, *spec) g.Expect(err).ToNot(gomega.HaveOccurred()) vmss := compute.VirtualMachineScaleSet{ @@ -406,7 +406,7 @@ func TestService_Reconcile(t *testing.T) { lbMock := mock_publicloadbalancers.NewMockClient(mockCtrl) svc.PublicLoadBalancersClient = lbMock - storageProfile, err := generateStorageProfile(*spec) + storageProfile, err := svc.generateStorageProfile(ctx, *spec) g.Expect(err).ToNot(gomega.HaveOccurred()) vmss := compute.VirtualMachineScaleSet{ @@ -527,7 +527,7 @@ func TestService_Reconcile(t *testing.T) { lbMock := mock_publicloadbalancers.NewMockClient(mockCtrl) svc.PublicLoadBalancersClient = lbMock - storageProfile, err := generateStorageProfile(*spec) + storageProfile, err := svc.generateStorageProfile(ctx, *spec) g.Expect(err).ToNot(gomega.HaveOccurred()) vmss := compute.VirtualMachineScaleSet{ diff --git a/cloud/services/virtualmachines/service.go b/cloud/services/virtualmachines/service.go index 0cbd3fef03d..083ad7d89c5 100644 --- a/cloud/services/virtualmachines/service.go +++ b/cloud/services/virtualmachines/service.go @@ -20,6 +20,7 @@ import ( "sigs.k8s.io/cluster-api-provider-azure/cloud/scope" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/networkinterfaces" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/publicips" + "sigs.k8s.io/cluster-api-provider-azure/cloud/services/resourceskus" "sigs.k8s.io/cluster-api-provider-azure/cloud/services/roleassignments" ) @@ -30,6 +31,7 @@ type Service struct { Client InterfacesClient networkinterfaces.Client PublicIPsClient publicips.Client + ResourceSkusClient resourceskus.Client RoleAssignmentsClient roleassignments.Client } @@ -41,6 +43,7 @@ func NewService(scope *scope.ClusterScope, machineScope *scope.MachineScope) *Se Client: NewClient(scope), InterfacesClient: networkinterfaces.NewClient(scope), PublicIPsClient: publicips.NewClient(scope), + ResourceSkusClient: resourceskus.NewClient(scope), RoleAssignmentsClient: roleassignments.NewClient(scope), } } diff --git a/cloud/services/virtualmachines/virtualmachines.go b/cloud/services/virtualmachines/virtualmachines.go index e92bf9ab6f1..4d21c49d7d7 100644 --- a/cloud/services/virtualmachines/virtualmachines.go +++ b/cloud/services/virtualmachines/virtualmachines.go @@ -85,7 +85,7 @@ func (s *Service) Reconcile(ctx context.Context, spec interface{}) error { return errors.New("invalid VM specification") } - storageProfile, err := generateStorageProfile(*vmSpec) + storageProfile, err := s.generateStorageProfile(ctx, *vmSpec) if err != nil { return err } @@ -327,7 +327,7 @@ func getResourceNameByID(resourceID string) string { } // generateStorageProfile generates a pointer to a compute.StorageProfile which can utilized for VM creation. -func generateStorageProfile(vmSpec Spec) (*compute.StorageProfile, error) { +func (s *Service) generateStorageProfile(ctx context.Context, vmSpec Spec) (*compute.StorageProfile, error) { storageProfile := &compute.StorageProfile{ OsDisk: &compute.OSDisk{ Name: to.StringPtr(azure.GenerateOSDiskName(vmSpec.Name)), @@ -340,6 +340,22 @@ func generateStorageProfile(vmSpec Spec) (*compute.StorageProfile, error) { }, } + // enable ephemeral OS + if vmSpec.OSDisk.ManagedDisk.DiffDiskSettings != nil { + hasEpehemeralOS, err := s.ResourceSkusClient.HasEphemeralOSDiskSupport(ctx, vmSpec.Size) + if err != nil { + return nil, errors.Wrap(err, "failed to get ephemeral os capability for vm") + } + + if !hasEpehemeralOS { + return nil, fmt.Errorf("vm size %s does not support ephemeral os. select a different vm size or disable ephemeral os", vmSpec.Size) + } + + storageProfile.OsDisk.DiffDiskSettings = &compute.DiffDiskSettings{ + Option: compute.DiffDiskOptions(vmSpec.OSDisk.ManagedDisk.DiffDiskSettings.Option), + } + } + dataDisks := []compute.DataDisk{} for _, disk := range vmSpec.DataDisks { dataDisks = append(dataDisks, compute.DataDisk{ diff --git a/config/crd/bases/exp.infrastructure.cluster.x-k8s.io_azuremachinepools.yaml b/config/crd/bases/exp.infrastructure.cluster.x-k8s.io_azuremachinepools.yaml index 17be93a5961..8974c293d42 100644 --- a/config/crd/bases/exp.infrastructure.cluster.x-k8s.io_azuremachinepools.yaml +++ b/config/crd/bases/exp.infrastructure.cluster.x-k8s.io_azuremachinepools.yaml @@ -235,6 +235,20 @@ spec: description: ManagedDisk defines the managed disk options for a VM. properties: + diffDiskSettings: + description: DiffDiskSettings describe ephemeral disk + settings for the os disk. + properties: + option: + description: Option enables ephemeral OS when set + to "Local" See https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ephemeral-os-disks + for full details + enum: + - Local + type: string + required: + - option + type: object storageAccountType: type: string required: diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_azureclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_azureclusters.yaml index 80415cf1462..9b228b5f58b 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_azureclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_azureclusters.yaml @@ -741,6 +741,20 @@ spec: description: ManagedDisk defines the managed disk options for a VM. properties: + diffDiskSettings: + description: DiffDiskSettings describe ephemeral disk + settings for the os disk. + properties: + option: + description: Option enables ephemeral OS when set + to "Local" See https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ephemeral-os-disks + for full details + enum: + - Local + type: string + required: + - option + type: object storageAccountType: type: string required: diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremachines.yaml index f12c453c0c1..9fc6d0a2f8a 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremachines.yaml @@ -404,6 +404,20 @@ spec: description: ManagedDisk defines the managed disk options for a VM. properties: + diffDiskSettings: + description: DiffDiskSettings describe ephemeral disk settings + for the os disk. + properties: + option: + description: Option enables ephemeral OS when set to "Local" + See https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ephemeral-os-disks + for full details + enum: + - Local + type: string + required: + - option + type: object storageAccountType: type: string required: diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremachinetemplates.yaml index eabdad09b7b..80a84307981 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremachinetemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremachinetemplates.yaml @@ -348,6 +348,20 @@ spec: description: ManagedDisk defines the managed disk options for a VM. properties: + diffDiskSettings: + description: DiffDiskSettings describe ephemeral disk + settings for the os disk. + properties: + option: + description: Option enables ephemeral OS when + set to "Local" See https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ephemeral-os-disks + for full details + enum: + - Local + type: string + required: + - option + type: object storageAccountType: type: string required: diff --git a/docs/topics/ephemeral-os.md b/docs/topics/ephemeral-os.md new file mode 100644 index 00000000000..b10305d270e --- /dev/null +++ b/docs/topics/ephemeral-os.md @@ -0,0 +1,35 @@ +# Ephemeral OS + +This document describes how to use ephemeral OS for VMs provisioned in Azure. Ephemeral OS uses local VM storage for changes to the OS disk. Storage devices local to the VM host will not be bound by normal managed disk SKU limits. Instead they will always be capable of saturating the VM level limits. This can significantly improve performance on the OS disk. Ephemeral storage used for the OS will not persist between maintenance events and VM redeployments. This is ideal for stateless base OS disks, where any stateful data is kept elsewhere. + + +## Azure Machine DiffDiskSettings + +Azure Machines support optionally specifying a field called `diffDiskSettings`. This mirrors the Azure Compute REST API. + +When `diffDiskSettings.option` is set to `Local`, ephemeral OS will be enabled. We use the API shape provided by compute directly as they expose other options, although this is the main one relevant at this time. + +## Example + +The below example shows how to enable ephemeral OS for a machine template. For control plane nodes, we strongly recommend using etcd data disks to avoid data loss. + +````yaml +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 +kind: AzureMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 + namespace: default +spec: + template: + spec: + location: ${AZURE_LOCATION} + osDisk: + diskSizeGB: 30 + managedDisk: + diffDiskSettings: + option: Local + storageAccountType: Standard_LRS + osType: Linux + sshPublicKey: ${AZURE_SSH_PUBLIC_KEY} + vmSize: ${AZURE_NODE_MACHINE_TYPE} +```` \ No newline at end of file diff --git a/exp/api/v1alpha3/zz_generated.deepcopy.go b/exp/api/v1alpha3/zz_generated.deepcopy.go index 8b86bd0943a..844ac25dbc0 100644 --- a/exp/api/v1alpha3/zz_generated.deepcopy.go +++ b/exp/api/v1alpha3/zz_generated.deepcopy.go @@ -151,7 +151,7 @@ func (in *AzureMachineTemplate) DeepCopyInto(out *AzureMachineTemplate) { *out = new(apiv1alpha3.Image) (*in).DeepCopyInto(*out) } - out.OSDisk = in.OSDisk + in.OSDisk.DeepCopyInto(&out.OSDisk) if in.DataDisks != nil { in, out := &in.DataDisks, &out.DataDisks *out = make([]apiv1alpha3.DataDisk, len(*in)) diff --git a/go.mod b/go.mod index 3c90482f460..9b58fc0ae24 100644 --- a/go.mod +++ b/go.mod @@ -21,14 +21,14 @@ require ( github.com/spf13/pflag v1.0.5 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/net v0.0.0-20200602114024-627f9648deb9 // indirect - k8s.io/api v0.17.7 - k8s.io/apimachinery v0.17.7 - k8s.io/client-go v0.17.7 - k8s.io/component-base v0.17.7 + k8s.io/api v0.17.8 + k8s.io/apimachinery v0.17.8 + k8s.io/client-go v0.17.8 + k8s.io/component-base v0.17.8 k8s.io/klog v1.0.0 - k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 + k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19 sigs.k8s.io/cluster-api v0.3.7-rc.1 - sigs.k8s.io/controller-runtime v0.5.7 + sigs.k8s.io/controller-runtime v0.5.8 ) replace github.com/Azure/go-autorest => github.com/Azure/go-autorest v14.2.0+incompatible diff --git a/go.sum b/go.sum index 5236336771c..7c85bbd9e2f 100644 --- a/go.sum +++ b/go.sum @@ -693,22 +693,29 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.17.7 h1:mvPe3zP7J8d5iRnsv3B/ddkvR+o+uoQluSAIFn7ySYo= k8s.io/api v0.17.7/go.mod h1:xL40O2BS4IEyvZVKVh6oaN4K5R5ndH8t42Rr7XL+C0k= -k8s.io/apiextensions-apiserver v0.17.7 h1:CiSmZ39RFmwPTS4XEIxqGrIm9qcAv4wpiT2WRU4LG0M= +k8s.io/api v0.17.8 h1:8JHlbqJ3A6sGhoacXfu/sASSD+HWWqVq67qt9lyB0kU= +k8s.io/api v0.17.8/go.mod h1:N++Llhs8kCixMUoCaXXAyMMPbo8dDVnh+IQ36xZV2/0= k8s.io/apiextensions-apiserver v0.17.7/go.mod h1:EKjjclxeShy9HP7VxJrvC47dlLPOc7anjH4KOZZbld0= +k8s.io/apiextensions-apiserver v0.17.8 h1:/E4h3wlnhdanffd/WzVJYd86I0fj76+4OPoHooAyHDI= +k8s.io/apiextensions-apiserver v0.17.8/go.mod h1:5H/i0XiKizIE9SkoAQaU/ou31JJBIffbsT0ALA18GmE= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.17.7 h1:1V+pxavZW+aYnjKzFi1yqvpdlDi396tnH6H7jkDgA20= k8s.io/apimachinery v0.17.7/go.mod h1:Lg8zZ5iC/O8UjCqW6DNhcQG2m4TdjF9kwG3891OWbbA= -k8s.io/apiserver v0.17.7 h1:HU93PXwhRhATjjw5ySGnnzN0786X1xHYEtH7Fv+HxS0= +k8s.io/apimachinery v0.17.8 h1:zXvd8rYMAjRJXpILP9tdAiUnFIENM9EmHuE81apIoms= +k8s.io/apimachinery v0.17.8/go.mod h1:Lg8zZ5iC/O8UjCqW6DNhcQG2m4TdjF9kwG3891OWbbA= k8s.io/apiserver v0.17.7/go.mod h1:N0CougU/nBM93Rz09fDGpCQuaVvEg3B4PZtmwPFNUnI= -k8s.io/client-go v0.17.7 h1:FjZwwd3WpTa1C1ZNTLg80g5cEhxHcxAvA1zQ5i8BhjE= +k8s.io/apiserver v0.17.8 h1:bazdS/BsMOo4SOh+EueJ0s34A1oHF+BQptI3+Dx9d3A= +k8s.io/apiserver v0.17.8/go.mod h1:XU2YBi1I/v/P1R5lb0lEwSQ1rnXE01k7yxVtdIWH4Lo= k8s.io/client-go v0.17.7/go.mod h1:hfACB9JP8K4uxFeIUDlmF8OLDbIZ4chzZzNIiN6h9lA= +k8s.io/client-go v0.17.8 h1:cuZSfjqVrNjoZ3wViQHljFPyWMOcgxUjjmQs5Rifbxk= +k8s.io/client-go v0.17.8/go.mod h1:SJsDS64AAtt9VZyeaQMb4Ck5etCitZ/FwajWdzua5eY= k8s.io/cluster-bootstrap v0.17.7 h1:dtI2bYeodFQodPBxwHnLBDZ75ZIAqqqtDsmPqF+O6lc= k8s.io/cluster-bootstrap v0.17.7/go.mod h1:CrKgg7bLbbpMvRC7UvA2qZawpBmYckb70PBMliKT6CI= k8s.io/code-generator v0.17.7/go.mod h1:iiHz51+oTx+Z9D0vB3CH3O4HDDPWrvZyUgUYaIE9h9M= -k8s.io/component-base v0.17.7 h1:/mei48fKDjn3QyQipJdaMW5sJcWF4SyOlQEKstcIM+s= +k8s.io/code-generator v0.17.8/go.mod h1:iiHz51+oTx+Z9D0vB3CH3O4HDDPWrvZyUgUYaIE9h9M= k8s.io/component-base v0.17.7/go.mod h1:eGIlfz1DvIChXUz7svn5KeiP3u2n+khdZ/fb9HD4x8I= +k8s.io/component-base v0.17.8 h1:3YilgRh9TcifVsKWReiZL1JfoUzqLesDc0wYIpimJN8= +k8s.io/component-base v0.17.8/go.mod h1:xfNNdTAMsYzdiAa8vXnqDhRVSEgkfza0iMt0FrZDY7s= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -723,8 +730,9 @@ k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 h1:NeQXVJ2XFSkRoPzRo8AId0 k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19 h1:7Nu2dTj82c6IaWvL7hImJzcXoTPz1MsSCH7r+0m6rfo= +k8s.io/utils v0.0.0-20200619165400-6e3d28b6ed19/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= @@ -734,8 +742,9 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/cluster-api v0.3.7-rc.1 h1:FqhgbzM2q/bpqG90uSC44z66g9ttGWCmV/MHLSzSbAw= sigs.k8s.io/cluster-api v0.3.7-rc.1/go.mod h1:unQmk4qqZSAeWP4qWxmyDG4kLg2kQwiSHVH7zpXF5j0= -sigs.k8s.io/controller-runtime v0.5.7 h1:QcB8YQTyMshLLspHiqAkKHO74PgmUAUmTDhol4VccOw= sigs.k8s.io/controller-runtime v0.5.7/go.mod h1:KjjGQrdWFaSTHwB5A5VDmX9sMLlvkXjVazxVbfOI3a8= +sigs.k8s.io/controller-runtime v0.5.8 h1:+pp4plYh2rpjuVo6HBJ1pVgN3cvAfQHfkKK27rLdxxI= +sigs.k8s.io/controller-runtime v0.5.8/go.mod h1:UI/unU7Q+mo/rWBrND0NAaVNj/Xjh/+aqSv/M3njpmo= sigs.k8s.io/kind v0.7.1-0.20200303021537-981bd80d3802 h1:L6/8hETA7jvdx3xBcbDifrIN2xaYHE7tA58n+Kdp2Zw= sigs.k8s.io/kind v0.7.1-0.20200303021537-981bd80d3802/go.mod h1:HIZ3PWUezpklcjkqpFbnYOqaqsAE1JeCTEwkgvPLXjk= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= diff --git a/templates/cluster-template-ephemeral.yaml b/templates/cluster-template-ephemeral.yaml new file mode 100644 index 00000000000..59af1ac76ee --- /dev/null +++ b/templates/cluster-template-ephemeral.yaml @@ -0,0 +1,208 @@ +apiVersion: cluster.x-k8s.io/v1alpha3 +kind: Cluster +metadata: + name: ${CLUSTER_NAME} + namespace: default +spec: + clusterNetwork: + pods: + cidrBlocks: + - 192.168.0.0/16 + controlPlaneRef: + apiVersion: controlplane.cluster.x-k8s.io/v1alpha3 + kind: KubeadmControlPlane + name: ${CLUSTER_NAME}-control-plane + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 + kind: AzureCluster + name: ${CLUSTER_NAME} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 +kind: AzureCluster +metadata: + name: ${CLUSTER_NAME} + namespace: default +spec: + location: ${AZURE_LOCATION} + networkSpec: + vnet: + name: ${AZURE_VNET_NAME} + resourceGroup: ${AZURE_RESOURCE_GROUP} + subscriptionID: ${AZURE_SUBSCRIPTION_ID} +--- +apiVersion: controlplane.cluster.x-k8s.io/v1alpha3 +kind: KubeadmControlPlane +metadata: + name: ${CLUSTER_NAME}-control-plane + namespace: default +spec: + infrastructureTemplate: + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 + kind: AzureMachineTemplate + name: ${CLUSTER_NAME}-control-plane + kubeadmConfigSpec: + clusterConfiguration: + apiServer: + extraArgs: + cloud-config: /etc/kubernetes/azure.json + cloud-provider: azure + extraVolumes: + - hostPath: /etc/kubernetes/azure.json + mountPath: /etc/kubernetes/azure.json + name: cloud-config + readOnly: true + timeoutForControlPlane: 20m + controllerManager: + extraArgs: + allocate-node-cidrs: "false" + cloud-config: /etc/kubernetes/azure.json + cloud-provider: azure + cluster-name: ${CLUSTER_NAME} + extraVolumes: + - hostPath: /etc/kubernetes/azure.json + mountPath: /etc/kubernetes/azure.json + name: cloud-config + readOnly: true + etcd: + local: + dataDir: /var/lib/etcddisk/etcd + diskSetup: + filesystems: + - device: /dev/disk/azure/scsi1/lun0 + extraOpts: + - -E + - lazy_itable_init=1,lazy_journal_init=1 + filesystem: ext4 + label: etcd_disk + - device: ephemeral0.1 + filesystem: ext4 + label: ephemeral0 + replaceFS: ntfs + partitions: + - device: /dev/disk/azure/scsi1/lun0 + layout: true + overwrite: false + tableType: gpt + files: + - contentFrom: + secret: + key: azure.json + name: azure-secret + owner: root:root + path: /etc/kubernetes/azure.json + permissions: "0644" + initConfiguration: + nodeRegistration: + kubeletExtraArgs: + cloud-config: /etc/kubernetes/azure.json + cloud-provider: azure + name: '{{ ds.meta_data["local_hostname"] }}' + joinConfiguration: + nodeRegistration: + kubeletExtraArgs: + cloud-config: /etc/kubernetes/azure.json + cloud-provider: azure + name: '{{ ds.meta_data["local_hostname"] }}' + mounts: + - - LABEL=etcd_disk + - /var/lib/etcddisk + useExperimentalRetryJoin: true + replicas: ${CONTROL_PLANE_MACHINE_COUNT} + version: ${KUBERNETES_VERSION} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 +kind: AzureMachineTemplate +metadata: + name: ${CLUSTER_NAME}-control-plane + namespace: default +spec: + template: + spec: + dataDisks: + - diskSizeGB: 256 + lun: 0 + nameSuffix: etcddisk + location: ${AZURE_LOCATION} + osDisk: + diskSizeGB: 128 + managedDisk: + diffDiskSettings: + option: Local + storageAccountType: Standard_LRS + osType: Linux + sshPublicKey: ${AZURE_SSH_PUBLIC_KEY} + vmSize: ${AZURE_CONTROL_PLANE_MACHINE_TYPE} +--- +apiVersion: v1 +data: + azure.json: ${AZURE_JSON_B64} +kind: Secret +metadata: + name: azure-secret + namespace: default +--- +apiVersion: cluster.x-k8s.io/v1alpha3 +kind: MachineDeployment +metadata: + name: ${CLUSTER_NAME}-md-0 + namespace: default +spec: + clusterName: ${CLUSTER_NAME} + replicas: ${WORKER_MACHINE_COUNT} + selector: + matchLabels: null + template: + spec: + bootstrap: + configRef: + apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 + kind: KubeadmConfigTemplate + name: ${CLUSTER_NAME}-md-0 + clusterName: ${CLUSTER_NAME} + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 + kind: AzureMachineTemplate + name: ${CLUSTER_NAME}-md-0 + version: ${KUBERNETES_VERSION} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha3 +kind: AzureMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 + namespace: default +spec: + template: + spec: + location: ${AZURE_LOCATION} + osDisk: + diskSizeGB: 30 + managedDisk: + diffDiskSettings: + option: Local + storageAccountType: Standard_LRS + osType: Linux + sshPublicKey: ${AZURE_SSH_PUBLIC_KEY} + vmSize: ${AZURE_NODE_MACHINE_TYPE} +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1alpha3 +kind: KubeadmConfigTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 + namespace: default +spec: + template: + spec: + files: + - contentFrom: + secret: + key: azure.json + name: azure-secret + owner: root:root + path: /etc/kubernetes/azure.json + permissions: "0644" + joinConfiguration: + nodeRegistration: + kubeletExtraArgs: + cloud-config: /etc/kubernetes/azure.json + cloud-provider: azure + name: '{{ ds.meta_data["local_hostname"] }}' diff --git a/templates/flavors/ephemeral/kustomization.yaml b/templates/flavors/ephemeral/kustomization.yaml new file mode 100644 index 00000000000..0e5aedb23b4 --- /dev/null +++ b/templates/flavors/ephemeral/kustomization.yaml @@ -0,0 +1,17 @@ +namespace: default +resources: + - ../default + +patchesJson6902: +- path: machine-deployment.yaml + target: + group: infrastructure.cluster.x-k8s.io + version: v1alpha3 + kind: AzureMachineTemplate + name: "${CLUSTER_NAME}-md-0" +- path: machine-deployment.yaml + target: + group: infrastructure.cluster.x-k8s.io + version: v1alpha3 + kind: AzureMachineTemplate + name: "${CLUSTER_NAME}-control-plane" diff --git a/templates/flavors/ephemeral/machine-deployment.yaml b/templates/flavors/ephemeral/machine-deployment.yaml new file mode 100644 index 00000000000..bde9fea5e4c --- /dev/null +++ b/templates/flavors/ephemeral/machine-deployment.yaml @@ -0,0 +1,6 @@ +- op: replace + path: /spec/template/spec/osDisk/managedDisk + value: + storageAccountType: "Standard_LRS" + diffDiskSettings: + option: Local