Initial addition of generated Image to Terraform. (#2769)

This commit is contained in:
The Magician 2019-01-02 16:05:38 -08:00 committed by Nathan McKinley
parent 74013c7dc6
commit 692d36cb3f
39 changed files with 892 additions and 397 deletions

View File

@ -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)
}

View File

@ -137,7 +137,6 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
"google_compute_attached_disk": resourceComputeAttachedDisk(), "google_compute_attached_disk": resourceComputeAttachedDisk(),
"google_compute_backend_service": resourceComputeBackendService(), "google_compute_backend_service": resourceComputeBackendService(),
"google_compute_global_forwarding_rule": resourceComputeGlobalForwardingRule(), "google_compute_global_forwarding_rule": resourceComputeGlobalForwardingRule(),
"google_compute_image": resourceComputeImage(),
"google_compute_instance": resourceComputeInstance(), "google_compute_instance": resourceComputeInstance(),
"google_compute_instance_from_template": resourceComputeInstanceFromTemplate(), "google_compute_instance_from_template": resourceComputeInstanceFromTemplate(),
"google_compute_instance_group": resourceComputeInstanceGroup(), "google_compute_instance_group": resourceComputeInstanceGroup(),

View File

@ -27,6 +27,7 @@ var GeneratedComputeResourcesMap = map[string]*schema.Resource{
"google_compute_http_health_check": resourceComputeHttpHealthCheck(), "google_compute_http_health_check": resourceComputeHttpHealthCheck(),
"google_compute_https_health_check": resourceComputeHttpsHealthCheck(), "google_compute_https_health_check": resourceComputeHttpsHealthCheck(),
"google_compute_health_check": resourceComputeHealthCheck(), "google_compute_health_check": resourceComputeHealthCheck(),
"google_compute_image": resourceComputeImage(),
"google_compute_interconnect_attachment": resourceComputeInterconnectAttachment(), "google_compute_interconnect_attachment": resourceComputeInterconnectAttachment(),
"google_compute_region_autoscaler": resourceComputeRegionAutoscaler(), "google_compute_region_autoscaler": resourceComputeRegionAutoscaler(),
"google_compute_region_disk": resourceComputeRegionDisk(), "google_compute_region_disk": resourceComputeRegionDisk(),

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -150,10 +151,13 @@ resource "google_compute_instance" "instance_with_ip" {
} }
func testAccCheckComputeAddressDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_address" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -107,10 +108,13 @@ data "google_compute_image" "debian_9" {
} }
func testAccCheckComputeAutoscalerDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_autoscaler" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -61,10 +62,13 @@ resource "google_storage_bucket" "image_bucket" {
} }
func testAccCheckComputeBackendBucketDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_backend_bucket" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -59,10 +60,13 @@ resource "google_compute_disk" "default" {
} }
func testAccCheckComputeDiskDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_disk" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -69,10 +70,13 @@ resource "google_compute_network" "default" {
} }
func testAccCheckComputeFirewallDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_firewall" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -59,10 +60,13 @@ resource "google_compute_target_pool" "default" {
} }
func testAccCheckComputeForwardingRuleDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_forwarding_rule" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -53,10 +54,13 @@ resource "google_compute_global_address" "default" {
} }
func testAccCheckComputeGlobalAddressDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_global_address" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -60,10 +61,13 @@ resource "google_compute_health_check" "internal-health-check" {
} }
func testAccCheckComputeHealthCheckDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_health_check" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -57,10 +58,13 @@ resource "google_compute_http_health_check" "default" {
} }
func testAccCheckComputeHttpHealthCheckDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_http_health_check" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -57,10 +58,13 @@ resource "google_compute_https_health_check" "default" {
} }
func testAccCheckComputeHttpsHealthCheckDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_https_health_check" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -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 package google
import ( import (
"fmt" "fmt"
"log" "log"
"reflect"
"strconv"
"time" "time"
"github.com/hashicorp/terraform/helper/schema" "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 { func resourceComputeImage() *schema.Resource {
return &schema.Resource{ return &schema.Resource{
Create: resourceComputeImageCreate, Create: resourceComputeImageCreate,
Read: resourceComputeImageRead, Read: resourceComputeImageRead,
Update: resourceComputeImageUpdate, Update: resourceComputeImageUpdate,
Delete: resourceComputeImageDelete, Delete: resourceComputeImageDelete,
Importer: &schema.ResourceImporter{ Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough, State: resourceComputeImageImport,
}, },
Timeouts: &schema.ResourceTimeout{ Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(computeImageCreateTimeoutDefault * time.Minute), Create: schema.DefaultTimeout(240 * time.Second),
Update: schema.DefaultTimeout(computeImageCreateTimeoutDefault * time.Minute), Update: schema.DefaultTimeout(240 * time.Second),
Delete: schema.DefaultTimeout(computeImageCreateTimeoutDefault * time.Minute), Delete: schema.DefaultTimeout(240 * time.Second),
}, },
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
// TODO(cblecker): one of source_disk or raw_disk is required
"name": { "name": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
ForceNew: true, ForceNew: true,
}, },
"description": { "description": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
}, },
"disk_size_gb": {
Type: schema.TypeInt,
Computed: true,
Optional: true,
ForceNew: true,
},
"family": { "family": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
}, },
"labels": {
"project": { Type: schema.TypeMap,
Type: schema.TypeString,
Optional: true, Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"licenses": {
Type: schema.TypeList,
Computed: true, Computed: true,
ForceNew: true,
},
"source_disk": {
Type: schema.TypeString,
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
}, },
"raw_disk": { "raw_disk": {
Type: schema.TypeList, Type: schema.TypeList,
Optional: true, Optional: true,
@ -73,48 +91,46 @@ func resourceComputeImage() *schema.Resource {
Required: true, Required: true,
ForceNew: true, ForceNew: true,
}, },
"container_type": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"TAR", ""}, false),
Default: "TAR",
},
"sha1": { "sha1": {
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
}, },
"container_type": {
Type: schema.TypeString,
Optional: true,
Default: "TAR",
ForceNew: true,
},
}, },
}, },
}, },
"source_disk": {
"self_link": { Type: schema.TypeString,
Optional: true,
ForceNew: true,
DiffSuppressFunc: compareSelfLinkOrResourceName,
},
"archive_size_bytes": {
Type: schema.TypeInt,
Computed: true,
},
"creation_timestamp": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
}, },
"label_fingerprint": {
"create_timeout": { Type: schema.TypeString,
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},
Computed: true, Computed: true,
}, },
"project": {
"label_fingerprint": { Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"self_link": {
Type: schema.TypeString, Type: schema.TypeString,
Computed: true, Computed: true,
}, },
@ -125,105 +141,159 @@ func resourceComputeImage() *schema.Resource {
func resourceComputeImageCreate(d *schema.ResourceData, meta interface{}) error { func resourceComputeImageCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) 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) project, err := getProject(d, config)
if err != nil { if err != nil {
return err return err
} }
op := &compute.Operation{}
// Build the image err = Convert(res, op)
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)
if err != nil { if err != nil {
return err 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) return resourceComputeImageRead(d, meta)
} }
func resourceComputeImageRead(d *schema.ResourceData, meta interface{}) error { func resourceComputeImageRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) 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 { if err != nil {
return err return err
} }
image, err := config.clientCompute.Images.Get( res, err := sendRequest(config, "GET", url, nil)
project, d.Id()).Do()
if err != 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 != "" { project, err := getProject(d, config)
d.Set("source_disk", image.SourceDisk) if err != nil {
} else if image.RawDisk != nil { return err
// `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. if err := d.Set("project", project); err != nil {
// Leaving `raw_disk` to current state value. return fmt.Errorf("Error reading Image: %s", err)
} else {
return fmt.Errorf("Either raw_disk or source_disk configuration is required.")
} }
d.Set("name", image.Name) if err := d.Set("archive_size_bytes", flattenComputeImageArchiveSizeBytes(res["archiveSizeBytes"], d)); err != nil {
d.Set("description", image.Description) return fmt.Errorf("Error reading Image: %s", err)
d.Set("family", image.Family) }
d.Set("self_link", image.SelfLink) if err := d.Set("creation_timestamp", flattenComputeImageCreationTimestamp(res["creationTimestamp"], d)); err != nil {
d.Set("labels", image.Labels) return fmt.Errorf("Error reading Image: %s", err)
d.Set("licenses", image.Licenses) }
d.Set("label_fingerprint", image.LabelFingerprint) if err := d.Set("description", flattenComputeImageDescription(res["description"], d)); err != nil {
d.Set("project", project) 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 return nil
} }
@ -231,77 +301,251 @@ func resourceComputeImageRead(d *schema.ResourceData, meta interface{}) error {
func resourceComputeImageUpdate(d *schema.ResourceData, meta interface{}) error { func resourceComputeImageUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) 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) d.Partial(true)
if d.HasChange("labels") { if d.HasChange("labels") || d.HasChange("label_fingerprint") {
labels := expandLabels(d) obj := make(map[string]interface{})
labelFingerprint := d.Get("label_fingerprint").(string) labelsProp, err := expandComputeImageLabels(d.Get("labels"), d, config)
setLabelsRequest := compute.GlobalSetLabelsRequest{ if err != nil {
LabelFingerprint: labelFingerprint, return err
Labels: labels, } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
ForceSendFields: []string{"Labels"}, 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 { if err != nil {
return err return err
} }
d.SetPartial("labels") 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.SetPartial("label_fingerprint")
} }
d.Partial(false) d.Partial(false)
return nil
return resourceComputeImageRead(d, meta)
} }
func resourceComputeImageDelete(d *schema.ResourceData, meta interface{}) error { func resourceComputeImageDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config) 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) project, err := getProject(d, config)
if err != nil { if err != nil {
return err return err
} }
op := &compute.Operation{}
// Delete the image err = Convert(res, op)
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()))
if err != nil { if err != nil {
return err 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 return nil
} }
func licenses(d *schema.ResourceData) []string { func resourceComputeImageImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
licensesCount := d.Get("licenses.#").(int) config := meta.(*Config)
data := make([]string, licensesCount) if err := parseImportId([]string{"projects/(?P<project>[^/]+)/global/images/(?P<name>[^/]+)", "(?P<project>[^/]+)/(?P<name>[^/]+)", "(?P<name>[^/]+)"}, d, config); err != nil {
for i := 0; i < licensesCount; i++ { return nil, err
data[i] = d.Get(fmt.Sprintf("licenses.%d", i)).(string)
} }
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
} }

View File

@ -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
}

View File

@ -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 { func testAccCheckComputeImageExists(n string, image *compute.Image) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n] 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 { func testAccCheckComputeImageDescription(image *compute.Image, description string) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
if image.Description != description { 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 { func testAccComputeImage_basic(name string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "google_compute_image" "foobar" { resource "google_compute_image" "foobar" {

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -107,10 +108,13 @@ data "google_compute_image" "debian_9" {
} }
func testAccCheckComputeRegionAutoscalerDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_region_autoscaler" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -72,10 +73,13 @@ resource "google_compute_snapshot" "snapdisk" {
} }
func testAccCheckComputeRegionDiskDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_region_disk" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -61,10 +62,13 @@ resource "google_compute_network" "default" {
} }
func testAccCheckComputeRouteDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_route" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -70,10 +71,13 @@ resource "google_compute_network" "foobar" {
} }
func testAccCheckComputeRouterDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_router" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -72,10 +73,13 @@ resource "google_compute_disk" "persistent" {
} }
func testAccCheckComputeSnapshotDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_snapshot" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -201,10 +202,13 @@ resource "google_compute_http_health_check" "default" {
} }
func testAccCheckComputeSslCertificateDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_ssl_certificate" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -67,10 +68,13 @@ resource "google_compute_ssl_policy" "custom-ssl-policy" {
} }
func testAccCheckComputeSslPolicyDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_ssl_policy" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -65,10 +66,13 @@ resource "google_compute_network" "custom-test" {
} }
func testAccCheckComputeSubnetworkDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_subnetwork" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -90,10 +91,13 @@ resource "google_compute_http_health_check" "default" {
} }
func testAccCheckComputeTargetHttpProxyDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_target_http_proxy" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -99,10 +100,13 @@ resource "google_compute_http_health_check" "default" {
} }
func testAccCheckComputeTargetHttpsProxyDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_target_https_proxy" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -76,10 +77,13 @@ resource "google_compute_health_check" "default" {
} }
func testAccCheckComputeTargetSslProxyDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_target_ssl_proxy" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -72,10 +73,13 @@ resource "google_compute_health_check" "default" {
} }
func testAccCheckComputeTargetTcpProxyDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_target_tcp_proxy" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -123,10 +124,13 @@ resource "google_storage_bucket" "static" {
} }
func testAccCheckComputeUrlMapDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_url_map" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -108,10 +109,13 @@ resource "google_compute_route" "route1" {
} }
func testAccCheckComputeVpnGatewayDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_vpn_gateway" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -109,10 +110,13 @@ resource "google_compute_route" "route1" {
} }
func testAccCheckComputeVpnTunnelDestroy(s *terraform.State) error { 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" { if rs.Type != "google_compute_vpn_tunnel" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -62,10 +63,13 @@ resource "random_id" "rnd" {
} }
func testAccCheckDnsManagedZoneDestroy(s *terraform.State) error { 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" { if rs.Type != "google_dns_managed_zone" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -91,10 +92,13 @@ resource "google_monitoring_group" "subgroup" {
} }
func testAccCheckMonitoringGroupDestroy(s *terraform.State) error { 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" { if rs.Type != "google_monitoring_group" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -57,10 +58,13 @@ resource "google_monitoring_notification_channel" "basic" {
} }
func testAccCheckMonitoringNotificationChannelDestroy(s *terraform.State) error { 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" { if rs.Type != "google_monitoring_notification_channel" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -116,10 +117,13 @@ resource "google_monitoring_group" "check" {
} }
func testAccCheckMonitoringUptimeCheckConfigDestroy(s *terraform.State) error { 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" { if rs.Type != "google_monitoring_uptime_check_config" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -105,10 +106,13 @@ resource "google_compute_network" "auto-network" {
} }
func testAccCheckRedisInstanceDestroy(s *terraform.State) error { 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" { if rs.Type != "google_redis_instance" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -60,10 +61,13 @@ resource "google_storage_bucket" "bucket" {
} }
func testAccCheckStorageDefaultObjectAccessControlDestroy(s *terraform.State) error { 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" { if rs.Type != "google_storage_default_object_access_control" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -16,6 +16,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -66,10 +67,13 @@ resource "google_storage_bucket" "bucket" {
} }
func testAccCheckStorageObjectAccessControlDestroy(s *terraform.State) error { 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" { if rs.Type != "google_storage_object_access_control" {
continue continue
} }
if strings.HasPrefix(name, "data.") {
continue
}
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)

View File

@ -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" layout: "google"
page_title: "Google: google_compute_image" page_title: "Google: google_compute_image"
sidebar_current: "docs-google-compute-image" sidebar_current: "docs-google-compute-image"
description: |- description: |-
Creates a bootable VM image for Google Compute Engine from an existing tarball. Represents an Image resource.
--- ---
# google\_compute\_image # google\_compute\_image
Creates a bootable VM image resource for Google Compute Engine from an existing Represents an Image resource.
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). 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)
<div class = "oics-button" style="float: right; margin: 0 0 -15px">
<a href="https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fdocs-examples.git&cloudshell_working_dir=image_basic&cloudshell_image=gcr.io%2Fgraphite-cloud-shell-images%2Fterraform%3Alatest&open_in_editor=main.tf&cloudshell_print=.%2Fmotd&cloudshell_tutorial=.%2Ftutorial.md" target="_blank">
<img alt="Open in Cloud Shell" src="//gstatic.com/cloudssh/images/open-btn.svg" style="max-height: 44px; margin: 32px auto; max-width: 100%;">
</a>
</div>
## Example Usage - Image Basic
```hcl ```hcl
resource "google_compute_image" "bootable-image" { resource "google_compute_image" "example" {
name = "my-custom-image" name = "example-image"
raw_disk { raw_disk {
source = "https://storage.googleapis.com/my-bucket/my-disk-image-tarball.tar.gz" source = "https://storage.googleapis.com/bosh-cpi-artifacts/bosh-stemcell-3262.4-google-kvm-ubuntu-trusty-go_agent-raw.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"
} }
} }
``` ```
## Argument Reference ## Argument Reference
The following arguments are supported: (Note that one of either source_disk or The following arguments are supported:
raw_disk is required)
* `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 * `family` -
image. Changing this forces a new resource to be created. (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 * `labels` -
is not provided, the provider project is used. (Optional)
Labels to apply to this Image.
* `raw_disk` - (Optional) The raw disk that will be used as the source of the image. * `licenses` -
Changing this forces a new resource to be created. Structure is documented (Optional)
below. 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: The `raw_disk` block supports:
* `source` - (Required) The full Google Cloud Storage URL where the disk * `container_type` -
image is stored. (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 * `sha1` -
to verify the source before creating the image. (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 * `source` -
block device. TAR is the only supported type and is the default. (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 ## Attributes Reference
In addition to the arguments listed above, the following computed attributes are In addition to the arguments listed above, the following computed attributes are exported:
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. * `self_link` - The URI of the created resource.
* `label_fingerprint` - The fingerprint of the assigned labels.
## Timeouts ## Timeouts
`google_compute_image` provides the following This resource provides the following
[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: [Timeouts](/docs/configuration/resources.html#timeouts) configuration options:
- `create` - Default `4 minutes` - `create` - Default is 4 minutes.
- `update` - Default `4 minutes` - `update` - Default is 4 minutes.
- `delete` - Default `4 minutes` - `delete` - Default is 4 minutes.
## Import ## 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.