Skip to content

Commit a138320

Browse files
committed
compute: Add scheduling.termination_time field to compute_instance resources
This patch adds the `scheduling.termination_time` field to the following resources: * `google_compute_instance` * `google_compute_instance_from_machine_image` (beta) * `google_compute_instance_from_template` * `google_compute_instance_template` * `google_compute_region_instance_template` It also adds a helper function `hasTerminationTimeChanged`, which allows to stop the instance while updating the `termination_time`. Signed-off-by: Norbert Kamiński <[email protected]>
1 parent 737a059 commit a138320

12 files changed

+453
-1
lines changed

mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.tmpl

+26-1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ func expandScheduling(v interface{}) (*compute.Scheduling, error) {
190190
scheduling.LocalSsdRecoveryTimeout = transformedLocalSsdRecoveryTimeout
191191
scheduling.ForceSendFields = append(scheduling.ForceSendFields, "LocalSsdRecoveryTimeout")
192192
}
193+
if v, ok := original["termination_time"]; ok {
194+
scheduling.TerminationTime = v.(string)
195+
scheduling.ForceSendFields = append(scheduling.ForceSendFields, "TerminationTime")
196+
}
193197
return scheduling, nil
194198
}
195199

@@ -286,6 +290,7 @@ func flattenScheduling(resp *compute.Scheduling) []map[string]interface{} {
286290
"provisioning_model": resp.ProvisioningModel,
287291
"instance_termination_action": resp.InstanceTerminationAction,
288292
"availability_domain": resp.AvailabilityDomain,
293+
"termination_time": resp.TerminationTime,
289294
}
290295

291296
if resp.AutomaticRestart != nil {
@@ -723,7 +728,9 @@ func schedulingHasChangeRequiringReboot(d *schema.ResourceData) bool {
723728
oScheduling := o.([]interface{})[0].(map[string]interface{})
724729
newScheduling := n.([]interface{})[0].(map[string]interface{})
725730

726-
return hasNodeAffinitiesChanged(oScheduling, newScheduling) || hasMaxRunDurationChanged(oScheduling, newScheduling)
731+
return (hasNodeAffinitiesChanged(oScheduling, newScheduling) ||
732+
hasMaxRunDurationChanged(oScheduling, newScheduling) ||
733+
hasTerminationTimeChanged(oScheduling, newScheduling))
727734
}
728735

729736
// Terraform doesn't correctly calculate changes on schema.Set, so we do it manually
@@ -773,6 +780,24 @@ func schedulingHasChangeWithoutReboot(d *schema.ResourceData) bool {
773780
return false
774781
}
775782

783+
func hasTerminationTimeChanged(oScheduling, nScheduling map[string]interface{}) bool {
784+
oTerminationTime := oScheduling["termination_time"].(string)
785+
nTerminationTime := nScheduling["termination_time"].(string)
786+
787+
if len(oTerminationTime) == 0 && len(nTerminationTime) == 0 {
788+
return false
789+
}
790+
if len(oTerminationTime) == 0 || len(nTerminationTime) == 0 {
791+
return true
792+
}
793+
794+
if oTerminationTime != nTerminationTime {
795+
return true
796+
}
797+
798+
return false
799+
}
800+
776801
func hasMaxRunDurationChanged(oScheduling, nScheduling map[string]interface{}) bool {
777802
oMrd := oScheduling["max_run_duration"].([]interface{})
778803
nMrd := nScheduling["max_run_duration"].([]interface{})

mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl

+9
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ var (
9494
"scheduling.0.min_node_cpus",
9595
"scheduling.0.provisioning_model",
9696
"scheduling.0.instance_termination_action",
97+
"scheduling.0.termination_time",
9798
"scheduling.0.availability_domain",
9899
"scheduling.0.max_run_duration",
99100
"scheduling.0.on_instance_stop_action",
@@ -897,6 +898,14 @@ func ResourceComputeInstance() *schema.Resource {
897898
AtLeastOneOf: schedulingKeys,
898899
Description: `Specifies the action GCE should take when SPOT VM is preempted.`,
899900
},
901+
"termination_time": {
902+
Type: schema.TypeString,
903+
Optional: true,
904+
AtLeastOneOf: schedulingKeys,
905+
Description: `Specifies the timestamp, when the instance will be terminated,
906+
in RFC3339 text format. If specified, the instance termination action
907+
will be performed at the termination time.`,
908+
},
900909
"availability_domain": {
901910
Type: schema.TypeInt,
902911
Optional: true,

mmv1/third_party/terraform/services/compute/resource_compute_instance_from_machine_image_test.go.tmpl

+89
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package compute_test
44
import (
55
"fmt"
66
"testing"
7+
"time"
78

89
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
910
"github.com/hashicorp/terraform-plugin-testing/terraform"
@@ -81,6 +82,35 @@ func TestAccComputeInstanceFromMachineImage_maxRunDuration(t *testing.T) {
8182
},
8283
})
8384
}
85+
86+
func TestAccComputeInstanceFromMachineImage_terminationTime(t *testing.T) {
87+
t.Parallel()
88+
89+
var instance compute.Instance
90+
instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
91+
generatedInstanceName := fmt.Sprintf("tf-test-generated-%s", acctest.RandString(t, 10))
92+
resourceName := "google_compute_instance_from_machine_image.foobar"
93+
now := time.Now().UTC()
94+
terminationTime := now.Add(24 * time.Hour).Format(time.RFC3339)
95+
96+
acctest.VcrTest(t, resource.TestCase{
97+
PreCheck: func() { acctest.AccTestPreCheck(t) },
98+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t),
99+
CheckDestroy: testAccCheckComputeInstanceFromMachineImageDestroyProducer(t),
100+
Steps: []resource.TestStep{
101+
{
102+
Config: testAccComputeInstanceFromMachineImage_terminationTime(instanceName, generatedInstanceName, terminationTime),
103+
Check: resource.ComposeTestCheckFunc(
104+
testAccCheckComputeInstanceExists(t, resourceName, &instance),
105+
106+
// Check that fields were set based on the template
107+
resource.TestCheckResourceAttr(resourceName, "scheduling.0.automatic_restart", "false"),
108+
resource.TestCheckResourceAttr(resourceName, "scheduling.0.termination_time", terminationTime),
109+
),
110+
},
111+
},
112+
})
113+
}
84114
{{- end }}
85115

86116
func TestAccComputeInstanceFromMachineImage_localSsdRecoveryTimeout(t *testing.T) {
@@ -709,6 +739,65 @@ resource "google_compute_instance_from_machine_image" "foobar" {
709739
}
710740
`, instance, instance, newInstance)
711741
}
742+
743+
func testAccComputeInstanceFromMachineImage_terminationTime(instance, newInstance, terminationTime string) string {
744+
return fmt.Sprintf(`
745+
resource "google_compute_instance" "vm" {
746+
provider = google-beta
747+
748+
boot_disk {
749+
initialize_params {
750+
image = "debian-cloud/debian-12"
751+
}
752+
}
753+
754+
name = "%s"
755+
machine_type = "n1-standard-1"
756+
757+
network_interface {
758+
network = "default"
759+
}
760+
761+
metadata = {
762+
foo = "bar"
763+
}
764+
765+
scheduling {
766+
automatic_restart = false
767+
instance_termination_action = "STOP"
768+
termination_time = "%s"
769+
}
770+
771+
}
772+
773+
resource "google_compute_machine_image" "foobar" {
774+
provider = google-beta
775+
name = "%s"
776+
source_instance = google_compute_instance.vm.self_link
777+
}
778+
779+
resource "google_compute_instance_from_machine_image" "foobar" {
780+
provider = google-beta
781+
name = "%s"
782+
zone = "us-central1-a"
783+
784+
source_machine_image = google_compute_machine_image.foobar.self_link
785+
786+
labels = {
787+
my_key = "my_value"
788+
}
789+
scheduling {
790+
automatic_restart = false
791+
provisioning_model = "STANDARD"
792+
instance_termination_action = "STOP"
793+
termination_time = "%s"
794+
on_instance_stop_action {
795+
discard_local_ssd = true
796+
}
797+
}
798+
}
799+
`, instance, terminationTime, instance, newInstance, terminationTime)
800+
}
712801
{{- end }}
713802

714803

mmv1/third_party/terraform/services/compute/resource_compute_instance_from_template_test.go.tmpl

+77
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"regexp"
66
"testing"
7+
"time"
78

89
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
910
"github.com/hashicorp/terraform-plugin-testing/terraform"
@@ -409,6 +410,32 @@ func TestAccComputeInstanceFromTemplate_overrideScheduling(t *testing.T) {
409410
})
410411
}
411412

413+
func TestAccComputeInstanceFromTemplate_TerminationTime(t *testing.T) {
414+
t.Parallel()
415+
416+
var instance compute.Instance
417+
instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
418+
templateName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
419+
templateDisk := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
420+
resourceName := "google_compute_instance_from_template.inst"
421+
now := time.Now().UTC()
422+
terminationTime := now.Add(24 * time.Hour).Format(time.RFC3339)
423+
424+
acctest.VcrTest(t, resource.TestCase{
425+
PreCheck: func() { acctest.AccTestPreCheck(t) },
426+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
427+
CheckDestroy: testAccCheckComputeInstanceFromTemplateDestroyProducer(t),
428+
Steps: []resource.TestStep{
429+
{
430+
Config: testAccComputeInstanceFromTemplate_terminationTime(templateDisk, templateName, terminationTime, instanceName),
431+
Check: resource.ComposeTestCheckFunc(
432+
testAccCheckComputeInstanceExists(t, resourceName, &instance),
433+
),
434+
},
435+
},
436+
})
437+
}
438+
412439
func TestAccComputeInstanceFromTemplate_overrideMetadataDotStartupScript(t *testing.T) {
413440
var instance compute.Instance
414441
instanceName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
@@ -1482,6 +1509,56 @@ resource "google_compute_instance_from_template" "inst" {
14821509
`, templateDisk, template, instance)
14831510
}
14841511

1512+
func testAccComputeInstanceFromTemplate_terminationTime(templateDisk, template, termination_time, instance string) string {
1513+
return fmt.Sprintf(`
1514+
data "google_compute_image" "my_image" {
1515+
family = "debian-11"
1516+
project = "debian-cloud"
1517+
}
1518+
1519+
resource "google_compute_disk" "foobar" {
1520+
name = "%s"
1521+
image = data.google_compute_image.my_image.self_link
1522+
size = 10
1523+
type = "pd-ssd"
1524+
zone = "us-central1-a"
1525+
}
1526+
1527+
resource "google_compute_instance_template" "foobar" {
1528+
name = "%s"
1529+
machine_type = "e2-medium"
1530+
1531+
disk {
1532+
source = google_compute_disk.foobar.name
1533+
auto_delete = false
1534+
boot = true
1535+
}
1536+
1537+
network_interface {
1538+
network = "default"
1539+
}
1540+
1541+
metadata = {
1542+
foo = "bar"
1543+
}
1544+
1545+
scheduling {
1546+
instance_termination_action = "STOP"
1547+
termination_time = "%s"
1548+
}
1549+
1550+
can_ip_forward = true
1551+
}
1552+
1553+
resource "google_compute_instance_from_template" "inst" {
1554+
name = "%s"
1555+
zone = "us-central1-a"
1556+
1557+
source_instance_template = google_compute_instance_template.foobar.self_link
1558+
}
1559+
`, templateDisk, template, termination_time, instance)
1560+
}
1561+
14851562
func testAccComputeInstanceFromTemplate_overrideMetadataDotStartupScript(instance, template string) string {
14861563
return fmt.Sprintf(`
14871564
data "google_compute_image" "my_image" {

mmv1/third_party/terraform/services/compute/resource_compute_instance_template.go.tmpl

+9
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ var (
3737
"scheduling.0.availability_domain",
3838
"scheduling.0.max_run_duration",
3939
"scheduling.0.on_instance_stop_action",
40+
"scheduling.0.termination_time",
4041
{{- if ne $.TargetVersionName "ga" }}
4142
"scheduling.0.maintenance_interval",
4243
"scheduling.0.host_error_timeout_seconds",
@@ -776,6 +777,14 @@ be from 0 to 999,999,999 inclusive.`,
776777
},
777778
},
778779
},
780+
"termination_time": {
781+
Type: schema.TypeString,
782+
Optional: true,
783+
AtLeastOneOf: schedulingKeys,
784+
Description: `Specifies the timestamp, when the instance will be terminated,
785+
in RFC3339 text format. If specified, the instance termination action
786+
will be performed at the termination time.`,
787+
},
779788
{{- if ne $.TargetVersionName "ga" }}
780789
"host_error_timeout_seconds": {
781790
Type: schema.TypeInt,

0 commit comments

Comments
 (0)