buildlet: use cloud client with AWS buildlet
Use the cloud package for AWS functions. Remove the unused
destroyVM function.
Updates golang/go#36841
Change-Id: I00e1a20c904f7c4be6460ac302085b28f518d161
Reviewed-on: https://go-review.googlesource.com/c/build/+/236300
Run-TryBot: Carlos Amedee <carlos@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alexander Rakoczy <alex@golang.org>
diff --git a/buildlet/ec2_test.go b/buildlet/ec2_test.go
new file mode 100644
index 0000000..8fe1ae6
--- /dev/null
+++ b/buildlet/ec2_test.go
@@ -0,0 +1,451 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package buildlet
+
+import (
+ "context"
+ "encoding/base64"
+ "encoding/json"
+ "testing"
+ "time"
+
+ "golang.org/x/build/buildenv"
+ "golang.org/x/build/dashboard"
+ "golang.org/x/build/internal/cloud"
+)
+
+func TestStartNewVM(t *testing.T) {
+ kp, err := NewKeyPair()
+ if err != nil {
+ t.Fatalf("unable to generate key pair: %s", err)
+ }
+ buildEnv := &buildenv.Environment{}
+ hconf := &dashboard.HostConfig{
+ VMImage: "image-x",
+ }
+ vmName := "sample-vm"
+ hostType := "host-sample-os"
+ opts := &VMOpts{
+ Zone: "us-west",
+ ProjectID: "project1",
+ TLS: kp,
+ Description: "Golang builder for sample",
+ Meta: map[string]string{
+ "Owner": "george",
+ },
+ DeleteIn: 45 * time.Second,
+ SkipEndpointVerification: true,
+ }
+ c := &EC2Client{
+ client: cloud.NewFakeAWSClient(),
+ }
+ gotClient, gotErr := c.StartNewVM(context.Background(), buildEnv, hconf, vmName, hostType, opts)
+ if gotErr != nil {
+ t.Fatalf("error is not nil: %v", gotErr)
+ }
+ if gotClient == nil {
+ t.Fatalf("response is nil")
+ }
+}
+
+func TestStartNewVMError(t *testing.T) {
+ kp, err := NewKeyPair()
+ if err != nil {
+ t.Fatalf("unable to generate key pair: %s", err)
+ }
+
+ testCases := []struct {
+ desc string
+ buildEnv *buildenv.Environment
+ hconf *dashboard.HostConfig
+ vmName string
+ hostType string
+ opts *VMOpts
+ }{
+ {
+ desc: "nil-buildenv",
+ hconf: &dashboard.HostConfig{},
+ vmName: "sample-vm",
+ hostType: "host-sample-os",
+ opts: &VMOpts{
+ Zone: "us-west",
+ ProjectID: "project1",
+ TLS: kp,
+ Description: "Golang builder for sample",
+ Meta: map[string]string{
+ "Owner": "george",
+ },
+ DeleteIn: 45 * time.Second,
+ },
+ },
+ {
+ desc: "nil-hconf",
+ buildEnv: &buildenv.Environment{},
+ vmName: "sample-vm",
+ hostType: "host-sample-os",
+ opts: &VMOpts{
+ Zone: "us-west",
+ ProjectID: "project1",
+ TLS: kp,
+ Description: "Golang builder for sample",
+ Meta: map[string]string{
+ "Owner": "george",
+ },
+ DeleteIn: 45 * time.Second,
+ },
+ },
+ {
+ desc: "empty-vnName",
+ buildEnv: &buildenv.Environment{},
+ hconf: &dashboard.HostConfig{},
+ vmName: "",
+ hostType: "host-sample-os",
+ opts: &VMOpts{
+ Zone: "us-west",
+ ProjectID: "project1",
+ TLS: kp,
+ Description: "Golang builder for sample",
+ Meta: map[string]string{
+ "Owner": "george",
+ },
+ DeleteIn: 45 * time.Second,
+ },
+ },
+ {
+ desc: "empty-hostType",
+ buildEnv: &buildenv.Environment{},
+ hconf: &dashboard.HostConfig{},
+ vmName: "sample-vm",
+ hostType: "",
+ opts: &VMOpts{
+ Zone: "us-west",
+ ProjectID: "project1",
+ TLS: kp,
+ Description: "Golang builder for sample",
+ Meta: map[string]string{
+ "Owner": "george",
+ },
+ DeleteIn: 45 * time.Second,
+ },
+ },
+ {
+ desc: "missing-certs",
+ buildEnv: &buildenv.Environment{},
+ hconf: &dashboard.HostConfig{},
+ vmName: "sample-vm",
+ hostType: "host-sample-os",
+ opts: &VMOpts{
+ Zone: "us-west",
+ ProjectID: "project1",
+ Description: "Golang builder for sample",
+ Meta: map[string]string{
+ "Owner": "george",
+ },
+ DeleteIn: 45 * time.Second,
+ },
+ },
+ {
+ desc: "nil-opts",
+ buildEnv: &buildenv.Environment{},
+ hconf: &dashboard.HostConfig{},
+ vmName: "sample-vm",
+ hostType: "host-sample-os",
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ c := &EC2Client{
+ client: cloud.NewFakeAWSClient(),
+ }
+ gotClient, gotErr := c.StartNewVM(context.Background(), tc.buildEnv, tc.hconf, tc.vmName, tc.hostType, tc.opts)
+ if gotErr == nil {
+ t.Errorf("StartNewVM(ctx, %+v, %+v, %s, %s, %+v) = %+v, nil; want error", tc.buildEnv, tc.hconf, tc.vmName, tc.hostType, tc.opts, gotClient)
+ }
+ if gotClient != nil {
+ t.Errorf("got %+v; expected nil", gotClient)
+ }
+ })
+ }
+}
+
+func TestWaitUntilInstanceExists(t *testing.T) {
+ vmConfig := &cloud.EC2VMConfiguration{
+ ImageID: "foo",
+ Type: "type-a",
+ Zone: "eu-15",
+ }
+ invoked := false
+ opts := &VMOpts{
+ OnInstanceCreated: func() {
+ invoked = true
+ },
+ }
+ ctx := context.Background()
+ c := &EC2Client{
+ client: cloud.NewFakeAWSClient(),
+ }
+ gotVM, gotErr := c.createVM(ctx, vmConfig, opts)
+ if gotErr != nil {
+ t.Fatalf("createVM(ctx, %v, %v) failed with %s", vmConfig, opts, gotErr)
+ }
+ gotErr = c.WaitUntilVMExists(ctx, gotVM.ID, opts)
+ if gotErr != nil {
+ t.Fatalf("WaitUntilVMExists(%v, %v, %v) failed with error %s", ctx, gotVM.ID, opts, gotErr)
+ }
+ if !invoked {
+ t.Errorf("OnInstanceCreated() was not invoked")
+ }
+}
+
+func TestCreateVM(t *testing.T) {
+ vmConfig := &cloud.EC2VMConfiguration{
+ ImageID: "foo",
+ Type: "type-a",
+ Zone: "eu-15",
+ }
+ invoked := false
+ opts := &VMOpts{
+ OnInstanceRequested: func() {
+ invoked = true
+ },
+ }
+ c := &EC2Client{
+ client: cloud.NewFakeAWSClient(),
+ }
+ gotVM, gotErr := c.createVM(context.Background(), vmConfig, opts)
+ if gotErr != nil {
+ t.Fatalf("createVM(ctx, %v, %v) failed with %s", vmConfig, opts, gotErr)
+ }
+ if gotVM.ImageID != vmConfig.ImageID || gotVM.Type != vmConfig.Type || gotVM.Zone != vmConfig.Zone {
+ t.Errorf("createVM(ctx, %+v, %+v) = %+v, nil; want vm to match config", vmConfig, opts, gotVM)
+ }
+ if !invoked {
+ t.Errorf("OnInstanceRequested() was not invoked")
+ }
+}
+
+func TestCreateVMError(t *testing.T) {
+ testCases := []struct {
+ desc string
+ vmConfig *cloud.EC2VMConfiguration
+ opts *VMOpts
+ }{
+ {
+ desc: "missing-vmConfig",
+ },
+ {
+ desc: "missing-image-id",
+ vmConfig: &cloud.EC2VMConfiguration{
+ Type: "type-a",
+ Zone: "eu-15",
+ },
+ opts: &VMOpts{
+ OnInstanceRequested: func() {},
+ },
+ },
+ {
+ desc: "missing-instance-id",
+ vmConfig: &cloud.EC2VMConfiguration{
+ ImageID: "foo",
+ Zone: "eu-15",
+ },
+ opts: &VMOpts{
+ OnInstanceRequested: func() {},
+ },
+ },
+ {
+ desc: "missing-placement",
+ vmConfig: &cloud.EC2VMConfiguration{
+ Name: "foo",
+ Type: "type-a",
+ },
+ opts: &VMOpts{
+ OnInstanceRequested: func() {},
+ },
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ c := &EC2Client{
+ client: cloud.NewFakeAWSClient(),
+ //client: &fakeAWSClient{},
+ }
+ gotVM, gotErr := c.createVM(context.Background(), tc.vmConfig, tc.opts)
+ if gotErr == nil {
+ t.Errorf("createVM(ctx, %v, %v) = %s, %v; want error", tc.vmConfig, tc.opts, gotVM.ID, gotErr)
+ }
+ if gotVM != nil {
+ t.Errorf("createVM(ctx, %v, %v) = %s, %v; %q, error", tc.vmConfig, tc.opts, gotVM.ID, gotErr, "")
+ }
+ })
+ }
+}
+
+func TestEC2BuildletParams(t *testing.T) {
+ testCases := []struct {
+ desc string
+ inst *cloud.Instance
+ opts *VMOpts
+ wantURL string
+ wantPort string
+ wantCalled bool
+ wantErr bool
+ }{
+ {
+ desc: "base-case",
+ inst: &cloud.Instance{
+ IPAddressExternal: "8.8.8.8",
+ IPAddressInternal: "3.3.3.3",
+ },
+ opts: &VMOpts{},
+ wantCalled: true,
+ wantURL: "https://8.8.8.8",
+ wantPort: "8.8.8.8:443",
+ wantErr: false,
+ },
+ {
+ desc: "missing-int-ip",
+ inst: &cloud.Instance{
+ IPAddressExternal: "8.8.8.8",
+ },
+ opts: &VMOpts{},
+ wantCalled: true,
+ wantURL: "https://8.8.8.8",
+ wantPort: "8.8.8.8:443",
+ wantErr: false,
+ },
+ {
+ desc: "missing-ext-ip",
+ inst: &cloud.Instance{
+ IPAddressInternal: "3.3.3.3",
+ },
+ opts: &VMOpts{},
+ wantCalled: true,
+ wantURL: "",
+ wantPort: "",
+ wantErr: true,
+ },
+ }
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ gotURL, gotPort, gotErr := ec2BuildletParams(tc.inst, tc.opts)
+ if gotURL != tc.wantURL || gotPort != tc.wantPort || tc.wantErr != (gotErr != nil) {
+ t.Errorf("ec2BuildletParams(%v, %v) = %q, %q, nil; want %q, %q, nil", tc.inst, tc.opts, gotURL, gotPort, tc.wantURL, tc.wantPort)
+ }
+ })
+ }
+}
+
+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
+ wantBuildletName string
+ wantBuildletImage string
+ }{
+ {
+ 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",
+ KonletVMImage: "gcr.io/symbolic-datum-552/gobuilder-arm64-aws",
+ },
+ vmName: "base-vm",
+ hostType: "host-foo-bar",
+ opts: &VMOpts{
+ Zone: "sa-west",
+ TLS: KeyPair{
+ CertPEM: "abc",
+ KeyPEM: "xyz",
+ },
+ Description: "test description",
+ Meta: map[string]string{
+ "sample": "value",
+ },
+ },
+ 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 {
+ t.Run(tc.desc, func(t *testing.T) {
+ got := configureVM(tc.buildEnv, tc.hconf, tc.vmName, tc.hostType, tc.opts)
+ if got.ImageID != tc.wantImageID {
+ t.Errorf("ImageId got %s; want %s", got.ImageID, tc.wantImageID)
+ }
+ if got.Type != tc.wantInstanceType {
+ t.Errorf("Type got %s; want %s", got.Type, tc.wantInstanceType)
+ }
+ if got.Zone != tc.wantZone {
+ t.Errorf("Zone got %s; want %s", got.Zone, tc.wantZone)
+ }
+ if got.Name != tc.wantName {
+ t.Errorf("Name got %s; want %s", got.Name, tc.wantName)
+ }
+ if got.Description != tc.wantDesc {
+ t.Errorf("Description got %s; want %s", got.Description, tc.wantDesc)
+ }
+ gotUDJson, err := base64.StdEncoding.DecodeString(got.UserData)
+ if err != nil {
+ t.Fatalf("unable to base64 decode string %q: %s", got.UserData, err)
+ }
+ gotUD := &cloud.EC2UserData{}
+ err = json.Unmarshal([]byte(gotUDJson), gotUD)
+ if err != nil {
+ t.Errorf("unable to unmarshal user data: %v", err)
+ }
+ if gotUD.BuildletBinaryURL != tc.hconf.BuildletBinaryURL(tc.buildEnv) {
+ t.Errorf("buildletBinaryURL got %s; want %s", gotUD.BuildletBinaryURL, tc.hconf.BuildletBinaryURL(tc.buildEnv))
+ }
+ 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)
+ }
+ if gotUD.TLSKey != tc.opts.TLS.KeyPEM {
+ t.Errorf("TLSKey got %s; want %s", gotUD.TLSKey, tc.opts.TLS.KeyPEM)
+ }
+ if gotUD.TLSPassword != tc.opts.TLS.Password() {
+ t.Errorf("TLSPassword got %s; want %s", gotUD.TLSPassword, tc.opts.TLS.Password())
+ }
+ })
+ }
+}