metadata: try different strategies for detecting OnGCE · googleapis/google-cloud-go@f95c875

2 min read Original article ↗

@@ -34,6 +34,9 @@ import (

3434

"google.golang.org/cloud/internal"

3535

)

363637+

// metadataIP is the documented metadata server IP address.

38+

const metadataIP = "169.254.169.254"

39+3740

type cachedValue struct {

3841

k string

3942

trim bool

@@ -52,18 +55,18 @@ var (

5255

Transport: &internal.Transport{

5356

Base: &http.Transport{

5457

Dial: (&net.Dialer{

55-

Timeout: 750 * time.Millisecond,

58+

Timeout: 2 * time.Second,

5659

KeepAlive: 30 * time.Second,

5760

}).Dial,

58-

ResponseHeaderTimeout: 750 * time.Millisecond,

61+

ResponseHeaderTimeout: 2 * time.Second,

5962

},

6063

},

6164

}

6265

subscribeClient = &http.Client{

6366

Transport: &internal.Transport{

6467

Base: &http.Transport{

6568

Dial: (&net.Dialer{

66-

Timeout: 750 * time.Millisecond,

69+

Timeout: 2 * time.Millisecond,

6770

KeepAlive: 30 * time.Second,

6871

}).Dial,

6972

},

@@ -111,7 +114,7 @@ func getETag(client *http.Client, suffix string) (value, etag string, err error)

111114

// know the search suffix for "metadata" is

112115

// ".google.internal", and this IP address is documented as

113116

// being stable anyway.

114-

host = "169.254.169.254"

117+

host = metadataIP

115118

}

116119

url := "http://" + host + "/computeMetadata/v1/" + suffix

117120

req, _ := http.NewRequest("GET", url, nil)

@@ -171,17 +174,42 @@ func OnGCE() bool {

171174

return onGCE.v

172175

}

173176

onGCE.set = true

174-175-

// We use the DNS name of the metadata service here instead of the IP address

176-

// because we expect that to fail faster in the not-on-GCE case.

177-

res, err := metaClient.Get("http://metadata.google.internal")

178-

if err != nil {

179-

return false

180-

}

181-

onGCE.v = res.Header.Get("Metadata-Flavor") == "Google"

177+

onGCE.v = testOnGCE()

182178

return onGCE.v

183179

}

184180181+

func testOnGCE() bool {

182+

cancel := make(chan struct{})

183+

defer close(cancel)

184+185+

resc := make(chan bool, 2)

186+187+

// Try two strategies in parallel.

188+

// See https://github.com/GoogleCloudPlatform/gcloud-golang/issues/194

189+

go func() {

190+

req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)

191+

req.Cancel = cancel

192+

res, err := metaClient.Do(req)

193+

if err != nil {

194+

resc <- false

195+

return

196+

}

197+

defer res.Body.Close()

198+

resc <- res.Header.Get("Metadata-Flavor") == "Google"

199+

}()

200+201+

go func() {

202+

addrs, err := net.LookupHost("metadata.google.internal")

203+

if err != nil || len(addrs) == 0 {

204+

resc <- false

205+

return

206+

}

207+

resc <- strsContains(addrs, metadataIP)

208+

}()

209+210+

return <-resc

211+

}

212+185213

// Subscribe subscribes to a value from the metadata service.

186214

// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".

187215

// The suffix may contain query parameters.

@@ -342,3 +370,12 @@ func Scopes(serviceAccount string) ([]string, error) {

342370

}

343371

return lines("instance/service-accounts/" + serviceAccount + "/scopes")

344372

}

373+374+

func strsContains(ss []string, s string) bool {

375+

for _, v := range ss {

376+

if v == s {

377+

return true

378+

}

379+

}

380+

return false

381+

}