github.com/phobos182/packer@v0.2.3-0.20130819023704-c84d2aeffc68/builder/amazon/ebs/builder.go (about)

     1  // The amazonebs package contains a packer.Builder implementation that
     2  // builds AMIs for Amazon EC2.
     3  //
     4  // In general, there are two types of AMIs that can be created: ebs-backed or
     5  // 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"
    15  	"log"
    16  )
    17  
    18  // The unique ID for this builder
    19  const BuilderId = "mitchellh.amazonebs"
    20  
    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"`
    27  
    28  	// Tags for the AMI
    29  	Tags map[string]string
    30  
    31  	tpl *packer.ConfigTemplate
    32  }
    33  
    34  type Builder struct {
    35  	config config
    36  	runner multistep.Runner
    37  }
    38  
    39  func (b *Builder) Prepare(raws ...interface{}) error {
    40  	md, err := common.DecodeConfig(&b.config, raws...)
    41  	if err != nil {
    42  		return err
    43  	}
    44  
    45  	b.config.tpl, err = packer.NewConfigTemplate()
    46  	if err != nil {
    47  		return err
    48  	}
    49  	b.config.tpl.UserVars = b.config.PackerUserVars
    50  
    51  	// Accumulate any errors
    52  	errs := common.CheckUnusedConfig(md)
    53  	errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(b.config.tpl)...)
    54  	errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(b.config.tpl)...)
    55  	errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(b.config.tpl)...)
    56  
    57  	// Accumulate any errors
    58  	newTags := make(map[string]string)
    59  	for k, v := range b.config.Tags {
    60  		k, err = b.config.tpl.Process(k, nil)
    61  		if err != nil {
    62  			errs = packer.MultiErrorAppend(errs,
    63  				fmt.Errorf("Error processing tag key %s: %s", k, err))
    64  			continue
    65  		}
    66  
    67  		v, err = b.config.tpl.Process(v, nil)
    68  		if err != nil {
    69  			errs = packer.MultiErrorAppend(errs,
    70  				fmt.Errorf("Error processing tag value '%s': %s", v, err))
    71  			continue
    72  		}
    73  
    74  		newTags[k] = v
    75  	}
    76  
    77  	b.config.Tags = newTags
    78  
    79  	if errs != nil && len(errs.Errors) > 0 {
    80  		return errs
    81  	}
    82  
    83  	log.Printf("Config: %+v", b.config)
    84  	return nil
    85  }
    86  
    87  func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
    88  	region, err := b.config.Region()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	auth, err := b.config.AccessConfig.Auth()
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	ec2conn := ec2.New(auth, region)
    99  
   100  	// Setup the state bag and initial state for the steps
   101  	state := make(map[string]interface{})
   102  	state["config"] = b.config
   103  	state["ec2"] = ec2conn
   104  	state["hook"] = hook
   105  	state["ui"] = ui
   106  
   107  	// Build the steps
   108  	steps := []multistep.Step{
   109  		&awscommon.StepKeyPair{},
   110  		&awscommon.StepSecurityGroup{
   111  			SecurityGroupId: b.config.SecurityGroupId,
   112  			SSHPort:         b.config.SSHPort,
   113  			VpcId:           b.config.VpcId,
   114  		},
   115  		&awscommon.StepRunSourceInstance{
   116  			ExpectedRootDevice: "ebs",
   117  			InstanceType:       b.config.InstanceType,
   118  			UserData:           b.config.UserData,
   119  			UserDataFile:       b.config.UserDataFile,
   120  			SourceAMI:          b.config.SourceAmi,
   121  			IamInstanceProfile: b.config.IamInstanceProfile,
   122  			SubnetId:           b.config.SubnetId,
   123  			BlockDevices:       b.config.BlockDevices,
   124  		},
   125  		&common.StepConnectSSH{
   126  			SSHAddress:     awscommon.SSHAddress(ec2conn, b.config.SSHPort),
   127  			SSHConfig:      awscommon.SSHConfig(b.config.SSHUsername),
   128  			SSHWaitTimeout: b.config.SSHTimeout(),
   129  		},
   130  		&common.StepProvision{},
   131  		&stepStopInstance{},
   132  		&stepCreateAMI{},
   133  		&awscommon.StepCreateTags{Tags: b.config.Tags},
   134  		&awscommon.StepModifyAMIAttributes{
   135  			Description: b.config.AMIDescription,
   136  			Users:       b.config.AMIUsers,
   137  			Groups:      b.config.AMIGroups,
   138  		},
   139  	}
   140  
   141  	// Run!
   142  	if b.config.PackerDebug {
   143  		b.runner = &multistep.DebugRunner{
   144  			Steps:   steps,
   145  			PauseFn: common.MultistepDebugFn(ui),
   146  		}
   147  	} else {
   148  		b.runner = &multistep.BasicRunner{Steps: steps}
   149  	}
   150  
   151  	b.runner.Run(state)
   152  
   153  	// If there was an error, return that
   154  	if rawErr, ok := state["error"]; ok {
   155  		return nil, rawErr.(error)
   156  	}
   157  
   158  	// If there are no AMIs, then just return
   159  	if _, ok := state["amis"]; !ok {
   160  		return nil, nil
   161  	}
   162  
   163  	// Build the artifact and return it
   164  	artifact := &awscommon.Artifact{
   165  		Amis:           state["amis"].(map[string]string),
   166  		BuilderIdValue: BuilderId,
   167  		Conn:           ec2conn,
   168  	}
   169  
   170  	return artifact, nil
   171  }
   172  
   173  func (b *Builder) Cancel() {
   174  	if b.runner != nil {
   175  		log.Println("Cancelling the step runner...")
   176  		b.runner.Cancel()
   177  	}
   178  }