Skip to content

Commit 9f559c7

Browse files
danawillowDmitry Vlasov
authored and
Dmitry Vlasov
committed
Add support for node pool autoscaling (hashicorp#157)
* add node pool autoscaling * docs for node pool autoscaling * remove enabled attribute * remove enabled from docs
1 parent c39d246 commit 9f559c7

File tree

3 files changed

+225
-5
lines changed

3 files changed

+225
-5
lines changed

google/resource_container_node_pool.go

+106
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ func resourceContainerNodePool() *schema.Resource {
1414
return &schema.Resource{
1515
Create: resourceContainerNodePoolCreate,
1616
Read: resourceContainerNodePoolRead,
17+
Update: resourceContainerNodePoolUpdate,
1718
Delete: resourceContainerNodePoolDelete,
1819
Exists: resourceContainerNodePoolExists,
1920

@@ -55,6 +56,41 @@ func resourceContainerNodePool() *schema.Resource {
5556
Required: true,
5657
ForceNew: true,
5758
},
59+
60+
"autoscaling": &schema.Schema{
61+
Type: schema.TypeList,
62+
Optional: true,
63+
MaxItems: 1,
64+
Elem: &schema.Resource{
65+
Schema: map[string]*schema.Schema{
66+
"min_node_count": &schema.Schema{
67+
Type: schema.TypeInt,
68+
Required: true,
69+
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
70+
value := v.(int)
71+
72+
if value < 1 {
73+
errors = append(errors, fmt.Errorf("%q must be >=1", k))
74+
}
75+
return
76+
},
77+
},
78+
79+
"max_node_count": &schema.Schema{
80+
Type: schema.TypeInt,
81+
Required: true,
82+
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
83+
value := v.(int)
84+
85+
if value < 1 {
86+
errors = append(errors, fmt.Errorf("%q must be >=1", k))
87+
}
88+
return
89+
},
90+
},
91+
},
92+
},
93+
},
5894
},
5995
}
6096
}
@@ -85,6 +121,15 @@ func resourceContainerNodePoolCreate(d *schema.ResourceData, meta interface{}) e
85121
InitialNodeCount: int64(nodeCount),
86122
}
87123

124+
if v, ok := d.GetOk("autoscaling"); ok {
125+
autoscaling := v.([]interface{})[0].(map[string]interface{})
126+
nodePool.Autoscaling = &container.NodePoolAutoscaling{
127+
Enabled: true,
128+
MinNodeCount: int64(autoscaling["min_node_count"].(int)),
129+
MaxNodeCount: int64(autoscaling["max_node_count"].(int)),
130+
}
131+
}
132+
88133
req := &container.CreateNodePoolRequest{
89134
NodePool: nodePool,
90135
}
@@ -130,9 +175,70 @@ func resourceContainerNodePoolRead(d *schema.ResourceData, meta interface{}) err
130175
d.Set("name", nodePool.Name)
131176
d.Set("initial_node_count", nodePool.InitialNodeCount)
132177

178+
autoscaling := []map[string]interface{}{}
179+
if nodePool.Autoscaling != nil && nodePool.Autoscaling.Enabled {
180+
autoscaling = []map[string]interface{}{
181+
map[string]interface{}{
182+
"min_node_count": nodePool.Autoscaling.MinNodeCount,
183+
"max_node_count": nodePool.Autoscaling.MaxNodeCount,
184+
},
185+
}
186+
}
187+
d.Set("autoscaling", autoscaling)
188+
133189
return nil
134190
}
135191

192+
func resourceContainerNodePoolUpdate(d *schema.ResourceData, meta interface{}) error {
193+
config := meta.(*Config)
194+
195+
project, err := getProject(d, config)
196+
if err != nil {
197+
return err
198+
}
199+
200+
zone := d.Get("zone").(string)
201+
name := d.Get("name").(string)
202+
cluster := d.Get("cluster").(string)
203+
204+
if d.HasChange("autoscaling") {
205+
update := &container.ClusterUpdate{
206+
DesiredNodePoolId: name,
207+
}
208+
if v, ok := d.GetOk("autoscaling"); ok {
209+
autoscaling := v.([]interface{})[0].(map[string]interface{})
210+
update.DesiredNodePoolAutoscaling = &container.NodePoolAutoscaling{
211+
Enabled: true,
212+
MinNodeCount: int64(autoscaling["min_node_count"].(int)),
213+
MaxNodeCount: int64(autoscaling["max_node_count"].(int)),
214+
}
215+
} else {
216+
update.DesiredNodePoolAutoscaling = &container.NodePoolAutoscaling{
217+
Enabled: false,
218+
}
219+
}
220+
221+
req := &container.UpdateClusterRequest{
222+
Update: update,
223+
}
224+
op, err := config.clientContainer.Projects.Zones.Clusters.Update(
225+
project, zone, cluster, req).Do()
226+
if err != nil {
227+
return err
228+
}
229+
230+
// Wait until it's updated
231+
waitErr := containerOperationWait(config, op, project, zone, "updating GKE node pool", 10, 2)
232+
if waitErr != nil {
233+
return waitErr
234+
}
235+
236+
log.Printf("[INFO] Updated autoscaling in Node Pool %s", d.Id())
237+
}
238+
239+
return resourceContainerNodePoolRead(d, meta)
240+
}
241+
136242
func resourceContainerNodePoolDelete(d *schema.ResourceData, meta interface{}) error {
137243
config := meta.(*Config)
138244

google/resource_container_node_pool_test.go

+109-5
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,47 @@ import (
1111
)
1212

1313
func TestAccContainerNodePool_basic(t *testing.T) {
14+
cluster := fmt.Sprintf("tf-nodepool-test-%s", acctest.RandString(10))
15+
np := fmt.Sprintf("tf-nodepool-test-%s", acctest.RandString(10))
16+
1417
resource.Test(t, resource.TestCase{
1518
PreCheck: func() { testAccPreCheck(t) },
1619
Providers: testAccProviders,
1720
CheckDestroy: testAccCheckContainerNodePoolDestroy,
1821
Steps: []resource.TestStep{
1922
resource.TestStep{
20-
Config: testAccContainerNodePool_basic,
23+
Config: testAccContainerNodePool_basic(cluster, np),
24+
Check: resource.ComposeTestCheckFunc(
25+
testAccCheckContainerNodePoolMatches("google_container_node_pool.np"),
26+
),
27+
},
28+
},
29+
})
30+
}
31+
32+
func TestAccContainerNodePool_autoscaling(t *testing.T) {
33+
cluster := fmt.Sprintf("tf-nodepool-test-%s", acctest.RandString(10))
34+
np := fmt.Sprintf("tf-nodepool-test-%s", acctest.RandString(10))
35+
36+
resource.Test(t, resource.TestCase{
37+
PreCheck: func() { testAccPreCheck(t) },
38+
Providers: testAccProviders,
39+
CheckDestroy: testAccCheckContainerNodePoolDestroy,
40+
Steps: []resource.TestStep{
41+
resource.TestStep{
42+
Config: testAccContainerNodePool_autoscaling(cluster, np),
43+
Check: resource.ComposeTestCheckFunc(
44+
testAccCheckContainerNodePoolMatches("google_container_node_pool.np"),
45+
),
46+
},
47+
resource.TestStep{
48+
Config: testAccContainerNodePool_updateAutoscaling(cluster, np),
49+
Check: resource.ComposeTestCheckFunc(
50+
testAccCheckContainerNodePoolMatches("google_container_node_pool.np"),
51+
),
52+
},
53+
resource.TestStep{
54+
Config: testAccContainerNodePool_basic(cluster, np),
2155
Check: resource.ComposeTestCheckFunc(
2256
testAccCheckContainerNodePoolMatches("google_container_node_pool.np"),
2357
),
@@ -77,13 +111,32 @@ func testAccCheckContainerNodePoolMatches(n string) resource.TestCheckFunc {
77111
return fmt.Errorf("Mismatched initialNodeCount. TF State: %s. GCP State: %d",
78112
attributes["initial_node_count"], found.InitialNodeCount)
79113
}
114+
115+
tfAS := attributes["autoscaling.#"] == "1"
116+
if gcpAS := found.Autoscaling != nil && found.Autoscaling.Enabled == true; tfAS != gcpAS {
117+
return fmt.Errorf("Mismatched autoscaling status. TF State: %t. GCP State: %t", tfAS, gcpAS)
118+
}
119+
if tfAS {
120+
if tf := attributes["autoscaling.0.min_node_count"]; strconv.FormatInt(found.Autoscaling.MinNodeCount, 10) != tf {
121+
return fmt.Errorf("Mismatched Autoscaling.MinNodeCount. TF State: %s. GCP State: %d",
122+
tf, found.Autoscaling.MinNodeCount)
123+
}
124+
125+
if tf := attributes["autoscaling.0.max_node_count"]; strconv.FormatInt(found.Autoscaling.MaxNodeCount, 10) != tf {
126+
return fmt.Errorf("Mismatched Autoscaling.MaxNodeCount. TF State: %s. GCP State: %d",
127+
tf, found.Autoscaling.MaxNodeCount)
128+
}
129+
130+
}
131+
80132
return nil
81133
}
82134
}
83135

84-
var testAccContainerNodePool_basic = fmt.Sprintf(`
136+
func testAccContainerNodePool_basic(cluster, np string) string {
137+
return fmt.Sprintf(`
85138
resource "google_container_cluster" "cluster" {
86-
name = "tf-cluster-nodepool-test-%s"
139+
name = "%s"
87140
zone = "us-central1-a"
88141
initial_node_count = 3
89142
@@ -94,8 +147,59 @@ resource "google_container_cluster" "cluster" {
94147
}
95148
96149
resource "google_container_node_pool" "np" {
97-
name = "tf-nodepool-test-%s"
150+
name = "%s"
98151
zone = "us-central1-a"
99152
cluster = "${google_container_cluster.cluster.name}"
100153
initial_node_count = 2
101-
}`, acctest.RandString(10), acctest.RandString(10))
154+
}`, cluster, np)
155+
}
156+
157+
func testAccContainerNodePool_autoscaling(cluster, np string) string {
158+
return fmt.Sprintf(`
159+
resource "google_container_cluster" "cluster" {
160+
name = "%s"
161+
zone = "us-central1-a"
162+
initial_node_count = 3
163+
164+
master_auth {
165+
username = "mr.yoda"
166+
password = "adoy.rm"
167+
}
168+
}
169+
170+
resource "google_container_node_pool" "np" {
171+
name = "%s"
172+
zone = "us-central1-a"
173+
cluster = "${google_container_cluster.cluster.name}"
174+
initial_node_count = 2
175+
autoscaling {
176+
min_node_count = 1
177+
max_node_count = 3
178+
}
179+
}`, cluster, np)
180+
}
181+
182+
func testAccContainerNodePool_updateAutoscaling(cluster, np string) string {
183+
return fmt.Sprintf(`
184+
resource "google_container_cluster" "cluster" {
185+
name = "%s"
186+
zone = "us-central1-a"
187+
initial_node_count = 3
188+
189+
master_auth {
190+
username = "mr.yoda"
191+
password = "adoy.rm"
192+
}
193+
}
194+
195+
resource "google_container_node_pool" "np" {
196+
name = "%s"
197+
zone = "us-central1-a"
198+
cluster = "${google_container_cluster.cluster.name}"
199+
initial_node_count = 2
200+
autoscaling {
201+
min_node_count = 1
202+
max_node_count = 5
203+
}
204+
}`, cluster, np)
205+
}

website/docs/r/container_node_pool.html.markdown

+10
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,13 @@ resource "google_container_cluster" "primary" {
6767

6868
* `name_prefix` - (Optional) Creates a unique name for the node pool beginning
6969
with the specified prefix. Conflicts with `name`.
70+
71+
* `autoscaling` - (Optional) Configuration required by cluster autoscaler to adjust
72+
the size of the node pool to the current cluster usage. Structure is documented below.
73+
74+
The `autoscaling` block supports:
75+
76+
* `minNodeCount` - (Required) Minimum number of nodes in the NodePool. Must be >=1 and
77+
<= `maxNodeCount`.
78+
79+
* `maxNodeCount` - (Required) Maximum number of nodes in the NodePool. Must be >= minNodeCount.

0 commit comments

Comments
 (0)