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