Skip to content

Commit

Permalink
✨ implement ephemeral os
Browse files Browse the repository at this point in the history
Signed-off-by: Alexander Eldeib <alexeldeib@gmail.com>
  • Loading branch information
alexeldeib committed Jul 11, 2020
1 parent a9d8ae5 commit 32102b9
Show file tree
Hide file tree
Showing 25 changed files with 582 additions and 51 deletions.
1 change: 1 addition & 0 deletions api/v1alpha2/azurecluster_conversion.go
Expand Up @@ -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 {
Expand Down
14 changes: 14 additions & 0 deletions api/v1alpha2/azuremachine_conversion.go
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
31 changes: 11 additions & 20 deletions api/v1alpha2/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions api/v1alpha3/azuremachine_validation.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}

Expand Down
34 changes: 30 additions & 4 deletions api/v1alpha3/azuremachine_validation_test.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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()...)

Expand Down Expand Up @@ -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,
})
Expand All @@ -159,7 +186,6 @@ func generateValidOSDisk() OSDisk {
},
}
}

func TestAzureMachine_ValidateDataDisks(t *testing.T) {
g := NewWithT(t)

Expand Down
8 changes: 7 additions & 1 deletion api/v1alpha3/azuremachine_webhook.go
Expand Up @@ -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...)
}
Expand All @@ -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
}
Expand Down
11 changes: 10 additions & 1 deletion api/v1alpha3/types.go
Expand Up @@ -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.
Expand Down
26 changes: 23 additions & 3 deletions api/v1alpha3/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions cloud/services/resourceskus/client.go
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

0 comments on commit 32102b9

Please sign in to comment.