github.com/supr/packer@v0.3.10-0.20131015195147-7b09e24ac3c1/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]interface{}
    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]RawVariable
    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  	TemplateOnlyExcept `mapstructure:",squash"`
    52  
    53  	Type              string
    54  	KeepInputArtifact bool `mapstructure:"keep_input_artifact"`
    55  	RawConfig         map[string]interface{}
    56  }
    57  
    58  // RawProvisionerConfig represents a raw, unprocessed provisioner configuration.
    59  // It contains the type of the provisioner as well as the raw configuration
    60  // that is handed to the provisioner for it to process.
    61  type RawProvisionerConfig struct {
    62  	TemplateOnlyExcept `mapstructure:",squash"`
    63  
    64  	Type     string
    65  	Override map[string]interface{}
    66  
    67  	RawConfig interface{}
    68  }
    69  
    70  // RawVariable represents a variable configuration within a template.
    71  type RawVariable struct {
    72  	Default  string
    73  	Required bool
    74  }
    75  
    76  // ParseTemplate takes a byte slice and parses a Template from it, returning
    77  // the template and possibly errors while loading the template. The error
    78  // could potentially be a MultiError, representing multiple errors. Knowing
    79  // and checking for this can be useful, if you wish to format it in a certain
    80  // way.
    81  func ParseTemplate(data []byte) (t *Template, err error) {
    82  	var rawTplInterface interface{}
    83  	err = jsonutil.Unmarshal(data, &rawTplInterface)
    84  	if err != nil {
    85  		return
    86  	}
    87  
    88  	// Decode the raw template interface into the actual rawTemplate
    89  	// structure, checking for any extranneous keys along the way.
    90  	var md mapstructure.Metadata
    91  	var rawTpl rawTemplate
    92  	decoderConfig := &mapstructure.DecoderConfig{
    93  		Metadata: &md,
    94  		Result:   &rawTpl,
    95  	}
    96  
    97  	decoder, err := mapstructure.NewDecoder(decoderConfig)
    98  	if err != nil {
    99  		return
   100  	}
   101  
   102  	err = decoder.Decode(rawTplInterface)
   103  	if err != nil {
   104  		return
   105  	}
   106  
   107  	errors := make([]error, 0)
   108  
   109  	if len(md.Unused) > 0 {
   110  		sort.Strings(md.Unused)
   111  		for _, unused := range md.Unused {
   112  			errors = append(
   113  				errors, fmt.Errorf("Unknown root level key in template: '%s'", unused))
   114  		}
   115  	}
   116  
   117  	t = &Template{}
   118  	t.Variables = make(map[string]RawVariable)
   119  	t.Builders = make(map[string]RawBuilderConfig)
   120  	t.Hooks = rawTpl.Hooks
   121  	t.PostProcessors = make([][]RawPostProcessorConfig, len(rawTpl.PostProcessors))
   122  	t.Provisioners = make([]RawProvisionerConfig, len(rawTpl.Provisioners))
   123  
   124  	// Gather all the variables
   125  	for k, v := range rawTpl.Variables {
   126  		var variable RawVariable
   127  		variable.Required = v == nil
   128  
   129  		// Create a new mapstructure decoder in order to decode the default
   130  		// value since this is the only value in the regular template that
   131  		// can be weakly typed.
   132  		decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   133  			Result:           &variable.Default,
   134  			WeaklyTypedInput: true,
   135  		})
   136  		if err != nil {
   137  			// This should never happen.
   138  			panic(err)
   139  		}
   140  
   141  		err = decoder.Decode(v)
   142  		if err != nil {
   143  			errors = append(errors,
   144  				fmt.Errorf("Error decoding default value for user var '%s': %s", k, err))
   145  			continue
   146  		}
   147  
   148  		t.Variables[k] = variable
   149  	}
   150  
   151  	// Gather all the builders
   152  	for i, v := range rawTpl.Builders {
   153  		var raw RawBuilderConfig
   154  		if err := mapstructure.Decode(v, &raw); err != nil {
   155  			if merr, ok := err.(*mapstructure.Error); ok {
   156  				for _, err := range merr.Errors {
   157  					errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err))
   158  				}
   159  			} else {
   160  				errors = append(errors, fmt.Errorf("builder %d: %s", i+1, err))
   161  			}
   162  
   163  			continue
   164  		}
   165  
   166  		if raw.Type == "" {
   167  			errors = append(errors, fmt.Errorf("builder %d: missing 'type'", i+1))
   168  			continue
   169  		}
   170  
   171  		// Attempt to get the name of the builder. If the "name" key
   172  		// missing, use the "type" field, which is guaranteed to exist
   173  		// at this point.
   174  		if raw.Name == "" {
   175  			raw.Name = raw.Type
   176  		}
   177  
   178  		// Check if we already have a builder with this name and error if so
   179  		if _, ok := t.Builders[raw.Name]; ok {
   180  			errors = append(errors, fmt.Errorf("builder with name '%s' already exists", raw.Name))
   181  			continue
   182  		}
   183  
   184  		// Now that we have the name, remove it from the config - as the builder
   185  		// itself doesn't know about, and it will cause a validation error.
   186  		delete(v, "name")
   187  
   188  		raw.RawConfig = v
   189  
   190  		t.Builders[raw.Name] = raw
   191  	}
   192  
   193  	// Gather all the post-processors. This is a complicated process since there
   194  	// are actually three different formats that the user can use to define
   195  	// a post-processor.
   196  	for i, rawV := range rawTpl.PostProcessors {
   197  		rawPP, err := parsePostProcessor(i, rawV)
   198  		if err != nil {
   199  			errors = append(errors, err...)
   200  			continue
   201  		}
   202  
   203  		configs := make([]RawPostProcessorConfig, 0, len(rawPP))
   204  		for j, pp := range rawPP {
   205  			var config RawPostProcessorConfig
   206  			if err := mapstructure.Decode(pp, &config); err != nil {
   207  				if merr, ok := err.(*mapstructure.Error); ok {
   208  					for _, err := range merr.Errors {
   209  						errors = append(errors,
   210  							fmt.Errorf("Post-processor #%d.%d: %s", i+1, j+1, err))
   211  					}
   212  				} else {
   213  					errors = append(errors,
   214  						fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
   215  				}
   216  
   217  				continue
   218  			}
   219  
   220  			if config.Type == "" {
   221  				errors = append(errors,
   222  					fmt.Errorf("Post-processor %d.%d: missing 'type'", i+1, j+1))
   223  				continue
   224  			}
   225  
   226  			// Remove the input keep_input_artifact option
   227  			config.TemplateOnlyExcept.Prune(pp)
   228  			delete(pp, "keep_input_artifact")
   229  
   230  			// Verify that the only settings are good
   231  			if errs := config.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 {
   232  				for _, err := range errs {
   233  					errors = append(errors,
   234  						fmt.Errorf("Post-processor %d.%d: %s", i+1, j+1, err))
   235  				}
   236  
   237  				continue
   238  			}
   239  
   240  			config.RawConfig = pp
   241  
   242  			// Add it to the list of configs
   243  			configs = append(configs, config)
   244  		}
   245  
   246  		t.PostProcessors[i] = configs
   247  	}
   248  
   249  	// Gather all the provisioners
   250  	for i, v := range rawTpl.Provisioners {
   251  		raw := &t.Provisioners[i]
   252  		if err := mapstructure.Decode(v, raw); err != nil {
   253  			if merr, ok := err.(*mapstructure.Error); ok {
   254  				for _, err := range merr.Errors {
   255  					errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err))
   256  				}
   257  			} else {
   258  				errors = append(errors, fmt.Errorf("provisioner %d: %s", i+1, err))
   259  			}
   260  
   261  			continue
   262  		}
   263  
   264  		if raw.Type == "" {
   265  			errors = append(errors, fmt.Errorf("provisioner %d: missing 'type'", i+1))
   266  			continue
   267  		}
   268  
   269  		// Delete the keys that we used
   270  		raw.TemplateOnlyExcept.Prune(v)
   271  		delete(v, "override")
   272  
   273  		// Verify that the override keys exist...
   274  		for name, _ := range raw.Override {
   275  			if _, ok := t.Builders[name]; !ok {
   276  				errors = append(
   277  					errors, fmt.Errorf("provisioner %d: build '%s' not found for override", i+1, name))
   278  			}
   279  		}
   280  
   281  		// Verify that the only settings are good
   282  		if errs := raw.TemplateOnlyExcept.Validate(t.Builders); len(errs) > 0 {
   283  			for _, err := range errs {
   284  				errors = append(errors,
   285  					fmt.Errorf("provisioner %d: %s", i+1, err))
   286  			}
   287  		}
   288  
   289  		raw.RawConfig = v
   290  	}
   291  
   292  	if len(t.Builders) == 0 {
   293  		errors = append(errors, fmt.Errorf("No builders are defined in the template."))
   294  	}
   295  
   296  	// If there were errors, we put it into a MultiError and return
   297  	if len(errors) > 0 {
   298  		err = &MultiError{errors}
   299  		t = nil
   300  		return
   301  	}
   302  
   303  	return
   304  }
   305  
   306  // ParseTemplateFile takes the given template file and parses it into
   307  // a single template.
   308  func ParseTemplateFile(path string) (*Template, error) {
   309  	var data []byte
   310  
   311  	if path == "-" {
   312  		// Read from stdin...
   313  		buf := new(bytes.Buffer)
   314  		_, err := io.Copy(buf, os.Stdin)
   315  		if err != nil {
   316  			return nil, err
   317  		}
   318  
   319  		data = buf.Bytes()
   320  	} else {
   321  		var err error
   322  		data, err = ioutil.ReadFile(path)
   323  		if err != nil {
   324  			return nil, err
   325  		}
   326  	}
   327  
   328  	return ParseTemplate(data)
   329  }
   330  
   331  func parsePostProcessor(i int, rawV interface{}) (result []map[string]interface{}, errors []error) {
   332  	switch v := rawV.(type) {
   333  	case string:
   334  		result = []map[string]interface{}{
   335  			{"type": v},
   336  		}
   337  	case map[string]interface{}:
   338  		result = []map[string]interface{}{v}
   339  	case []interface{}:
   340  		result = make([]map[string]interface{}, len(v))
   341  		errors = make([]error, 0)
   342  		for j, innerRawV := range v {
   343  			switch innerV := innerRawV.(type) {
   344  			case string:
   345  				result[j] = map[string]interface{}{"type": innerV}
   346  			case map[string]interface{}:
   347  				result[j] = innerV
   348  			case []interface{}:
   349  				errors = append(
   350  					errors,
   351  					fmt.Errorf("Post-processor %d.%d: sequences not allowed to be nested in sequences", i+1, j+1))
   352  			default:
   353  				errors = append(errors, fmt.Errorf("Post-processor %d.%d is in a bad format.", i+1, j+1))
   354  			}
   355  		}
   356  
   357  		if len(errors) == 0 {
   358  			errors = nil
   359  		}
   360  	default:
   361  		result = nil
   362  		errors = []error{fmt.Errorf("Post-processor %d is in a bad format.", i+1)}
   363  	}
   364  
   365  	return
   366  }
   367  
   368  // BuildNames returns a slice of the available names of builds that
   369  // this template represents.
   370  func (t *Template) BuildNames() []string {
   371  	names := make([]string, 0, len(t.Builders))
   372  	for name, _ := range t.Builders {
   373  		names = append(names, name)
   374  	}
   375  
   376  	return names
   377  }
   378  
   379  // Build returns a Build for the given name.
   380  //
   381  // If the build does not exist as part of this template, an error is
   382  // returned.
   383  func (t *Template) Build(name string, components *ComponentFinder) (b Build, err error) {
   384  	// Setup the Builder
   385  	builderConfig, ok := t.Builders[name]
   386  	if !ok {
   387  		err = fmt.Errorf("No such build found in template: %s", name)
   388  		return
   389  	}
   390  
   391  	// We panic if there is no builder function because this is really
   392  	// an internal bug that always needs to be fixed, not an error.
   393  	if components.Builder == nil {
   394  		panic("no builder function")
   395  	}
   396  
   397  	// Panic if there are provisioners on the template but no provisioner
   398  	// component finder. This is always an internal error, so we panic.
   399  	if len(t.Provisioners) > 0 && components.Provisioner == nil {
   400  		panic("no provisioner function")
   401  	}
   402  
   403  	builder, err := components.Builder(builderConfig.Type)
   404  	if err != nil {
   405  		return
   406  	}
   407  
   408  	if builder == nil {
   409  		err = fmt.Errorf("Builder type not found: %s", builderConfig.Type)
   410  		return
   411  	}
   412  
   413  	// Gather the Hooks
   414  	hooks := make(map[string][]Hook)
   415  	for tplEvent, tplHooks := range t.Hooks {
   416  		curHooks := make([]Hook, 0, len(tplHooks))
   417  
   418  		for _, hookName := range tplHooks {
   419  			var hook Hook
   420  			hook, err = components.Hook(hookName)
   421  			if err != nil {
   422  				return
   423  			}
   424  
   425  			if hook == nil {
   426  				err = fmt.Errorf("Hook not found: %s", hookName)
   427  				return
   428  			}
   429  
   430  			curHooks = append(curHooks, hook)
   431  		}
   432  
   433  		hooks[tplEvent] = curHooks
   434  	}
   435  
   436  	// Prepare the post-processors
   437  	postProcessors := make([][]coreBuildPostProcessor, 0, len(t.PostProcessors))
   438  	for _, rawPPs := range t.PostProcessors {
   439  		current := make([]coreBuildPostProcessor, 0, len(rawPPs))
   440  		for _, rawPP := range rawPPs {
   441  			if rawPP.TemplateOnlyExcept.Skip(name) {
   442  				continue
   443  			}
   444  
   445  			pp, err := components.PostProcessor(rawPP.Type)
   446  			if err != nil {
   447  				return nil, err
   448  			}
   449  
   450  			if pp == nil {
   451  				return nil, fmt.Errorf("PostProcessor type not found: %s", rawPP.Type)
   452  			}
   453  
   454  			current = append(current, coreBuildPostProcessor{
   455  				processor:         pp,
   456  				processorType:     rawPP.Type,
   457  				config:            rawPP.RawConfig,
   458  				keepInputArtifact: rawPP.KeepInputArtifact,
   459  			})
   460  		}
   461  
   462  		// If we have no post-processors in this chain, just continue.
   463  		// This can happen if the post-processors skip certain builds.
   464  		if len(current) == 0 {
   465  			continue
   466  		}
   467  
   468  		postProcessors = append(postProcessors, current)
   469  	}
   470  
   471  	// Prepare the provisioners
   472  	provisioners := make([]coreBuildProvisioner, 0, len(t.Provisioners))
   473  	for _, rawProvisioner := range t.Provisioners {
   474  		if rawProvisioner.TemplateOnlyExcept.Skip(name) {
   475  			continue
   476  		}
   477  
   478  		var provisioner Provisioner
   479  		provisioner, err = components.Provisioner(rawProvisioner.Type)
   480  		if err != nil {
   481  			return
   482  		}
   483  
   484  		if provisioner == nil {
   485  			err = fmt.Errorf("Provisioner type not found: %s", rawProvisioner.Type)
   486  			return
   487  		}
   488  
   489  		configs := make([]interface{}, 1, 2)
   490  		configs[0] = rawProvisioner.RawConfig
   491  
   492  		if rawProvisioner.Override != nil {
   493  			if override, ok := rawProvisioner.Override[name]; ok {
   494  				configs = append(configs, override)
   495  			}
   496  		}
   497  
   498  		coreProv := coreBuildProvisioner{provisioner, configs}
   499  		provisioners = append(provisioners, coreProv)
   500  	}
   501  
   502  	// Prepare the variables
   503  	variables := make(map[string]coreBuildVariable)
   504  	for k, v := range t.Variables {
   505  		variables[k] = coreBuildVariable{
   506  			Default:  v.Default,
   507  			Required: v.Required,
   508  		}
   509  	}
   510  
   511  	b = &coreBuild{
   512  		name:           name,
   513  		builder:        builder,
   514  		builderConfig:  builderConfig.RawConfig,
   515  		builderType:    builderConfig.Type,
   516  		hooks:          hooks,
   517  		postProcessors: postProcessors,
   518  		provisioners:   provisioners,
   519  		variables:      variables,
   520  	}
   521  
   522  	return
   523  }
   524  
   525  // TemplateOnlyExcept contains the logic required for "only" and "except"
   526  // meta-parameters.
   527  type TemplateOnlyExcept struct {
   528  	Only   []string
   529  	Except []string
   530  }
   531  
   532  // Prune will prune out the used values from the raw map.
   533  func (t *TemplateOnlyExcept) Prune(raw map[string]interface{}) {
   534  	delete(raw, "except")
   535  	delete(raw, "only")
   536  }
   537  
   538  // Skip tests if we should skip putting this item onto a build.
   539  func (t *TemplateOnlyExcept) Skip(name string) bool {
   540  	if len(t.Only) > 0 {
   541  		onlyFound := false
   542  		for _, n := range t.Only {
   543  			if n == name {
   544  				onlyFound = true
   545  				break
   546  			}
   547  		}
   548  
   549  		if !onlyFound {
   550  			// Skip this provisioner
   551  			return true
   552  		}
   553  	}
   554  
   555  	// If the name is in the except list, then skip that
   556  	for _, n := range t.Except {
   557  		if n == name {
   558  			return true
   559  		}
   560  	}
   561  
   562  	return false
   563  }
   564  
   565  // Validates the only/except parameters.
   566  func (t *TemplateOnlyExcept) Validate(b map[string]RawBuilderConfig) (e []error) {
   567  	if len(t.Only) > 0 && len(t.Except) > 0 {
   568  		e = append(e,
   569  			fmt.Errorf("Only one of 'only' or 'except' may be specified."))
   570  	}
   571  
   572  	if len(t.Only) > 0 {
   573  		for _, n := range t.Only {
   574  			if _, ok := b[n]; !ok {
   575  				e = append(e,
   576  					fmt.Errorf("'only' specified builder '%s' not found", n))
   577  			}
   578  		}
   579  	}
   580  
   581  	for _, n := range t.Except {
   582  		if _, ok := b[n]; !ok {
   583  			e = append(e,
   584  				fmt.Errorf("'except' specified builder '%s' not found", n))
   585  		}
   586  	}
   587  
   588  	return
   589  }