builder.go 4.15 KB
Newer Older
1 2 3 4 5
// The amazonebs package contains a packer.Builder implementation that
// builds AMIs for Amazon EC2.
//
// In general, there are two types of AMIs that can be created: ebs-backed or
// instance-store. This builder _only_ builds ebs-backed images.
6
package ebs
7 8

import (
9
	"fmt"
10
	"github.com/mitchellh/goamz/ec2"
11
	"github.com/mitchellh/multistep"
12
	awscommon "github.com/mitchellh/packer/builder/amazon/common"
13
	"github.com/mitchellh/packer/common"
14
	"github.com/mitchellh/packer/packer"
Mitchell Hashimoto's avatar
Mitchell Hashimoto committed
15
	"log"
16 17
)

18 19 20
// The unique ID for this builder
const BuilderId = "mitchellh.amazonebs"

21
type config struct {
22
	common.PackerConfig    `mapstructure:",squash"`
23
	awscommon.AccessConfig `mapstructure:",squash"`
24
	awscommon.AMIConfig    `mapstructure:",squash"`
25
	awscommon.BlockDevices `mapstructure:",squash"`
26
	awscommon.RunConfig    `mapstructure:",squash"`
Mitchell Hashimoto's avatar
Mitchell Hashimoto committed
27

28
	tpl *packer.ConfigTemplate
29 30 31 32
}

type Builder struct {
	config config
33
	runner multistep.Runner
34 35
}

36
func (b *Builder) Prepare(raws ...interface{}) error {
37
	md, err := common.DecodeConfig(&b.config, raws...)
38 39 40
	if err != nil {
		return err
	}
41

42
	b.config.tpl, err = packer.NewConfigTemplate()
43 44 45
	if err != nil {
		return err
	}
46
	b.config.tpl.UserVars = b.config.PackerUserVars
47

48
	// Accumulate any errors
49
	errs := common.CheckUnusedConfig(md)
50
	errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(b.config.tpl)...)
51
	errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(b.config.tpl)...)
52
	errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(b.config.tpl)...)
53

54 55
	if errs != nil && len(errs.Errors) > 0 {
		return errs
56
	}
57

58
	log.Printf("Config: %+v", b.config)
59
	return nil
60
}
61

62
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
63 64 65
	region, err := b.config.Region()
	if err != nil {
		return nil, err
66 67
	}

68
	auth, err := b.config.AccessConfig.Auth()
69 70 71 72
	if err != nil {
		return nil, err
	}

73 74
	ec2conn := ec2.New(auth, region)

75 76 77 78 79 80
	// Setup the state bag and initial state for the steps
	state := make(map[string]interface{})
	state["config"] = b.config
	state["ec2"] = ec2conn
	state["hook"] = hook
	state["ui"] = ui
81

82
	// Build the steps
83
	steps := []multistep.Step{
84 85 86 87
		&awscommon.StepKeyPair{
			Debug:        b.config.PackerDebug,
			DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
		},
88 89
		&awscommon.StepSecurityGroup{
			SecurityGroupId: b.config.SecurityGroupId,
90
			SSHPort:         b.config.SSHPort,
Mitchell Hashimoto's avatar
fmt  
Mitchell Hashimoto committed
91
			VpcId:           b.config.VpcId,
92 93
		},
		&awscommon.StepRunSourceInstance{
94
			Debug:              b.config.PackerDebug,
95 96
			ExpectedRootDevice: "ebs",
			InstanceType:       b.config.InstanceType,
97
			UserData:           b.config.UserData,
98
			UserDataFile:       b.config.UserDataFile,
99
			SourceAMI:          b.config.SourceAmi,
100
			IamInstanceProfile: b.config.IamInstanceProfile,
Mitchell Hashimoto's avatar
fmt  
Mitchell Hashimoto committed
101
			SubnetId:           b.config.SubnetId,
102
			BlockDevices:       b.config.BlockDevices,
103
		},
104
		&common.StepConnectSSH{
105
			SSHAddress:     awscommon.SSHAddress(ec2conn, b.config.SSHPort),
106
			SSHConfig:      awscommon.SSHConfig(b.config.SSHUsername),
107
			SSHWaitTimeout: b.config.SSHTimeout(),
108
		},
109
		&common.StepProvision{},
110
		&stepStopInstance{},
111
		&stepCreateAMI{},
112 113 114 115 116
		&awscommon.StepModifyAMIAttributes{
			Description: b.config.AMIDescription,
			Users:       b.config.AMIUsers,
			Groups:      b.config.AMIGroups,
		},
117 118
		&awscommon.StepAMIRegionCopy{
			Regions: b.config.AMIRegions,
119 120 121 122
			Tags:    b.config.AMITags,
		},
		&awscommon.StepCreateTags{
			Tags: b.config.AMITags,
123
		},
124 125
	}

126
	// Run!
127 128 129 130 131 132 133 134 135
	if b.config.PackerDebug {
		b.runner = &multistep.DebugRunner{
			Steps:   steps,
			PauseFn: common.MultistepDebugFn(ui),
		}
	} else {
		b.runner = &multistep.BasicRunner{Steps: steps}
	}

136
	b.runner.Run(state)
137

138 139 140 141 142
	// If there was an error, return that
	if rawErr, ok := state["error"]; ok {
		return nil, rawErr.(error)
	}

Mitchell Hashimoto's avatar
fmt  
Mitchell Hashimoto committed
143
	// If there are no AMIs, then just return
144
	if _, ok := state["amis"]; !ok {
145
		return nil, nil
146 147
	}

148
	// Build the artifact and return it
149
	artifact := &awscommon.Artifact{
Mitchell Hashimoto's avatar
fmt  
Mitchell Hashimoto committed
150
		Amis:           state["amis"].(map[string]string),
151
		BuilderIdValue: BuilderId,
Mitchell Hashimoto's avatar
fmt  
Mitchell Hashimoto committed
152
		Conn:           ec2conn,
153 154 155
	}

	return artifact, nil
156
}
157 158

func (b *Builder) Cancel() {
159 160 161 162
	if b.runner != nil {
		log.Println("Cancelling the step runner...")
		b.runner.Cancel()
	}
163
}