Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support health extension for rolling ugrade mode #9136

Merged
merged 13 commits into from
Apr 14, 2021
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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we'll get here, line 352 above will kick us out first if health_probe_id is unset, so that will need investigating for refactoring.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... something is messed up during the merging from master. I need to check these carefully again.

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