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