github.com/jerryclinesmith/packer@v0.3.7/packer/build.go (about)

     1  package packer
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"sync"
     7  )
     8  
     9  const (
    10  	// This is the key in configurations that is set to the name of the
    11  	// build.
    12  	BuildNameConfigKey = "packer_build_name"
    13  
    14  	// This is the key in the configuration that is set to the type
    15  	// of the builder that is run. This is useful for provisioners and
    16  	// such who want to make use of this.
    17  	BuilderTypeConfigKey = "packer_builder_type"
    18  
    19  	// This is the key in configurations that is set to "true" when Packer
    20  	// debugging is enabled.
    21  	DebugConfigKey = "packer_debug"
    22  
    23  	// This is the key in configurations that is set to "true" when Packer
    24  	// force build is enabled.
    25  	ForceConfigKey = "packer_force"
    26  
    27  	// This key contains a map[string]string of the user variables for
    28  	// template processing.
    29  	UserVariablesConfigKey = "packer_user_variables"
    30  )
    31  
    32  // A Build represents a single job within Packer that is responsible for
    33  // building some machine image artifact. Builds are meant to be parallelized.
    34  type Build interface {
    35  	// Name is the name of the build. This is unique across a single template,
    36  	// but not absolutely unique. This is meant more to describe to the user
    37  	// what is being built rather than being a unique identifier.
    38  	Name() string
    39  
    40  	// Prepare configures the various components of this build and reports
    41  	// any errors in doing so (such as syntax errors, validation errors, etc.)
    42  	Prepare(v map[string]string) error
    43  
    44  	// Run runs the actual builder, returning an artifact implementation
    45  	// of what is built. If anything goes wrong, an error is returned.
    46  	Run(Ui, Cache) ([]Artifact, error)
    47  
    48  	// Cancel will cancel a running build. This will block until the build
    49  	// is actually completely cancelled.
    50  	Cancel()
    51  
    52  	// SetDebug will enable/disable debug mode. Debug mode is always
    53  	// enabled by adding the additional key "packer_debug" to boolean
    54  	// true in the configuration of the various components. This must
    55  	// be called prior to Prepare.
    56  	//
    57  	// When SetDebug is set to true, parallelism between builds is
    58  	// strictly prohibited.
    59  	SetDebug(bool)
    60  
    61  	// SetForce will enable/disable forcing a build when artifacts exist.
    62  	//
    63  	// When SetForce is set to true, existing artifacts from the build are
    64  	// deleted prior to the build.
    65  	SetForce(bool)
    66  }
    67  
    68  // A build struct represents a single build job, the result of which should
    69  // be a single machine image artifact. This artifact may be comprised of
    70  // multiple files, of course, but it should be for only a single provider
    71  // (such as VirtualBox, EC2, etc.).
    72  type coreBuild struct {
    73  	name           string
    74  	builder        Builder
    75  	builderConfig  interface{}
    76  	builderType    string
    77  	hooks          map[string][]Hook
    78  	postProcessors [][]coreBuildPostProcessor
    79  	provisioners   []coreBuildProvisioner
    80  	variables      map[string]coreBuildVariable
    81  
    82  	debug         bool
    83  	force         bool
    84  	l             sync.Mutex
    85  	prepareCalled bool
    86  }
    87  
    88  // Keeps track of the post-processor and the configuration of the
    89  // post-processor used within a build.
    90  type coreBuildPostProcessor struct {
    91  	processor         PostProcessor
    92  	processorType     string
    93  	config            map[string]interface{}
    94  	keepInputArtifact bool
    95  }
    96  
    97  // Keeps track of the provisioner and the configuration of the provisioner
    98  // within the build.
    99  type coreBuildProvisioner struct {
   100  	provisioner Provisioner
   101  	config      []interface{}
   102  }
   103  
   104  // A user-variable that is part of a single build.
   105  type coreBuildVariable struct {
   106  	Default  string
   107  	Required bool
   108  }
   109  
   110  // Returns the name of the build.
   111  func (b *coreBuild) Name() string {
   112  	return b.name
   113  }
   114  
   115  // Prepare prepares the build by doing some initialization for the builder
   116  // and any hooks. This _must_ be called prior to Run. The parameter is the
   117  // overrides for the variables within the template (if any).
   118  func (b *coreBuild) Prepare(userVars map[string]string) (err error) {
   119  	b.l.Lock()
   120  	defer b.l.Unlock()
   121  
   122  	if b.prepareCalled {
   123  		panic("prepare already called")
   124  	}
   125  
   126  	b.prepareCalled = true
   127  
   128  	// Compile the variables
   129  	varErrs := make([]error, 0)
   130  	variables := make(map[string]string)
   131  	for k, v := range b.variables {
   132  		variables[k] = v.Default
   133  
   134  		if v.Required {
   135  			if _, ok := userVars[k]; !ok {
   136  				varErrs = append(varErrs,
   137  					fmt.Errorf("Required user variable '%s' not set", k))
   138  			}
   139  		}
   140  	}
   141  
   142  	if userVars != nil {
   143  		for k, v := range userVars {
   144  			if _, ok := variables[k]; !ok {
   145  				varErrs = append(
   146  					varErrs, fmt.Errorf("Unknown user variable: %s", k))
   147  				continue
   148  			}
   149  
   150  			variables[k] = v
   151  		}
   152  	}
   153  
   154  	// If there were any problem with variables, return an error right
   155  	// away because we can't be certain anything else will actually work.
   156  	if len(varErrs) > 0 {
   157  		return &MultiError{
   158  			Errors: varErrs,
   159  		}
   160  	}
   161  
   162  	packerConfig := map[string]interface{}{
   163  		BuildNameConfigKey:     b.name,
   164  		BuilderTypeConfigKey:   b.builderType,
   165  		DebugConfigKey:         b.debug,
   166  		ForceConfigKey:         b.force,
   167  		UserVariablesConfigKey: variables,
   168  	}
   169  
   170  	// Prepare the builder
   171  	err = b.builder.Prepare(b.builderConfig, packerConfig)
   172  	if err != nil {
   173  		log.Printf("Build '%s' prepare failure: %s\n", b.name, err)
   174  		return
   175  	}
   176  
   177  	// Prepare the provisioners
   178  	for _, coreProv := range b.provisioners {
   179  		configs := make([]interface{}, len(coreProv.config), len(coreProv.config)+1)
   180  		copy(configs, coreProv.config)
   181  		configs = append(configs, packerConfig)
   182  
   183  		if err = coreProv.provisioner.Prepare(configs...); err != nil {
   184  			return
   185  		}
   186  	}
   187  
   188  	// Prepare the post-processors
   189  	for _, ppSeq := range b.postProcessors {
   190  		for _, corePP := range ppSeq {
   191  			err = corePP.processor.Configure(corePP.config, packerConfig)
   192  			if err != nil {
   193  				return
   194  			}
   195  		}
   196  	}
   197  
   198  	return
   199  }
   200  
   201  // Runs the actual build. Prepare must be called prior to running this.
   202  func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) {
   203  	if !b.prepareCalled {
   204  		panic("Prepare must be called first")
   205  	}
   206  
   207  	// Copy the hooks
   208  	hooks := make(map[string][]Hook)
   209  	for hookName, hookList := range b.hooks {
   210  		hooks[hookName] = make([]Hook, len(hookList))
   211  		copy(hooks[hookName], hookList)
   212  	}
   213  
   214  	// Add a hook for the provisioners if we have provisioners
   215  	if len(b.provisioners) > 0 {
   216  		provisioners := make([]Provisioner, len(b.provisioners))
   217  		for i, p := range b.provisioners {
   218  			provisioners[i] = p.provisioner
   219  		}
   220  
   221  		if _, ok := hooks[HookProvision]; !ok {
   222  			hooks[HookProvision] = make([]Hook, 0, 1)
   223  		}
   224  
   225  		hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{
   226  			Provisioners: provisioners,
   227  		})
   228  	}
   229  
   230  	hook := &DispatchHook{Mapping: hooks}
   231  	artifacts := make([]Artifact, 0, 1)
   232  
   233  	// The builder just has a normal Ui, but targetted
   234  	builderUi := &TargettedUi{
   235  		Target: b.Name(),
   236  		Ui:     originalUi,
   237  	}
   238  
   239  	log.Printf("Running builder: %s", b.builderType)
   240  	builderArtifact, err := b.builder.Run(builderUi, hook, cache)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	// If there was no result, don't worry about running post-processors
   246  	// because there is nothing they can do, just return.
   247  	if builderArtifact == nil {
   248  		return nil, nil
   249  	}
   250  
   251  	errors := make([]error, 0)
   252  	keepOriginalArtifact := len(b.postProcessors) == 0
   253  
   254  	// Run the post-processors
   255  PostProcessorRunSeqLoop:
   256  	for _, ppSeq := range b.postProcessors {
   257  		priorArtifact := builderArtifact
   258  		for i, corePP := range ppSeq {
   259  			ppUi := &TargettedUi{
   260  				Target: fmt.Sprintf("%s (%s)", b.Name(), corePP.processorType),
   261  				Ui:     originalUi,
   262  			}
   263  
   264  			builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.processorType))
   265  			artifact, keep, err := corePP.processor.PostProcess(ppUi, priorArtifact)
   266  			if err != nil {
   267  				errors = append(errors, fmt.Errorf("Post-processor failed: %s", err))
   268  				continue PostProcessorRunSeqLoop
   269  			}
   270  
   271  			if artifact == nil {
   272  				log.Println("Nil artifact, halting post-processor chain.")
   273  				continue PostProcessorRunSeqLoop
   274  			}
   275  
   276  			keep = keep || corePP.keepInputArtifact
   277  			if i == 0 {
   278  				// This is the first post-processor. We handle deleting
   279  				// previous artifacts a bit different because multiple
   280  				// post-processors may be using the original and need it.
   281  				if !keepOriginalArtifact && keep {
   282  					log.Printf(
   283  						"Flagging to keep original artifact from post-processor '%s'",
   284  						corePP.processorType)
   285  					keepOriginalArtifact = true
   286  				}
   287  			} else {
   288  				// We have a prior artifact. If we want to keep it, we append
   289  				// it to the results list. Otherwise, we destroy it.
   290  				if keep {
   291  					artifacts = append(artifacts, priorArtifact)
   292  				} else {
   293  					log.Printf("Deleting prior artifact from post-processor '%s'", corePP.processorType)
   294  					if err := priorArtifact.Destroy(); err != nil {
   295  						errors = append(errors, fmt.Errorf("Failed cleaning up prior artifact: %s", err))
   296  					}
   297  				}
   298  			}
   299  
   300  			priorArtifact = artifact
   301  		}
   302  
   303  		// Add on the last artifact to the results
   304  		if priorArtifact != nil {
   305  			artifacts = append(artifacts, priorArtifact)
   306  		}
   307  	}
   308  
   309  	if keepOriginalArtifact {
   310  		artifacts = append(artifacts, nil)
   311  		copy(artifacts[1:], artifacts)
   312  		artifacts[0] = builderArtifact
   313  	} else {
   314  		log.Printf("Deleting original artifact for build '%s'", b.name)
   315  		if err := builderArtifact.Destroy(); err != nil {
   316  			errors = append(errors, fmt.Errorf("Error destroying builder artifact: %s", err))
   317  		}
   318  	}
   319  
   320  	if len(errors) > 0 {
   321  		err = &MultiError{errors}
   322  	}
   323  
   324  	return artifacts, err
   325  }
   326  
   327  func (b *coreBuild) SetDebug(val bool) {
   328  	if b.prepareCalled {
   329  		panic("prepare has already been called")
   330  	}
   331  
   332  	b.debug = val
   333  }
   334  
   335  func (b *coreBuild) SetForce(val bool) {
   336  	if b.prepareCalled {
   337  		panic("prepare has already been called")
   338  	}
   339  
   340  	b.force = val
   341  }
   342  
   343  // Cancels the build if it is running.
   344  func (b *coreBuild) Cancel() {
   345  	b.builder.Cancel()
   346  }