github.com/google/cloudprober@v0.11.3/sysvars/sysvars_gce.go (about) 1 // Copyright 2017 The Cloudprober Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sysvars 16 17 import ( 18 "context" 19 "fmt" 20 "os" 21 "strings" 22 23 "cloud.google.com/go/compute/metadata" 24 md "github.com/google/cloudprober/common/metadata" 25 "github.com/google/cloudprober/logger" 26 compute "google.golang.org/api/compute/v1" 27 ) 28 29 // maxNICs is the number of NICs allowed on a VM. Used by addGceNicInfo. 30 var maxNICs = 8 31 32 // commonGCEGKEVars sets the variables that are available on both - GCE and GKE 33 // metadata. This list is based on 34 // https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#gke_mds 35 func commonGCEGKEVars(vars map[string]string) error { 36 metadataFuncs := map[string]func() (string, error){ 37 "project": metadata.ProjectID, 38 "project_id": metadata.NumericProjectID, 39 "instance_id": metadata.InstanceID, 40 } 41 42 for k, fn := range metadataFuncs { 43 v, err := fn() 44 if err != nil { 45 return fmt.Errorf("sysvars_gce: error while getting %s from metadata: %v", k, err) 46 } 47 vars[k] = v 48 } 49 50 return nil 51 } 52 53 var gceVars = func(vars map[string]string, l *logger.Logger) (bool, error) { 54 onGCE := metadata.OnGCE() 55 if !onGCE { 56 return false, nil 57 } 58 59 if err := commonGCEGKEVars(vars); err != nil { 60 return onGCE, err 61 } 62 63 // If running on Kubernetes, don't bother setting rest of the variables. They 64 // may or may not be available. Also, they may not be very relevant for 65 // Kubernetes use case. 66 if md.IsKubernetes() { 67 // TODO(manugarg): See if we want to try setting variables on Kubernetes, 68 // e.g. pod-name, cluster-name, cluser-location, etc. 69 vars["namespace"] = md.KubernetesNamespace() 70 return onGCE, nil 71 } 72 73 // Helper function we use below. 74 getLastToken := func(value string) string { 75 tokens := strings.Split(value, "/") 76 return tokens[len(tokens)-1] 77 } 78 79 for _, k := range []string{ 80 "zone", 81 "internal_ip", 82 "external_ip", 83 "instance_template", 84 "machine_type", 85 } { 86 var v string 87 var err error 88 switch k { 89 case "zone": 90 v, err = metadata.Zone() 91 case "internal_ip": 92 v, err = metadata.InternalIP() 93 case "external_ip": 94 v, err = metadata.ExternalIP() 95 case "instance_template": 96 // `instance_template` may not be defined, depending on how cloudprober 97 // was deployed. If an error is returned when fetching the metadata, 98 // just fall back "undefined". 99 v, err = metadata.InstanceAttributeValue("instance-template") 100 if err != nil { 101 l.Infof("No instance_template found. Defaulting to undefined.") 102 v = "undefined" 103 err = nil 104 } else { 105 v = getLastToken(v) 106 } 107 case "machine_type": 108 v, err = metadata.Get("instance/machine-type") 109 if err != nil { 110 l.Infof("Could not fetch machine type. Defaulting to undefined.") 111 v = "undefined" 112 err = nil 113 } else { 114 v = getLastToken(v) 115 } 116 default: 117 return onGCE, fmt.Errorf("sysvars_gce: unknown variable key %q", k) 118 } 119 if err != nil { 120 return onGCE, fmt.Errorf("sysvars_gce: error while getting %s from metadata: %v", k, err) 121 } 122 vars[k] = v 123 } 124 125 zoneParts := strings.Split(vars["zone"], "-") 126 vars["region"] = strings.Join(zoneParts[0:len(zoneParts)-1], "-") 127 addGceNicInfo(vars, l) 128 129 // Fetching instance name fails on some versions of GKE. 130 if instance, err := metadata.InstanceName(); err != nil { 131 l.Warningf("Error getting instance name on GCE, using HOSTNAME environment variable: %v", err) 132 vars["instance"] = os.Getenv("HOSTNAME") 133 } else { 134 vars["instance"] = instance 135 } 136 137 labels, err := labelsFromGCE(vars["project"], vars["zone"], vars["instance"]) 138 if err != nil { 139 return onGCE, err 140 } 141 142 for k, v := range labels { 143 // Adds GCE labels to the dictionary with a 'label_' prefix so they can be 144 // referenced in the cfg file. 145 vars["label_"+k] = v 146 147 } 148 return onGCE, nil 149 } 150 151 func labelsFromGCE(project, zone, instance string) (map[string]string, error) { 152 ctx := context.Background() 153 computeService, err := compute.NewService(ctx) 154 if err != nil { 155 return nil, fmt.Errorf("error creating compute service to get instance labels: %v", err) 156 } 157 158 // Following call requires read-only access to compute API. We don't want to 159 // fail initialization if that happens. 160 i, err := computeService.Instances.Get(project, zone, instance).Context(ctx).Do() 161 if err != nil { 162 l.Warningf("sysvars_gce: Error while fetching the instance resource using GCE API: %v. Continuing without labels info.", err) 163 return nil, nil 164 } 165 166 return i.Labels, nil 167 } 168 169 // addGceNicInfo adds nic information to vars. 170 // The following information is added for each nic. 171 // - Primary IP, if one is assigned to nic. 172 // If no primary IP is found, assume that NIC doesn't exist. 173 // - IPv6 IP, if one is assigned to nic. 174 // If nic0 has IPv6 IP, then assign ip to key: "internal_ipv6_ip" 175 // - External IP, if one is assigned to nic. 176 // - An IP alias, if any IP alias ranges are assigned to nic. 177 // 178 // See the following document for more information on metadata. 179 // https://cloud.google.com/compute/docs/storing-retrieving-metadata 180 func addGceNicInfo(vars map[string]string, l *logger.Logger) { 181 for i := 0; i < maxNICs; i++ { 182 k := fmt.Sprintf("instance/network-interfaces/%v/ip", i) 183 v, err := metadata.Get(k) 184 // If there is no private IP for NIC, NIC doesn't exist. 185 if err != nil { 186 continue 187 } 188 vars[fmt.Sprintf("nic_%d_ip", i)] = v 189 190 k = fmt.Sprintf("instance/network-interfaces/%v/ipv6s", i) 191 v, err = metadata.Get(k) 192 if err != nil { 193 l.Debugf("VM does not have ipv6 ip on interface# %d", i) 194 } else { 195 v = strings.TrimSpace(v) 196 vars[k] = v 197 if i == 0 { 198 vars["internal_ipv6_ip"] = v 199 } 200 } 201 } 202 }