github.com/supr/packer@v0.3.10-0.20131015195147-7b09e24ac3c1/post-processor/vagrant/post-processor.go (about)

     1  // vagrant implements the packer.PostProcessor interface and adds a
     2  // post-processor that turns artifacts of known builders into Vagrant
     3  // boxes.
     4  package vagrant
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/mitchellh/mapstructure"
     9  	"github.com/mitchellh/packer/common"
    10  	"github.com/mitchellh/packer/packer"
    11  	"log"
    12  )
    13  
    14  var builtins = map[string]string{
    15  	"mitchellh.amazonebs":       "aws",
    16  	"mitchellh.amazon.instance": "aws",
    17  	"mitchellh.virtualbox":      "virtualbox",
    18  	"mitchellh.vmware":          "vmware",
    19  }
    20  
    21  type Config struct {
    22  	common.PackerConfig `mapstructure:",squash"`
    23  
    24  	OutputPath string `mapstructure:"output"`
    25  }
    26  
    27  type PostProcessor struct {
    28  	config      Config
    29  	premade     map[string]packer.PostProcessor
    30  	extraConfig map[string]interface{}
    31  }
    32  
    33  func (p *PostProcessor) Configure(raws ...interface{}) error {
    34  	_, err := common.DecodeConfig(&p.config, raws...)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	tpl, err := packer.NewConfigTemplate()
    40  	if err != nil {
    41  		return err
    42  	}
    43  	tpl.UserVars = p.config.PackerUserVars
    44  
    45  	// Defaults
    46  	if p.config.OutputPath == "" {
    47  		p.config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box"
    48  	}
    49  
    50  	// Accumulate any errors
    51  	errs := new(packer.MultiError)
    52  	if err := tpl.Validate(p.config.OutputPath); err != nil {
    53  		errs = packer.MultiErrorAppend(
    54  			errs, fmt.Errorf("Error parsing output template: %s", err))
    55  	}
    56  
    57  	// Store extra configuration we'll send to each post-processor type
    58  	p.extraConfig = make(map[string]interface{})
    59  	p.extraConfig["output"] = p.config.OutputPath
    60  	p.extraConfig["packer_build_name"] = p.config.PackerBuildName
    61  	p.extraConfig["packer_builder_type"] = p.config.PackerBuilderType
    62  	p.extraConfig["packer_debug"] = p.config.PackerDebug
    63  	p.extraConfig["packer_force"] = p.config.PackerForce
    64  	p.extraConfig["packer_user_variables"] = p.config.PackerUserVars
    65  
    66  	// TODO(mitchellh): Properly handle multiple raw configs. This isn't
    67  	// very pressing at the moment because at the time of this comment
    68  	// only the first member of raws can contain the actual type-overrides.
    69  	var mapConfig map[string]interface{}
    70  	if err := mapstructure.Decode(raws[0], &mapConfig); err != nil {
    71  		errs = packer.MultiErrorAppend(errs,
    72  			fmt.Errorf("Failed to decode config: %s", err))
    73  		return errs
    74  	}
    75  
    76  	p.premade = make(map[string]packer.PostProcessor)
    77  	for k, raw := range mapConfig {
    78  		pp, err := p.subPostProcessor(k, raw, p.extraConfig)
    79  		if err != nil {
    80  			errs = packer.MultiErrorAppend(errs, err)
    81  			continue
    82  		}
    83  
    84  		if pp == nil {
    85  			continue
    86  		}
    87  
    88  		p.premade[k] = pp
    89  	}
    90  
    91  	if len(errs.Errors) > 0 {
    92  		return errs
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
    99  	ppName, ok := builtins[artifact.BuilderId()]
   100  	if !ok {
   101  		return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
   102  	}
   103  
   104  	// Use the premade PostProcessor if we have one. Otherwise, we
   105  	// create it and configure it here.
   106  	pp, ok := p.premade[ppName]
   107  	if !ok {
   108  		log.Printf("Premade post-processor for '%s' not found. Creating.", ppName)
   109  
   110  		var err error
   111  		pp, err = p.subPostProcessor(ppName, nil, p.extraConfig)
   112  		if err != nil {
   113  			return nil, false, err
   114  		}
   115  
   116  		if pp == nil {
   117  			return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName)
   118  		}
   119  	}
   120  
   121  	ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", ppName))
   122  	return pp.PostProcess(ui, artifact)
   123  }
   124  
   125  func (p *PostProcessor) subPostProcessor(key string, specific interface{}, extra map[string]interface{}) (packer.PostProcessor, error) {
   126  	pp := keyToPostProcessor(key)
   127  	if pp == nil {
   128  		return nil, nil
   129  	}
   130  
   131  	if err := pp.Configure(extra, specific); err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	return pp, nil
   136  }
   137  
   138  // keyToPostProcessor maps a configuration key to the actual post-processor
   139  // it will be configuring. This returns a new instance of that post-processor.
   140  func keyToPostProcessor(key string) packer.PostProcessor {
   141  	switch key {
   142  	case "aws":
   143  		return new(AWSBoxPostProcessor)
   144  	case "virtualbox":
   145  		return new(VBoxBoxPostProcessor)
   146  	case "vmware":
   147  		return new(VMwareBoxPostProcessor)
   148  	default:
   149  		return nil
   150  	}
   151  }