Skip to content

Commit 6d6d183

Browse files
committed
cmd/buildlet: retrieve metadata on EC2 instances
User defined metadata on EC2 instances are stored in the user data section of the metadata. This CL enables the retrieval of that metadata and retrieval of that data via already defined keys. Updates golang/go#36841 Change-Id: I485b36676ca636bbf2caef9af3fd7174f93dff5e Reviewed-on: https://go-review.googlesource.com/c/build/+/233800 Run-TryBot: Carlos Amedee <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Alexander Rakoczy <[email protected]>
1 parent 6900034 commit 6d6d183

File tree

1 file changed

+71
-6
lines changed

1 file changed

+71
-6
lines changed

cmd/buildlet/buildlet.go

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import (
4141
"time"
4242

4343
"cloud.google.com/go/compute/metadata"
44+
"github.com/aws/aws-sdk-go/aws/ec2metadata"
45+
"github.com/aws/aws-sdk-go/aws/session"
4446
"golang.org/x/build/buildlet"
4547
"golang.org/x/build/pargzip"
4648
)
@@ -84,12 +86,13 @@ func defaultListenAddr() string {
8486
// root).
8587
return ":5936"
8688
}
87-
if !metadata.OnGCE() {
89+
// check if if env is dev
90+
if !metadata.OnGCE() && !onEC2() {
8891
return "localhost:5936"
8992
}
9093
// In production, default to port 80 or 443, depending on
9194
// whether TLS is configured.
92-
if metadataValue("tls-cert") != "" {
95+
if metadataValue(metaKeyTLSCert) != "" {
9396
return ":443"
9497
}
9598
return ":80"
@@ -109,6 +112,12 @@ var (
109112
processGoCacheEnv string
110113
)
111114

115+
const (
116+
metaKeyPassword = "password"
117+
metaKeyTLSCert = "tls-cert"
118+
metaKeyTLSkey = "tls-key"
119+
)
120+
112121
func main() {
113122
builderEnv := os.Getenv("GO_BUILDER_ENV")
114123

@@ -164,7 +173,7 @@ func main() {
164173
*listenAddr = v
165174
}
166175

167-
if !onGCE && !isReverse && !strings.HasPrefix(*listenAddr, "localhost:") {
176+
if !onGCE && !isReverse && !onEC2() && !strings.HasPrefix(*listenAddr, "localhost:") {
168177
log.Printf("** WARNING *** This server is unsafe and offers no security. Be careful.")
169178
}
170179
if onGCE {
@@ -215,7 +224,7 @@ func main() {
215224

216225
var password string
217226
if !isReverse {
218-
password = metadataValue("password")
227+
password = metadataValue(metaKeyPassword)
219228
}
220229
requireAuth := func(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
221230
return requirePasswordHandler{http.HandlerFunc(handler), password}
@@ -254,7 +263,7 @@ func initGorootBootstrap() {
254263
}
255264

256265
func listenForCoordinator() {
257-
tlsCert, tlsKey := metadataValue("tls-cert"), metadataValue("tls-key")
266+
tlsCert, tlsKey := metadataValue(metaKeyTLSCert), metadataValue(metaKeyTLSkey)
258267
if (tlsCert == "") != (tlsKey == "") {
259268
log.Fatalf("tls-cert and tls-key must both be supplied, or neither.")
260269
}
@@ -310,10 +319,48 @@ var registerSignal func(chan<- os.Signal)
310319

311320
var inKube = os.Getenv("KUBERNETES_SERVICE_HOST") != ""
312321

322+
var (
323+
// ec2UD contains a copy of the EC2 vm user data retrieved from the metadata.
324+
ec2UD *buildlet.EC2UserData
325+
// ec2MdC is an EC2 metadata client.
326+
ec2MdC *ec2metadata.EC2Metadata
327+
)
328+
329+
// onEC2 evaluates if the buildlet is running on an EC2 instance.
330+
func onEC2() bool {
331+
if ec2MdC != nil {
332+
return ec2MdC.Available()
333+
}
334+
ses, err := session.NewSession()
335+
if err != nil {
336+
log.Printf("unable to create aws session: %s", err)
337+
return false
338+
}
339+
ec2MdC = ec2metadata.New(ses)
340+
return ec2MdC.Available()
341+
}
342+
343+
// mdValueFromUserData maps a metadata key value into the corresponding
344+
// EC2UserData value. If a mapping is not found, an empty string is returned.
345+
func mdValueFromUserData(ud *buildlet.EC2UserData, key string) string {
346+
switch key {
347+
case metaKeyTLSCert:
348+
return ud.TLSCert
349+
case metaKeyTLSkey:
350+
return ud.TLSKey
351+
case metaKeyPassword:
352+
return ud.TLSPassword
353+
default:
354+
return ""
355+
}
356+
}
357+
313358
// metadataValue returns the GCE metadata instance value for the given key.
359+
// If the instance is on EC2 the corresponding value will be extracted from
360+
// the user data available via the metadata.
314361
// If the metadata is not defined, the returned string is empty.
315362
//
316-
// If not running on GCE, it falls back to using environment variables
363+
// If not running on GCE or EC2, it falls back to using environment variables
317364
// for local development.
318365
func metadataValue(key string) string {
319366
// The common case (on GCE, but not in Kubernetes):
@@ -328,6 +375,24 @@ func metadataValue(key string) string {
328375
return v
329376
}
330377

378+
if onEC2() {
379+
if ec2UD != nil {
380+
return mdValueFromUserData(ec2UD, key)
381+
}
382+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
383+
defer cancel()
384+
ec2MetaJson, err := ec2MdC.GetUserDataWithContext(ctx)
385+
if err != nil {
386+
log.Fatalf("unable to retrieve EC2 user data: %v", err)
387+
}
388+
ec2UD = &buildlet.EC2UserData{}
389+
err = json.Unmarshal([]byte(ec2MetaJson), ec2UD)
390+
if err != nil {
391+
log.Fatalf("unable to unmarshal user data json: %v", err)
392+
}
393+
return mdValueFromUserData(ec2UD, key)
394+
}
395+
331396
// Else allow use of environment variables to fake
332397
// metadata keys, for Kubernetes pods or local testing.
333398
envKey := "META_" + strings.Replace(key, "-", "_", -1)

0 commit comments

Comments
 (0)