Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

id of instance templates now relies on the unique id of the resource #14128

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/7358.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
compute: added output-only attribute `self_link_unique` id for `google_compute_instance_template` to point to the unique id of the resource (instead of its name). It is recommended to use `self_link_unique` for references over `self_link` and `id` for `google_compute_instance_template`
```
25 changes: 21 additions & 4 deletions google/data_source_google_compute_instance_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,22 @@ func DataSourceGoogleComputeInstanceTemplate() *schema.Resource {
Type: schema.TypeString,
Optional: true,
}
dsSchema["self_link_unique"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
}
dsSchema["most_recent"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}

// Set 'Optional' schema elements
addOptionalFieldsToSchema(dsSchema, "name", "filter", "most_recent", "project")
addOptionalFieldsToSchema(dsSchema, "name", "filter", "most_recent", "project", "self_link_unique")

dsSchema["name"].ExactlyOneOf = []string{"name", "filter"}
dsSchema["filter"].ExactlyOneOf = []string{"name", "filter"}
mutuallyExclusive := []string{"name", "filter", "self_link_unique"}
for _, n := range mutuallyExclusive {
dsSchema[n].ExactlyOneOf = mutuallyExclusive
}

return &schema.Resource{
Read: datasourceComputeInstanceTemplateRead,
Expand Down Expand Up @@ -68,8 +74,11 @@ func datasourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interfac

return fmt.Errorf("your filter has returned %d instance template(s). Please refine your filter or set most_recent to return exactly one instance template", len(templates.Items))
}
if v, ok := d.GetOk("self_link_unique"); ok {
return retrieveInstanceFromUniqueId(d, meta, project, v.(string))
}

return fmt.Errorf("one of name or filters must be set")
return fmt.Errorf("one of name, filters or self_link_unique must be set")
}

func retrieveInstance(d *schema.ResourceData, meta interface{}, project, name string) error {
Expand All @@ -78,6 +87,14 @@ func retrieveInstance(d *schema.ResourceData, meta interface{}, project, name st
return resourceComputeInstanceTemplateRead(d, meta)
}

func retrieveInstanceFromUniqueId(d *schema.ResourceData, meta interface{}, project, self_link_unique string) error {
normalId, _ := parseUniqueId(self_link_unique)
d.SetId(normalId)
d.Set("self_link_unique", self_link_unique)

return resourceComputeInstanceTemplateRead(d, meta)
}

// ByCreationTimestamp implements sort.Interface for []*InstanceTemplate based on
// the CreationTimestamp field.
type ByCreationTimestamp []*compute.InstanceTemplate
Expand Down
53 changes: 53 additions & 0 deletions google/data_source_google_compute_instance_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ func TestAccInstanceTemplateDatasource_filter_mostRecent(t *testing.T) {
})
}

func TestAccInstanceTemplateDatasource_self_link_unique(t *testing.T) {
t.Parallel()

VcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: TestAccProviders,
Steps: []resource.TestStep{
{
Config: testAccInstanceTemplate_self_link_unique(GetTestProjectFromEnv(), RandString(t, 10)),
Check: resource.ComposeTestCheckFunc(
checkDataSourceStateMatchesResourceStateWithIgnores(
"data.google_compute_instance_template.default",
"google_compute_instance_template.default",
// we don't compare the id here as we start this test from a self_link_unique url
// and the resource's ID will have the standard format project/projectname/global/instanceTemplates/tf-test-template-random
map[string]struct{}{
"id": {},
},
),
),
},
},
})
}

func testAccInstanceTemplate_name(project, suffix string) string {
return Nprintf(`
resource "google_compute_instance_template" "default" {
Expand Down Expand Up @@ -238,3 +263,31 @@ data "google_compute_instance_template" "default" {
}
`, map[string]interface{}{"project": project, "suffix": suffix})
}

func testAccInstanceTemplate_self_link_unique(project, suffix string) string {
return Nprintf(`
resource "google_compute_instance_template" "default" {
name = "tf-test-template-%{suffix}"
description = "Example template."

machine_type = "e2-small"

tags = ["foo", "bar"]

disk {
source_image = "cos-cloud/cos-stable"
auto_delete = true
boot = true
}

network_interface {
network = "default"
}
}

data "google_compute_instance_template" "default" {
project = "%{project}"
self_link_unique = google_compute_instance_template.default.self_link_unique
}
`, map[string]interface{}{"project": project, "suffix": suffix})
}
3 changes: 1 addition & 2 deletions google/resource_compute_instance_from_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ func resourceComputeInstanceFromTemplateCreate(d *schema.ResourceData, meta inte
return err
}

sourceInstanceTemplate := d.Get("source_instance_template").(string)

sourceInstanceTemplate := ConvertToUniqueIdWhenPresent(d.Get("source_instance_template").(string))
tpl, err := ParseInstanceTemplateFieldValue(sourceInstanceTemplate, d, config)
if err != nil {
return err
Expand Down
102 changes: 102 additions & 0 deletions google/resource_compute_instance_from_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,34 @@ func TestAccComputeInstanceFromTemplate_basic(t *testing.T) {
})
}

func TestAccComputeInstanceFromTemplate_self_link_unique(t *testing.T) {
t.Parallel()

var instance compute.Instance
instanceName := fmt.Sprintf("tf-test-%s", RandString(t, 10))
templateName := fmt.Sprintf("tf-test-%s", RandString(t, 10))
resourceName := "google_compute_instance_from_template.foobar"

VcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: TestAccProviders,
CheckDestroy: testAccCheckComputeInstanceFromTemplateDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccComputeInstanceFromTemplate_self_link_unique(instanceName, templateName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(t, resourceName, &instance),

// Check that fields were set based on the template
resource.TestCheckResourceAttr(resourceName, "machine_type", "n1-standard-1"),
resource.TestCheckResourceAttr(resourceName, "attached_disk.#", "1"),
resource.TestCheckResourceAttr(resourceName, "scheduling.0.automatic_restart", "false"),
),
},
},
})
}

func TestAccComputeInstanceFromRegionTemplate_basic(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -413,6 +441,80 @@ resource "google_compute_instance_from_template" "foobar" {
`, template, template, instance)
}

func testAccComputeInstanceFromTemplate_self_link_unique(instance, template string) string {
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
family = "debian-11"
project = "debian-cloud"
}

resource "google_compute_disk" "foobar" {
name = "%s"
image = data.google_compute_image.my_image.self_link
size = 10
type = "pd-ssd"
zone = "us-central1-a"
}

resource "google_compute_instance_template" "foobar" {
name = "%s"
machine_type = "n1-standard-1" // can't be e2 because of local-ssd

disk {
source = google_compute_disk.foobar.name
auto_delete = false
boot = true
}

disk {
disk_type = "local-ssd"
type = "SCRATCH"
interface = "NVME"
disk_size_gb = 375
}

disk {
source_image = data.google_compute_image.my_image.self_link
auto_delete = true
disk_size_gb = 100
boot = false
disk_type = "pd-ssd"
type = "PERSISTENT"
}

network_interface {
network = "default"
}

metadata = {
foo = "bar"
}

scheduling {
automatic_restart = true
}

can_ip_forward = true
}

resource "google_compute_instance_from_template" "foobar" {
name = "%s"
zone = "us-central1-a"

source_instance_template = google_compute_instance_template.foobar.self_link_unique

// Overrides
can_ip_forward = false
labels = {
my_key = "my_value"
}
scheduling {
automatic_restart = false
}
}
`, template, template, instance)
}

func testAccComputeInstanceFromTemplate_overrideBootDisk(templateDisk, overrideDisk, template, instance string) string {
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
Expand Down
31 changes: 29 additions & 2 deletions google/resource_compute_instance_group_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func ResourceComputeInstanceGroupManager() *schema.Resource {
"instance_template": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: compareSelfLinkRelativePaths,
DiffSuppressFunc: compareSelfLinkRelativePathsIgnoreParams,
Description: `The full URL to an instance template from which all new instances of this version will be created.`,
},

Expand Down Expand Up @@ -371,6 +371,33 @@ func ResourceComputeInstanceGroupManager() *schema.Resource {
}
}

func parseUniqueId(s string) (string, string) {
splits := strings.SplitN(s, "?uniqueId=", 2)
if len(splits) == 2 {
return splits[0], splits[1]
}
return s, ""
}

func compareSelfLinkRelativePathsIgnoreParams(_unused1, old, new string, _unused2 *schema.ResourceData) bool {
oldName, oldUniqueId := parseUniqueId(old)
newName, newUniqueId := parseUniqueId(new)
if oldUniqueId != "" && newUniqueId != "" && oldUniqueId != newUniqueId {
return false
}
return compareSelfLinkRelativePaths(_unused1, oldName, newName, _unused2)
}

func ConvertToUniqueIdWhenPresent(s string) string {
original, uniqueId := parseUniqueId(s)
if uniqueId != "" {
splits := strings.Split(original, "/")
splits[len(splits)-1] = uniqueId
return strings.Join(splits, "/")
}
return s
}

func getNamedPorts(nps []interface{}) []*compute.NamedPort {
namedPorts := make([]*compute.NamedPort, 0, len(nps))
for _, v := range nps {
Expand Down Expand Up @@ -935,7 +962,7 @@ func expandVersions(configured []interface{}) []*compute.InstanceGroupManagerVer

version := compute.InstanceGroupManagerVersion{
Name: data["name"].(string),
InstanceTemplate: data["instance_template"].(string),
InstanceTemplate: ConvertToUniqueIdWhenPresent(data["instance_template"].(string)),
TargetSize: expandFixedOrPercent(data["target_size"].([]interface{})),
}

Expand Down
Loading