Skip to content

Commit

Permalink
Allow for setting desired_status to different values than "RUNNING"…
Browse files Browse the repository at this point in the history
… on creation of `google_compute_instance` (GoogleCloudPlatform#11902)
  • Loading branch information
karolgorc authored and amanMahendroo committed Dec 17, 2024
1 parent d9c5a1c commit 9e3889a
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,6 @@ be from 0 to 999,999,999 inclusive.`,
},
suppressEmptyGuestAcceleratorDiff,
),
desiredStatusDiff,
validateSubnetworkProject,
forceNewIfNetworkIPNotUpdatable,
tpgresource.SetLabelsDiff,
Expand Down Expand Up @@ -1477,10 +1476,34 @@ func getAllStatusBut(status string) []string {
return computeInstanceStatus
}

func waitUntilInstanceHasDesiredStatus(config *transport_tpg.Config, d *schema.ResourceData) error {
desiredStatus := d.Get("desired_status").(string)
func changeInstanceStatusOnCreation(config *transport_tpg.Config, d *schema.ResourceData, project, zone, status, userAgent string) error {
var op *compute.Operation
var err error
if status == "TERMINATED" {
op, err = config.NewComputeClient(userAgent).Instances.Stop(project, zone, d.Get("name").(string)).Do()
} else if status == "SUSPENDED" {
op, err = config.NewComputeClient(userAgent).Instances.Suspend(project, zone, d.Get("name").(string)).Do()
}
if err != nil {
return fmt.Errorf("Error changing instance status after creation: %s", err)
}

waitErr := ComputeOperationWaitTime(config, op, project, "changing instance status", userAgent, d.Timeout(schema.TimeoutCreate))
if waitErr != nil {
d.SetId("")
return waitErr
}

err = waitUntilInstanceHasDesiredStatus(config, d, status)
if err != nil {
return fmt.Errorf("Error waiting for status: %s", err)
}

return nil
}

if desiredStatus != "" {
func waitUntilInstanceHasDesiredStatus(config *transport_tpg.Config, d *schema.ResourceData, status string) error {
if status != "" {
stateRefreshFunc := func() (interface{}, string, error) {
instance, err := getInstance(config, d)
if err != nil || instance == nil {
Expand All @@ -1491,17 +1514,17 @@ func waitUntilInstanceHasDesiredStatus(config *transport_tpg.Config, d *schema.R
}
stateChangeConf := retry.StateChangeConf{
Delay: 5 * time.Second,
Pending: getAllStatusBut(desiredStatus),
Pending: getAllStatusBut(status),
Refresh: stateRefreshFunc,
Target: []string{desiredStatus},
Target: []string{status},
Timeout: d.Timeout(schema.TimeoutUpdate),
MinTimeout: 2 * time.Second,
}
_, err := stateChangeConf.WaitForState()

if err != nil {
return fmt.Errorf(
"Error waiting for instance to reach desired status %s: %s", desiredStatus, err)
"Error waiting for instance to reach desired status %s: %s", status, err)
}
}

Expand Down Expand Up @@ -1568,11 +1591,20 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err
}
{{- end }}

err = waitUntilInstanceHasDesiredStatus(config, d)
err = waitUntilInstanceHasDesiredStatus(config, d, "RUNNING")
if err != nil {
return fmt.Errorf("Error waiting for status: %s", err)
}

if val, ok := d.GetOk("desired_status"); ok {
if val.(string) != "RUNNING" {
err = changeInstanceStatusOnCreation(config, d, project, zone.Name, val.(string), userAgent)
if err != nil {
return fmt.Errorf("Error changing instance status after creation: %s", err)
}
}
}

return resourceComputeInstanceRead(d, meta)
}

Expand Down Expand Up @@ -2916,25 +2948,6 @@ func suppressEmptyGuestAcceleratorDiff(_ context.Context, d *schema.ResourceDiff
return nil
}

// return an error if the desired_status field is set to a value other than RUNNING on Create.
func desiredStatusDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error {
// when creating an instance, name is not set
oldName, _ := diff.GetChange("name")

if oldName == nil || oldName == "" {
_, newDesiredStatus := diff.GetChange("desired_status")

if newDesiredStatus == nil || newDesiredStatus == "" {
return nil
} else if newDesiredStatus != "RUNNING" {
return fmt.Errorf("When creating an instance, desired_status can only accept RUNNING value")
}
return nil
}

return nil
}

func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*transport_tpg.Config)
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2288,27 +2288,80 @@ func TestAccComputeInstance_enableDisplay(t *testing.T) {
})
}

func TestAccComputeInstance_desiredStatusOnCreation(t *testing.T) {
func TestAccComputeInstance_desiredStatusTerminatedOnCreation(t *testing.T) {
t.Parallel()

var instance compute.Instance
var instanceName = fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))

context_1 := map[string]interface{}{
"instance_name": fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)),
"zone": "us-central1-a",
"desired_status": "TERMINATED",
}

context_2 := map[string]interface{}{
"instance_name": context_1["instance_name"],
"zone": context_1["zone"],
"desired_status": "RUNNING",
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t),
CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "e2-medium", "TERMINATED", false),
ExpectError: regexp.MustCompile("When creating an instance, desired_status can only accept RUNNING value"),
Config: testAccComputeInstance_desiredStatusOnCreation(context_1),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(t, "google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceHasStatus(&instance, context_1["desired_status"].(string)),
),
},
{
Config: testAccComputeInstance_machineType_desiredStatus_allowStoppingForUpdate(instanceName, "e2-medium", "RUNNING", false),
Config: testAccComputeInstance_desiredStatusOnCreation(context_2),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
t, "google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceHasStatus(&instance, "RUNNING"),
testAccCheckComputeInstanceExists(t, "google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceHasStatus(&instance, context_2["desired_status"].(string)),
),
},
},
})
}

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

var instance compute.Instance

context_1 := map[string]interface{}{
"instance_name": fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)),
"zone": "us-central1-a",
"desired_status": "SUSPENDED",
}

context_2 := map[string]interface{}{
"instance_name": context_1["instance_name"],
"zone": context_1["zone"],
"desired_status": "RUNNING",
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccComputeInstance_desiredStatusOnCreation(context_1),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(t, "google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceHasStatus(&instance, context_1["desired_status"].(string)),
),
},
{
Config: testAccComputeInstance_desiredStatusOnCreation(context_2),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(t, "google_compute_instance.foobar", &instance),
testAccCheckComputeInstanceHasStatus(&instance, context_2["desired_status"].(string)),
),
},
},
Expand Down Expand Up @@ -8974,6 +9027,33 @@ resource "google_compute_instance" "foobar" {
`, instance)
}

func testAccComputeInstance_desiredStatusOnCreation(context map[string]interface{}) string {
return acctest.Nprintf(`
data "google_compute_image" "my_image" {
family = "debian-11"
project = "debian-cloud"
}

resource "google_compute_instance" "foobar" {
name = "%{instance_name}"
machine_type = "e2-medium"
zone = "%{zone}"

boot_disk {
initialize_params{
image = "${data.google_compute_image.my_image.self_link}"
}
}

network_interface {
network = "default"
}

desired_status = "%{desired_status}"
}
`, context)
}

func testAccComputeInstance_resourcePolicyCollocate(instance, suffix string) string {
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
Expand Down

0 comments on commit 9e3889a

Please sign in to comment.