github.com/phobos182/packer@v0.2.3-0.20130819023704-c84d2aeffc68/packer/template.go (about) 1 package packer 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/mitchellh/mapstructure" 7 jsonutil "github.com/mitchellh/packer/common/json" 8 "io" 9 "io/ioutil" 10 "os" 11 "sort" 12 ) 13 14 // The rawTemplate struct represents the structure of a template read 15 // directly from a file. The builders and other components map just to 16 // "interface{}" pointers since we actually don't know what their contents 17 // are until we read the "type" field. 18 type rawTemplate struct { 19 Variables map[string]string 20 Builders []map[string]interface{} 21 Hooks map[string][]string 22 Provisioners []map[string]interface{} 23 PostProcessors []interface{} `mapstructure:"post-processors"` 24 } 25 26 // The Template struct represents a parsed template, parsed into the most 27 // completed form it can be without additional processing by the caller. 28 type Template struct { 29 Variables map[string]string 30 Builders map[string]RawBuilderConfig 31 Hooks map[string][]string 32 PostProcessors [][]RawPostProcessorConfig 33 Provisioners []RawProvisionerConfig 34 } 35 36 // The RawBuilderConfig struct represents a raw, unprocessed builder 37 // configuration. It contains the name of the builder as well as the 38 // raw configuration. If requested, this is used to compile into a full 39 // builder configuration at some point. 40 type RawBuilderConfig struct { 41 Name string 42 Type string 43 44 RawConfig interface{} 45 } 46 47 // RawPostProcessorConfig represents a raw, unprocessed post-processor 48 // configuration. It contains the type of the post processor as well as the 49 // raw configuration that is handed to the post-processor for it to process. 50 type RawPostProcessorConfig struct { 51 Type string 52 KeepInputArtifact bool `mapstructure:"keep_input_artifact"` 53 RawConfig interface{} 54 } 55 56 // RawProvisionerConfig represents a raw, unprocessed provisioner configuration. 57 // It contains the type of the provisioner as well as the raw configuration 58 // that is handed to the provisioner for it to process. 59 type RawProvisionerConfig struct { 60 Type string 61 Override map[string]interface{} 62 63 RawConfig interface{} 64 } 65 66 // ParseTemplate takes a byte slice and parses a Template from it, returning 67 // the template and possibly errors while loading the template. The error 68 // could potentially be a MultiError, representing multiple errors. Knowing 69 // and checking for this can be useful, if you wish to format it in a certain 70 // way. 71 func ParseTemplate(data []byte) (t *Template, err error) { 72 var rawTplInterface interface{} 73 err = jsonutil.Unmarshal(data, &rawTplInterface) 74 if err != nil { 75 return 76 } 77 78 // Decode the raw template interface into the actual rawTemplate 79 // structure, checking for any extranneous keys along the way. 80 var md mapstructure.Metadata 81 var rawTpl rawTemplate 82 decoderConfig := &mapstructure.DecoderConfig{ 83 Metadata: &md, 84 Result: &rawTpl, 85 } 86 87 decoder, err := mapstructure.NewDecoder(decoderConfig) 88 if err != nil { 89 return 90 } 91 92 err = decoder.Decode(rawTplInterface) 93 if err != nil { 94 return 95 } 96 97 errors := make([]error, 0) 98 99 if len(md.Unused) > 0 { 100 sort.Strings(md.Unused) 101 for _, unused := range md.Unused { 102 errors = append( 103 errors, fmt.Errorf("Unknown root level key in template: '%s'", unused)) 104 } 105 } 106 107 t = &Template{} 108 t.Variables = make(map[string]string) 109 t.Builders = make(map[string]RawBuilderConfig) 110 t.Hooks = rawTpl.Hooks 111 t.PostProcessors = make([][]RawPostProcessorConfig, len(rawTpl.PostProcessors)) 112 t.Provisioners = make([]RawProvisionerConfig, len(rawTpl.Provisioners)) 113 114 // Gather all the variables 115 for k, v := range rawTpl.Variables { 116 t.Variables[k] = v 117 } 118 119 // Gather all the builders 120 for i, v := range rawTpl.Builders { 121 var raw RawBuilderConfig 122 if err := mapstructure.Decode(v, &raw); err != nil { 123 if merr, ok := err.(*mapstructure.Error); ok { 124 for _, err := range merr.Errors { 125 errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err)) 126 } 127 } else { 128 errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err)) 129 } 130 131 continue 132 } 133 134 if raw.Type == "" { 135 errors = append(errors, fmt.Errorf("builder %d: missing 'type'", i+1)) 136 continue 137 } 138 139 // Attempt to get the name of the builder. If the "name" key 140 // missing, use the "type" field, which is guaranteed to exist 141 // at this point. 142 if raw.Name == "" { 143 raw.Name = raw.Type 144 } 145 146 // Check if we already have a builder with this name and error if so 147 if _, ok := t.Builders[raw.Name]; ok { 148 errors = append(errors, fmt.Errorf("builder with name '%s' already exists", raw.Name)) 149 continue 150 } 151 152 // Now that we have the name, remove it from the config - as the builder 153 // itself doesn't know about, and it will cause a validation error. 154 delete(v, "name") 155 156 raw.RawConfig = v 157 158 t.Builders[raw.Name] = raw 159 } 160 161 // Gather all the post-processors. This is a complicated process since there 162 // are actually three different formats that the user can use to define 163 // a post-processor. 164 for i, rawV := range rawTpl.PostProcessors { 165 rawPP, err := parsePostProvisioner(i, rawV) 166 if err != nil { 167 errors = append(errors, err...) 168 continue 169 } 170 171 t.PostProcessors[i] = make([]RawPostProcessorConfig, len(rawPP)) 172 configs := t.PostProcessors[i] 173 for j, pp := range rawPP { 174 config := &configs[j] 175 if err := mapstructure.Decode(pp, config); err != nil { 176 if merr, ok := err.(*mapstructure.Error); ok { 177 for _, err := range merr.Errors { 178 errors = append(errors, fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err)) 179 } 180 } else { 181 errors = append(errors, fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err)) 182 } 183 184 continue 185 } 186 187 if config.Type == "" { 188 errors = append(errors, fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1)) 189 continue 190 } 191 192 config.RawConfig = pp 193 } 194 } 195 196 // Gather all the provisioners 197 for i, v := range rawTpl.Provisioners { 198 raw := &t.Provisioners[i] 199 if err := mapstructure.Decode(v, raw); err != nil { 200 if merr, ok := err.(*mapstructure.Error); ok { 201 for _, err := range merr.Errors { 202 errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err)) 203 } 204 } else { 205 errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err)) 206 } 207 208 continue 209 } 210 211 if raw.Type == "" { 212 errors = append(errors, fmt.Errorf("provisioner %d: missing 'type'", i+1)) 213 continue 214 } 215 216 // The provisioners not only don't need or want the override settings 217 // (as they are processed as part of the preparation below), but will 218 // actively reject them as invalid configuration. 219 delete(v, "override") 220 221 raw.RawConfig = v 222 } 223 224 if len(t.Builders) == 0 { 225 errors = append(errors, fmt.Errorf("No builders are defined in the template.")) 226 } 227 228 // If there were errors, we put it into a MultiError and return 229 if len(errors) > 0 { 230 err = &MultiError{errors} 231 t = nil 232 return 233 } 234 235 return 236 } 237 238 // ParseTemplateFile takes the given template file and parses it into 239 // a single template. 240 func ParseTemplateFile(path string) (*Template, error) { 241 var data []byte 242 243 if path == "-" { 244 // Read from stdin... 245 buf := new(bytes.Buffer) 246 _, err := io.Copy(buf, os.Stdin) 247 if err != nil { 248 return nil, err 249 } 250 251 data = buf.Bytes() 252 } else { 253 var err error 254 data, err = ioutil.ReadFile(path) 255 if err != nil { 256 return nil, err 257 } 258 } 259 260 return ParseTemplate(data) 261 } 262 263 func parsePostProvisioner(i int, rawV interface{}) (result []map[string]interface{}, errors []error) { 264 switch v := rawV.(type) { 265 case string: 266 result = []map[string]interface{}{ 267 {"type": v}, 268 } 269 case map[string]interface{}: 270 result = []map[string]interface{}{v} 271 case []interface{}: 272 result = make([]map[string]interface{}, len(v)) 273 errors = make([]error, 0) 274 for j, innerRawV := range v { 275 switch innerV := innerRawV.(type) { 276 case string: 277 result[j] = map[string]interface{}{"type": innerV} 278 case map[string]interface{}: 279 result[j] = innerV 280 case []interface{}: 281 errors = append( 282 errors, 283 fmt.Errorf("Post-processor %d.%d: sequences not allowed to be nested in sequences", i+1, j+1)) 284 default: 285 errors = append(errors, fmt.Errorf("Post-processor %d.%d is in a bad format.", i+1, j+1)) 286 } 287 } 288 289 if len(errors) == 0 { 290 errors = nil 291 } 292 default: 293 result = nil 294 errors = []error{fmt.Errorf("Post-processor %d is in a bad format.", i+1)} 295 } 296 297 return 298 } 299 300 // BuildNames returns a slice of the available names of builds that 301 // this template represents. 302 func (t *Template) BuildNames() []string { 303 names := make([]string, 0, len(t.Builders)) 304 for name, _ := range t.Builders { 305 names = append(names, name) 306 } 307 308 return names 309 } 310 311 // Build returns a Build for the given name. 312 // 313 // If the build does not exist as part of this template, an error is 314 // returned. 315 func (t *Template) Build(name string, components *ComponentFinder) (b Build, err error) { 316 // Setup the Builder 317 builderConfig, ok := t.Builders[name] 318 if !ok { 319 err = fmt.Errorf("No such build found in template: %s", name) 320 return 321 } 322 323 // We panic if there is no builder function because this is really 324 // an internal bug that always needs to be fixed, not an error. 325 if components.Builder == nil { 326 panic("no builder function") 327 } 328 329 // Panic if there are provisioners on the template but no provisioner 330 // component finder. This is always an internal error, so we panic. 331 if len(t.Provisioners) > 0 && components.Provisioner == nil { 332 panic("no provisioner function") 333 } 334 335 builder, err := components.Builder(builderConfig.Type) 336 if err != nil { 337 return 338 } 339 340 if builder == nil { 341 err = fmt.Errorf("Builder type not found: %s", builderConfig.Type) 342 return 343 } 344 345 // Gather the Hooks 346 hooks := make(map[string][]Hook) 347 for tplEvent, tplHooks := range t.Hooks { 348 curHooks := make([]Hook, 0, len(tplHooks)) 349 350 for _, hookName := range tplHooks { 351 var hook Hook 352 hook, err = components.Hook(hookName) 353 if err != nil { 354 return 355 } 356 357 if hook == nil { 358 err = fmt.Errorf("Hook not found: %s", hookName) 359 return 360 } 361 362 curHooks = append(curHooks, hook) 363 } 364 365 hooks[tplEvent] = curHooks 366 } 367 368 // Prepare the post-processors 369 postProcessors := make([][]coreBuildPostProcessor, 0, len(t.PostProcessors)) 370 for _, rawPPs := range t.PostProcessors { 371 current := make([]coreBuildPostProcessor, len(rawPPs)) 372 for i, rawPP := range rawPPs { 373 pp, err := components.PostProcessor(rawPP.Type) 374 if err != nil { 375 return nil, err 376 } 377 378 if pp == nil { 379 return nil, fmt.Errorf("PostProcessor type not found: %s", rawPP.Type) 380 } 381 382 current[i] = coreBuildPostProcessor{ 383 processor: pp, 384 processorType: rawPP.Type, 385 config: rawPP.RawConfig, 386 keepInputArtifact: rawPP.KeepInputArtifact, 387 } 388 } 389 390 postProcessors = append(postProcessors, current) 391 } 392 393 // Prepare the provisioners 394 provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners)) 395 for _, rawProvisioner := range t.Provisioners { 396 var provisioner Provisioner 397 provisioner, err = components.Provisioner(rawProvisioner.Type) 398 if err != nil { 399 return 400 } 401 402 if provisioner == nil { 403 err = fmt.Errorf("Provisioner type not found: %s", rawProvisioner.Type) 404 return 405 } 406 407 configs := make([]interface{}, 1, 2) 408 configs[0] = rawProvisioner.RawConfig 409 410 if rawProvisioner.Override != nil { 411 if override, ok := rawProvisioner.Override[name]; ok { 412 configs = append(configs, override) 413 } 414 } 415 416 coreProv := coreBuildProvisioner{provisioner, configs} 417 provisioners = append(provisioners, coreProv) 418 } 419 420 b = &coreBuild{ 421 name: name, 422 builder: builder, 423 builderConfig: builderConfig.RawConfig, 424 builderType: builderConfig.Type, 425 hooks: hooks, 426 postProcessors: postProcessors, 427 provisioners: provisioners, 428 variables: t.Variables, 429 } 430 431 return 432 }