Skip to content

Turndown fails on GKE due to empty zone string #52

Description

@michaelmdresser

Observed problem

Turndown fails to run on a GKE cluster with the following config info:

  • cluster-turndown-2.0.1
  • GKE v1.22.8-gke.202

Logs from user environment:

I0706 21:00:43.033004       1 main.go:118] Running Kubecost Turndown on: REDACTED
I0706 21:00:43.059698       1 validator.go:41] Validating Provider...
I0706 21:00:43.061743       1 gkemetadata.go:92] [Error] metadata: GCE metadata "instance/attributes/kube-env" not defined
I0706 21:00:43.063220       1 gkemetadata.go:92] [Error] metadata: GCE metadata "instance/attributes/kube-env" not defined
I0706 21:00:43.063445       1 namedlogger.go:24] [GKEClusterProvider] Loading node pools for: [ProjectID: REDACTED, Zone: , ClusterID: REDACTED]
I0706 21:00:43.192046       1 validator.go:27] [Error]: Failed to load node groups: rpc error: code = InvalidArgument desc = Location "" does not exist.

Source of the error in code

This "Loading node pools" message, followed by the error comes from here in the GKE provider.

// GetNodePools loads all of the provider NodePools in a cluster and returns them.
func (p *GKEClusterProvider) GetNodePools() ([]NodePool, error) {
ctx := context.TODO()
projectID := p.metadata.GetProjectID()
zone := p.metadata.GetMasterZone()
cluster := p.metadata.GetClusterID()
req := &container.ListNodePoolsRequest{Parent: p.getClusterResourcePath()}
p.log.Log("Loading node pools for: [ProjectID: %s, Zone: %s, ClusterID: %s]", projectID, zone, cluster)
resp, err := p.clusterManager.ListNodePools(ctx, req)
if err != nil {
return nil, err
}

The request being executed uses a path generator which is filling in the empty zone string, causing the error.

// gets the fully qualified resource path for the cluster
func (p *GKEClusterProvider) getClusterResourcePath() string {
return fmt.Sprintf("projects/%s/locations/%s/clusters/%s",
p.metadata.GetProjectID(), p.metadata.GetMasterZone(), p.metadata.GetClusterID())
}

We're using md.client.InstanceAttributeValue("kube-env") to get the GCP zone/locaion:

func (md *GKEMetaData) GetMasterZone() string {
z, ok := md.cache[GKEMetaDataMasterZoneKey]
if ok {
return z
}
results, err := md.client.InstanceAttributeValue("kube-env")
if err != nil {
klog.V(1).Infof("[Error] %s", err.Error())
return ""
}

Possible cause

This may not be caused by the absence of kube-env metadata, but rather a lack of access to it. GKE offers "metadata concealment" which specifically calls out kube-env as data to be hidden. kube-env is also mentioned in GKE's NodeMetadata config "SECURE" setting.

Possible solution

Reporting user has suggested a different attribute value to use: cluster-location

curl -L -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/attributes/cluster-location
europe-west2

If this is a stable attribute provided by GKE-provisioned VMs this probably works. We could also investigate using v1/instance/zone as an alternative. It seems to be officially guaranteed on all GCP VMs. Other stable sources of node(pool) location information may be preferable, I just haven't dug deep enough to find them yet.

Other considerations

It is currently unclear if this is affecting all GKE environments or those of only a certain version, region, or configuration (e.g. metadata concealment). Any fixes here should be tested on earlier GKE versions to ensure compatibility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions