github.com/tonnydourado/packer@v0.6.1-0.20140701134019-5d0cd9676a37/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  }