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  }