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 }