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