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  }