github.com/daniellockard/packer@v0.7.6-0.20141210173435-5a9390934716/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  	"log"
    11  
    12  	"github.com/mitchellh/goamz/ec2"
    13  	"github.com/mitchellh/multistep"
    14  	awscommon "github.com/mitchellh/packer/builder/amazon/common"
    15  	"github.com/mitchellh/packer/common"
    16  	"github.com/mitchellh/packer/packer"
    17  )
    18  
    19  // The unique ID for this builder
    20  const BuilderId = "mitchellh.amazonebs"
    21  
    22  type config struct {
    23  	common.PackerConfig    `mapstructure:",squash"`
    24  	awscommon.AccessConfig `mapstructure:",squash"`
    25  	awscommon.AMIConfig    `mapstructure:",squash"`
    26  	awscommon.BlockDevices `mapstructure:",squash"`
    27  	awscommon.RunConfig    `mapstructure:",squash"`
    28  
    29  	tpl *packer.ConfigTemplate
    30  }
    31  
    32  type Builder struct {
    33  	config config
    34  	runner multistep.Runner
    35  }
    36  
    37  func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
    38  	md, err := common.DecodeConfig(&b.config, raws...)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	b.config.tpl, err = packer.NewConfigTemplate()
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	b.config.tpl.UserVars = b.config.PackerUserVars
    48  	b.config.tpl.Funcs(awscommon.TemplateFuncs)
    49  
    50  	// Accumulate any errors
    51  	errs := common.CheckUnusedConfig(md)
    52  	errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(b.config.tpl)...)
    53  	errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.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  	if errs != nil && len(errs.Errors) > 0 {
    58  		return nil, errs
    59  	}
    60  
    61  	log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey))
    62  	return nil, nil
    63  }
    64  
    65  func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
    66  	region, err := b.config.Region()
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	auth, err := b.config.AccessConfig.Auth()
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	ec2conn := ec2.New(auth, region)
    77  
    78  	// Setup the state bag and initial state for the steps
    79  	state := new(multistep.BasicStateBag)
    80  	state.Put("config", b.config)
    81  	state.Put("ec2", ec2conn)
    82  	state.Put("hook", hook)
    83  	state.Put("ui", ui)
    84  
    85  	// Build the steps
    86  	steps := []multistep.Step{
    87  		&awscommon.StepSourceAMIInfo{
    88  			SourceAmi:          b.config.SourceAmi,
    89  			EnhancedNetworking: b.config.AMIEnhancedNetworking,
    90  		},
    91  		&awscommon.StepKeyPair{
    92  			Debug:          b.config.PackerDebug,
    93  			DebugKeyPath:   fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
    94  			KeyPairName:    b.config.TemporaryKeyPairName,
    95  			PrivateKeyFile: b.config.SSHPrivateKeyFile,
    96  		},
    97  		&awscommon.StepSecurityGroup{
    98  			SecurityGroupIds: b.config.SecurityGroupIds,
    99  			SSHPort:          b.config.SSHPort,
   100  			VpcId:            b.config.VpcId,
   101  		},
   102  		&awscommon.StepRunSourceInstance{
   103  			Debug:                    b.config.PackerDebug,
   104  			ExpectedRootDevice:       "ebs",
   105  			SpotPrice:                b.config.SpotPrice,
   106  			SpotPriceProduct:         b.config.SpotPriceAutoProduct,
   107  			InstanceType:             b.config.InstanceType,
   108  			UserData:                 b.config.UserData,
   109  			UserDataFile:             b.config.UserDataFile,
   110  			SourceAMI:                b.config.SourceAmi,
   111  			IamInstanceProfile:       b.config.IamInstanceProfile,
   112  			SubnetId:                 b.config.SubnetId,
   113  			AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
   114  			AvailabilityZone:         b.config.AvailabilityZone,
   115  			BlockDevices:             b.config.BlockDevices,
   116  			Tags:                     b.config.RunTags,
   117  		},
   118  		&common.StepConnectSSH{
   119  			SSHAddress: awscommon.SSHAddress(
   120  				ec2conn, b.config.SSHPort, b.config.SSHPrivateIp),
   121  			SSHConfig:      awscommon.SSHConfig(b.config.SSHUsername),
   122  			SSHWaitTimeout: b.config.SSHTimeout(),
   123  		},
   124  		&common.StepProvision{},
   125  		&stepStopInstance{SpotPrice: b.config.SpotPrice},
   126  		// TODO(mitchellh): verify works with spots
   127  		&stepModifyInstance{},
   128  		&stepCreateAMI{},
   129  		&awscommon.StepAMIRegionCopy{
   130  			Regions: b.config.AMIRegions,
   131  		},
   132  		&awscommon.StepModifyAMIAttributes{
   133  			Description: b.config.AMIDescription,
   134  			Users:       b.config.AMIUsers,
   135  			Groups:      b.config.AMIGroups,
   136  		},
   137  		&awscommon.StepCreateTags{
   138  			Tags: b.config.AMITags,
   139  		},
   140  	}
   141  
   142  	// Run!
   143  	if b.config.PackerDebug {
   144  		b.runner = &multistep.DebugRunner{
   145  			Steps:   steps,
   146  			PauseFn: common.MultistepDebugFn(ui),
   147  		}
   148  	} else {
   149  		b.runner = &multistep.BasicRunner{Steps: steps}
   150  	}
   151  
   152  	b.runner.Run(state)
   153  
   154  	// If there was an error, return that
   155  	if rawErr, ok := state.GetOk("error"); ok {
   156  		return nil, rawErr.(error)
   157  	}
   158  
   159  	// If there are no AMIs, then just return
   160  	if _, ok := state.GetOk("amis"); !ok {
   161  		return nil, nil
   162  	}
   163  
   164  	// Build the artifact and return it
   165  	artifact := &awscommon.Artifact{
   166  		Amis:           state.Get("amis").(map[string]string),
   167  		BuilderIdValue: BuilderId,
   168  		Conn:           ec2conn,
   169  	}
   170  
   171  	return artifact, nil
   172  }
   173  
   174  func (b *Builder) Cancel() {
   175  	if b.runner != nil {
   176  		log.Println("Cancelling the step runner...")
   177  		b.runner.Cancel()
   178  	}
   179  }