Skip to content

Commit

Permalink
Support health extension for rolling ugrade mode (#9136)
Browse files Browse the repository at this point in the history
* Support health extension for automatic ugrade mode

* Fix

* Resolve comments

* Fix test

* terrafmt

* resolve comments

* terrafmt

* gofmt

* minor change

* rename one test case to align with others

* fix test case

* fixing test cases
  • Loading branch information
ArcturusZhang authored Apr 14, 2021
1 parent 3153a4a commit bd84b37
Show file tree
Hide file tree
Showing 5 changed files with 360 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,36 @@ func TestAccLinuxVirtualMachineScaleSet_extensionsUpdate(t *testing.T) {
})
}

func TestAccLinuxVirtualMachineScaleSet_extensionsRollingUpgradeWithHealthExtension(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test")
r := LinuxVirtualMachineScaleSetResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.extensionsRollingUpgradeWithHealthExtension(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep("admin_password", "extension.0.protected_settings"),
})
}

func TestAccLinuxVirtualMachineScaleSet_extensionsAutomaticUpgradeWithHealthExtension(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test")
r := LinuxVirtualMachineScaleSetResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.extensionsAutomaticUpgradeWithHealthExtension(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep("admin_password", "extension.0.protected_settings"),
})
}

func TestAccLinuxVirtualMachineScaleSet_extensionWithTimeBudget(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine_scale_set", "test")
r := LinuxVirtualMachineScaleSetResource{}
Expand Down Expand Up @@ -623,6 +653,128 @@ resource "azurerm_linux_virtual_machine_scale_set" "test" {
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineScaleSetResource) extensionsRollingUpgradeWithHealthExtension(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s
provider "azurerm" {
features {}
}
resource "azurerm_linux_virtual_machine_scale_set" "test" {
name = "acctestvmss-%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "Standard_F2"
instances = 1
admin_username = "adminuser"
admin_password = "P@ssword1234!"
disable_password_authentication = false
upgrade_mode = "Rolling"
rolling_upgrade_policy {
max_batch_instance_percent = 21
max_unhealthy_instance_percent = 22
max_unhealthy_upgraded_instance_percent = 23
pause_time_between_batches = "PT30S"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
os_disk {
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
network_interface {
name = "example"
primary = true
ip_configuration {
name = "internal"
primary = true
subnet_id = azurerm_subnet.test.id
}
}
extension {
name = "HealthExtension"
publisher = "Microsoft.ManagedServices"
type = "ApplicationHealthLinux"
type_handler_version = "1.0"
auto_upgrade_minor_version = true
settings = jsonencode({
protocol = "https"
port = 443
})
}
tags = {
accTest = "true"
}
}
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineScaleSetResource) extensionsAutomaticUpgradeWithHealthExtension(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s
provider "azurerm" {
features {}
}
resource "azurerm_linux_virtual_machine_scale_set" "test" {
name = "acctestvmss-%d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
sku = "Standard_F2"
instances = 1
admin_username = "adminuser"
admin_password = "P@ssword1234!"
disable_password_authentication = false
upgrade_mode = "Automatic"
automatic_os_upgrade_policy {
disable_automatic_rollback = true
enable_automatic_os_upgrade = true
}
rolling_upgrade_policy {
max_batch_instance_percent = 21
max_unhealthy_instance_percent = 22
max_unhealthy_upgraded_instance_percent = 23
pause_time_between_batches = "PT30S"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
os_disk {
storage_account_type = "Standard_LRS"
caching = "ReadWrite"
}
network_interface {
name = "example"
primary = true
ip_configuration {
name = "internal"
primary = true
subnet_id = azurerm_subnet.test.id
}
}
extension {
name = "HealthExtension"
publisher = "Microsoft.ManagedServices"
type = "ApplicationHealthLinux"
type_handler_version = "1.0"
auto_upgrade_minor_version = true
settings = jsonencode({
protocol = "https"
port = 443
})
}
tags = {
accTest = "true"
}
}
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineScaleSetResource) extensionWithTimeBudget(data acceptance.TestData, duration string) string {
template := r.template(data)
return fmt.Sprintf(`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,6 @@ func resourceLinuxVirtualMachineScaleSetCreate(d *schema.ResourceData, meta inte
return fmt.Errorf("An `automatic_os_upgrade_policy` block cannot be specified when `upgrade_mode` is not set to `Automatic`")
}

if upgradeMode == compute.Automatic && len(automaticOSUpgradePolicyRaw) > 0 && healthProbeId == "" {
return fmt.Errorf("`healthProbeId` must be set when `upgrade_mode` is set to %q and `automatic_os_upgrade_policy` block exists", string(upgradeMode))
}

shouldHaveRollingUpgradePolicy := upgradeMode == compute.Automatic || upgradeMode == compute.Rolling
if !shouldHaveRollingUpgradePolicy && len(rollingUpgradePolicyRaw) > 0 {
return fmt.Errorf("A `rolling_upgrade_policy` block cannot be specified when `upgrade_mode` is set to %q", string(upgradeMode))
Expand Down Expand Up @@ -419,8 +415,9 @@ func resourceLinuxVirtualMachineScaleSetCreate(d *schema.ResourceData, meta inte
},
}

hasHealthExtension := false
if vmExtensionsRaw, ok := d.GetOk("extension"); ok {
virtualMachineProfile.ExtensionProfile, err = expandVirtualMachineScaleSetExtensions(vmExtensionsRaw.([]interface{}))
virtualMachineProfile.ExtensionProfile, hasHealthExtension, err = expandVirtualMachineScaleSetExtensions(vmExtensionsRaw.([]interface{}))
if err != nil {
return err
}
Expand All @@ -433,6 +430,18 @@ func resourceLinuxVirtualMachineScaleSetCreate(d *schema.ResourceData, meta inte
virtualMachineProfile.ExtensionProfile.ExtensionsTimeBudget = utils.String(v.(string))
}

// otherwise the service return the error:
// Automatic OS Upgrade is not supported for this Virtual Machine Scale Set because a health probe or health extension was not specified.
if upgradeMode == compute.Automatic && len(automaticOSUpgradePolicyRaw) > 0 && (healthProbeId == "" && !hasHealthExtension) {
return fmt.Errorf("`health_probe_id` must be set or a health extension must be specified when `upgrade_mode` is set to %q and `automatic_os_upgrade_policy` block exists", string(upgradeMode))
}

// otherwise the service return the error:
// Rolling Upgrade mode is not supported for this Virtual Machine Scale Set because a health probe or health extension was not provided.
if upgradeMode == compute.Rolling && (healthProbeId == "" && !hasHealthExtension) {
return fmt.Errorf("`health_probe_id` must be set or a health extension must be specified when `upgrade_mode` is set to %q", string(upgradeMode))
}

if adminPassword, ok := d.GetOk("admin_password"); ok {
virtualMachineProfile.OsProfile.AdminPassword = utils.String(adminPassword.(string))
}
Expand Down Expand Up @@ -803,7 +812,7 @@ func resourceLinuxVirtualMachineScaleSetUpdate(d *schema.ResourceData, meta inte
if d.HasChanges("extension", "extensions_time_budget") {
updateInstances = true

extensionProfile, err := expandVirtualMachineScaleSetExtensions(d.Get("extension").([]interface{}))
extensionProfile, _, err := expandVirtualMachineScaleSetExtensions(d.Get("extension").([]interface{}))
if err != nil {
return err
}
Expand Down
21 changes: 13 additions & 8 deletions azurerm/internal/services/compute/virtual_machine_scale_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -1408,10 +1408,10 @@ func VirtualMachineScaleSetExtensionsSchema() *schema.Schema {
}
}

func expandVirtualMachineScaleSetExtensions(input []interface{}) (*compute.VirtualMachineScaleSetExtensionProfile, error) {
result := &compute.VirtualMachineScaleSetExtensionProfile{}
func expandVirtualMachineScaleSetExtensions(input []interface{}) (extensionProfile *compute.VirtualMachineScaleSetExtensionProfile, hasHealthExtension bool, err error) {
extensionProfile = &compute.VirtualMachineScaleSetExtensionProfile{}
if len(input) == 0 {
return result, nil
return nil, false, nil
}

extensions := make([]compute.VirtualMachineScaleSetExtension, 0)
Expand All @@ -1420,41 +1420,46 @@ func expandVirtualMachineScaleSetExtensions(input []interface{}) (*compute.Virtu
extension := compute.VirtualMachineScaleSetExtension{
Name: utils.String(extensionRaw["name"].(string)),
}
extensionType := extensionRaw["type"].(string)

extensionProps := compute.VirtualMachineScaleSetExtensionProperties{
Publisher: utils.String(extensionRaw["publisher"].(string)),
Type: utils.String(extensionRaw["type"].(string)),
Type: &extensionType,
TypeHandlerVersion: utils.String(extensionRaw["type_handler_version"].(string)),
AutoUpgradeMinorVersion: utils.Bool(extensionRaw["auto_upgrade_minor_version"].(bool)),
ProvisionAfterExtensions: utils.ExpandStringSlice(extensionRaw["provision_after_extensions"].([]interface{})),
}

if extensionType == "ApplicationHealthLinux" || extensionType == "ApplicationHealthWindows" {
hasHealthExtension = true
}

if forceUpdateTag := extensionRaw["force_update_tag"]; forceUpdateTag != nil {
extensionProps.ForceUpdateTag = utils.String(forceUpdateTag.(string))
}

if val, ok := extensionRaw["settings"]; ok && val.(string) != "" {
settings, err := structure.ExpandJsonFromString(val.(string))
if err != nil {
return nil, fmt.Errorf("failed to parse JSON from `settings`: %+v", err)
return nil, false, fmt.Errorf("failed to parse JSON from `settings`: %+v", err)
}
extensionProps.Settings = settings
}

if val, ok := extensionRaw["protected_settings"]; ok && val.(string) != "" {
protectedSettings, err := structure.ExpandJsonFromString(val.(string))
if err != nil {
return nil, fmt.Errorf("failed to parse JSON from `protected_settings`: %+v", err)
return nil, false, fmt.Errorf("failed to parse JSON from `protected_settings`: %+v", err)
}
extensionProps.ProtectedSettings = protectedSettings
}

extension.VirtualMachineScaleSetExtensionProperties = &extensionProps
extensions = append(extensions, extension)
}
result.Extensions = &extensions
extensionProfile.Extensions = &extensions

return result, nil
return extensionProfile, hasHealthExtension, nil
}

func flattenVirtualMachineScaleSetExtensions(input *compute.VirtualMachineScaleSetExtensionProfile, d *schema.ResourceData) ([]map[string]interface{}, error) {
Expand Down
Loading

0 comments on commit bd84b37

Please sign in to comment.