builder/amazon/instance: register the AMI

This commit is contained in:
Mitchell Hashimoto 2013-07-25 00:19:04 -05:00
parent 03a2cc8b22
commit 0552bc7306
5 changed files with 119 additions and 2 deletions

View File

@ -14,6 +14,7 @@ import (
"log" "log"
"os" "os"
"strings" "strings"
"text/template"
) )
// The unique ID for this builder // The unique ID for this builder
@ -27,6 +28,7 @@ type Config struct {
awscommon.RunConfig `mapstructure:",squash"` awscommon.RunConfig `mapstructure:",squash"`
AccountId string `mapstructure:"account_id"` AccountId string `mapstructure:"account_id"`
AMIName string `mapstructure:"ami_name"`
BundleDestination string `mapstructure:"bundle_destination"` BundleDestination string `mapstructure:"bundle_destination"`
BundlePrefix string `mapstructure:"bundle_prefix"` BundlePrefix string `mapstructure:"bundle_prefix"`
BundleUploadCommand string `mapstructure:"bundle_upload_command"` BundleUploadCommand string `mapstructure:"bundle_upload_command"`
@ -90,6 +92,17 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.AccountId = strings.Replace(b.config.AccountId, "-", "", -1) b.config.AccountId = strings.Replace(b.config.AccountId, "-", "", -1)
} }
if b.config.AMIName == "" {
errs = packer.MultiErrorAppend(
errs, errors.New("ami_name must be specified"))
} else {
_, err = template.New("ami").Parse(b.config.AMIName)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Failed parsing ami_name: %s", err))
}
}
if b.config.S3Bucket == "" { if b.config.S3Bucket == "" {
errs = packer.MultiErrorAppend(errs, errors.New("s3_bucket is required")) errs = packer.MultiErrorAppend(errs, errors.New("s3_bucket is required"))
} }
@ -163,6 +176,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepUploadX509Cert{}, &StepUploadX509Cert{},
&StepBundleVolume{}, &StepBundleVolume{},
&StepUploadBundle{}, &StepUploadBundle{},
&StepRegisterAMI{},
} }
// Run! // Run!
@ -187,7 +201,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, nil return nil, nil
} }
return nil, nil // Build the artifact and return it
artifact := &awscommon.Artifact{
Amis: state["amis"].(map[string]string),
BuilderIdValue: BuilderId,
Conn: ec2conn,
}
return artifact, nil
} }
func (b *Builder) Cancel() { func (b *Builder) Cancel() {

View File

@ -15,6 +15,7 @@ func testConfig() map[string]interface{} {
return map[string]interface{}{ return map[string]interface{}{
"account_id": "foo", "account_id": "foo",
"ami_name": "foo",
"instance_type": "m1.small", "instance_type": "m1.small",
"region": "us-east-1", "region": "us-east-1",
"s3_bucket": "foo", "s3_bucket": "foo",
@ -61,6 +62,34 @@ func TestBuilderPrepare_AccountId(t *testing.T) {
} }
} }
func TestBuilderPrepare_AMIName(t *testing.T) {
var b Builder
config := testConfig()
// Test good
config["ami_name"] = "foo"
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test bad
config["ami_name"] = "foo {{"
b = Builder{}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
// Test bad
delete(config, "ami_name")
b = Builder{}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_BundleDestination(t *testing.T) { func TestBuilderPrepare_BundleDestination(t *testing.T) {
b := &Builder{} b := &Builder{}
config := testConfig() config := testConfig()

View File

@ -93,8 +93,10 @@ func (s *StepBundleVolume) Run(state map[string]interface{}) multistep.StepActio
} }
// Store the manifest path // Store the manifest path
manifestName := bundlePrefix.String() + ".manifest.xml"
state["manifest_name"] = manifestName
state["manifest_path"] = fmt.Sprintf( state["manifest_path"] = fmt.Sprintf(
"%s/%s.manifest.xml", config.BundleDestination, bundlePrefix.String()) "%s/%s", config.BundleDestination, manifestName)
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -0,0 +1,61 @@
package instance
import (
"bytes"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"strconv"
"text/template"
"time"
)
type amiNameData struct {
CreateTime string
}
type StepRegisterAMI struct{}
func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*Config)
manifestPath := state["remote_manifest_path"].(string)
ui := state["ui"].(packer.Ui)
// Parse the name of the AMI
amiNameBuf := new(bytes.Buffer)
tData := amiNameData{
strconv.FormatInt(time.Now().UTC().Unix(), 10),
}
t := template.Must(template.New("ami").Parse(config.AMIName))
t.Execute(amiNameBuf, tData)
amiName := amiNameBuf.String()
ui.Say("Registering the AMI...")
cmd := &packer.RemoteCmd{
Command: fmt.Sprintf(
"ec2-register %s -n '%s' -O '%s' -W '%s'",
manifestPath,
amiName,
config.AccessKey,
config.SecretKey),
}
if err := cmd.StartWithUi(comm, ui); err != nil {
state["error"] = fmt.Errorf("Error registering AMI: %s", err)
ui.Error(state["error"].(error).Error())
return multistep.ActionHalt
}
if cmd.ExitStatus != 0 {
state["error"] = fmt.Errorf(
"AMI registration failed. Please see the output above for more\n" +
"details on what went wrong.")
ui.Error(state["error"].(error).Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepRegisterAMI) Cleanup(map[string]interface{}) {}

View File

@ -21,6 +21,7 @@ type StepUploadBundle struct{}
func (s *StepUploadBundle) Run(state map[string]interface{}) multistep.StepAction { func (s *StepUploadBundle) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator) comm := state["communicator"].(packer.Communicator)
config := state["config"].(*Config) config := state["config"].(*Config)
manifestName := state["manifest_name"].(string)
manifestPath := state["manifest_path"].(string) manifestPath := state["manifest_path"].(string)
ui := state["ui"].(packer.Ui) ui := state["ui"].(packer.Ui)
@ -51,6 +52,9 @@ func (s *StepUploadBundle) Run(state map[string]interface{}) multistep.StepActio
return multistep.ActionHalt return multistep.ActionHalt
} }
state["remote_manifest_path"] = fmt.Sprintf(
"%s/%s", config.S3Bucket, manifestName)
return multistep.ActionContinue return multistep.ActionContinue
} }