diff --git a/buildlet/aws.go b/buildlet/aws.go index a2c3e5535b..1d96c66601 100644 --- a/buildlet/aws.go +++ b/buildlet/aws.go @@ -29,6 +29,8 @@ import ( type EC2UserData struct { BuildletBinaryURL string `json:"buildlet_binary_url,omitempty"` BuildletHostType string `json:"buildlet_host_type,omitempty"` + BuildletImageURL string `json:"buildlet_image_url,omitempty"` + BuildletName string `json:"buildlet_name,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` TLSCert string `json:"tls_cert,omitempty"` TLSKey string `json:"tls_key,omitempty"` @@ -184,12 +186,14 @@ func (c *AWSClient) configureVM(buildEnv *buildenv.Environment, hconf *dashboard func (c *AWSClient) vmUserDataSpec(vmConfig *ec2.RunInstancesInput, buildEnv *buildenv.Environment, hconf *dashboard.HostConfig, vmName, hostType string, opts *VMOpts) { // add custom metadata to the user data. ud := EC2UserData{ + BuildletName: vmName, BuildletBinaryURL: hconf.BuildletBinaryURL(buildEnv), BuildletHostType: hostType, + BuildletImageURL: hconf.ContainerVMImage(), + Metadata: make(map[string]string), TLSCert: opts.TLS.CertPEM, TLSKey: opts.TLS.KeyPEM, TLSPassword: opts.TLS.Password(), - Metadata: make(map[string]string), } for k, v := range opts.Meta { ud.Metadata[k] = v diff --git a/buildlet/aws_test.go b/buildlet/aws_test.go index 1815f02949..d13871bbb7 100644 --- a/buildlet/aws_test.go +++ b/buildlet/aws_test.go @@ -432,33 +432,40 @@ func TestEC2BuildletParams(t *testing.T) { func TestConfigureVM(t *testing.T) { testCases := []struct { - desc string - buildEnv *buildenv.Environment - hconf *dashboard.HostConfig - hostType string - opts *VMOpts - vmName string - wantDesc string - wantImageID string - wantInstanceType string - wantName string - wantZone string + desc string + buildEnv *buildenv.Environment + hconf *dashboard.HostConfig + hostType string + opts *VMOpts + vmName string + wantDesc string + wantImageID string + wantInstanceType string + wantName string + wantZone string + wantBuildletName string + wantBuildletImage string }{ { - desc: "default-values", - buildEnv: &buildenv.Environment{}, - hconf: &dashboard.HostConfig{}, - vmName: "base_vm", - hostType: "host-foo-bar", - opts: &VMOpts{}, - wantInstanceType: "n1-highcpu-2", - wantName: "base_vm", + desc: "default-values", + buildEnv: &buildenv.Environment{}, + hconf: &dashboard.HostConfig{ + KonletVMImage: "gcr.io/symbolic-datum-552/gobuilder-arm64-aws", + }, + vmName: "base_vm", + hostType: "host-foo-bar", + opts: &VMOpts{}, + wantInstanceType: "n1-highcpu-2", + wantName: "base_vm", + wantBuildletName: "base_vm", + wantBuildletImage: "gcr.io/symbolic-datum-552/gobuilder-arm64-aws", }, { desc: "full-configuration", buildEnv: &buildenv.Environment{}, hconf: &dashboard.HostConfig{ - VMImage: "awesome_image", + VMImage: "awesome_image", + KonletVMImage: "gcr.io/symbolic-datum-552/gobuilder-arm64-aws", }, vmName: "base-vm", hostType: "host-foo-bar", @@ -473,11 +480,13 @@ func TestConfigureVM(t *testing.T) { "sample": "value", }, }, - wantDesc: "test description", - wantImageID: "awesome_image", - wantInstanceType: "n1-highcpu-2", - wantName: "base-vm", - wantZone: "sa-west", + wantDesc: "test description", + wantImageID: "awesome_image", + wantInstanceType: "n1-highcpu-2", + wantName: "base-vm", + wantZone: "sa-west", + wantBuildletName: "base-vm", + wantBuildletImage: "gcr.io/symbolic-datum-552/gobuilder-arm64-aws", }, } for _, tc := range testCases { @@ -533,6 +542,13 @@ func TestConfigureVM(t *testing.T) { if gotUD.BuildletHostType != tc.hostType { t.Errorf("buildletHostType got %s; want %s", gotUD.BuildletHostType, tc.hostType) } + if gotUD.BuildletName != tc.wantBuildletName { + t.Errorf("buildletName got %s; want %s", gotUD.BuildletName, tc.wantBuildletName) + } + if gotUD.BuildletImageURL != tc.wantBuildletImage { + t.Errorf("buildletImageURL got %s; want %s", gotUD.BuildletImageURL, tc.wantBuildletImage) + } + if gotUD.TLSCert != tc.opts.TLS.CertPEM { t.Errorf("TLSCert got %s; want %s", gotUD.TLSCert, tc.opts.TLS.CertPEM) } diff --git a/cmd/rundockerbuildlet/rundockerbuildlet.go b/cmd/rundockerbuildlet/rundockerbuildlet.go index c437b08a59..59805e3755 100644 --- a/cmd/rundockerbuildlet/rundockerbuildlet.go +++ b/cmd/rundockerbuildlet/rundockerbuildlet.go @@ -10,6 +10,7 @@ package main import ( "bytes" + "context" "encoding/json" "flag" "fmt" @@ -23,7 +24,10 @@ import ( "strings" "time" + "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/aws/aws-sdk-go/aws/session" "golang.org/x/build/buildenv" + "golang.org/x/build/buildlet" ) var ( @@ -40,6 +44,12 @@ var ( var ( buildKey []byte scalewayMeta = new(scalewayMetadata) + isReverse = true + isSingleRun = false + // ec2UD contains a copy of the EC2 vm user data retrieved from the metadata. + ec2UD *buildlet.EC2UserData + // ec2MetaClient is an EC2 metadata client. + ec2MetaClient *ec2metadata.EC2Metadata ) func main() { @@ -52,9 +62,19 @@ func main() { *numInst = 1 *basename = "scaleway" initScalewayMeta() + } else if onEC2() { + initEC2Meta() + *memory = "" + *image = ec2UD.BuildletImageURL + *pull = true + *numInst = 1 + isReverse = false + isSingleRun = true } - buildKey = getBuildKey() + if isReverse { + buildKey = getBuildKey() + } if *image == "" { log.Fatalf("docker --image is required") @@ -65,10 +85,35 @@ func main() { if err := checkFix(); err != nil { log.Print(err) } + if isSingleRun { + log.Printf("Configured to run a single instance. Exiting") + os.Exit(0) + } time.Sleep(time.Second) // TODO: docker wait on the running containers? } } +func ec2MdClient() *ec2metadata.EC2Metadata { + if ec2MetaClient != nil { + return ec2MetaClient + } + ses, err := session.NewSession() + if err != nil { + return nil + } + ec2MetaClient = ec2metadata.New(ses) + return ec2MetaClient +} + +func onEC2() bool { + if ec2MdClient() == nil { + return false + } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + return ec2MdClient().AvailableWithContext(ctx) +} + func onScaleway() bool { if *builderEnv == "host-linux-arm-scaleway" { return true @@ -136,6 +181,8 @@ func checkFix() error { // c1 instance hostname for debugability. // There should only be one running container per c1 instance. name = scalewayMeta.Hostname + } else if onEC2() { + name = ec2UD.BuildletName } else { name = fmt.Sprintf("%s%02d", *basename, num) } @@ -185,13 +232,17 @@ func checkFix() error { cmd := exec.Command("docker", "run", "-d", "--name="+name, - "-v", filepath.Dir(keyFile)+":/buildkey/", "-e", "HOSTNAME="+name, "--security-opt=seccomp=unconfined", // Issue 35547 "--tmpfs=/workdir:rw,exec") if *memory != "" { cmd.Args = append(cmd.Args, "--memory="+*memory) } + if isReverse { + cmd.Args = append(cmd.Args, "-v", filepath.Dir(keyFile)+":/buildkey/") + } else { + cmd.Args = append(cmd.Args, "-p", "443:443") + } if *cpu > 0 { cmd.Args = append(cmd.Args, fmt.Sprintf("--cpuset-cpus=%d-%d", *cpu*(num-1), *cpu*num-1)) } @@ -234,6 +285,29 @@ func (m *scalewayMetadata) HasTag(t string) bool { return false } +func initEC2Meta() { + if !onEC2() { + log.Fatal("attempt to initialize metadata on non-EC2 instance") + } + if ec2UD != nil { + return + } + if ec2MdClient() == nil { + log.Fatalf("unable to retrieve EC2 metadata client") + } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + ec2MetaJson, err := ec2MdClient().GetUserDataWithContext(ctx) + if err != nil { + log.Fatalf("unable to retrieve EC2 user data: %v", err) + } + ec2UD = &buildlet.EC2UserData{} + err = json.Unmarshal([]byte(ec2MetaJson), ec2UD) + if err != nil { + log.Fatalf("unable to unmarshal user data json: %v", err) + } +} + func initScalewayMeta() { const metaURL = "http://169.254.42.42/conf?format=json" res, err := http.Get(metaURL)