1package awsat 2 3import ( 4 "encoding/base64" 5 "io/ioutil" 6 "os" 7 "testing" 8 9 "github.com/aws/aws-sdk-go/aws" 10 "github.com/aws/aws-sdk-go/aws/client/metadata" 11 "github.com/aws/aws-sdk-go/aws/request" 12 "github.com/aws/aws-sdk-go/service/ec2" 13 "github.com/aws/aws-sdk-go/service/elbv2" 14 "github.com/wallix/awless/aws/spec" 15) 16 17func TestInstance(t *testing.T) { 18 t.Run("create", func(t *testing.T) { 19 t.Run("with distro", func(t *testing.T) { 20 awsspec.DefaultImageResolverCache.Store("canonical:ubuntu:xenial:x86_64:hvm:ebs", []*awsspec.AwsImage{{Id: "ami-123456"}}) 21 22 Template("create instance distro=canonical name=myinstance subnet=sub_1 type=t2.nano count=1"). 23 Mock(&ec2Mock{ 24 RunInstancesFunc: func(input *ec2.RunInstancesInput) (*ec2.Reservation, error) { 25 return &ec2.Reservation{Instances: []*ec2.Instance{{InstanceId: String("new-instance-id")}}}, nil 26 }, 27 CreateTagsRequestFunc: func(input *ec2.CreateTagsInput) (req *request.Request, output *ec2.CreateTagsOutput) { 28 output = &ec2.CreateTagsOutput{} 29 req = request.New(aws.Config{}, metadata.ClientInfo{}, request.Handlers{}, nil, &request.Operation{}, input, output) 30 return 31 }, 32 }).ExpectInput("RunInstances", &ec2.RunInstancesInput{ 33 SubnetId: String("sub_1"), 34 ImageId: String("ami-123456"), 35 InstanceType: String("t2.nano"), 36 MinCount: Int64(1), 37 MaxCount: Int64(1), 38 }).ExpectInput("CreateTagsRequest", &ec2.CreateTagsInput{ 39 Resources: []*string{String("new-instance-id")}, 40 Tags: []*ec2.Tag{ 41 {Key: String("Name"), Value: String("myinstance")}, 42 }, 43 }).ExpectCommandResult("new-instance-id").ExpectCalls("RunInstances", "CreateTagsRequest"). 44 ExpectRevert("delete instance id=new-instance-id").Run(t) 45 }) 46 47 t.Run("with user data", func(t *testing.T) { 48 _, userdataFile, cleanup := generateTmpFile("this is my content with {{ .AWLESS.oneRef }} content") 49 defer cleanup() 50 51 Template("oneRef=awesome\n"+ 52 "create instance count=3 image=ami-1234 "+ 53 "name=myinstance subnet=sub_1 type=t2.nano keypair=mykp ip=10.2.3.4 "+ 54 "userdata="+userdataFile+" securitygroup=sg-1234 lock=true role=myrole"). 55 Mock(&ec2Mock{ 56 RunInstancesFunc: func(input *ec2.RunInstancesInput) (*ec2.Reservation, error) { 57 return &ec2.Reservation{Instances: []*ec2.Instance{{InstanceId: String("new-instance-id")}}}, nil 58 }, 59 CreateTagsRequestFunc: func(input *ec2.CreateTagsInput) (req *request.Request, output *ec2.CreateTagsOutput) { 60 output = &ec2.CreateTagsOutput{} 61 req = request.New(aws.Config{}, metadata.ClientInfo{}, request.Handlers{}, nil, &request.Operation{}, input, output) 62 return 63 }, 64 }).ExpectInput("RunInstances", &ec2.RunInstancesInput{ 65 SubnetId: String("sub_1"), 66 ImageId: String("ami-1234"), 67 InstanceType: String("t2.nano"), 68 MinCount: Int64(3), 69 MaxCount: Int64(3), 70 KeyName: String("mykp"), 71 PrivateIpAddress: String("10.2.3.4"), 72 SecurityGroupIds: []*string{String("sg-1234")}, 73 DisableApiTermination: Bool(true), 74 IamInstanceProfile: &ec2.IamInstanceProfileSpecification{Name: String("myrole")}, 75 UserData: String(base64.StdEncoding.EncodeToString([]byte("this is my content with awesome content"))), 76 }).ExpectInput("CreateTagsRequest", &ec2.CreateTagsInput{ 77 Resources: []*string{String("new-instance-id")}, 78 Tags: []*ec2.Tag{ 79 {Key: String("Name"), Value: String("myinstance")}, 80 }, 81 }).ExpectCommandResult("new-instance-id").ExpectCalls("RunInstances", "CreateTagsRequest").Run(t) 82 }) 83 }) 84 85 t.Run("update", func(t *testing.T) { 86 Template("update instance id=id-1234 type=t2.micro lock=true").Mock(&ec2Mock{ 87 ModifyInstanceAttributeFunc: func(param0 *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) { 88 return nil, nil 89 }, 90 }).ExpectInput("ModifyInstanceAttribute", &ec2.ModifyInstanceAttributeInput{ 91 InstanceId: String("id-1234"), 92 InstanceType: &ec2.AttributeValue{Value: String("t2.micro")}, 93 DisableApiTermination: &ec2.AttributeBooleanValue{Value: Bool(true)}, 94 }). 95 ExpectCalls("ModifyInstanceAttribute").Run(t) 96 }) 97 98 t.Run("delete", func(t *testing.T) { 99 t.Run("one id", func(t *testing.T) { 100 Template("delete instance id=id-1234").Mock(&ec2Mock{ 101 TerminateInstancesFunc: func(param0 *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) { return nil, nil }, 102 }).ExpectInput("TerminateInstances", &ec2.TerminateInstancesInput{InstanceIds: []*string{String("id-1234")}}). 103 ExpectCalls("TerminateInstances").Run(t) 104 }) 105 106 t.Run("multiple ids", func(t *testing.T) { 107 Template("delete instance ids=id-1234,id-2345").Mock(&ec2Mock{ 108 TerminateInstancesFunc: func(param0 *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) { return nil, nil }, 109 }).ExpectInput("TerminateInstances", &ec2.TerminateInstancesInput{InstanceIds: []*string{String("id-1234"), String("id-2345")}}). 110 ExpectCalls("TerminateInstances").Run(t) 111 }) 112 }) 113 114 t.Run("start", func(t *testing.T) { 115 t.Run("one id", func(t *testing.T) { 116 Template("start instance id=id-1234").Mock(&ec2Mock{ 117 StartInstancesFunc: func(param0 *ec2.StartInstancesInput) (*ec2.StartInstancesOutput, error) { 118 return &ec2.StartInstancesOutput{ 119 StartingInstances: []*ec2.InstanceStateChange{{InstanceId: String("id-1234")}}}, nil 120 }, 121 }).ExpectInput("StartInstances", &ec2.StartInstancesInput{InstanceIds: []*string{String("id-1234")}}). 122 ExpectCalls("StartInstances").Run(t) 123 }) 124 125 t.Run("multiple ids", func(t *testing.T) { 126 Template("start instance ids=id-1234,id-2345").Mock(&ec2Mock{ 127 StartInstancesFunc: func(param0 *ec2.StartInstancesInput) (*ec2.StartInstancesOutput, error) { 128 return &ec2.StartInstancesOutput{ 129 StartingInstances: []*ec2.InstanceStateChange{{InstanceId: String("id-1234")}, {InstanceId: String("id-2345")}}}, nil 130 }, 131 }).ExpectInput("StartInstances", &ec2.StartInstancesInput{InstanceIds: []*string{String("id-1234"), String("id-2345")}}). 132 ExpectCalls("StartInstances").Run(t) 133 }) 134 }) 135 136 t.Run("stop", func(t *testing.T) { 137 t.Run("one id", func(t *testing.T) { 138 Template("stop instance id=id-1234").Mock(&ec2Mock{ 139 StopInstancesFunc: func(param0 *ec2.StopInstancesInput) (*ec2.StopInstancesOutput, error) { 140 return &ec2.StopInstancesOutput{ 141 StoppingInstances: []*ec2.InstanceStateChange{{InstanceId: String("id-1234")}}}, nil 142 }, 143 }).ExpectInput("StopInstances", &ec2.StopInstancesInput{InstanceIds: []*string{String("id-1234")}}). 144 ExpectCalls("StopInstances").Run(t) 145 }) 146 147 t.Run("multiple ids", func(t *testing.T) { 148 Template("stop instance ids=id-1234,id-2345").Mock(&ec2Mock{ 149 StopInstancesFunc: func(param0 *ec2.StopInstancesInput) (*ec2.StopInstancesOutput, error) { 150 return &ec2.StopInstancesOutput{ 151 StoppingInstances: []*ec2.InstanceStateChange{{InstanceId: String("id-1234")}, {InstanceId: String("id-2345")}}}, nil 152 }, 153 }).ExpectInput("StopInstances", &ec2.StopInstancesInput{InstanceIds: []*string{String("id-1234"), String("id-2345")}}). 154 ExpectCalls("StopInstances").Run(t) 155 }) 156 }) 157 158 t.Run("restart", func(t *testing.T) { 159 t.Run("one id", func(t *testing.T) { 160 Template("restart instance id=id-1234").Mock(&ec2Mock{ 161 RebootInstancesFunc: func(param0 *ec2.RebootInstancesInput) (*ec2.RebootInstancesOutput, error) { 162 return &ec2.RebootInstancesOutput{}, nil 163 }, 164 }).ExpectInput("RebootInstances", &ec2.RebootInstancesInput{InstanceIds: []*string{String("id-1234")}}). 165 ExpectCalls("RebootInstances").Run(t) 166 }) 167 168 t.Run("multiple ids", func(t *testing.T) { 169 Template("restart instance ids=id-1234,id-2345").Mock(&ec2Mock{ 170 RebootInstancesFunc: func(param0 *ec2.RebootInstancesInput) (*ec2.RebootInstancesOutput, error) { 171 return &ec2.RebootInstancesOutput{}, nil 172 }, 173 }).ExpectInput("RebootInstances", &ec2.RebootInstancesInput{InstanceIds: []*string{String("id-1234"), String("id-2345")}}). 174 ExpectCalls("RebootInstances").Run(t) 175 }) 176 }) 177 178 t.Run("check", func(t *testing.T) { 179 Template("check instance id=id-1234 state=running timeout=1").Mock(&ec2Mock{ 180 DescribeInstancesFunc: func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { 181 return &ec2.DescribeInstancesOutput{Reservations: []*ec2.Reservation{ 182 {Instances: []*ec2.Instance{{InstanceId: input.InstanceIds[0], State: &ec2.InstanceState{Name: String("running")}}}}, 183 }}, nil 184 }}).ExpectInput("DescribeInstances", &ec2.DescribeInstancesInput{InstanceIds: []*string{String("id-1234")}}). 185 ExpectCalls("DescribeInstances").Run(t) 186 }) 187 188 t.Run("attach", func(t *testing.T) { 189 Template("attach instance id=id-1234 targetgroup=arn:of:target:group port=8080").Mock(&elbv2Mock{ 190 RegisterTargetsFunc: func(param0 *elbv2.RegisterTargetsInput) (*elbv2.RegisterTargetsOutput, error) { 191 return nil, nil 192 }, 193 }).ExpectInput("RegisterTargets", &elbv2.RegisterTargetsInput{ 194 TargetGroupArn: String("arn:of:target:group"), 195 Targets: []*elbv2.TargetDescription{ 196 {Id: String("id-1234"), Port: Int64(8080)}, 197 }, 198 }). 199 ExpectCalls("RegisterTargets").Run(t) 200 }) 201 202 t.Run("detach", func(t *testing.T) { 203 Template("detach instance id=id-1234 targetgroup=arn:of:target:group").Mock(&elbv2Mock{ 204 DeregisterTargetsFunc: func(param0 *elbv2.DeregisterTargetsInput) (*elbv2.DeregisterTargetsOutput, error) { 205 return nil, nil 206 }, 207 }).ExpectInput("DeregisterTargets", &elbv2.DeregisterTargetsInput{ 208 TargetGroupArn: String("arn:of:target:group"), 209 Targets: []*elbv2.TargetDescription{ 210 {Id: String("id-1234")}, 211 }, 212 }). 213 ExpectCalls("DeregisterTargets").Run(t) 214 }) 215} 216 217func generateTmpFile(content string) (*os.File, string, func()) { 218 file, err := ioutil.TempFile("", "awless-at-tmpfile") 219 if err != nil { 220 panic(err) 221 } 222 if err := ioutil.WriteFile(file.Name(), []byte(content), 0644); err != nil { 223 panic(err) 224 } 225 226 cleanup := func() { 227 file.Close() 228 if err := os.Remove(file.Name()); err != nil { 229 panic(err) 230 } 231 } 232 return file, file.Name(), cleanup 233} 234