github.com/emate/packer@v0.8.1-0.20150625195101-fe0fde195dc6/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/aws/aws-sdk-go/service/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/helper/communicator"
    17  	"github.com/mitchellh/packer/helper/config"
    18  	"github.com/mitchellh/packer/packer"
    19  	"github.com/mitchellh/packer/template/interpolate"
    20  )
    21  
    22  // The unique ID for this builder
    23  const BuilderId = "mitchellh.amazonebs"
    24  
    25  type Config struct {
    26  	common.PackerConfig    `mapstructure:",squash"`
    27  	awscommon.AccessConfig `mapstructure:",squash"`
    28  	awscommon.AMIConfig    `mapstructure:",squash"`
    29  	awscommon.BlockDevices `mapstructure:",squash"`
    30  	awscommon.RunConfig    `mapstructure:",squash"`
    31  
    32  	ctx interpolate.Context
    33  }
    34  
    35  type Builder struct {
    36  	config Config
    37  	runner multistep.Runner
    38  }
    39  
    40  func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
    41  	b.config.ctx.Funcs = awscommon.TemplateFuncs
    42  	err := config.Decode(&b.config, &config.DecodeOpts{
    43  		Interpolate:        true,
    44  		InterpolateContext: &b.config.ctx,
    45  	}, raws...)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	// Accumulate any errors
    51  	var errs *packer.MultiError
    52  	errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
    53  	errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...)
    54  	errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(&b.config.ctx)...)
    55  	errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
    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  	config, err := b.config.Config()
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	ec2conn := ec2.New(config)
    72  
    73  	// Setup the state bag and initial state for the steps
    74  	state := new(multistep.BasicStateBag)
    75  	state.Put("config", b.config)
    76  	state.Put("ec2", ec2conn)
    77  	state.Put("hook", hook)
    78  	state.Put("ui", ui)
    79  
    80  	// Build the steps
    81  	steps := []multistep.Step{
    82  		&awscommon.StepPreValidate{
    83  			DestAmiName:     b.config.AMIName,
    84  			ForceDeregister: b.config.AMIForceDeregister,
    85  		},
    86  		&awscommon.StepSourceAMIInfo{
    87  			SourceAmi:          b.config.SourceAmi,
    88  			EnhancedNetworking: b.config.AMIEnhancedNetworking,
    89  		},
    90  		&awscommon.StepKeyPair{
    91  			Debug:                b.config.PackerDebug,
    92  			DebugKeyPath:         fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
    93  			KeyPairName:          b.config.SSHKeyPairName,
    94  			TemporaryKeyPairName: b.config.TemporaryKeyPairName,
    95  			PrivateKeyFile:       b.config.RunConfig.Comm.SSHPrivateKey,
    96  		},
    97  		&awscommon.StepSecurityGroup{
    98  			SecurityGroupIds: b.config.SecurityGroupIds,
    99  			CommConfig:       &b.config.RunConfig.Comm,
   100  			VpcId:            b.config.VpcId,
   101  		},
   102  		&stepCleanupVolumes{
   103  			BlockDevices: b.config.BlockDevices,
   104  		},
   105  		&awscommon.StepRunSourceInstance{
   106  			Debug:                    b.config.PackerDebug,
   107  			ExpectedRootDevice:       "ebs",
   108  			SpotPrice:                b.config.SpotPrice,
   109  			SpotPriceProduct:         b.config.SpotPriceAutoProduct,
   110  			InstanceType:             b.config.InstanceType,
   111  			UserData:                 b.config.UserData,
   112  			UserDataFile:             b.config.UserDataFile,
   113  			SourceAMI:                b.config.SourceAmi,
   114  			IamInstanceProfile:       b.config.IamInstanceProfile,
   115  			SubnetId:                 b.config.SubnetId,
   116  			AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
   117  			AvailabilityZone:         b.config.AvailabilityZone,
   118  			BlockDevices:             b.config.BlockDevices,
   119  			Tags:                     b.config.RunTags,
   120  		},
   121  		&awscommon.StepGetPassword{
   122  			Comm:    &b.config.RunConfig.Comm,
   123  			Timeout: b.config.WindowsPasswordTimeout,
   124  		},
   125  		&communicator.StepConnect{
   126  			Config: &b.config.RunConfig.Comm,
   127  			Host: awscommon.SSHHost(
   128  				ec2conn,
   129  				b.config.SSHPrivateIp),
   130  			SSHConfig: awscommon.SSHConfig(
   131  				b.config.RunConfig.Comm.SSHUsername),
   132  		},
   133  		&common.StepProvision{},
   134  		&stepStopInstance{SpotPrice: b.config.SpotPrice},
   135  		// TODO(mitchellh): verify works with spots
   136  		&stepModifyInstance{},
   137  		&awscommon.StepDeregisterAMI{
   138  			ForceDeregister: b.config.AMIForceDeregister,
   139  			AMIName:         b.config.AMIName,
   140  		},
   141  		&stepCreateAMI{},
   142  		&awscommon.StepAMIRegionCopy{
   143  			AccessConfig: &b.config.AccessConfig,
   144  			Regions:      b.config.AMIRegions,
   145  			Name:         b.config.AMIName,
   146  		},
   147  		&awscommon.StepModifyAMIAttributes{
   148  			Description: b.config.AMIDescription,
   149  			Users:       b.config.AMIUsers,
   150  			Groups:      b.config.AMIGroups,
   151  		},
   152  		&awscommon.StepCreateTags{
   153  			Tags: b.config.AMITags,
   154  		},
   155  	}
   156  
   157  	// Run!
   158  	if b.config.PackerDebug {
   159  		b.runner = &multistep.DebugRunner{
   160  			Steps:   steps,
   161  			PauseFn: common.MultistepDebugFn(ui),
   162  		}
   163  	} else {
   164  		b.runner = &multistep.BasicRunner{Steps: steps}
   165  	}
   166  
   167  	b.runner.Run(state)
   168  
   169  	// If there was an error, return that
   170  	if rawErr, ok := state.GetOk("error"); ok {
   171  		return nil, rawErr.(error)
   172  	}
   173  
   174  	// If there are no AMIs, then just return
   175  	if _, ok := state.GetOk("amis"); !ok {
   176  		return nil, nil
   177  	}
   178  
   179  	// Build the artifact and return it
   180  	artifact := &awscommon.Artifact{
   181  		Amis:           state.Get("amis").(map[string]string),
   182  		BuilderIdValue: BuilderId,
   183  		Conn:           ec2conn,
   184  	}
   185  
   186  	return artifact, nil
   187  }
   188  
   189  func (b *Builder) Cancel() {
   190  	if b.runner != nil {
   191  		log.Println("Cancelling the step runner...")
   192  		b.runner.Cancel()
   193  	}
   194  }