diff --git a/builtin/providers/google/image.go b/builtin/providers/google/image.go index 48fff5402..074202283 100644 --- a/builtin/providers/google/image.go +++ b/builtin/providers/google/image.go @@ -1,44 +1,75 @@ package google import ( + "fmt" "strings" - - "code.google.com/p/google-api-go-client/compute/v1" ) -// readImage finds the image with the given name. -func readImage(c *Config, name string) (*compute.Image, error) { - // First, always try ourselves first. - image, err := c.clientCompute.Images.Get(c.Project, name).Do() - if err == nil && image != nil && image.SelfLink != "" { - return image, nil - } +// If the given name is a URL, return it. +// If it is of the form project/name, use that URL. +// If it is of the form name then look in the configured project and then hosted image projects. +func resolveImage(c *Config, name string) (string, error) { - // This is a map of names to the project name where a public image is - // hosted. GCE doesn't have an API to simply look up an image without - // a project so we do this jank thing. - imageMap := map[string]string{ - "centos": "centos-cloud", - "coreos": "coreos-cloud", - "debian": "debian-cloud", - "opensuse": "opensuse-cloud", - "rhel": "rhel-cloud", - "sles": "suse-cloud", - "ubuntu": "ubuntu-os-cloud", - } - // If we match a lookup for an alternate project, then try that next. - // If not, we return the error. - var project string - for k, v := range imageMap { - if strings.Contains(name, k) { - project = v - break + if strings.HasPrefix(name, "https://www.googleapis.com/compute/v1/") { + return name, nil + + } else { + splitName := strings.Split(name, "/") + if len(splitName) == 1 { + + // Must infer the project name: + + // First, try the configured project. + image, err := c.clientCompute.Images.Get(c.Project, name).Do() + if err == nil { + return image.SelfLink, nil + } + + // If we match a lookup for an alternate project, then try that next. + // If not, we return the original error. + + // If the image name contains the left hand side, we use the project from the right hand + // side. + imageMap := map[string]string{ + "centos": "centos-cloud", + "coreos": "coreos-cloud", + "debian": "debian-cloud", + "opensuse": "opensuse-cloud", + "rhel": "rhel-cloud", + "sles": "suse-cloud", + "ubuntu": "ubuntu-os-cloud", + "windows": "windows-cloud", + } + var project string + for k, v := range imageMap { + if strings.Contains(name, k) { + project = v + break + } + } + if project == "" { + return "", err + } + + // There was a match, but the image still may not exist, so check it: + image, err = c.clientCompute.Images.Get(project, name).Do() + if err == nil { + return image.SelfLink, nil + } + + return "", err + + } else if len(splitName) == 2 { + image, err := c.clientCompute.Images.Get(splitName[0], splitName[1]).Do() + if err == nil { + return image.SelfLink, nil + } + return "", err + + } else { + return "", fmt.Errorf("Invalid image name, require URL, project/name, or just name: %s", name) } } - if project == "" { - return nil, err - } - return c.clientCompute.Images.Get(project, name).Do() } diff --git a/builtin/providers/google/resource_compute_disk.go b/builtin/providers/google/resource_compute_disk.go index 378b0171e..b4c203467 100644 --- a/builtin/providers/google/resource_compute_disk.go +++ b/builtin/providers/google/resource_compute_disk.go @@ -70,15 +70,15 @@ func resourceComputeDiskCreate(d *schema.ResourceData, meta interface{}) error { // If we were given a source image, load that. if v, ok := d.GetOk("image"); ok { - log.Printf("[DEBUG] Loading image: %s", v.(string)) - image, err := readImage(config, v.(string)) + log.Printf("[DEBUG] Resolving image name: %s", v.(string)) + imageUrl, err := resolveImage(config, v.(string)) if err != nil { return fmt.Errorf( - "Error loading image '%s': %s", + "Error resolving image name '%s': %s", v.(string), err) } - disk.SourceImage = image.SelfLink + disk.SourceImage = imageUrl } if v, ok := d.GetOk("type"); ok { diff --git a/builtin/providers/google/resource_compute_instance.go b/builtin/providers/google/resource_compute_instance.go index 98e9faf95..e4438d876 100644 --- a/builtin/providers/google/resource_compute_instance.go +++ b/builtin/providers/google/resource_compute_instance.go @@ -231,15 +231,17 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err // Load up the image for this disk if specified if v, ok := d.GetOk(prefix + ".image"); ok { imageName := v.(string) - image, err := readImage(config, imageName) + + + imageUrl, err := resolveImage(config, imageName) if err != nil { return fmt.Errorf( - "Error loading image '%s': %s", + "Error resolving image name '%s': %s", imageName, err) } disk.InitializeParams = &compute.AttachedDiskInitializeParams{ - SourceImage: image.SelfLink, + SourceImage: imageUrl, } } diff --git a/builtin/providers/google/resource_compute_instance_test.go b/builtin/providers/google/resource_compute_instance_test.go index f765a44c4..424351993 100644 --- a/builtin/providers/google/resource_compute_instance_test.go +++ b/builtin/providers/google/resource_compute_instance_test.go @@ -240,6 +240,48 @@ resource "google_compute_instance" "foobar" { } }` +const testAccComputeInstance_basic2 = ` +resource "google_compute_instance" "foobar" { + name = "terraform-test" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + image = "debian-cloud/debian-7-wheezy-v20140814" + } + + network { + source = "default" + } + + metadata { + foo = "bar" + } +}` + +const testAccComputeInstance_basic3 = ` +resource "google_compute_instance" "foobar" { + name = "terraform-test" + machine_type = "n1-standard-1" + zone = "us-central1-a" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + image = "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140814" + } + + network { + source = "default" + } + + metadata { + foo = "bar" + } +}` + const testAccComputeInstance_update = ` resource "google_compute_instance" "foobar" { name = "terraform-test" diff --git a/website/source/docs/providers/google/r/compute_disk.html.markdown b/website/source/docs/providers/google/r/compute_disk.html.markdown index e2fae7380..4a8388bca 100644 --- a/website/source/docs/providers/google/r/compute_disk.html.markdown +++ b/website/source/docs/providers/google/r/compute_disk.html.markdown @@ -30,7 +30,9 @@ The following arguments are supported: * `zone` - (Required) The zone where this disk will be available. -* `image` - (Optional) The machine image to base this disk off of. +* `image` - (Optional) The image from which to initialize this disk. Either the full URL, a + contraction of the form "project/name", or just a name (in which case the current project is +used). * `size` - (Optional) The size of the image in gigabytes. If not specified, it will inherit the size of its base image. diff --git a/website/source/docs/providers/google/r/compute_instance.html.markdown b/website/source/docs/providers/google/r/compute_instance.html.markdown index a9e6a6687..a9cc69fb2 100644 --- a/website/source/docs/providers/google/r/compute_instance.html.markdown +++ b/website/source/docs/providers/google/r/compute_instance.html.markdown @@ -73,8 +73,9 @@ The `disk` block supports: * `disk` - (Required if image not set) The name of the disk (such as those managed by `google_compute_disk`) to attach. -* `image` - (Required if disk not set) The name of the image to base - this disk off of. +* `image` - (Required if disk not set) The image from which to initialize this + disk. Either the full URL, a contraction of the form "project/name", or just + a name (in which case the current project is used). * `auto_delete` - (Optional) Whether or not the disk should be auto-deleted. This defaults to true.