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