github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/packer/template.go (about) 1 package packer 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/hashicorp/go-version" 7 "github.com/mitchellh/mapstructure" 8 jsonutil "github.com/mitchellh/packer/common/json" 9 "io" 10 "io/ioutil" 11 "os" 12 "sort" 13 "text/template" 14 "time" 15 ) 16 17 // The rawTemplate struct represents the structure of a template read 18 // directly from a file. The builders and other components map just to 19 // "interface{}" pointers since we actually don't know what their contents 20 // are until we read the "type" field. 21 type rawTemplate struct { 22 MinimumPackerVersion string `mapstructure:"min_packer_version"` 23 24 Description string 25 Builders []map[string]interface{} 26 Hooks map[string][]string 27 PostProcessors []interface{} `mapstructure:"post-processors"` 28 Provisioners []map[string]interface{} 29 Variables map[string]interface{} 30 } 31 32 // The Template struct represents a parsed template, parsed into the most 33 // completed form it can be without additional processing by the caller. 34 type Template struct { 35 Description string 36 Variables map[string]RawVariable 37 Builders map[string]RawBuilderConfig 38 Hooks map[string][]string 39 PostProcessors [][]RawPostProcessorConfig 40 Provisioners []RawProvisionerConfig 41 } 42 43 // The RawBuilderConfig struct represents a raw, unprocessed builder 44 // configuration. It contains the name of the builder as well as the 45 // raw configuration. If requested, this is used to compile into a full 46 // builder configuration at some point. 47 type RawBuilderConfig struct { 48 Name string 49 Type string 50 51 RawConfig interface{} 52 } 53 54 // RawPostProcessorConfig represents a raw, unprocessed post-processor 55 // configuration. It contains the type of the post processor as well as the 56 // raw configuration that is handed to the post-processor for it to process. 57 type RawPostProcessorConfig struct { 58 TemplateOnlyExcept `mapstructure:",squash"` 59 60 Type string 61 KeepInputArtifact bool `mapstructure:"keep_input_artifact"` 62 RawConfig map[string]interface{} 63 } 64 65 // RawProvisionerConfig represents a raw, unprocessed provisioner configuration. 66 // It contains the type of the provisioner as well as the raw configuration 67 // that is handed to the provisioner for it to process. 68 type RawProvisionerConfig struct { 69 TemplateOnlyExcept `mapstructure:",squash"` 70 71 Type string 72 Override map[string]interface{} 73 RawPauseBefore string `mapstructure:"pause_before"` 74 75 RawConfig interface{} 76 77 pauseBefore time.Duration 78 } 79 80 // RawVariable represents a variable configuration within a template. 81 type RawVariable struct { 82 Default string // The default value for this variable 83 Required bool // If the variable is required or not 84 Value string // The set value for this variable 85 HasValue bool // True if the value was set 86 } 87 88 // ParseTemplate takes a byte slice and parses a Template from it, returning 89 // the template and possibly errors while loading the template. The error 90 // could potentially be a MultiError, representing multiple errors. Knowing 91 // and checking for this can be useful, if you wish to format it in a certain 92 // way. 93 // 94 // The second parameter, vars, are the values for a set of user variables. 95 func ParseTemplate(data []byte, vars map[string]string) (t *Template, err error) { 96 var rawTplInterface interface{} 97 err = jsonutil.Unmarshal(data, &rawTplInterface) 98 if err != nil { 99 return 100 } 101 102 // Decode the raw template interface into the actual rawTemplate 103 // structure, checking for any extranneous keys along the way. 104 var md mapstructure.Metadata 105 var rawTpl rawTemplate 106 decoderConfig := &mapstructure.DecoderConfig{ 107 Metadata: &md, 108 Result: &rawTpl, 109 } 110 111 decoder, err := mapstructure.NewDecoder(decoderConfig) 112 if err != nil { 113 return 114 } 115 116 err = decoder.Decode(rawTplInterface) 117 if err != nil { 118 return 119 } 120 121 if rawTpl.MinimumPackerVersion != "" { 122 vCur, err := version.NewVersion(Version) 123 if err != nil { 124 panic(err) 125 } 126 vReq, err := version.NewVersion(rawTpl.MinimumPackerVersion) 127 if err != nil { 128 return nil, fmt.Errorf( 129 "'minimum_packer_version' error: %s", err) 130 } 131 132 if vCur.LessThan(vReq) { 133 return nil, fmt.Errorf( 134 "Template requires Packer version %s. "+ 135 "Running version is %s.", 136 vReq, vCur) 137 } 138 } 139 140 errors := make([]error, 0) 141 142 if len(md.Unused) > 0 { 143 sort.Strings(md.Unused) 144 for _, unused := range md.Unused { 145 errors = append( 146 errors, fmt.Errorf("Unknown root level key in template: '%s'", unused)) 147 } 148 } 149 150 t = &Template{} 151 t.Description = rawTpl.Description 152 t.Variables = make(map[string]RawVariable) 153 t.Builders = make(map[string]RawBuilderConfig) 154 t.Hooks = rawTpl.Hooks 155 t.PostProcessors = make([][]RawPostProcessorConfig, len(rawTpl.PostProcessors)) 156 t.Provisioners = make([]RawProvisionerConfig, len(rawTpl.Provisioners)) 157 158 // Gather all the variables 159 for k, v := range rawTpl.Variables { 160 var variable RawVariable 161 variable.Required = v == nil 162 163 // Create a new mapstructure decoder in order to decode the default 164 // value since this is the only value in the regular template that 165 // can be weakly typed. 166 decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 167 Result: &variable.Default, 168 WeaklyTypedInput: true, 169 }) 170 if err != nil { 171 // This should never happen. 172 panic(err) 173 } 174 175 err = decoder.Decode(v) 176 if err != nil { 177 errors = append(errors, 178 fmt.Errorf("Error decoding default value for user var '%s': %s", k, err)) 179 continue 180 } 181 182 // Set the value of this variable if we have it 183 if val, ok := vars[k]; ok { 184 variable.HasValue = true 185 variable.Value = val 186 delete(vars, k) 187 } 188 189 t.Variables[k] = variable 190 } 191 192 // Gather all the builders 193 for i, v := range rawTpl.Builders { 194 var raw RawBuilderConfig 195 if err := mapstructure.Decode(v, &raw); err != nil { 196 if merr, ok := err.(*mapstructure.Error); ok { 197 for _, err := range merr.Errors { 198 errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err)) 199 } 200 } else { 201 errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err)) 202 } 203 204 continue 205 } 206 207 if raw.Type == "" { 208 errors = append(errors, fmt.Errorf("builder %d: missing 'type'", i+1)) 209 continue 210 } 211 212 // Attempt to get the name of the builder. If the "name" key 213 // missing, use the "type" field, which is guaranteed to exist 214 // at this point. 215 if raw.Name == "" { 216 raw.Name = raw.Type 217 } 218 219 // Check if we already have a builder with this name and error if so 220 if _, ok := t.Builders[raw.Name]; ok { 221 errors = append(errors, fmt.Errorf("builder with name '%s' already exists", raw.Name)) 222 continue 223 } 224 225 // Now that we have the name, remove it from the config - as the builder 226 // itself doesn't know about, and it will cause a validation error. 227 delete(v, "name") 228 229 raw.RawConfig = v 230 231 t.Builders[raw.Name] = raw 232 } 233 234 // Gather all the post-processors. This is a complicated process since there 235 // are actually three different formats that the user can use to define 236 // a post-processor. 237 for i, rawV := range rawTpl.PostProcessors { 238 rawPP, err := parsePostProcessor(i, rawV) 239 if err != nil { 240 errors = append(errors, err...) 241 continue 242 } 243 244 configs := make([]RawPostProcessorConfig, 0, len(rawPP)) 245 for j, pp := range rawPP { 246 var config RawPostProcessorConfig 247 if err := mapstructure.Decode(pp, &config); err != nil { 248 if merr, ok := err.(*mapstructure.Error); ok { 249 for _, err := range merr.Errors { 250 errors = append(errors, 251 fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err)) 252 } 253 } else { 254 errors = append(errors, 255 fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err)) 256 } 257 258 continue 259 } 260 261 if config.Type == "" { 262 errors = append(errors, 263 fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1)) 264 continue 265 } 266 267 // Remove the input keep_input_artifact option 268 config.TemplateOnlyExcept.Prune(pp) 269 delete(pp, "keep_input_artifact") 270 271 // Verify that the only settings are good 272 if errs := config.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 { 273 for _, err := range errs { 274 errors = append(errors, 275 fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err)) 276 } 277 278 continue 279 } 280 281 config.RawConfig = pp 282 283 // Add it to the list of configs 284 configs = append(configs, config) 285 } 286 287 t.PostProcessors[i] = configs 288 } 289 290 // Gather all the provisioners 291 for i, v := range rawTpl.Provisioners { 292 raw := &t.Provisioners[i] 293 if err := mapstructure.Decode(v, raw); err != nil { 294 if merr, ok := err.(*mapstructure.Error); ok { 295 for _, err := range merr.Errors { 296 errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err)) 297 } 298 } else { 299 errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err)) 300 } 301 302 continue 303 } 304 305 if raw.Type == "" { 306 errors = append(errors, fmt.Errorf("provisioner %d: missing 'type'", i+1)) 307 continue 308 } 309 310 // Delete the keys that we used 311 raw.TemplateOnlyExcept.Prune(v) 312 delete(v, "override") 313 314 // Verify that the override keys exist... 315 for name, _ := range raw.Override { 316 if _, ok := t.Builders[name]; !ok { 317 errors = append( 318 errors, fmt.Errorf("provisioner %d: build '%s' not found for override", i+1, name)) 319 } 320 } 321 322 // Verify that the only settings are good 323 if errs := raw.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 { 324 for _, err := range errs { 325 errors = append(errors, 326 fmt.Errorf("provisioner %d: %s", i+1, err)) 327 } 328 } 329 330 // Setup the pause settings 331 if raw.RawPauseBefore != "" { 332 duration, err := time.ParseDuration(raw.RawPauseBefore) 333 if err != nil { 334 errors = append( 335 errors, fmt.Errorf( 336 "provisioner %d: pause_before invalid: %s", 337 i+1, err)) 338 } 339 340 raw.pauseBefore = duration 341 } 342 343 // Remove the pause_before setting if it is there so that we don't 344 // get template validation errors later. 345 delete(v, "pause_before") 346 347 raw.RawConfig = v 348 } 349 350 if len(t.Builders) == 0 { 351 errors = append(errors, fmt.Errorf("No builders are defined in the template.")) 352 } 353 354 // Verify that all the variable sets were for real variables. 355 for k, _ := range vars { 356 errors = append(errors, fmt.Errorf("Unknown user variables: %s", k)) 357 } 358 359 // If there were errors, we put it into a MultiError and return 360 if len(errors) > 0 { 361 err = &MultiError{errors} 362 t = nil 363 return 364 } 365 366 return 367 } 368 369 // ParseTemplateFile takes the given template file and parses it into 370 // a single template. 371 func ParseTemplateFile(path string, vars map[string]string) (*Template, error) { 372 var data []byte 373 374 if path == "-" { 375 // Read from stdin... 376 buf := new(bytes.Buffer) 377 _, err := io.Copy(buf, os.Stdin) 378 if err != nil { 379 return nil, err 380 } 381 382 data = buf.Bytes() 383 } else { 384 var err error 385 data, err = ioutil.ReadFile(path) 386 if err != nil { 387 return nil, err 388 } 389 } 390 391 return ParseTemplate(data, vars) 392 } 393 394 func parsePostProcessor(i int, rawV interface{}) (result []map[string]interface{}, errors []error) { 395 switch v := rawV.(type) { 396 case string: 397 result = []map[string]interface{}{ 398 {"type": v}, 399 } 400 case map[string]interface{}: 401 result = []map[string]interface{}{v} 402 case []interface{}: 403 result = make([]map[string]interface{}, len(v)) 404 errors = make([]error, 0) 405 for j, innerRawV := range v { 406 switch innerV := innerRawV.(type) { 407 case string: 408 result[j] = map[string]interface{}{"type": innerV} 409 case map[string]interface{}: 410 result[j] = innerV 411 case []interface{}: 412 errors = append( 413 errors, 414 fmt.Errorf("Post-processor %d.%d: sequences not allowed to be nested in sequences", i+1, j+1)) 415 default: 416 errors = append(errors, fmt.Errorf("Post-processor %d.%d is in a bad format.", i+1, j+1)) 417 } 418 } 419 420 if len(errors) == 0 { 421 errors = nil 422 } 423 default: 424 result = nil 425 errors = []error{fmt.Errorf("Post-processor %d is in a bad format.", i+1)} 426 } 427 428 return 429 } 430 431 // BuildNames returns a slice of the available names of builds that 432 // this template represents. 433 func (t *Template) BuildNames() []string { 434 names := make([]string, 0, len(t.Builders)) 435 for name, _ := range t.Builders { 436 names = append(names, name) 437 } 438 439 return names 440 } 441 442 // Build returns a Build for the given name. 443 // 444 // If the build does not exist as part of this template, an error is 445 // returned. 446 func (t *Template) Build(name string, components *ComponentFinder) (b Build, err error) { 447 // Setup the Builder 448 builderConfig, ok := t.Builders[name] 449 if !ok { 450 err = fmt.Errorf("No such build found in template: %s", name) 451 return 452 } 453 454 // We panic if there is no builder function because this is really 455 // an internal bug that always needs to be fixed, not an error. 456 if components.Builder == nil { 457 panic("no builder function") 458 } 459 460 // Panic if there are provisioners on the template but no provisioner 461 // component finder. This is always an internal error, so we panic. 462 if len(t.Provisioners) > 0 && components.Provisioner == nil { 463 panic("no provisioner function") 464 } 465 466 builder, err := components.Builder(builderConfig.Type) 467 if err != nil { 468 return 469 } 470 471 if builder == nil { 472 err = fmt.Errorf("Builder type not found: %s", builderConfig.Type) 473 return 474 } 475 476 // Prepare the variable template processor, which is a bit unique 477 // because we don't allow user variable usage and we add a function 478 // to read from the environment. 479 varTpl, err := NewConfigTemplate() 480 if err != nil { 481 return nil, err 482 } 483 varTpl.Funcs(template.FuncMap{ 484 "env": templateEnv, 485 "user": templateDisableUser, 486 }) 487 488 // Prepare the variables 489 var varErrors []error 490 variables := make(map[string]string) 491 for k, v := range t.Variables { 492 if v.Required && !v.HasValue { 493 varErrors = append(varErrors, 494 fmt.Errorf("Required user variable '%s' not set", k)) 495 } 496 497 var val string 498 if v.HasValue { 499 val = v.Value 500 } else { 501 val, err = varTpl.Process(v.Default, nil) 502 if err != nil { 503 varErrors = append(varErrors, 504 fmt.Errorf("Error processing user variable '%s': %s'", k, err)) 505 } 506 } 507 508 variables[k] = val 509 } 510 511 if len(varErrors) > 0 { 512 return nil, &MultiError{varErrors} 513 } 514 515 // Process the name 516 tpl, err := NewConfigTemplate() 517 if err != nil { 518 return nil, err 519 } 520 tpl.UserVars = variables 521 522 name, err = tpl.Process(name, nil) 523 if err != nil { 524 return nil, err 525 } 526 527 // Gather the Hooks 528 hooks := make(map[string][]Hook) 529 for tplEvent, tplHooks := range t.Hooks { 530 curHooks := make([]Hook, 0, len(tplHooks)) 531 532 for _, hookName := range tplHooks { 533 var hook Hook 534 hook, err = components.Hook(hookName) 535 if err != nil { 536 return 537 } 538 539 if hook == nil { 540 err = fmt.Errorf("Hook not found: %s", hookName) 541 return 542 } 543 544 curHooks = append(curHooks, hook) 545 } 546 547 hooks[tplEvent] = curHooks 548 } 549 550 // Prepare the post-processors 551 postProcessors := make([][]coreBuildPostProcessor, 0, len(t.PostProcessors)) 552 for _, rawPPs := range t.PostProcessors { 553 current := make([]coreBuildPostProcessor, 0, len(rawPPs)) 554 for _, rawPP := range rawPPs { 555 if rawPP.TemplateOnlyExcept.Skip(name) { 556 continue 557 } 558 559 pp, err := components.PostProcessor(rawPP.Type) 560 if err != nil { 561 return nil, err 562 } 563 564 if pp == nil { 565 return nil, fmt.Errorf("PostProcessor type not found: %s", rawPP.Type) 566 } 567 568 current = append(current, coreBuildPostProcessor{ 569 processor: pp, 570 processorType: rawPP.Type, 571 config: rawPP.RawConfig, 572 keepInputArtifact: rawPP.KeepInputArtifact, 573 }) 574 } 575 576 // If we have no post-processors in this chain, just continue. 577 // This can happen if the post-processors skip certain builds. 578 if len(current) == 0 { 579 continue 580 } 581 582 postProcessors = append(postProcessors, current) 583 } 584 585 // Prepare the provisioners 586 provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners)) 587 for _, rawProvisioner := range t.Provisioners { 588 if rawProvisioner.TemplateOnlyExcept.Skip(name) { 589 continue 590 } 591 592 var provisioner Provisioner 593 provisioner, err = components.Provisioner(rawProvisioner.Type) 594 if err != nil { 595 return 596 } 597 598 if provisioner == nil { 599 err = fmt.Errorf("Provisioner type not found: %s", rawProvisioner.Type) 600 return 601 } 602 603 configs := make([]interface{}, 1, 2) 604 configs[0] = rawProvisioner.RawConfig 605 606 if rawProvisioner.Override != nil { 607 if override, ok := rawProvisioner.Override[name]; ok { 608 configs = append(configs, override) 609 } 610 } 611 612 if rawProvisioner.pauseBefore > 0 { 613 provisioner = &PausedProvisioner{ 614 PauseBefore: rawProvisioner.pauseBefore, 615 Provisioner: provisioner, 616 } 617 } 618 619 coreProv := coreBuildProvisioner{provisioner, configs} 620 provisioners = append(provisioners, coreProv) 621 } 622 623 b = &coreBuild{ 624 name: name, 625 builder: builder, 626 builderConfig: builderConfig.RawConfig, 627 builderType: builderConfig.Type, 628 hooks: hooks, 629 postProcessors: postProcessors, 630 provisioners: provisioners, 631 variables: variables, 632 } 633 634 return 635 } 636 637 // TemplateOnlyExcept contains the logic required for "only" and "except" 638 // meta-parameters. 639 type TemplateOnlyExcept struct { 640 Only []string 641 Except []string 642 } 643 644 // Prune will prune out the used values from the raw map. 645 func (t *TemplateOnlyExcept) Prune(raw map[string]interface{}) { 646 delete(raw, "except") 647 delete(raw, "only") 648 } 649 650 // Skip tests if we should skip putting this item onto a build. 651 func (t *TemplateOnlyExcept) Skip(name string) bool { 652 if len(t.Only) > 0 { 653 onlyFound := false 654 for _, n := range t.Only { 655 if n == name { 656 onlyFound = true 657 break 658 } 659 } 660 661 if !onlyFound { 662 // Skip this provisioner 663 return true 664 } 665 } 666 667 // If the name is in the except list, then skip that 668 for _, n := range t.Except { 669 if n == name { 670 return true 671 } 672 } 673 674 return false 675 } 676 677 // Validates the only/except parameters. 678 func (t *TemplateOnlyExcept) Validate(b map[string]RawBuilderConfig) (e []error) { 679 if len(t.Only) > 0 && len(t.Except) > 0 { 680 e = append(e, 681 fmt.Errorf("Only one of 'only' or 'except' may be specified.")) 682 } 683 684 if len(t.Only) > 0 { 685 for _, n := range t.Only { 686 if _, ok := b[n]; !ok { 687 e = append(e, 688 fmt.Errorf("'only' specified builder '%s' not found", n)) 689 } 690 } 691 } 692 693 for _, n := range t.Except { 694 if _, ok := b[n]; !ok { 695 e = append(e, 696 fmt.Errorf("'except' specified builder '%s' not found", n)) 697 } 698 } 699 700 return 701 }