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