Skip to content

Commit cb7a5fb

Browse files
List and cache InstanceGroupManagers instead of getting them separately to improve node pool reconciliation time (#15086) (#10714)
[upstream:634129d661508334a07a089c653708e76cea0c65] Signed-off-by: Modular Magician <[email protected]>
1 parent 995b73d commit cb7a5fb

File tree

1 file changed

+92
-15
lines changed

1 file changed

+92
-15
lines changed

google-beta/services/container/resource_container_node_pool.go

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package container
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"log"
2223
"regexp"
@@ -33,6 +34,7 @@ import (
3334
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
3435
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
3536

37+
compute "google.golang.org/api/compute/v0.beta"
3638
container "google.golang.org/api/container/v1beta1"
3739
)
3840

@@ -60,13 +62,13 @@ func (nodePoolCache *nodePoolCache) get(nodePool string) (*container.NodePool, e
6062
}
6163

6264
func (nodePoolCache *nodePoolCache) refreshIfNeeded(d *schema.ResourceData, config *transport_tpg.Config, userAgent string, nodePoolInfo *NodePoolInformation, name string) error {
65+
nodePoolCache.mutex.Lock()
66+
defer nodePoolCache.mutex.Unlock()
67+
6368
if !nodePoolCache.needsRefresh(nodePoolInfo.fullyQualifiedName(name)) {
6469
return nil
6570
}
6671

67-
nodePoolCache.mutex.Lock()
68-
defer nodePoolCache.mutex.Unlock()
69-
7072
parent := fmt.Sprintf("projects/%s/locations/%s/clusters/%s", nodePoolInfo.project, nodePoolInfo.location, nodePoolInfo.cluster)
7173
clusterNodePoolsListCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.List(parent)
7274
if config.UserProjectOverride {
@@ -88,8 +90,6 @@ func (nodePoolCache *nodePoolCache) refreshIfNeeded(d *schema.ResourceData, conf
8890
}
8991

9092
func (nodePoolCache *nodePoolCache) needsRefresh(nodePool string) bool {
91-
nodePoolCache.mutex.RLock()
92-
defer nodePoolCache.mutex.RUnlock()
9393
np, ok := nodePoolCache.nodePools[nodePool]
9494
if !ok {
9595
return true
@@ -103,11 +103,80 @@ func (nodePoolCache *nodePoolCache) remove(nodePool string) {
103103
delete(nodePoolCache.nodePools, nodePool)
104104
}
105105

106-
var npCache = &nodePoolCache{
107-
nodePools: make(map[string]*nodePoolWithUpdateTime),
108-
ttl: 30 * time.Second,
106+
type instanceGroupManagerWithUpdateTime struct {
107+
instanceGroupManager *compute.InstanceGroupManager
108+
updateTime time.Time
109+
}
110+
111+
type instanceGroupManagerCache struct {
112+
instanceGroupManagers map[string]*instanceGroupManagerWithUpdateTime
113+
ttl time.Duration
114+
mutex sync.RWMutex
109115
}
110116

117+
func (instanceGroupManagerCache *instanceGroupManagerCache) get(fullyQualifiedName string) (*compute.InstanceGroupManager, bool) {
118+
instanceGroupManagerCache.mutex.RLock()
119+
defer instanceGroupManagerCache.mutex.RUnlock()
120+
igm, ok := instanceGroupManagerCache.instanceGroupManagers[fullyQualifiedName]
121+
if !ok {
122+
return nil, false
123+
}
124+
return igm.instanceGroupManager, true
125+
}
126+
127+
func (instanceGroupManagerCache *instanceGroupManagerCache) refreshIfNeeded(d *schema.ResourceData, config *transport_tpg.Config, userAgent string, npName string, igmUrl string) error {
128+
instanceGroupManagerCache.mutex.Lock()
129+
defer instanceGroupManagerCache.mutex.Unlock()
130+
131+
matches := instanceGroupManagerURL.FindStringSubmatch(igmUrl)
132+
if len(matches) < 4 {
133+
return fmt.Errorf("Error reading instance group manager URL %q", igmUrl)
134+
}
135+
136+
if !instanceGroupManagerCache.needsRefresh(matches[0]) {
137+
return nil
138+
}
139+
140+
updateTime := time.Now()
141+
err := config.NewComputeClient(userAgent).InstanceGroupManagers.List(matches[1], matches[2]).Pages(context.Background(), instanceGroupManagerCache.processList(updateTime))
142+
if err != nil {
143+
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("InstanceGroupManagers for node pool %q", npName))
144+
}
145+
return nil
146+
}
147+
148+
func (instanceGroupManagerCache *instanceGroupManagerCache) processList(updateTime time.Time) func(*compute.InstanceGroupManagerList) error {
149+
return func(igmList *compute.InstanceGroupManagerList) error {
150+
for _, instanceGroupManager := range igmList.Items {
151+
fullyQualifiedName := instanceGroupManagerURL.FindString(instanceGroupManager.SelfLink)
152+
instanceGroupManagerCache.instanceGroupManagers[fullyQualifiedName] = &instanceGroupManagerWithUpdateTime{
153+
instanceGroupManager: instanceGroupManager,
154+
updateTime: updateTime,
155+
}
156+
}
157+
return nil
158+
}
159+
}
160+
161+
func (instanceGroupManagerCache *instanceGroupManagerCache) needsRefresh(fullyQualifiedName string) bool {
162+
igm, ok := instanceGroupManagerCache.instanceGroupManagers[fullyQualifiedName]
163+
if !ok {
164+
return true
165+
}
166+
return time.Since(igm.updateTime) > instanceGroupManagerCache.ttl
167+
}
168+
169+
var (
170+
npCache = &nodePoolCache{
171+
nodePools: make(map[string]*nodePoolWithUpdateTime),
172+
ttl: 30 * time.Second,
173+
}
174+
igmCache = &instanceGroupManagerCache{
175+
instanceGroupManagers: make(map[string]*instanceGroupManagerWithUpdateTime),
176+
ttl: 30 * time.Second,
177+
}
178+
)
179+
111180
func ResourceContainerNodePool() *schema.Resource {
112181
return &schema.Resource{
113182
Create: resourceContainerNodePoolCreate,
@@ -790,7 +859,9 @@ func resourceContainerNodePoolRead(d *schema.ResourceData, meta interface{}) err
790859

791860
name := getNodePoolName(d.Id())
792861

793-
npCache.refreshIfNeeded(d, config, userAgent, nodePoolInfo, name)
862+
if err := npCache.refreshIfNeeded(d, config, userAgent, nodePoolInfo, name); err != nil {
863+
return err
864+
}
794865
nodePool, err := npCache.get(nodePoolInfo.fullyQualifiedName(name))
795866
if err != nil {
796867
log.Printf("[WARN] Removing %s because it's gone", fmt.Sprintf("NodePool %q from cluster %q", name, nodePoolInfo.cluster))
@@ -953,7 +1024,9 @@ func resourceContainerNodePoolExists(d *schema.ResourceData, meta interface{}) (
9531024
}
9541025

9551026
name := getNodePoolName(d.Id())
956-
npCache.refreshIfNeeded(d, config, userAgent, nodePoolInfo, name)
1027+
if err := npCache.refreshIfNeeded(d, config, userAgent, nodePoolInfo, name); err != nil {
1028+
return false, err
1029+
}
9571030
_, err = npCache.get(nodePoolInfo.fullyQualifiedName(name))
9581031
if err != nil {
9591032
log.Printf("[WARN] Removing %s because it's gone", fmt.Sprintf("NodePool %q from cluster %q", name, nodePoolInfo.cluster))
@@ -1215,13 +1288,17 @@ func flattenNodePool(d *schema.ResourceData, config *transport_tpg.Config, np *c
12151288
if len(matches) < 4 {
12161289
return nil, fmt.Errorf("Error reading instance group manage URL '%q'", url)
12171290
}
1218-
igm, err := config.NewComputeClient(userAgent).InstanceGroupManagers.Get(matches[1], matches[2], matches[3]).Do()
1219-
if transport_tpg.IsGoogleApiErrorWithCode(err, 404) {
1220-
// The IGM URL in is stale; don't include it
1291+
if strings.HasPrefix("gk3", matches[3]) {
1292+
// IGM is autopilot so we know it will not be found, skip it
12211293
continue
12221294
}
1223-
if err != nil {
1224-
return nil, fmt.Errorf("Error reading instance group manager returned as an instance group URL: %q", err)
1295+
if err := igmCache.refreshIfNeeded(d, config, userAgent, np.Name, url); err != nil {
1296+
return nil, err
1297+
}
1298+
igm, ok := igmCache.get(matches[0])
1299+
if !ok {
1300+
// The IGM URL is stale; don't include it
1301+
continue
12251302
}
12261303
size += int(igm.TargetSize)
12271304
igmUrls = append(igmUrls, url)

0 commit comments

Comments
 (0)