Skip to content

Commit e827d1a

Browse files
AsiderrNA2047
authored andcommitted
compute: Add scheduling.termination_time field to compute_instance resources (GoogleCloudPlatform#12791)
Signed-off-by: Norbert Kamiński <[email protected]>
1 parent 90da23f commit e827d1a

13 files changed

+551
-2
lines changed

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

+27-2
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ func expandScheduling(v interface{}) (*compute.Scheduling, error) {
199199
scheduling.LocalSsdRecoveryTimeout = transformedLocalSsdRecoveryTimeout
200200
scheduling.ForceSendFields = append(scheduling.ForceSendFields, "LocalSsdRecoveryTimeout")
201201
}
202+
if v, ok := original["termination_time"]; ok {
203+
scheduling.TerminationTime = v.(string)
204+
}
202205
return scheduling, nil
203206
}
204207

@@ -344,6 +347,7 @@ func flattenScheduling(resp *compute.Scheduling) []map[string]interface{} {
344347
"provisioning_model": resp.ProvisioningModel,
345348
"instance_termination_action": resp.InstanceTerminationAction,
346349
"availability_domain": resp.AvailabilityDomain,
350+
"termination_time": resp.TerminationTime,
347351
}
348352

349353
if resp.AutomaticRestart != nil {
@@ -796,9 +800,12 @@ func schedulingHasChangeRequiringReboot(d *schema.ResourceData) bool {
796800
{{ if ne $.TargetVersionName `ga` -}}
797801
return hasNodeAffinitiesChanged(oScheduling, newScheduling) ||
798802
hasMaxRunDurationChanged(oScheduling, newScheduling) ||
799-
hasGracefulShutdownChangedWithReboot(d, oScheduling, newScheduling)
803+
hasGracefulShutdownChangedWithReboot(d, oScheduling, newScheduling) ||
804+
hasTerminationTimeChanged(oScheduling, newScheduling)
800805
{{- else }}
801-
return hasNodeAffinitiesChanged(oScheduling, newScheduling) || hasMaxRunDurationChanged(oScheduling, newScheduling)
806+
return (hasNodeAffinitiesChanged(oScheduling, newScheduling) ||
807+
hasMaxRunDurationChanged(oScheduling, newScheduling) ||
808+
hasTerminationTimeChanged(oScheduling, newScheduling))
802809
{{- end }}
803810
}
804811

@@ -853,6 +860,24 @@ func schedulingHasChangeWithoutReboot(d *schema.ResourceData) bool {
853860
return false
854861
}
855862

863+
func hasTerminationTimeChanged(oScheduling, nScheduling map[string]interface{}) bool {
864+
oTerminationTime := oScheduling["termination_time"].(string)
865+
nTerminationTime := nScheduling["termination_time"].(string)
866+
867+
if len(oTerminationTime) == 0 && len(nTerminationTime) == 0 {
868+
return false
869+
}
870+
if len(oTerminationTime) == 0 || len(nTerminationTime) == 0 {
871+
return true
872+
}
873+
874+
if oTerminationTime != nTerminationTime {
875+
return true
876+
}
877+
878+
return false
879+
}
880+
856881
{{ if ne $.TargetVersionName `ga` -}}
857882
func hasGracefulShutdownChangedWithReboot(d *schema.ResourceData, oScheduling, nScheduling map[string]interface{}) bool {
858883
allow_stopping_for_update := d.Get("allow_stopping_for_update").(bool)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package compute
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestHasTerminationTimeChanged(t *testing.T) {
8+
t.Parallel()
9+
cases := map[string]struct {
10+
Old, New map[string]interface{}
11+
Expect bool
12+
}{
13+
"empty": {
14+
Old: map[string]interface{}{"termination_time": ""},
15+
New: map[string]interface{}{"termination_time": ""},
16+
Expect: false,
17+
},
18+
"new": {
19+
Old: map[string]interface{}{"termination_time": ""},
20+
New: map[string]interface{}{"termination_time": "2025-01-31T15:04:05Z"},
21+
Expect: true,
22+
},
23+
"changed": {
24+
Old: map[string]interface{}{"termination_time": "2025-01-30T15:04:05Z"},
25+
New: map[string]interface{}{"termination_time": "2025-01-31T15:04:05Z"},
26+
Expect: true,
27+
},
28+
"same": {
29+
Old: map[string]interface{}{"termination_time": "2025-01-30T15:04:05Z"},
30+
New: map[string]interface{}{"termination_time": "2025-01-30T15:04:05Z"},
31+
Expect: false,
32+
},
33+
}
34+
for tn, tc := range cases {
35+
if hasTerminationTimeChanged(tc.Old, tc.New) != tc.Expect {
36+
t.Errorf("%s: expected %t for whether termination time matched for old = %q, new = %q", tn, tc.Expect, tc.Old, tc.New)
37+
}
38+
}
39+
}

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

+10
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",
@@ -896,6 +897,15 @@ func ResourceComputeInstance() *schema.Resource {
896897
AtLeastOneOf: schedulingKeys,
897898
Description: `Specifies the action GCE should take when SPOT VM is preempted.`,
898899
},
900+
"termination_time": {
901+
Type: schema.TypeString,
902+
Optional: true,
903+
ForceNew: 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+
},
899909
"availability_domain": {
900910
Type: schema.TypeInt,
901911
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 := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 9999, now.Location()).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

+82
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 := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 9999, now.Location()).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,61 @@ 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+
scheduling {
1560+
instance_termination_action = "STOP"
1561+
termination_time = "%s"
1562+
}
1563+
}
1564+
`, templateDisk, template, termination_time, instance, termination_time)
1565+
}
1566+
14851567
func testAccComputeInstanceFromTemplate_overrideMetadataDotStartupScript(instance, template string) string {
14861568
return fmt.Sprintf(`
14871569
data "google_compute_image" "my_image" {

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

+10
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",
@@ -777,6 +778,15 @@ be from 0 to 999,999,999 inclusive.`,
777778
},
778779
},
779780
},
781+
"termination_time": {
782+
Type: schema.TypeString,
783+
Optional: true,
784+
ForceNew: true,
785+
AtLeastOneOf: schedulingKeys,
786+
Description: `Specifies the timestamp, when the instance will be terminated,
787+
in RFC3339 text format. If specified, the instance termination action
788+
will be performed at the termination time.`,
789+
},
780790
{{- if ne $.TargetVersionName "ga" }}
781791
"host_error_timeout_seconds": {
782792
Type: schema.TypeInt,

0 commit comments

Comments
 (0)