diff --git a/google/image_test.go b/google/image_test.go deleted file mode 100644 index 026d02e5..00000000 --- a/google/image_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package google - -import ( - "fmt" - "testing" - - "google.golang.org/api/compute/v1" - - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" -) - -func TestAccComputeImage_resolveImage(t *testing.T) { - t.Parallel() - - var image compute.Image - rand := acctest.RandString(10) - name := fmt.Sprintf("test-image-%s", rand) - fam := fmt.Sprintf("test-image-family-%s", rand) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckComputeImageDestroy, - Steps: []resource.TestStep{ - { - Config: testAccComputeImage_resolving(name, fam), - Check: resource.ComposeTestCheckFunc( - testAccCheckComputeImageExists( - "google_compute_image.foobar", &image), - testAccCheckComputeImageResolution("google_compute_image.foobar"), - ), - }, - }, - }) -} - -func testAccCheckComputeImageResolution(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - config := testAccProvider.Meta().(*Config) - project := config.Project - - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Resource not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - if rs.Primary.Attributes["name"] == "" { - return fmt.Errorf("No image name is set") - } - if rs.Primary.Attributes["family"] == "" { - return fmt.Errorf("No image family is set") - } - if rs.Primary.Attributes["self_link"] == "" { - return fmt.Errorf("No self_link is set") - } - - name := rs.Primary.Attributes["name"] - family := rs.Primary.Attributes["family"] - link := rs.Primary.Attributes["self_link"] - - latestDebian, err := config.clientCompute.Images.GetFromFamily("debian-cloud", "debian-9").Do() - if err != nil { - return fmt.Errorf("Error retrieving latest debian: %s", err) - } - - images := map[string]string{ - "family/" + latestDebian.Family: "projects/debian-cloud/global/images/family/" + latestDebian.Family, - "projects/debian-cloud/global/images/" + latestDebian.Name: "projects/debian-cloud/global/images/" + latestDebian.Name, - latestDebian.Family: "projects/debian-cloud/global/images/family/" + latestDebian.Family, - latestDebian.Name: "projects/debian-cloud/global/images/" + latestDebian.Name, - latestDebian.SelfLink: latestDebian.SelfLink, - - "global/images/" + name: "global/images/" + name, - "global/images/family/" + family: "global/images/family/" + family, - name: "global/images/" + name, - family: "global/images/family/" + family, - "family/" + family: "global/images/family/" + family, - project + "/" + name: "projects/" + project + "/global/images/" + name, - project + "/" + family: "projects/" + project + "/global/images/family/" + family, - link: link, - } - - for input, expectation := range images { - result, err := resolveImage(config, project, input) - if err != nil { - return fmt.Errorf("Error resolving input %s to image: %+v\n", input, err) - } - if result != expectation { - return fmt.Errorf("Expected input '%s' to resolve to '%s', it resolved to '%s' instead.\n", input, expectation, result) - } - } - return nil - } -} - -func testAccComputeImage_resolving(name, family string) string { - return fmt.Sprintf(` -data "google_compute_image" "my_image" { - family = "debian-9" - project = "debian-cloud" -} - -resource "google_compute_disk" "foobar" { - name = "%s" - zone = "us-central1-a" - image = "${data.google_compute_image.my_image.self_link}" -} -resource "google_compute_image" "foobar" { - name = "%s" - family = "%s" - source_disk = "${google_compute_disk.foobar.self_link}" -} -`, name, name, family) -} diff --git a/google/provider.go b/google/provider.go index 626ec03a..b3abc889 100644 --- a/google/provider.go +++ b/google/provider.go @@ -137,7 +137,6 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_compute_attached_disk": resourceComputeAttachedDisk(), "google_compute_backend_service": resourceComputeBackendService(), "google_compute_global_forwarding_rule": resourceComputeGlobalForwardingRule(), - "google_compute_image": resourceComputeImage(), "google_compute_instance": resourceComputeInstance(), "google_compute_instance_from_template": resourceComputeInstanceFromTemplate(), "google_compute_instance_group": resourceComputeInstanceGroup(), diff --git a/google/provider_compute_gen.go b/google/provider_compute_gen.go index 0472a7e8..bc9d84de 100644 --- a/google/provider_compute_gen.go +++ b/google/provider_compute_gen.go @@ -27,6 +27,7 @@ var GeneratedComputeResourcesMap = map[string]*schema.Resource{ "google_compute_http_health_check": resourceComputeHttpHealthCheck(), "google_compute_https_health_check": resourceComputeHttpsHealthCheck(), "google_compute_health_check": resourceComputeHealthCheck(), + "google_compute_image": resourceComputeImage(), "google_compute_interconnect_attachment": resourceComputeInterconnectAttachment(), "google_compute_region_autoscaler": resourceComputeRegionAutoscaler(), "google_compute_region_disk": resourceComputeRegionDisk(), diff --git a/google/resource_compute_address_generated_test.go b/google/resource_compute_address_generated_test.go index cbe3b9e3..1315a3f7 100644 --- a/google/resource_compute_address_generated_test.go +++ b/google/resource_compute_address_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -150,10 +151,13 @@ resource "google_compute_instance" "instance_with_ip" { } func testAccCheckComputeAddressDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_address" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_autoscaler_generated_test.go b/google/resource_compute_autoscaler_generated_test.go index 33f06e5b..20fcdf9f 100644 --- a/google/resource_compute_autoscaler_generated_test.go +++ b/google/resource_compute_autoscaler_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -107,10 +108,13 @@ data "google_compute_image" "debian_9" { } func testAccCheckComputeAutoscalerDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_autoscaler" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_backend_bucket_generated_test.go b/google/resource_compute_backend_bucket_generated_test.go index 84462f5a..9d2eb6b9 100644 --- a/google/resource_compute_backend_bucket_generated_test.go +++ b/google/resource_compute_backend_bucket_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -61,10 +62,13 @@ resource "google_storage_bucket" "image_bucket" { } func testAccCheckComputeBackendBucketDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_backend_bucket" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_disk_generated_test.go b/google/resource_compute_disk_generated_test.go index 0062eda7..d0b6978e 100644 --- a/google/resource_compute_disk_generated_test.go +++ b/google/resource_compute_disk_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -59,10 +60,13 @@ resource "google_compute_disk" "default" { } func testAccCheckComputeDiskDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_disk" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_firewall_generated_test.go b/google/resource_compute_firewall_generated_test.go index 6266a3d1..5ecf04a6 100644 --- a/google/resource_compute_firewall_generated_test.go +++ b/google/resource_compute_firewall_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -69,10 +70,13 @@ resource "google_compute_network" "default" { } func testAccCheckComputeFirewallDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_firewall" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_forwarding_rule_generated_test.go b/google/resource_compute_forwarding_rule_generated_test.go index 91ce039a..1a554782 100644 --- a/google/resource_compute_forwarding_rule_generated_test.go +++ b/google/resource_compute_forwarding_rule_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -59,10 +60,13 @@ resource "google_compute_target_pool" "default" { } func testAccCheckComputeForwardingRuleDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_forwarding_rule" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_global_address_generated_test.go b/google/resource_compute_global_address_generated_test.go index 18510ad2..7f246d10 100644 --- a/google/resource_compute_global_address_generated_test.go +++ b/google/resource_compute_global_address_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -53,10 +54,13 @@ resource "google_compute_global_address" "default" { } func testAccCheckComputeGlobalAddressDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_global_address" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_health_check_generated_test.go b/google/resource_compute_health_check_generated_test.go index 88ca0c14..d32fe02e 100644 --- a/google/resource_compute_health_check_generated_test.go +++ b/google/resource_compute_health_check_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -60,10 +61,13 @@ resource "google_compute_health_check" "internal-health-check" { } func testAccCheckComputeHealthCheckDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_health_check" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_http_health_check_generated_test.go b/google/resource_compute_http_health_check_generated_test.go index 7bb291a8..4e8b92fb 100644 --- a/google/resource_compute_http_health_check_generated_test.go +++ b/google/resource_compute_http_health_check_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -57,10 +58,13 @@ resource "google_compute_http_health_check" "default" { } func testAccCheckComputeHttpHealthCheckDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_http_health_check" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_https_health_check_generated_test.go b/google/resource_compute_https_health_check_generated_test.go index 0cc50d17..4ca02d03 100644 --- a/google/resource_compute_https_health_check_generated_test.go +++ b/google/resource_compute_https_health_check_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -57,10 +58,13 @@ resource "google_compute_https_health_check" "default" { } func testAccCheckComputeHttpsHealthCheckDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_https_health_check" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_image.go b/google/resource_compute_image.go index 25f6423d..8c8fca9c 100644 --- a/google/resource_compute_image.go +++ b/google/resource_compute_image.go @@ -1,66 +1,84 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + package google import ( "fmt" "log" + "reflect" + "strconv" "time" "github.com/hashicorp/terraform/helper/schema" - "google.golang.org/api/compute/v1" + "github.com/hashicorp/terraform/helper/validation" + compute "google.golang.org/api/compute/v1" ) -const computeImageCreateTimeoutDefault = 4 - func resourceComputeImage() *schema.Resource { return &schema.Resource{ Create: resourceComputeImageCreate, Read: resourceComputeImageRead, Update: resourceComputeImageUpdate, Delete: resourceComputeImageDelete, + Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: resourceComputeImageImport, }, Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(computeImageCreateTimeoutDefault * time.Minute), - Update: schema.DefaultTimeout(computeImageCreateTimeoutDefault * time.Minute), - Delete: schema.DefaultTimeout(computeImageCreateTimeoutDefault * time.Minute), + Create: schema.DefaultTimeout(240 * time.Second), + Update: schema.DefaultTimeout(240 * time.Second), + Delete: schema.DefaultTimeout(240 * time.Second), }, Schema: map[string]*schema.Schema{ - // TODO(cblecker): one of source_disk or raw_disk is required - "name": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "description": { Type: schema.TypeString, Optional: true, ForceNew: true, }, - + "disk_size_gb": { + Type: schema.TypeInt, + Computed: true, + Optional: true, + ForceNew: true, + }, "family": { Type: schema.TypeString, Optional: true, ForceNew: true, }, - - "project": { - Type: schema.TypeString, + "labels": { + Type: schema.TypeMap, Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "licenses": { + Type: schema.TypeList, Computed: true, - ForceNew: true, - }, - - "source_disk": { - Type: schema.TypeString, Optional: true, ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, }, - "raw_disk": { Type: schema.TypeList, Optional: true, @@ -73,48 +91,46 @@ func resourceComputeImage() *schema.Resource { Required: true, ForceNew: true, }, + "container_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"TAR", ""}, false), + Default: "TAR", + }, "sha1": { Type: schema.TypeString, Optional: true, ForceNew: true, }, - "container_type": { - Type: schema.TypeString, - Optional: true, - Default: "TAR", - ForceNew: true, - }, }, }, }, - - "self_link": { + "source_disk": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, + "archive_size_bytes": { + Type: schema.TypeInt, + Computed: true, + }, + "creation_timestamp": { Type: schema.TypeString, Computed: true, }, - - "create_timeout": { - Type: schema.TypeInt, - Optional: true, - Removed: "Use timeouts block instead. See https://www.terraform.io/docs/configuration/resources.html#timeouts.", - }, - - "labels": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, - - "licenses": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, + "label_fingerprint": { + Type: schema.TypeString, Computed: true, }, - - "label_fingerprint": { + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "self_link": { Type: schema.TypeString, Computed: true, }, @@ -125,105 +141,159 @@ func resourceComputeImage() *schema.Resource { func resourceComputeImageCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) + obj := make(map[string]interface{}) + descriptionProp, err := expandComputeImageDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + diskSizeGbProp, err := expandComputeImageDiskSizeGb(d.Get("disk_size_gb"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("disk_size_gb"); !isEmptyValue(reflect.ValueOf(diskSizeGbProp)) && (ok || !reflect.DeepEqual(v, diskSizeGbProp)) { + obj["diskSizeGb"] = diskSizeGbProp + } + familyProp, err := expandComputeImageFamily(d.Get("family"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("family"); !isEmptyValue(reflect.ValueOf(familyProp)) && (ok || !reflect.DeepEqual(v, familyProp)) { + obj["family"] = familyProp + } + labelsProp, err := expandComputeImageLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + labelFingerprintProp, err := expandComputeImageLabelFingerprint(d.Get("label_fingerprint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("label_fingerprint"); !isEmptyValue(reflect.ValueOf(labelFingerprintProp)) && (ok || !reflect.DeepEqual(v, labelFingerprintProp)) { + obj["labelFingerprint"] = labelFingerprintProp + } + licensesProp, err := expandComputeImageLicenses(d.Get("licenses"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("licenses"); !isEmptyValue(reflect.ValueOf(licensesProp)) && (ok || !reflect.DeepEqual(v, licensesProp)) { + obj["licenses"] = licensesProp + } + nameProp, err := expandComputeImageName(d.Get("name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp + } + rawDiskProp, err := expandComputeImageRawDisk(d.Get("raw_disk"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("raw_disk"); !isEmptyValue(reflect.ValueOf(rawDiskProp)) && (ok || !reflect.DeepEqual(v, rawDiskProp)) { + obj["rawDisk"] = rawDiskProp + } + sourceDiskProp, err := expandComputeImageSourceDisk(d.Get("source_disk"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("source_disk"); !isEmptyValue(reflect.ValueOf(sourceDiskProp)) && (ok || !reflect.DeepEqual(v, sourceDiskProp)) { + obj["sourceDisk"] = sourceDiskProp + } + + url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/images") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Image: %#v", obj) + res, err := sendRequestWithTimeout(config, "POST", url, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating Image: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + project, err := getProject(d, config) if err != nil { return err } - - // Build the image - image := &compute.Image{ - Name: d.Get("name").(string), - } - - if v, ok := d.GetOk("description"); ok { - image.Description = v.(string) - } - - if v, ok := d.GetOk("family"); ok { - image.Family = v.(string) - } - - // Load up the source_disk for this image if specified - if v, ok := d.GetOk("source_disk"); ok { - image.SourceDisk = v.(string) - } - - // Load up the raw_disk for this image if specified - if v, ok := d.GetOk("raw_disk"); ok { - rawDiskEle := v.([]interface{})[0].(map[string]interface{}) - imageRawDisk := &compute.ImageRawDisk{ - Source: rawDiskEle["source"].(string), - ContainerType: rawDiskEle["container_type"].(string), - } - if val, ok := rawDiskEle["sha1"]; ok { - imageRawDisk.Sha1Checksum = val.(string) - } - - image.RawDisk = imageRawDisk - } - - if _, ok := d.GetOk("labels"); ok { - image.Labels = expandLabels(d) - } - - // Load up the licenses for this image if specified - if _, ok := d.GetOk("licenses"); ok { - image.Licenses = licenses(d) - } - - // Read create timeout - createTimeout := int(d.Timeout(schema.TimeoutCreate).Minutes()) - - // Insert the image - op, err := config.clientCompute.Images.Insert( - project, image).Do() - if err != nil { - return fmt.Errorf("Error creating image: %s", err) - } - - // Store the ID - d.SetId(image.Name) - - err = computeOperationWaitTime(config.clientCompute, op, project, "Creating Image", createTimeout) + op := &compute.Operation{} + err = Convert(res, op) if err != nil { return err } + waitErr := computeOperationWaitTime( + config.clientCompute, op, project, "Creating Image", + int(d.Timeout(schema.TimeoutCreate).Minutes())) + + if waitErr != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create Image: %s", waitErr) + } + + log.Printf("[DEBUG] Finished creating Image %q: %#v", d.Id(), res) + return resourceComputeImageRead(d, meta) } func resourceComputeImageRead(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - project, err := getProject(d, config) + url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/images/{{name}}") if err != nil { return err } - image, err := config.clientCompute.Images.Get( - project, d.Id()).Do() + res, err := sendRequest(config, "GET", url, nil) if err != nil { - return handleNotFoundError(err, d, fmt.Sprintf("Image %q", d.Get("name").(string))) + return handleNotFoundError(err, d, fmt.Sprintf("ComputeImage %q", d.Id())) } - if image.SourceDisk != "" { - d.Set("source_disk", image.SourceDisk) - } else if image.RawDisk != nil { - // `raw_disk.*.source` is only used at image creation but is not returned when calling Get. - // `raw_disk.*.sha1` is not supported, the value is simply discarded by the server. - // Leaving `raw_disk` to current state value. - } else { - return fmt.Errorf("Either raw_disk or source_disk configuration is required.") + project, err := getProject(d, config) + if err != nil { + return err + } + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Image: %s", err) } - d.Set("name", image.Name) - d.Set("description", image.Description) - d.Set("family", image.Family) - d.Set("self_link", image.SelfLink) - d.Set("labels", image.Labels) - d.Set("licenses", image.Licenses) - d.Set("label_fingerprint", image.LabelFingerprint) - d.Set("project", project) + if err := d.Set("archive_size_bytes", flattenComputeImageArchiveSizeBytes(res["archiveSizeBytes"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("creation_timestamp", flattenComputeImageCreationTimestamp(res["creationTimestamp"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("description", flattenComputeImageDescription(res["description"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("disk_size_gb", flattenComputeImageDiskSizeGb(res["diskSizeGb"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("family", flattenComputeImageFamily(res["family"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("labels", flattenComputeImageLabels(res["labels"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("label_fingerprint", flattenComputeImageLabelFingerprint(res["labelFingerprint"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("licenses", flattenComputeImageLicenses(res["licenses"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("name", flattenComputeImageName(res["name"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("source_disk", flattenComputeImageSourceDisk(res["sourceDisk"], d)); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } + if err := d.Set("self_link", ConvertSelfLinkToV1(res["selfLink"].(string))); err != nil { + return fmt.Errorf("Error reading Image: %s", err) + } return nil } @@ -231,77 +301,251 @@ func resourceComputeImageRead(d *schema.ResourceData, meta interface{}) error { func resourceComputeImageUpdate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - project, err := getProject(d, config) - if err != nil { - return err - } - - // Technically we are only updating one attribute, but setting d.Partial here makes it easier to add updates later d.Partial(true) - if d.HasChange("labels") { - labels := expandLabels(d) - labelFingerprint := d.Get("label_fingerprint").(string) - setLabelsRequest := compute.GlobalSetLabelsRequest{ - LabelFingerprint: labelFingerprint, - Labels: labels, - ForceSendFields: []string{"Labels"}, + if d.HasChange("labels") || d.HasChange("label_fingerprint") { + obj := make(map[string]interface{}) + labelsProp, err := expandComputeImageLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + labelFingerprintProp, err := expandComputeImageLabelFingerprint(d.Get("label_fingerprint"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("label_fingerprint"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelFingerprintProp)) { + obj["labelFingerprint"] = labelFingerprintProp } - op, err := config.clientCompute.Images.SetLabels(project, d.Id(), &setLabelsRequest).Do() + url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/images/{{name}}/setLabels") + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "POST", url, obj, d.Timeout(schema.TimeoutUpdate)) + if err != nil { + return fmt.Errorf("Error updating Image %q: %s", d.Id(), err) + } + + project, err := getProject(d, config) + if err != nil { + return err + } + op := &compute.Operation{} + err = Convert(res, op) + if err != nil { + return err + } + + err = computeOperationWaitTime( + config.clientCompute, op, project, "Updating Image", + int(d.Timeout(schema.TimeoutUpdate).Minutes())) + if err != nil { return err } d.SetPartial("labels") - - err = computeOperationWaitTime(config.clientCompute, op, project, "Setting labels", int(d.Timeout(schema.TimeoutUpdate).Minutes())) - if err != nil { - return err - } - // Perform a read to see the new label_fingerprint value - image, err := config.clientCompute.Images.Get(project, d.Id()).Do() - if err != nil { - return err - } - d.Set("label_fingerprint", image.LabelFingerprint) d.SetPartial("label_fingerprint") } d.Partial(false) - return nil + + return resourceComputeImageRead(d, meta) } func resourceComputeImageDelete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) + url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/images/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting Image %q", d.Id()) + res, err := sendRequestWithTimeout(config, "DELETE", url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "Image") + } + project, err := getProject(d, config) if err != nil { return err } - - // Delete the image - log.Printf("[DEBUG] image delete request") - op, err := config.clientCompute.Images.Delete( - project, d.Id()).Do() - if err != nil { - return fmt.Errorf("Error deleting image: %s", err) - } - - err = computeOperationWaitTime(config.clientCompute, op, project, "Deleting image", int(d.Timeout(schema.TimeoutDelete).Minutes())) + op := &compute.Operation{} + err = Convert(res, op) if err != nil { return err } - d.SetId("") + err = computeOperationWaitTime( + config.clientCompute, op, project, "Deleting Image", + int(d.Timeout(schema.TimeoutDelete).Minutes())) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Image %q: %#v", d.Id(), res) return nil } -func licenses(d *schema.ResourceData) []string { - licensesCount := d.Get("licenses.#").(int) - data := make([]string, licensesCount) - for i := 0; i < licensesCount; i++ { - data[i] = d.Get(fmt.Sprintf("licenses.%d", i)).(string) +func resourceComputeImageImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{"projects/(?P[^/]+)/global/images/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config); err != nil { + return nil, err } - return data + + // Replace import id for the resource id + id, err := replaceVars(d, config, "{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenComputeImageArchiveSizeBytes(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. + } + return v +} + +func flattenComputeImageCreationTimestamp(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeImageDescription(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeImageDiskSizeGb(v interface{}, d *schema.ResourceData) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil { + return intVal + } // let terraform core handle it if we can't convert the string to an int. + } + return v +} + +func flattenComputeImageFamily(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeImageLabels(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeImageLabelFingerprint(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeImageLicenses(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeImageName(v interface{}, d *schema.ResourceData) interface{} { + return v +} + +func flattenComputeImageSourceDisk(v interface{}, d *schema.ResourceData) interface{} { + if v == nil { + return v + } + return ConvertSelfLinkToV1(v.(string)) +} + +func expandComputeImageDescription(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageDiskSizeGb(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageFamily(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageLabels(v interface{}, d *schema.ResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} + +func expandComputeImageLabelFingerprint(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageLicenses(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageName(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageRawDisk(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedContainerType, err := expandComputeImageRawDiskContainerType(original["container_type"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedContainerType); val.IsValid() && !isEmptyValue(val) { + transformed["containerType"] = transformedContainerType + } + + transformedSha1, err := expandComputeImageRawDiskSha1(original["sha1"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSha1); val.IsValid() && !isEmptyValue(val) { + transformed["sha1Checksum"] = transformedSha1 + } + + transformedSource, err := expandComputeImageRawDiskSource(original["source"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSource); val.IsValid() && !isEmptyValue(val) { + transformed["source"] = transformedSource + } + + return transformed, nil +} + +func expandComputeImageRawDiskContainerType(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageRawDiskSha1(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageRawDiskSource(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandComputeImageSourceDisk(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + f, err := parseZonalFieldValue("disks", v.(string), "project", "zone", d, config, true) + if err != nil { + return nil, fmt.Errorf("Invalid value for source_disk: %s", err) + } + return f.RelativeLink(), nil } diff --git a/google/resource_compute_image_generated_test.go b/google/resource_compute_image_generated_test.go new file mode 100644 index 00000000..7c66e914 --- /dev/null +++ b/google/resource_compute_image_generated_test.go @@ -0,0 +1,84 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccComputeImage_imageBasicExample(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeImageDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeImage_imageBasicExample(acctest.RandString(10)), + }, + { + ResourceName: "google_compute_image.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"raw_disk"}, + }, + }, + }) +} + +func testAccComputeImage_imageBasicExample(val string) string { + return fmt.Sprintf(` +resource "google_compute_image" "example" { + name = "example-image-%s" + + raw_disk { + source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz" + } +} +`, val, + ) +} + +func testAccCheckComputeImageDestroy(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_image" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := testAccProvider.Meta().(*Config) + + url, err := replaceVarsForTest(rs, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/images/{{name}}") + if err != nil { + return err + } + + _, err = sendRequest(config, "GET", url, nil) + if err == nil { + return fmt.Errorf("ComputeImage still exists at %s", url) + } + } + + return nil +} diff --git a/google/resource_compute_image_test.go b/google/resource_compute_image_test.go index b2ebf652..9c1f440a 100644 --- a/google/resource_compute_image_test.go +++ b/google/resource_compute_image_test.go @@ -133,24 +133,6 @@ func TestAccComputeImage_basedondisk(t *testing.T) { }) } -func testAccCheckComputeImageDestroy(s *terraform.State) error { - config := testAccProvider.Meta().(*Config) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "google_compute_image" { - continue - } - - _, err := config.clientCompute.Images.Get( - config.Project, rs.Primary.ID).Do() - if err == nil { - return fmt.Errorf("Image still exists") - } - } - - return nil -} - func testAccCheckComputeImageExists(n string, image *compute.Image) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -180,6 +162,93 @@ func testAccCheckComputeImageExists(n string, image *compute.Image) resource.Tes } } +func TestAccComputeImage_resolveImage(t *testing.T) { + t.Parallel() + + var image compute.Image + rand := acctest.RandString(10) + name := fmt.Sprintf("test-image-%s", rand) + fam := fmt.Sprintf("test-image-family-%s", rand) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeImageDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeImage_resolving(name, fam), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeImageExists( + "google_compute_image.foobar", &image), + testAccCheckComputeImageResolution("google_compute_image.foobar"), + ), + }, + }, + }) +} + +func testAccCheckComputeImageResolution(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + config := testAccProvider.Meta().(*Config) + project := config.Project + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + if rs.Primary.Attributes["name"] == "" { + return fmt.Errorf("No image name is set") + } + if rs.Primary.Attributes["family"] == "" { + return fmt.Errorf("No image family is set") + } + if rs.Primary.Attributes["self_link"] == "" { + return fmt.Errorf("No self_link is set") + } + + name := rs.Primary.Attributes["name"] + family := rs.Primary.Attributes["family"] + link := rs.Primary.Attributes["self_link"] + + latestDebian, err := config.clientCompute.Images.GetFromFamily("debian-cloud", "debian-9").Do() + if err != nil { + return fmt.Errorf("Error retrieving latest debian: %s", err) + } + + images := map[string]string{ + "family/" + latestDebian.Family: "projects/debian-cloud/global/images/family/" + latestDebian.Family, + "projects/debian-cloud/global/images/" + latestDebian.Name: "projects/debian-cloud/global/images/" + latestDebian.Name, + latestDebian.Family: "projects/debian-cloud/global/images/family/" + latestDebian.Family, + latestDebian.Name: "projects/debian-cloud/global/images/" + latestDebian.Name, + latestDebian.SelfLink: latestDebian.SelfLink, + + "global/images/" + name: "global/images/" + name, + "global/images/family/" + family: "global/images/family/" + family, + name: "global/images/" + name, + family: "global/images/family/" + family, + "family/" + family: "global/images/family/" + family, + project + "/" + name: "projects/" + project + "/global/images/" + name, + project + "/" + family: "projects/" + project + "/global/images/family/" + family, + link: link, + } + + for input, expectation := range images { + result, err := resolveImage(config, project, input) + if err != nil { + return fmt.Errorf("Error resolving input %s to image: %+v\n", input, err) + } + if result != expectation { + return fmt.Errorf("Expected input '%s' to resolve to '%s', it resolved to '%s' instead.\n", input, expectation, result) + } + } + return nil + } +} + func testAccCheckComputeImageDescription(image *compute.Image, description string) resource.TestCheckFunc { return func(s *terraform.State) error { if image.Description != description { @@ -265,6 +334,26 @@ func testAccCheckComputeImageHasSourceDisk(image *compute.Image) resource.TestCh } } +func testAccComputeImage_resolving(name, family string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-9" + project = "debian-cloud" +} + +resource "google_compute_disk" "foobar" { + name = "%s" + zone = "us-central1-a" + image = "${data.google_compute_image.my_image.self_link}" +} +resource "google_compute_image" "foobar" { + name = "%s" + family = "%s" + source_disk = "${google_compute_disk.foobar.self_link}" +} +`, name, name, family) +} + func testAccComputeImage_basic(name string) string { return fmt.Sprintf(` resource "google_compute_image" "foobar" { diff --git a/google/resource_compute_region_autoscaler_generated_test.go b/google/resource_compute_region_autoscaler_generated_test.go index b9d78e65..baa8847a 100644 --- a/google/resource_compute_region_autoscaler_generated_test.go +++ b/google/resource_compute_region_autoscaler_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -107,10 +108,13 @@ data "google_compute_image" "debian_9" { } func testAccCheckComputeRegionAutoscalerDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_region_autoscaler" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_region_disk_generated_test.go b/google/resource_compute_region_disk_generated_test.go index 78c0e2c1..94db0b07 100644 --- a/google/resource_compute_region_disk_generated_test.go +++ b/google/resource_compute_region_disk_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -72,10 +73,13 @@ resource "google_compute_snapshot" "snapdisk" { } func testAccCheckComputeRegionDiskDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_region_disk" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_route_generated_test.go b/google/resource_compute_route_generated_test.go index 6f9b7f48..5e00edb6 100644 --- a/google/resource_compute_route_generated_test.go +++ b/google/resource_compute_route_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -61,10 +62,13 @@ resource "google_compute_network" "default" { } func testAccCheckComputeRouteDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_route" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_router_generated_test.go b/google/resource_compute_router_generated_test.go index cbfb49af..f09d2713 100644 --- a/google/resource_compute_router_generated_test.go +++ b/google/resource_compute_router_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -70,10 +71,13 @@ resource "google_compute_network" "foobar" { } func testAccCheckComputeRouterDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_router" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_snapshot_generated_test.go b/google/resource_compute_snapshot_generated_test.go index fc56e833..7c2fb87f 100644 --- a/google/resource_compute_snapshot_generated_test.go +++ b/google/resource_compute_snapshot_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -72,10 +73,13 @@ resource "google_compute_disk" "persistent" { } func testAccCheckComputeSnapshotDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_snapshot" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_ssl_certificate_generated_test.go b/google/resource_compute_ssl_certificate_generated_test.go index 53ed424c..e9b369e4 100644 --- a/google/resource_compute_ssl_certificate_generated_test.go +++ b/google/resource_compute_ssl_certificate_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -201,10 +202,13 @@ resource "google_compute_http_health_check" "default" { } func testAccCheckComputeSslCertificateDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_ssl_certificate" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_ssl_policy_generated_test.go b/google/resource_compute_ssl_policy_generated_test.go index deaf3f4a..0c832dbf 100644 --- a/google/resource_compute_ssl_policy_generated_test.go +++ b/google/resource_compute_ssl_policy_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -67,10 +68,13 @@ resource "google_compute_ssl_policy" "custom-ssl-policy" { } func testAccCheckComputeSslPolicyDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_ssl_policy" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_subnetwork_generated_test.go b/google/resource_compute_subnetwork_generated_test.go index 1bfc2907..000b0808 100644 --- a/google/resource_compute_subnetwork_generated_test.go +++ b/google/resource_compute_subnetwork_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -65,10 +66,13 @@ resource "google_compute_network" "custom-test" { } func testAccCheckComputeSubnetworkDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_subnetwork" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_target_http_proxy_generated_test.go b/google/resource_compute_target_http_proxy_generated_test.go index 745eea08..3b264fa7 100644 --- a/google/resource_compute_target_http_proxy_generated_test.go +++ b/google/resource_compute_target_http_proxy_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -90,10 +91,13 @@ resource "google_compute_http_health_check" "default" { } func testAccCheckComputeTargetHttpProxyDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_target_http_proxy" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_target_https_proxy_generated_test.go b/google/resource_compute_target_https_proxy_generated_test.go index 216633c3..d93bb075 100644 --- a/google/resource_compute_target_https_proxy_generated_test.go +++ b/google/resource_compute_target_https_proxy_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -99,10 +100,13 @@ resource "google_compute_http_health_check" "default" { } func testAccCheckComputeTargetHttpsProxyDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_target_https_proxy" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_target_ssl_proxy_generated_test.go b/google/resource_compute_target_ssl_proxy_generated_test.go index 4dd05295..ae3fdc46 100644 --- a/google/resource_compute_target_ssl_proxy_generated_test.go +++ b/google/resource_compute_target_ssl_proxy_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -76,10 +77,13 @@ resource "google_compute_health_check" "default" { } func testAccCheckComputeTargetSslProxyDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_target_ssl_proxy" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_target_tcp_proxy_generated_test.go b/google/resource_compute_target_tcp_proxy_generated_test.go index f9fcde16..5e76bc62 100644 --- a/google/resource_compute_target_tcp_proxy_generated_test.go +++ b/google/resource_compute_target_tcp_proxy_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -72,10 +73,13 @@ resource "google_compute_health_check" "default" { } func testAccCheckComputeTargetTcpProxyDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_target_tcp_proxy" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_url_map_generated_test.go b/google/resource_compute_url_map_generated_test.go index a5fc26f3..5190ee3a 100644 --- a/google/resource_compute_url_map_generated_test.go +++ b/google/resource_compute_url_map_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -123,10 +124,13 @@ resource "google_storage_bucket" "static" { } func testAccCheckComputeUrlMapDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_url_map" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_vpn_gateway_generated_test.go b/google/resource_compute_vpn_gateway_generated_test.go index 2916436d..49d64e56 100644 --- a/google/resource_compute_vpn_gateway_generated_test.go +++ b/google/resource_compute_vpn_gateway_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -108,10 +109,13 @@ resource "google_compute_route" "route1" { } func testAccCheckComputeVpnGatewayDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_vpn_gateway" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_compute_vpn_tunnel_generated_test.go b/google/resource_compute_vpn_tunnel_generated_test.go index 488218e4..e08b7005 100644 --- a/google/resource_compute_vpn_tunnel_generated_test.go +++ b/google/resource_compute_vpn_tunnel_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -109,10 +110,13 @@ resource "google_compute_route" "route1" { } func testAccCheckComputeVpnTunnelDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_compute_vpn_tunnel" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_dns_managed_zone_generated_test.go b/google/resource_dns_managed_zone_generated_test.go index de9a846c..f51c868d 100644 --- a/google/resource_dns_managed_zone_generated_test.go +++ b/google/resource_dns_managed_zone_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -62,10 +63,13 @@ resource "random_id" "rnd" { } func testAccCheckDnsManagedZoneDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_dns_managed_zone" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_monitoring_group_generated_test.go b/google/resource_monitoring_group_generated_test.go index 3a217987..2940b2f1 100644 --- a/google/resource_monitoring_group_generated_test.go +++ b/google/resource_monitoring_group_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -91,10 +92,13 @@ resource "google_monitoring_group" "subgroup" { } func testAccCheckMonitoringGroupDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_monitoring_group" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_monitoring_notification_channel_generated_test.go b/google/resource_monitoring_notification_channel_generated_test.go index f1907493..6751892f 100644 --- a/google/resource_monitoring_notification_channel_generated_test.go +++ b/google/resource_monitoring_notification_channel_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -57,10 +58,13 @@ resource "google_monitoring_notification_channel" "basic" { } func testAccCheckMonitoringNotificationChannelDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_monitoring_notification_channel" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_monitoring_uptime_check_config_generated_test.go b/google/resource_monitoring_uptime_check_config_generated_test.go index a5ae987b..1e7e4ea9 100644 --- a/google/resource_monitoring_uptime_check_config_generated_test.go +++ b/google/resource_monitoring_uptime_check_config_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -116,10 +117,13 @@ resource "google_monitoring_group" "check" { } func testAccCheckMonitoringUptimeCheckConfigDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_monitoring_uptime_check_config" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_redis_instance_generated_test.go b/google/resource_redis_instance_generated_test.go index e9538fb7..e8781a02 100644 --- a/google/resource_redis_instance_generated_test.go +++ b/google/resource_redis_instance_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -105,10 +106,13 @@ resource "google_compute_network" "auto-network" { } func testAccCheckRedisInstanceDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_redis_instance" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_storage_default_object_access_control_generated_test.go b/google/resource_storage_default_object_access_control_generated_test.go index 5b42cc4b..7cee156e 100644 --- a/google/resource_storage_default_object_access_control_generated_test.go +++ b/google/resource_storage_default_object_access_control_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -60,10 +61,13 @@ resource "google_storage_bucket" "bucket" { } func testAccCheckStorageDefaultObjectAccessControlDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_storage_default_object_access_control" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/google/resource_storage_object_access_control_generated_test.go b/google/resource_storage_object_access_control_generated_test.go index 67503b44..3d4c36cb 100644 --- a/google/resource_storage_object_access_control_generated_test.go +++ b/google/resource_storage_object_access_control_generated_test.go @@ -16,6 +16,7 @@ package google import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -66,10 +67,13 @@ resource "google_storage_bucket" "bucket" { } func testAccCheckStorageObjectAccessControlDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { + for name, rs := range s.RootModule().Resources { if rs.Type != "google_storage_object_access_control" { continue } + if strings.HasPrefix(name, "data.") { + continue + } config := testAccProvider.Meta().(*Config) diff --git a/website/docs/r/compute_image.html.markdown b/website/docs/r/compute_image.html.markdown index 495b5c75..f0706980 100644 --- a/website/docs/r/compute_image.html.markdown +++ b/website/docs/r/compute_image.html.markdown @@ -1,112 +1,181 @@ --- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- layout: "google" page_title: "Google: google_compute_image" sidebar_current: "docs-google-compute-image" description: |- - Creates a bootable VM image for Google Compute Engine from an existing tarball. + Represents an Image resource. --- # google\_compute\_image -Creates a bootable VM image resource for Google Compute Engine from an existing -tarball. For more information see [the official documentation](https://cloud.google.com/compute/docs/images) and -[API](https://cloud.google.com/compute/docs/reference/latest/images). +Represents an Image resource. + +Google Compute Engine uses operating system images to create the root +persistent disks for your instances. You specify an image when you create +an instance. Images contain a boot loader, an operating system, and a +root file system. Linux operating system images are also capable of +running containers on Compute Engine. + +Images can be either public or custom. + +Public images are provided and maintained by Google, open-source +communities, and third-party vendors. By default, all projects have +access to these images and can use them to create instances. Custom +images are available only to your project. You can create a custom image +from root persistent disks and other images. Then, use the custom image +to create an instance. -## Example Usage +To get more information about Image, see: + +* [API documentation](https://cloud.google.com/compute/docs/reference/latest/images) +* How-to Guides + * [Official Documentation](https://cloud.google.com/compute/docs/images) + + +## Example Usage - Image Basic + ```hcl -resource "google_compute_image" "bootable-image" { - name = "my-custom-image" +resource "google_compute_image" "example" { + name = "example-image" raw_disk { - source = "https://storage.googleapis.com/my-bucket/my-disk-image-tarball.tar.gz" - } - - licenses = [ - "https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx", - ] -} - -resource "google_compute_instance" "vm" { - name = "vm-from-custom-image" - machine_type = "n1-standard-1" - zone = "us-east1-c" - - boot_disk { - initialize_params { - image = "${google_compute_image.bootable-image.self_link}" - } - } - - network_interface { - network = "default" + source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.tar.gz" } } ``` ## Argument Reference -The following arguments are supported: (Note that one of either source_disk or - raw_disk is required) +The following arguments are supported: + + +* `name` - + (Required) + Name of the resource; provided by the client when the resource is + created. The name must be 1-63 characters long, and comply with + RFC1035. Specifically, the name must be 1-63 characters long and + match the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?` which means + the first character must be a lowercase letter, and all following + characters must be a dash, lowercase letter, or digit, except the + last character, which cannot be a dash. -* `name` - (Required) A unique name for the resource, required by GCE. - Changing this forces a new resource to be created. - - - -* `description` - (Optional) The description of the image to be created -* `family` - (Optional) The name of the image family to which this image belongs. +* `description` - + (Optional) + An optional description of this resource. Provide this property when + you create the resource. -* `labels` - (Optional) A set of key/value label pairs to assign to the image. +* `disk_size_gb` - + (Optional) + Size of the image when restored onto a persistent disk (in GB). -* `source_disk` - (Optional) The URL of a disk that will be used as the source of the - image. Changing this forces a new resource to be created. +* `family` - + (Optional) + The name of the image family to which this image belongs. You can + create disks by specifying an image family instead of a specific + image name. The image family always returns its latest image that is + not deprecated. The name of the image family must comply with + RFC1035. -* `project` - (Optional) The ID of the project in which the resource belongs. If it - is not provided, the provider project is used. +* `labels` - + (Optional) + Labels to apply to this Image. -* `raw_disk` - (Optional) The raw disk that will be used as the source of the image. - Changing this forces a new resource to be created. Structure is documented - below. +* `licenses` - + (Optional) + Any applicable license URI. + +* `raw_disk` - + (Optional) + The parameters of the raw disk image. Structure is documented below. + +* `source_disk` - + (Optional) + Refers to a gcompute_disk object + You must provide either this property or the + rawDisk.source property but not both to create an image. +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. -* `licenses` - (Optional) A list of license URIs to apply to this image. Changing this - forces a new resource to be created. The `raw_disk` block supports: -* `source` - (Required) The full Google Cloud Storage URL where the disk - image is stored. +* `container_type` - + (Optional) + The format used to encode and transmit the block device, which + should be TAR. This is just a container and transmission format + and not a runtime format. Provided by the client when the disk + image is created. -* `sha1` - (Optional) SHA1 checksum of the source tarball that will be used - to verify the source before creating the image. +* `sha1` - + (Optional) + An optional SHA1 checksum of the disk image before unpackaging. + This is provided by the client when the disk image is created. -* `container_type` - (Optional) The format used to encode and transmit the - block device. TAR is the only supported type and is the default. +* `source` - + (Required) + The full Google Cloud Storage URL where disk storage is stored + You must provide either this property or the sourceDisk property + but not both. ## Attributes Reference -In addition to the arguments listed above, the following computed attributes are -exported: +In addition to the arguments listed above, the following computed attributes are exported: + +* `archive_size_bytes` - + Size of the image tar.gz archive stored in Google Cloud Storage (in + bytes). + +* `creation_timestamp` - + Creation timestamp in RFC3339 text format. + +* `label_fingerprint` - + The fingerprint used for optimistic locking of this resource. Used + internally during updates. * `self_link` - The URI of the created resource. -* `label_fingerprint` - The fingerprint of the assigned labels. ## Timeouts -`google_compute_image` provides the following +This resource provides the following [Timeouts](/docs/configuration/resources.html#timeouts) configuration options: -- `create` - Default `4 minutes` -- `update` - Default `4 minutes` -- `delete` - Default `4 minutes` +- `create` - Default is 4 minutes. +- `update` - Default is 4 minutes. +- `delete` - Default is 4 minutes. ## Import -VM image can be imported using the `name`, e.g. +Image can be imported using any of these accepted formats: ``` -$ terraform import google_compute_image.web-image my-custom-image +$ terraform import google_compute_image.default projects/{{project}}/global/images/{{name}} +$ terraform import google_compute_image.default {{project}}/{{name}} +$ terraform import google_compute_image.default {{name}} ``` + +-> If you're importing a resource with beta features, make sure to include `-provider=google-beta` +as an argument so that Terraform uses the correct provider to import your resource.