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 HyperVGeneration attribute for shared_image #6511

Merged
merged 4 commits into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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