From e071739cd874fc63bdfcd7f10886f9c67585a8ca Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 10 May 2013 15:21:11 -0700 Subject: [PATCH] builder/amazonebs: Can make an AMI. WHAT! --- builder/amazonebs/builder.go | 95 +++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/builder/amazonebs/builder.go b/builder/amazonebs/builder.go index c0ca44868..10df6eb83 100644 --- a/builder/amazonebs/builder.go +++ b/builder/amazonebs/builder.go @@ -7,8 +7,12 @@ package amazonebs import ( "encoding/json" + "fmt" + "github.com/mitchellh/goamz/aws" + "github.com/mitchellh/goamz/ec2" "github.com/mitchellh/packer/packer" "log" + "time" ) type config struct { @@ -43,6 +47,93 @@ func (b *Builder) Prepare(raw interface{}) (err error) { return } -func (*Builder) Run(b packer.Build, ui packer.Ui) { - ui.Say("Building an AWS image...\n") +func (b *Builder) Run(build packer.Build, ui packer.Ui) { + auth := aws.Auth{b.config.AccessKey, b.config.SecretKey} + region := aws.Regions[b.config.Region] + ec2conn := ec2.New(auth, region) + + runOpts := &ec2.RunInstances{ + ImageId: b.config.SourceAmi, + InstanceType: "m1.small", + MinCount: 0, + MaxCount: 0, + } + + ui.Say("Launching a source AWS instance...\n") + runResp, err := ec2conn.RunInstances(runOpts) + if err != nil { + ui.Error("%s\n", err.Error()) + return + } + + instance := &runResp.Instances[0] + log.Printf("instance id: %s\n", instance.InstanceId) + ui.Say("Waiting for instance to become ready...\n") + err = waitForState(ec2conn, instance, "running") + if err != nil { + ui.Error("%s\n", err.Error()) + return + } + + // Stop the instance so we can create an AMI from it + _, err = ec2conn.StopInstances(instance.InstanceId) + if err != nil { + ui.Error("%s\n", err.Error()) + return + } + + // Wait for the instance to actual stop + // TODO: Handle diff source states, i.e. this force state sucks + ui.Say("Waiting for the instance to stop...\n") + instance.State.Name = "stopping" + err = waitForState(ec2conn, instance, "stopped") + if err != nil { + ui.Error("%s\n", err.Error()) + return + } + + // Create the image + ui.Say("Creating the AMI...\n") + createOpts := &ec2.CreateImage{ + InstanceId: instance.InstanceId, + Name: "ZIMAGINE", + } + + createResp, err := ec2conn.CreateImage(createOpts) + if err != nil { + ui.Error("%s\n", err.Error()) + return + } + + ui.Say("AMI: %s\n", createResp.ImageId) + + // Make sure we clean up the instance by terminating it, no matter what + defer func() { + // TODO: error handling + ui.Say("Terminating the source AWS instance...\n") + ec2conn.TerminateInstances([]string{instance.InstanceId}) + }() +} + +func waitForState(ec2conn *ec2.EC2, i *ec2.Instance, target string) (err error) { + log.Printf("Waiting for instance state to become: %s\n", target) + + original := i.State.Name + for i.State.Name == original { + var resp *ec2.InstancesResp + resp, err = ec2conn.Instances([]string{i.InstanceId}, ec2.NewFilter()) + if err != nil { + return + } + + i = &resp.Reservations[0].Instances[0] + + time.Sleep(2 * time.Second) + } + + if i.State.Name != target { + return fmt.Errorf("unexpected target state '%s', wanted '%s'", i.State.Name, target) + } + + return }