Skip to content

Commit

Permalink
id of instance templates now relies on the unique id of the resource (G…
Browse files Browse the repository at this point in the history
  • Loading branch information
irsl authored and hao-nan-li committed Apr 11, 2023
1 parent d311afd commit 4727fd0
Show file tree
Hide file tree
Showing 14 changed files with 443 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,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 @@ -73,8 +79,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 @@ -83,6 +92,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
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,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
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,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 @@ -495,6 +495,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 @@ -1160,7 +1187,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
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,12 @@ be from 0 to 999,999,999 inclusive.`,
Description: `The URI of the created resource.`,
},

"self_link_unique": {
Type: schema.TypeString,
Computed: true,
Description: `A special URI of the created resource that uniquely identifies this instance template.`,
},

"service_account": {
Type: schema.TypeList,
MaxItems: 1,
Expand Down Expand Up @@ -1256,6 +1262,8 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac

// Store the ID now
d.SetId(fmt.Sprintf("projects/%s/global/instanceTemplates/%s", project, instanceTemplate.Name))
// And also the unique ID
d.Set("self_link_unique", fmt.Sprintf("%v?uniqueId=%v", d.Id(), op.TargetId))

err = ComputeOperationWaitTime(config, op, project, "Creating Instance Template", userAgent, d.Timeout(schema.TimeoutCreate))
if err != nil {
Expand Down Expand Up @@ -1498,12 +1506,16 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{
return err
}

splits := strings.Split(d.Id(), "/")
idStr := d.Id()
if v, ok := d.GetOk("self_link_unique"); ok && v != "" {
idStr = ConvertToUniqueIdWhenPresent(v.(string))
}

splits := strings.Split(idStr, "/")
instanceTemplate, err := config.NewComputeClient(userAgent).InstanceTemplates.Get(project, splits[len(splits)-1]).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Instance Template %q", d.Get("name").(string)))
}

// Set the metadata fingerprint if there is one.
if instanceTemplate.Properties.Metadata != nil {
if err = d.Set("metadata_fingerprint", instanceTemplate.Properties.Metadata.Fingerprint); err != nil {
Expand Down Expand Up @@ -1545,6 +1557,9 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{
if err = d.Set("self_link", instanceTemplate.SelfLink); err != nil {
return fmt.Errorf("Error setting self_link: %s", err)
}
if err = d.Set("self_link_unique", fmt.Sprintf("%v?uniqueId=%v", instanceTemplate.SelfLink, instanceTemplate.Id)); err != nil {
return fmt.Errorf("Error setting self_link_unique: %s", err)
}
if err = d.Set("name", instanceTemplate.Name); err != nil {
return fmt.Errorf("Error setting name: %s", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func ResourceComputeRegionInstanceGroupManager() *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
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})
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,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 @@ -418,6 +446,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
Loading

0 comments on commit 4727fd0

Please sign in to comment.