github.com/kikitux/packer@v0.10.1-0.20160322154024-6237df566f9f/packer/core.go (about)

     1  package packer
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	"github.com/hashicorp/go-multierror"
     8  	"github.com/hashicorp/go-version"
     9  	"github.com/mitchellh/packer/template"
    10  	"github.com/mitchellh/packer/template/interpolate"
    11  )
    12  
    13  // Core is the main executor of Packer. If Packer is being used as a
    14  // library, this is the struct you'll want to instantiate to get anything done.
    15  type Core struct {
    16  	Template *template.Template
    17  
    18  	components ComponentFinder
    19  	variables  map[string]string
    20  	builds     map[string]*template.Builder
    21  	version    string
    22  }
    23  
    24  // CoreConfig is the structure for initializing a new Core. Once a CoreConfig
    25  // is used to initialize a Core, it shouldn't be re-used or modified again.
    26  type CoreConfig struct {
    27  	Components ComponentFinder
    28  	Template   *template.Template
    29  	Variables  map[string]string
    30  	Version    string
    31  }
    32  
    33  // The function type used to lookup Builder implementations.
    34  type BuilderFunc func(name string) (Builder, error)
    35  
    36  // The function type used to lookup Hook implementations.
    37  type HookFunc func(name string) (Hook, error)
    38  
    39  // The function type used to lookup PostProcessor implementations.
    40  type PostProcessorFunc func(name string) (PostProcessor, error)
    41  
    42  // The function type used to lookup Provisioner implementations.
    43  type ProvisionerFunc func(name string) (Provisioner, error)
    44  
    45  // ComponentFinder is a struct that contains the various function
    46  // pointers necessary to look up components of Packer such as builders,
    47  // commands, etc.
    48  type ComponentFinder struct {
    49  	Builder       BuilderFunc
    50  	Hook          HookFunc
    51  	PostProcessor PostProcessorFunc
    52  	Provisioner   ProvisionerFunc
    53  }
    54  
    55  // NewCore creates a new Core.
    56  func NewCore(c *CoreConfig) (*Core, error) {
    57  	result := &Core{
    58  		Template:   c.Template,
    59  		components: c.Components,
    60  		variables:  c.Variables,
    61  		version:    c.Version,
    62  	}
    63  	if err := result.validate(); err != nil {
    64  		return nil, err
    65  	}
    66  	if err := result.init(); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	// Go through and interpolate all the build names. We shuld be able
    71  	// to do this at this point with the variables.
    72  	result.builds = make(map[string]*template.Builder)
    73  	for _, b := range c.Template.Builders {
    74  		v, err := interpolate.Render(b.Name, result.Context())
    75  		if err != nil {
    76  			return nil, fmt.Errorf(
    77  				"Error interpolating builder '%s': %s",
    78  				b.Name, err)
    79  		}
    80  
    81  		result.builds[v] = b
    82  	}
    83  
    84  	return result, nil
    85  }
    86  
    87  // BuildNames returns the builds that are available in this configured core.
    88  func (c *Core) BuildNames() []string {
    89  	r := make([]string, 0, len(c.builds))
    90  	for n, _ := range c.builds {
    91  		r = append(r, n)
    92  	}
    93  	sort.Strings(r)
    94  
    95  	return r
    96  }
    97  
    98  // Build returns the Build object for the given name.
    99  func (c *Core) Build(n string) (Build, error) {
   100  	// Setup the builder
   101  	configBuilder, ok := c.builds[n]
   102  	if !ok {
   103  		return nil, fmt.Errorf("no such build found: %s", n)
   104  	}
   105  	builder, err := c.components.Builder(configBuilder.Type)
   106  	if err != nil {
   107  		return nil, fmt.Errorf(
   108  			"error initializing builder '%s': %s",
   109  			configBuilder.Type, err)
   110  	}
   111  	if builder == nil {
   112  		return nil, fmt.Errorf(
   113  			"builder type not found: %s", configBuilder.Type)
   114  	}
   115  
   116  	// rawName is the uninterpolated name that we use for various lookups
   117  	rawName := configBuilder.Name
   118  
   119  	// Setup the provisioners for this build
   120  	provisioners := make([]coreBuildProvisioner, 0, len(c.Template.Provisioners))
   121  	for _, rawP := range c.Template.Provisioners {
   122  		// If we're skipping this, then ignore it
   123  		if rawP.Skip(rawName) {
   124  			continue
   125  		}
   126  
   127  		// Get the provisioner
   128  		provisioner, err := c.components.Provisioner(rawP.Type)
   129  		if err != nil {
   130  			return nil, fmt.Errorf(
   131  				"error initializing provisioner '%s': %s",
   132  				rawP.Type, err)
   133  		}
   134  		if provisioner == nil {
   135  			return nil, fmt.Errorf(
   136  				"provisioner type not found: %s", rawP.Type)
   137  		}
   138  
   139  		// Get the configuration
   140  		config := make([]interface{}, 1, 2)
   141  		config[0] = rawP.Config
   142  		if rawP.Override != nil {
   143  			if override, ok := rawP.Override[rawName]; ok {
   144  				config = append(config, override)
   145  			}
   146  		}
   147  
   148  		// If we're pausing, we wrap the provisioner in a special pauser.
   149  		if rawP.PauseBefore > 0 {
   150  			provisioner = &PausedProvisioner{
   151  				PauseBefore: rawP.PauseBefore,
   152  				Provisioner: provisioner,
   153  			}
   154  		}
   155  
   156  		provisioners = append(provisioners, coreBuildProvisioner{
   157  			provisioner: provisioner,
   158  			config:      config,
   159  		})
   160  	}
   161  
   162  	// Setup the post-processors
   163  	postProcessors := make([][]coreBuildPostProcessor, 0, len(c.Template.PostProcessors))
   164  	for _, rawPs := range c.Template.PostProcessors {
   165  		current := make([]coreBuildPostProcessor, 0, len(rawPs))
   166  		for _, rawP := range rawPs {
   167  			// If we skip, ignore
   168  			if rawP.Skip(rawName) {
   169  				continue
   170  			}
   171  
   172  			// Get the post-processor
   173  			postProcessor, err := c.components.PostProcessor(rawP.Type)
   174  			if err != nil {
   175  				return nil, fmt.Errorf(
   176  					"error initializing post-processor '%s': %s",
   177  					rawP.Type, err)
   178  			}
   179  			if postProcessor == nil {
   180  				return nil, fmt.Errorf(
   181  					"post-processor type not found: %s", rawP.Type)
   182  			}
   183  
   184  			current = append(current, coreBuildPostProcessor{
   185  				processor:         postProcessor,
   186  				processorType:     rawP.Type,
   187  				config:            rawP.Config,
   188  				keepInputArtifact: rawP.KeepInputArtifact,
   189  			})
   190  		}
   191  
   192  		// If we have no post-processors in this chain, just continue.
   193  		if len(current) == 0 {
   194  			continue
   195  		}
   196  
   197  		postProcessors = append(postProcessors, current)
   198  	}
   199  
   200  	// TODO hooks one day
   201  
   202  	return &coreBuild{
   203  		name:           n,
   204  		builder:        builder,
   205  		builderConfig:  configBuilder.Config,
   206  		builderType:    configBuilder.Type,
   207  		postProcessors: postProcessors,
   208  		provisioners:   provisioners,
   209  		templatePath:   c.Template.Path,
   210  		variables:      c.variables,
   211  	}, nil
   212  }
   213  
   214  // Context returns an interpolation context.
   215  func (c *Core) Context() *interpolate.Context {
   216  	return &interpolate.Context{
   217  		TemplatePath:  c.Template.Path,
   218  		UserVariables: c.variables,
   219  	}
   220  }
   221  
   222  // validate does a full validation of the template.
   223  //
   224  // This will automatically call template.validate() in addition to doing
   225  // richer semantic checks around variables and so on.
   226  func (c *Core) validate() error {
   227  	// First validate the template in general, we can't do anything else
   228  	// unless the template itself is valid.
   229  	if err := c.Template.Validate(); err != nil {
   230  		return err
   231  	}
   232  
   233  	// Validate the minimum version is satisfied
   234  	if c.Template.MinVersion != "" {
   235  		versionActual, err := version.NewVersion(c.version)
   236  		if err != nil {
   237  			// This shouldn't happen since we set it via the compiler
   238  			panic(err)
   239  		}
   240  
   241  		versionMin, err := version.NewVersion(c.Template.MinVersion)
   242  		if err != nil {
   243  			return fmt.Errorf(
   244  				"min_version is invalid: %s", err)
   245  		}
   246  
   247  		if versionActual.LessThan(versionMin) {
   248  			return fmt.Errorf(
   249  				"This template requires Packer version %s or higher; using %s",
   250  				versionMin,
   251  				versionActual)
   252  		}
   253  	}
   254  
   255  	// Validate variables are set
   256  	var err error
   257  	for n, v := range c.Template.Variables {
   258  		if v.Required {
   259  			if _, ok := c.variables[n]; !ok {
   260  				err = multierror.Append(err, fmt.Errorf(
   261  					"required variable not set: %s", n))
   262  			}
   263  		}
   264  	}
   265  
   266  	// TODO: validate all builders exist
   267  	// TODO: ^^ provisioner
   268  	// TODO: ^^ post-processor
   269  
   270  	return err
   271  }
   272  
   273  func (c *Core) init() error {
   274  	if c.variables == nil {
   275  		c.variables = make(map[string]string)
   276  	}
   277  
   278  	// Go through the variables and interpolate the environment variables
   279  	ctx := c.Context()
   280  	ctx.EnableEnv = true
   281  	ctx.UserVariables = nil
   282  	for k, v := range c.Template.Variables {
   283  		// Ignore variables that are required
   284  		if v.Required {
   285  			continue
   286  		}
   287  
   288  		// Ignore variables that have a value
   289  		if _, ok := c.variables[k]; ok {
   290  			continue
   291  		}
   292  
   293  		// Interpolate the default
   294  		def, err := interpolate.Render(v.Default, ctx)
   295  		if err != nil {
   296  			return fmt.Errorf(
   297  				"error interpolating default value for '%s': %s",
   298  				k, err)
   299  		}
   300  
   301  		c.variables[k] = def
   302  	}
   303  
   304  	// Interpolate the push configuration
   305  	if _, err := interpolate.RenderInterface(&c.Template.Push, c.Context()); err != nil {
   306  		return fmt.Errorf("Error interpolating 'push': %s", err)
   307  	}
   308  
   309  	return nil
   310  }