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_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(),

View File

@ -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(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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
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<project>[^/]+)/global/images/(?P<name>[^/]+)", "(?P<project>[^/]+)/(?P<name>[^/]+)", "(?P<name>[^/]+)"}, 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
}

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 {
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" {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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"
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)
<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
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.