Skip to content

Commit 1e6799e

Browse files
modular-magicianchrisst
authored andcommitted
Allow count=0 for guest_accelerators (hashicorp#851)
Signed-off-by: Modular Magician <[email protected]>
1 parent 40d2c97 commit 1e6799e

4 files changed

+199
-1
lines changed

google-beta/resource_container_cluster.go

+62-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/hashicorp/errwrap"
1212
"github.com/hashicorp/go-version"
13+
"github.com/hashicorp/terraform/helper/customdiff"
1314
"github.com/hashicorp/terraform/helper/resource"
1415
"github.com/hashicorp/terraform/helper/schema"
1516
"github.com/hashicorp/terraform/helper/validation"
@@ -54,7 +55,10 @@ func resourceContainerCluster() *schema.Resource {
5455
Update: resourceContainerClusterUpdate,
5556
Delete: resourceContainerClusterDelete,
5657

57-
CustomizeDiff: resourceContainerClusterIpAllocationCustomizeDiff,
58+
CustomizeDiff: customdiff.All(
59+
resourceContainerClusterIpAllocationCustomizeDiff,
60+
resourceNodeConfigEmptyGuestAccelerator,
61+
),
5862

5963
Timeouts: &schema.ResourceTimeout{
6064
Create: schema.DefaultTimeout(30 * time.Minute),
@@ -753,6 +757,63 @@ func resourceContainerCluster() *schema.Resource {
753757
}
754758
}
755759

760+
// Setting a guest accelerator block to count=0 is the equivalent to omitting the block: it won't get
761+
// sent to the API and it won't be stored in state. This diffFunc will try to compare the old + new state
762+
// by only comparing the blocks with a positive count and ignoring those with count=0
763+
//
764+
// One quirk with this approach is that configs with mixed count=0 and count>0 accelerator blocks will
765+
// show a confusing diff if one of there are config changes that result in a legitimate diff as the count=0
766+
// blocks will not be in state.
767+
//
768+
// This could also be modelled by setting `guest_accelerator = []` in the config. However since the
769+
// previous syntax requires that schema.SchemaConfigModeAttr is set on the field it is advisable that
770+
// we have a work around for removing guest accelerators. Also Terraform 0.11 cannot use dynamic blocks
771+
// so this isn't a solution for module authors who want to dynamically omit guest accelerators
772+
// See https://github.com/terraform-providers/terraform-provider-google/issues/3786
773+
func resourceNodeConfigEmptyGuestAccelerator(diff *schema.ResourceDiff, meta interface{}) error {
774+
old, new := diff.GetChange("node_config.0.guest_accelerator")
775+
oList := old.([]interface{})
776+
nList := new.([]interface{})
777+
778+
if len(nList) == len(oList) || len(nList) == 0 {
779+
return nil
780+
}
781+
var hasAcceleratorWithEmptyCount bool
782+
// the list of blocks in a desired state with count=0 accelerator blocks in it
783+
// will be longer than the current state.
784+
// this index tracks the location of positive count accelerator blocks
785+
index := 0
786+
for i, item := range nList {
787+
accel := item.(map[string]interface{})
788+
if accel["count"].(int) == 0 {
789+
hasAcceleratorWithEmptyCount = true
790+
// Ignore any 'empty' accelerators because they aren't sent to the API
791+
continue
792+
}
793+
if index >= len(oList) {
794+
// Return early if there are more positive count accelerator blocks in the desired state
795+
// than the current state since a difference in 'legit' blocks is a valid diff.
796+
// This will prevent array index overruns
797+
return nil
798+
}
799+
if !reflect.DeepEqual(nList[i], oList[index]) {
800+
return nil
801+
}
802+
index += 1
803+
}
804+
805+
if hasAcceleratorWithEmptyCount && index == len(oList) {
806+
// If the number of count>0 blocks match, there are count=0 blocks present and we
807+
// haven't already returned due to a legitimate diff
808+
err := diff.Clear("node_config.0.guest_accelerator")
809+
if err != nil {
810+
return err
811+
}
812+
}
813+
814+
return nil
815+
}
816+
756817
func resourceContainerClusterIpAllocationCustomizeDiff(diff *schema.ResourceDiff, meta interface{}) error {
757818
// separate func to allow unit testing
758819
return resourceContainerClusterIpAllocationCustomizeDiffFunc(diff)

google-beta/resource_container_node_pool.go

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77
"time"
88

9+
"github.com/hashicorp/terraform/helper/customdiff"
910
"github.com/hashicorp/terraform/helper/resource"
1011
"github.com/hashicorp/terraform/helper/schema"
1112
"github.com/hashicorp/terraform/helper/validation"
@@ -33,6 +34,10 @@ func resourceContainerNodePool() *schema.Resource {
3334
State: resourceContainerNodePoolStateImporter,
3435
},
3536

37+
CustomizeDiff: customdiff.All(
38+
resourceNodeConfigEmptyGuestAccelerator,
39+
),
40+
3641
Schema: mergeSchemas(
3742
schemaNodePool,
3843
map[string]*schema.Schema{

google-beta/resource_container_node_pool_test.go

+131
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,53 @@ func TestAccContainerNodePool_012_ConfigModeAttr(t *testing.T) {
570570
})
571571
}
572572

573+
func TestAccContainerNodePool_EmptyGuestAccelerator(t *testing.T) {
574+
t.Parallel()
575+
576+
cluster := fmt.Sprintf("tf-nodepool-test-%s", acctest.RandString(10))
577+
np := fmt.Sprintf("tf-nodepool-test-%s", acctest.RandString(10))
578+
579+
resource.Test(t, resource.TestCase{
580+
PreCheck: func() { testAccPreCheck(t) },
581+
Providers: testAccProviders,
582+
CheckDestroy: testAccCheckContainerNodePoolDestroy,
583+
Steps: []resource.TestStep{
584+
{
585+
// Test alternative way to specify an empty node pool
586+
Config: testAccContainerNodePool_EmptyGuestAccelerator(cluster, np),
587+
},
588+
{
589+
ResourceName: "google_container_node_pool.np",
590+
ImportState: true,
591+
ImportStateVerify: true,
592+
ImportStateVerifyIgnore: []string{"max_pods_per_node"},
593+
},
594+
{
595+
// Test alternative way to specify an empty node pool
596+
Config: testAccContainerNodePool_PartialEmptyGuestAccelerator(cluster, np, 1),
597+
},
598+
{
599+
ResourceName: "google_container_node_pool.np",
600+
ImportState: true,
601+
ImportStateVerify: true,
602+
ImportStateVerifyIgnore: []string{"max_pods_per_node"},
603+
},
604+
{
605+
// Assert that changes in count from 1 result in a diff
606+
Config: testAccContainerNodePool_PartialEmptyGuestAccelerator(cluster, np, 2),
607+
ExpectNonEmptyPlan: true,
608+
PlanOnly: true,
609+
},
610+
{
611+
// Assert that adding another accelerator block will also result in a diff
612+
Config: testAccContainerNodePool_PartialEmptyGuestAccelerator2(cluster, np),
613+
ExpectNonEmptyPlan: true,
614+
PlanOnly: true,
615+
},
616+
},
617+
})
618+
}
619+
573620
func testAccCheckContainerNodePoolDestroy(s *terraform.State) error {
574621
config := testAccProvider.Meta().(*Config)
575622

@@ -1163,3 +1210,87 @@ resource "google_container_node_pool" "np" {
11631210
}
11641211
}`, cluster, np)
11651212
}
1213+
1214+
func testAccContainerNodePool_EmptyGuestAccelerator(cluster, np string) string {
1215+
return fmt.Sprintf(`
1216+
resource "google_container_cluster" "cluster" {
1217+
name = "%s"
1218+
zone = "us-central1-f"
1219+
initial_node_count = 3
1220+
}
1221+
1222+
resource "google_container_node_pool" "np" {
1223+
name = "%s"
1224+
zone = "us-central1-f"
1225+
cluster = "${google_container_cluster.cluster.name}"
1226+
initial_node_count = 1
1227+
1228+
node_config {
1229+
guest_accelerator {
1230+
count = 0
1231+
type = "nvidia-tesla-p100"
1232+
}
1233+
}
1234+
}`, cluster, np)
1235+
}
1236+
1237+
func testAccContainerNodePool_PartialEmptyGuestAccelerator(cluster, np string, count int) string {
1238+
return fmt.Sprintf(`
1239+
resource "google_container_cluster" "cluster" {
1240+
name = "%s"
1241+
zone = "us-central1-f"
1242+
initial_node_count = 3
1243+
}
1244+
1245+
resource "google_container_node_pool" "np" {
1246+
name = "%s"
1247+
zone = "us-central1-f"
1248+
cluster = "${google_container_cluster.cluster.name}"
1249+
initial_node_count = 1
1250+
1251+
node_config {
1252+
guest_accelerator {
1253+
count = 0
1254+
type = "nvidia-tesla-p100"
1255+
}
1256+
1257+
guest_accelerator {
1258+
count = %d
1259+
type = "nvidia-tesla-p100"
1260+
}
1261+
}
1262+
}`, cluster, np, count)
1263+
}
1264+
1265+
func testAccContainerNodePool_PartialEmptyGuestAccelerator2(cluster, np string) string {
1266+
return fmt.Sprintf(`
1267+
resource "google_container_cluster" "cluster" {
1268+
name = "%s"
1269+
zone = "us-central1-f"
1270+
initial_node_count = 3
1271+
}
1272+
1273+
resource "google_container_node_pool" "np" {
1274+
name = "%s"
1275+
zone = "us-central1-f"
1276+
cluster = "${google_container_cluster.cluster.name}"
1277+
initial_node_count = 1
1278+
1279+
node_config {
1280+
guest_accelerator {
1281+
count = 0
1282+
type = "nvidia-tesla-p100"
1283+
}
1284+
1285+
guest_accelerator {
1286+
count = 1
1287+
type = "nvidia-tesla-p100"
1288+
}
1289+
1290+
guest_accelerator {
1291+
count = 1
1292+
type = "nvidia-tesla-p9000"
1293+
}
1294+
}
1295+
}`, cluster, np)
1296+
}

google-beta/resource_sql_database_instance_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/hashicorp/terraform/helper/acctest"
1010
"github.com/hashicorp/terraform/helper/resource"
1111
"github.com/hashicorp/terraform/terraform"
12+
1213
sqladmin "google.golang.org/api/sqladmin/v1beta4"
1314
)
1415

0 commit comments

Comments
 (0)