github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/scripts/amimgr/main.go (about) 1 package main 2 3 import ( 4 "errors" 5 "flag" 6 "fmt" 7 "io" 8 "log" 9 "os" 10 "time" 11 12 "golang.org/x/crypto/ssh" 13 14 "github.com/awslabs/aws-sdk-go/aws" 15 "github.com/awslabs/aws-sdk-go/service/ec2" 16 ) 17 18 var script string = ` 19 sudo su --login root -c ' 20 mkdir -p ~/go/src/github.com/cloudfoundry-incubator && 21 cd ~/go/src/github.com/cloudfoundry-incubator && 22 git clone git://github.com/cloudfoundry-incubator/garden-linux.git && 23 cd garden-linux && 24 git reset --hard %s && 25 scripts/drone-test 26 ' 27 ` 28 29 func main() { 30 commit := flag.String("commit", "", "Commit SHA to run against") 31 imageId := flag.String("imageID", "", "The ami imageID") 32 user := flag.String("user", "ubuntu", "ssh user") 33 instanceType := flag.String("instanceType", "m3.xlarge", "The aws instance type") 34 keyName := flag.String("keyName", "ci_aws_key", "The aws key name") 35 region := flag.String("region", "us-east-1", "The aws region") 36 37 flag.Parse() 38 39 if *commit == "" { 40 log.Fatal("The commit SHA is missing.") 41 } 42 43 if *imageId == "" { 44 log.Fatal("The imageID is missing.") 45 } 46 47 creds, err := aws.EnvCreds() 48 if err != nil { 49 log.Fatal("Please ensure that the AWS_SECRET_KEY and AWS_ACCESS_KEY variables are set") 50 } 51 ec2Config := &aws.Config{ 52 Region: *region, 53 Credentials: creds, 54 } 55 56 ec2Client := ec2.New(ec2Config) 57 58 maxCount := new(int64) 59 *maxCount = 1 60 minCount := new(int64) 61 *minCount = 1 62 ebsOptimized := new(bool) 63 *ebsOptimized = true 64 instanceSpec := &ec2.RunInstancesInput{ 65 ImageID: imageId, 66 InstanceType: instanceType, 67 KeyName: keyName, 68 MaxCount: maxCount, 69 MinCount: minCount, 70 EBSOptimized: ebsOptimized, 71 } 72 instance := EC2Instance{ 73 ec2Client: ec2Client, 74 instanceSpec: instanceSpec, 75 } 76 77 signer, err := ssh.ParsePrivateKey([]byte(os.Getenv("SSH_PRIVATE_KEY"))) 78 if err != nil { 79 log.Fatal(err) 80 } 81 82 auths := []ssh.AuthMethod{ssh.PublicKeys(signer)} 83 84 sshConfig := &ssh.ClientConfig{ 85 User: *user, 86 Auth: auths, 87 } 88 89 sshClient := SSHClient{ 90 sshConfig: sshConfig, 91 protocol: "tcp", 92 stdin: os.Stdin, 93 stdout: os.Stdout, 94 stderr: os.Stderr, 95 } 96 97 exitStatus, err := runCommand(sshClient, instance, fmt.Sprintf(script, *commit)) 98 if err != nil { 99 log.Fatal(err) 100 } 101 102 if exitStatus != 0 { 103 log.Printf("Remote command exited non zero: %d\n", exitStatus) 104 } 105 106 os.Exit(exitStatus) 107 } 108 109 func runCommand(sshClient SSHClient, instance EC2Instance, command string) (int, error) { 110 err := instance.Start() 111 if err != nil { 112 return 1, err 113 } 114 fmt.Printf("Instance Started: %s\n", instance.InstanceId()) 115 defer terminate(instance) 116 117 address, err := instance.PublicDNS() 118 if err != nil { 119 return 1, err 120 } 121 fmt.Printf("Instance available at: %s\n", address) 122 123 sshClient.host = address 124 sshClient.port = 22 125 126 fmt.Println("Running Command") 127 exitStatus, err := sshClient.Run(command) 128 if err != nil { 129 return 1, err 130 } 131 132 return exitStatus, nil 133 } 134 135 func terminate(instance EC2Instance) { 136 fmt.Printf("Terminating instance: %s\n", instance.InstanceId()) 137 err := instance.Terminate() 138 if err != nil { 139 panic(err) 140 } 141 } 142 143 type EC2Instance struct { 144 ec2Client *ec2.EC2 145 instanceSpec *ec2.RunInstancesInput 146 instanceInfo *ec2.Instance 147 } 148 149 func (inst *EC2Instance) InstanceId() string { 150 if inst.instanceInfo != nil { 151 return *inst.instanceInfo.InstanceID 152 } 153 return "" 154 } 155 156 func (inst *EC2Instance) Start() error { 157 startResp, err := inst.ec2Client.RunInstances(inst.instanceSpec) 158 if err != nil { 159 return err 160 } 161 162 instance := startResp.Instances[0] 163 inst.instanceInfo = instance 164 165 key := "Name" 166 value := "garden-ci-test-instance" 167 _, err = inst.ec2Client.CreateTags(&ec2.CreateTagsInput{ 168 Resources: []*string{instance.InstanceID}, 169 Tags: []*ec2.Tag{ 170 &ec2.Tag{Key: &key, Value: &value}, 171 }, 172 }) 173 174 return err 175 } 176 177 func (inst *EC2Instance) PublicDNS() (string, error) { 178 if name := *inst.instanceInfo.PublicDNSName; name != "" { 179 return name, nil 180 } 181 182 for attempt := 1; attempt <= 100; attempt++ { 183 time.Sleep(500 * time.Millisecond) 184 185 r, err := inst.ec2Client.DescribeInstances(&ec2.DescribeInstancesInput{ 186 InstanceIDs: []*string{inst.instanceInfo.InstanceID}, 187 }) 188 if err != nil { 189 return "", err 190 } 191 192 if len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 { 193 return "", fmt.Errorf("instance not found: %s", inst.instanceInfo.InstanceID) 194 } 195 196 rinstance := r.Reservations[0].Instances[0] 197 if host := *rinstance.PublicDNSName; host != "" { 198 inst.instanceInfo = rinstance 199 return host, nil 200 } 201 } 202 203 return "", errors.New("couldn't determine IP address for instance") 204 } 205 206 func (inst *EC2Instance) Terminate() error { 207 _, err := inst.ec2Client.TerminateInstances(&ec2.TerminateInstancesInput{ 208 InstanceIDs: []*string{inst.instanceInfo.InstanceID}, 209 }) 210 return err 211 } 212 213 type SSHClient struct { 214 sshConfig *ssh.ClientConfig 215 protocol string 216 host string 217 port int 218 stdin io.Reader 219 stdout io.Writer 220 stderr io.Writer 221 } 222 223 func (cl *SSHClient) Run(command string) (int, error) { 224 address := fmt.Sprintf("%s:%d", cl.host, cl.port) 225 226 var client *ssh.Client 227 var err error 228 for attempt := 1; attempt <= 100; attempt++ { 229 time.Sleep(500 * time.Millisecond) 230 client, err = ssh.Dial(cl.protocol, address, cl.sshConfig) 231 if err == nil { 232 break 233 } 234 } 235 if err != nil { 236 return 0, fmt.Errorf("Failed to dial: %s", err) 237 } 238 239 session, err := client.NewSession() 240 if err != nil { 241 return 0, fmt.Errorf("Failed to create session: %s", err) 242 } 243 defer session.Close() 244 245 modes := ssh.TerminalModes{ 246 ssh.ECHO: 0, // disable echoing 247 ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud 248 ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud 249 } 250 251 if err := session.RequestPty("xterm", 80, 40, modes); err != nil { 252 return 0, fmt.Errorf("request for pseudo terminal failed: %s", err) 253 } 254 255 stdin, err := session.StdinPipe() 256 if err != nil { 257 return 0, fmt.Errorf("Unable to setup stdin for session: %v\n", err) 258 } 259 260 stdout, err := session.StdoutPipe() 261 if err != nil { 262 return 0, fmt.Errorf("Unable to setup stdout for session: %v\n", err) 263 } 264 265 stderr, err := session.StderrPipe() 266 if err != nil { 267 return 0, fmt.Errorf("Unable to setup stderr for session: %v\n", err) 268 } 269 270 go io.Copy(stdin, cl.stdin) 271 go io.Copy(cl.stdout, stdout) 272 go io.Copy(cl.stderr, stderr) 273 274 err = session.Run(command) 275 if err != nil { 276 if exitErr, ok := err.(*ssh.ExitError); ok { 277 return exitErr.ExitStatus(), nil 278 } else { 279 return 0, err 280 } 281 } 282 283 return 0, nil 284 }