Skip to content

Commit

Permalink
Merge pull request #6511 from terraform-providers/r/shared-image/hype…
Browse files Browse the repository at this point in the history
…rv-generation

Support HyperVGeneration attribute for shared_image
  • Loading branch information
manicminer authored Apr 23, 2020
2 parents 9fb1998 + 05e6882 commit d2aa827
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 24 deletions.
6 changes: 6 additions & 0 deletions azurerm/internal/services/compute/data_source_shared_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ func dataSourceArmSharedImage() *schema.Resource {
Computed: true,
},

"hyper_v_generation": {
Type: schema.TypeString,
Computed: true,
},

"identifier": {
Type: schema.TypeList,
Computed: true,
Expand Down Expand Up @@ -120,6 +125,7 @@ func dataSourceArmSharedImageRead(d *schema.ResourceData, meta interface{}) erro
d.Set("description", props.Description)
d.Set("eula", props.Eula)
d.Set("os_type", string(props.OsType))
d.Set("hyper_v_generation", string(props.HyperVGeneration))
d.Set("privacy_statement_uri", props.PrivacyStatementURI)
d.Set("release_note_uri", props.ReleaseNoteURI)

Expand Down
62 changes: 52 additions & 10 deletions azurerm/internal/services/compute/resource_arm_shared_image.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package compute

import (
"context"
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
"github.com/hashicorp/go-azure-helpers/response"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
Expand Down Expand Up @@ -64,6 +65,17 @@ func resourceArmSharedImage() *schema.Resource {
}, false),
},

"hyper_v_generation": {
Type: schema.TypeString,
Optional: true,
Default: string(compute.HyperVGenerationTypesV1),
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
string(compute.V1),
string(compute.V2),
}, false),
},

"identifier": {
Type: schema.TypeList,
Required: true,
Expand Down Expand Up @@ -123,6 +135,7 @@ func resourceArmSharedImageCreateUpdate(d *schema.ResourceData, meta interface{}
resourceGroup := d.Get("resource_group_name").(string)
location := azure.NormalizeLocation(d.Get("location").(string))
description := d.Get("description").(string)
hyperVGeneration := d.Get("hyper_v_generation").(string)

eula := d.Get("eula").(string)
privacyStatementUri := d.Get("privacy_statement_uri").(string)
Expand Down Expand Up @@ -156,6 +169,7 @@ func resourceArmSharedImageCreateUpdate(d *schema.ResourceData, meta interface{}
ReleaseNoteURI: utils.String(releaseNoteURI),
OsType: compute.OperatingSystemTypes(osType),
OsState: compute.Generalized,
HyperVGeneration: compute.HyperVGeneration(hyperVGeneration),
},
Tags: tags.Expand(t),
}
Expand Down Expand Up @@ -219,6 +233,7 @@ func resourceArmSharedImageRead(d *schema.ResourceData, meta interface{}) error
d.Set("description", props.Description)
d.Set("eula", props.Eula)
d.Set("os_type", string(props.OsType))
d.Set("hyper_v_generation", string(props.HyperVGeneration))
d.Set("privacy_statement_uri", props.PrivacyStatementURI)
d.Set("release_note_uri", props.ReleaseNoteURI)

Expand Down Expand Up @@ -247,23 +262,50 @@ func resourceArmSharedImageDelete(d *schema.ResourceData, meta interface{}) erro

future, err := client.Delete(ctx, resourceGroup, galleryName, name)
if err != nil {
// deleted outside of Terraform
if response.WasNotFound(future.Response()) {
return nil
}

return fmt.Errorf("Error deleting Shared Image %q (Gallery %q / Resource Group %q): %+v", name, galleryName, resourceGroup, err)
return fmt.Errorf("deleting Shared Image %q (Gallery %q / Resource Group %q): %+v", name, galleryName, resourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
if !response.WasNotFound(future.Response()) {
return fmt.Errorf("Error waiting for the deletion of Shared Image %q (Gallery %q / Resource Group %q): %+v", name, galleryName, resourceGroup, err)
}
return fmt.Errorf("failed to wait for deleting Shared Image %q (Gallery %q / Resource Group %q): %+v", name, galleryName, resourceGroup, err)
}

log.Printf("[DEBUG] Waiting for Shared Image %q (Gallery %q / Resource Group %q) to be eventually deleted", name, galleryName, resourceGroup)
stateConf := &resource.StateChangeConf{
Pending: []string{"Exists"},
Target: []string{"NotFound"},
Refresh: sharedImageDeleteStateRefreshFunc(ctx, client, resourceGroup, name, galleryName),
MinTimeout: 10 * time.Second,
ContinuousTargetOccurence: 10,
Timeout: d.Timeout(schema.TimeoutDelete),
}

if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf("failed to wait for Shared Image %q (Gallery %q / Resource Group %q) to be deleted: %+v", name, galleryName, resourceGroup, err)
}

return nil
}

func sharedImageDeleteStateRefreshFunc(ctx context.Context, client *compute.GalleryImagesClient, resourceGroupName string, imageName string, galleryName string) resource.StateRefreshFunc {
// The resource Shared Image depends on the resource Shared Image Gallery.
// Although the delete API returns 404 which means the Shared Image resource has been deleted.
// Then it tries to immediately delete Shared Image Gallery but it still throws error `Can not delete resource before nested resources are deleted.`
// In this case we're going to try triggering the Deletion again, in-case it didn't work prior to this attempt.
// For more details, see related Bug: https://github.com/Azure/azure-sdk-for-go/issues/8314
return func() (interface{}, string, error) {
res, err := client.Get(ctx, resourceGroupName, galleryName, imageName)
if err != nil {
if utils.ResponseWasNotFound(res.Response) {
return "NotFound", "NotFound", nil
}

return nil, "", fmt.Errorf("failed to poll to check if the Shared Image has been deleted: %+v", err)
}

return res, "Exists", nil
}
}

func expandGalleryImageIdentifier(d *schema.ResourceData) *compute.GalleryImageIdentifier {
vs := d.Get("identifier").([]interface{})
v := vs[0].(map[string]interface{})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestAccDataSourceAzureRMSharedImage_basic(t *testing.T) {
CheckDestroy: testCheckAzureRMSharedImageDestroy,
Steps: []resource.TestStep{
{
Config: testAccDataSourceSharedImage_basic(data),
Config: testAccDataSourceSharedImage_basic(data, ""),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(data.ResourceName, "tags.%", "0"),
),
Expand All @@ -25,6 +25,24 @@ func TestAccDataSourceAzureRMSharedImage_basic(t *testing.T) {
})
}

func TestAccDataSourceAzureRMSharedImage_basic_hyperVGeneration_V2(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azurerm_shared_image", "test")
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMSharedImageDestroy,
Steps: []resource.TestStep{
{
Config: testAccDataSourceSharedImage_basic(data, "V2"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(data.ResourceName, "tags.%", "0"),
resource.TestCheckResourceAttr(data.ResourceName, "hyper_v_generation", "V2"),
),
},
},
})
}

func TestAccDataSourceAzureRMSharedImage_complete(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azurerm_shared_image", "test")
resource.ParallelTest(t, resource.TestCase{
Expand All @@ -33,17 +51,18 @@ func TestAccDataSourceAzureRMSharedImage_complete(t *testing.T) {
CheckDestroy: testCheckAzureRMSharedImageDestroy,
Steps: []resource.TestStep{
{
Config: testAccDataSourceSharedImage_complete(data),
Config: testAccDataSourceSharedImage_complete(data, "V1"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(data.ResourceName, "tags.%", "0"),
resource.TestCheckResourceAttr(data.ResourceName, "hyper_v_generation", "V1"),
),
},
},
})
}

func testAccDataSourceSharedImage_basic(data acceptance.TestData) string {
template := testAccAzureRMSharedImage_basic(data)
func testAccDataSourceSharedImage_basic(data acceptance.TestData, hyperVGen string) string {
template := testAccAzureRMSharedImage_basic(data, hyperVGen)
return fmt.Sprintf(`
%s
Expand All @@ -55,8 +74,8 @@ data "azurerm_shared_image" "test" {
`, template)
}

func testAccDataSourceSharedImage_complete(data acceptance.TestData) string {
template := testAccAzureRMSharedImage_complete(data)
func testAccDataSourceSharedImage_complete(data acceptance.TestData, hyperVGen string) string {
template := testAccAzureRMSharedImage_complete(data, hyperVGen)
return fmt.Sprintf(`
%s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestAccAzureRMSharedImage_basic(t *testing.T) {
CheckDestroy: testCheckAzureRMSharedImageDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMSharedImage_basic(data),
Config: testAccAzureRMSharedImage_basic(data, ""),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSharedImageExists(data.ResourceName),
resource.TestCheckResourceAttr(data.ResourceName, "description", ""),
Expand All @@ -30,6 +30,27 @@ func TestAccAzureRMSharedImage_basic(t *testing.T) {
},
})
}

func TestAccAzureRMSharedImage_basic_hyperVGeneration_V2(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_shared_image", "test")
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMSharedImageDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMSharedImage_basic(data, "V2"),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSharedImageExists(data.ResourceName),
resource.TestCheckResourceAttr(data.ResourceName, "description", ""),
resource.TestCheckResourceAttr(data.ResourceName, "hyper_v_generation", "V2"),
),
},
data.ImportStep(),
},
})
}

func TestAccAzureRMSharedImage_requiresImport(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_shared_image", "test")

Expand All @@ -39,7 +60,7 @@ func TestAccAzureRMSharedImage_requiresImport(t *testing.T) {
CheckDestroy: testCheckAzureRMSharedImageDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMSharedImage_basic(data),
Config: testAccAzureRMSharedImage_basic(data, ""),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSharedImageExists(data.ResourceName),
resource.TestCheckResourceAttr(data.ResourceName, "description", ""),
Expand All @@ -58,10 +79,11 @@ func TestAccAzureRMSharedImage_complete(t *testing.T) {
CheckDestroy: testCheckAzureRMSharedImageDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMSharedImage_complete(data),
Config: testAccAzureRMSharedImage_complete(data, "V1"),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSharedImageExists(data.ResourceName),
resource.TestCheckResourceAttr(data.ResourceName, "os_type", "Linux"),
resource.TestCheckResourceAttr(data.ResourceName, "hyper_v_generation", "V1"),
resource.TestCheckResourceAttr(data.ResourceName, "description", "Wubba lubba dub dub"),
resource.TestCheckResourceAttr(data.ResourceName, "eula", "Do you agree there's infinite Rick's and Infinite Morty's?"),
resource.TestCheckResourceAttr(data.ResourceName, "privacy_statement_uri", "https://council.of.ricks/privacy-statement"),
Expand Down Expand Up @@ -133,12 +155,16 @@ func testCheckAzureRMSharedImageExists(resourceName string) resource.TestCheckFu
}
}

func testAccAzureRMSharedImage_basic(data acceptance.TestData) string {
func testAccAzureRMSharedImage_basic(data acceptance.TestData, hyperVGen string) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
variable "hyper_v_generation" {
default = "%s"
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
Expand All @@ -156,18 +182,19 @@ resource "azurerm_shared_image" "test" {
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
os_type = "Linux"
hyper_v_generation = var.hyper_v_generation != "" ? var.hyper_v_generation : null
identifier {
publisher = "AccTesPublisher%d"
offer = "AccTesOffer%d"
sku = "AccTesSku%d"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger)
`, hyperVGen, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger)
}

func testAccAzureRMSharedImage_requiresImport(data acceptance.TestData) string {
template := testAccAzureRMSharedImage_basic(data)
template := testAccAzureRMSharedImage_basic(data, "")
return fmt.Sprintf(`
%s
Expand All @@ -187,7 +214,7 @@ resource "azurerm_shared_image" "import" {
`, template, data.RandomInteger, data.RandomInteger, data.RandomInteger)
}

func testAccAzureRMSharedImage_complete(data acceptance.TestData) string {
func testAccAzureRMSharedImage_complete(data acceptance.TestData, hyperVGen string) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
Expand All @@ -210,6 +237,7 @@ resource "azurerm_shared_image" "test" {
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
os_type = "Linux"
hyper_v_generation = "%s"
description = "Wubba lubba dub dub"
eula = "Do you agree there's infinite Rick's and Infinite Morty's?"
privacy_statement_uri = "https://council.of.ricks/privacy-statement"
Expand All @@ -221,5 +249,5 @@ resource "azurerm_shared_image" "test" {
sku = "AccTesSku%d"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger)
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, hyperVGen, data.RandomInteger, data.RandomInteger, data.RandomInteger)
}
2 changes: 2 additions & 0 deletions website/docs/d/shared_image.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ The following attributes are exported:

* `os_type` - The type of Operating System present in this Shared Image.

* `hyper_v_generation` - The generation of HyperV that the Virtual Machine used to create the Shared Image is based on.

* `privacy_statement_uri` - The URI containing the Privacy Statement for this Shared Image.

* `release_note_uri` - The URI containing the Release Notes for this Shared Image.
Expand Down
2 changes: 2 additions & 0 deletions website/docs/r/shared_image.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ The following arguments are supported:

* `eula` - (Optional) The End User Licence Agreement for the Shared Image.

* `hyper_v_generation` - (Optional) The generation of HyperV that the Virtual Machine used to create the Shared Image is based on. Possible values are `V1` and `V2`. Defaults to `V1`. Changing this forces a new resource to be created.

* `privacy_statement_uri` - (Optional) The URI containing the Privacy Statement associated with this Shared Image.

* `release_note_uri` - (Optional) The URI containing the Release Notes associated with this Shared Image.
Expand Down

0 comments on commit d2aa827

Please sign in to comment.