github.com/rahart/packer@v0.12.2-0.20161229105310-282bb6ad370f/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 cancelled. 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 provisioner Provisioner 119 config []interface{} 120 } 121 122 // Returns the name of the build. 123 func (b *coreBuild) Name() string { 124 return b.name 125 } 126 127 // Prepare prepares the build by doing some initialization for the builder 128 // and any hooks. This _must_ be called prior to Run. The parameter is the 129 // overrides for the variables within the template (if any). 130 func (b *coreBuild) Prepare() (warn []string, err error) { 131 b.l.Lock() 132 defer b.l.Unlock() 133 134 if b.prepareCalled { 135 panic("prepare already called") 136 } 137 138 b.prepareCalled = true 139 140 packerConfig := map[string]interface{}{ 141 BuildNameConfigKey: b.name, 142 BuilderTypeConfigKey: b.builderType, 143 DebugConfigKey: b.debug, 144 ForceConfigKey: b.force, 145 OnErrorConfigKey: b.onError, 146 TemplatePathKey: b.templatePath, 147 UserVariablesConfigKey: b.variables, 148 } 149 150 // Prepare the builder 151 warn, err = b.builder.Prepare(b.builderConfig, packerConfig) 152 if err != nil { 153 log.Printf("Build '%s' prepare failure: %s\n", b.name, err) 154 return 155 } 156 157 // Prepare the provisioners 158 for _, coreProv := range b.provisioners { 159 configs := make([]interface{}, len(coreProv.config), len(coreProv.config)+1) 160 copy(configs, coreProv.config) 161 configs = append(configs, packerConfig) 162 163 if err = coreProv.provisioner.Prepare(configs...); err != nil { 164 return 165 } 166 } 167 168 // Prepare the post-processors 169 for _, ppSeq := range b.postProcessors { 170 for _, corePP := range ppSeq { 171 err = corePP.processor.Configure(corePP.config, packerConfig) 172 if err != nil { 173 return 174 } 175 } 176 } 177 178 return 179 } 180 181 // Runs the actual build. Prepare must be called prior to running this. 182 func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) { 183 if !b.prepareCalled { 184 panic("Prepare must be called first") 185 } 186 187 // Copy the hooks 188 hooks := make(map[string][]Hook) 189 for hookName, hookList := range b.hooks { 190 hooks[hookName] = make([]Hook, len(hookList)) 191 copy(hooks[hookName], hookList) 192 } 193 194 // Add a hook for the provisioners if we have provisioners 195 if len(b.provisioners) > 0 { 196 provisioners := make([]Provisioner, len(b.provisioners)) 197 for i, p := range b.provisioners { 198 provisioners[i] = p.provisioner 199 } 200 201 if _, ok := hooks[HookProvision]; !ok { 202 hooks[HookProvision] = make([]Hook, 0, 1) 203 } 204 205 hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{ 206 Provisioners: provisioners, 207 }) 208 } 209 210 hook := &DispatchHook{Mapping: hooks} 211 artifacts := make([]Artifact, 0, 1) 212 213 // The builder just has a normal Ui, but targetted 214 builderUi := &TargettedUi{ 215 Target: b.Name(), 216 Ui: originalUi, 217 } 218 219 log.Printf("Running builder: %s", b.builderType) 220 builderArtifact, err := b.builder.Run(builderUi, hook, cache) 221 if err != nil { 222 return nil, err 223 } 224 225 // If there was no result, don't worry about running post-processors 226 // because there is nothing they can do, just return. 227 if builderArtifact == nil { 228 return nil, nil 229 } 230 231 errors := make([]error, 0) 232 keepOriginalArtifact := len(b.postProcessors) == 0 233 234 // Run the post-processors 235 PostProcessorRunSeqLoop: 236 for _, ppSeq := range b.postProcessors { 237 priorArtifact := builderArtifact 238 for i, corePP := range ppSeq { 239 ppUi := &TargettedUi{ 240 Target: fmt.Sprintf("%s (%s)", b.Name(), corePP.processorType), 241 Ui: originalUi, 242 } 243 244 builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.processorType)) 245 artifact, keep, err := corePP.processor.PostProcess(ppUi, priorArtifact) 246 if err != nil { 247 errors = append(errors, fmt.Errorf("Post-processor failed: %s", err)) 248 continue PostProcessorRunSeqLoop 249 } 250 251 if artifact == nil { 252 log.Println("Nil artifact, halting post-processor chain.") 253 continue PostProcessorRunSeqLoop 254 } 255 256 keep = keep || corePP.keepInputArtifact 257 if i == 0 { 258 // This is the first post-processor. We handle deleting 259 // previous artifacts a bit different because multiple 260 // post-processors may be using the original and need it. 261 if !keepOriginalArtifact && keep { 262 log.Printf( 263 "Flagging to keep original artifact from post-processor '%s'", 264 corePP.processorType) 265 keepOriginalArtifact = true 266 } 267 } else { 268 // We have a prior artifact. If we want to keep it, we append 269 // it to the results list. Otherwise, we destroy it. 270 if keep { 271 artifacts = append(artifacts, priorArtifact) 272 } else { 273 log.Printf("Deleting prior artifact from post-processor '%s'", corePP.processorType) 274 if err := priorArtifact.Destroy(); err != nil { 275 errors = append(errors, fmt.Errorf("Failed cleaning up prior artifact: %s", err)) 276 } 277 } 278 } 279 280 priorArtifact = artifact 281 } 282 283 // Add on the last artifact to the results 284 if priorArtifact != nil { 285 artifacts = append(artifacts, priorArtifact) 286 } 287 } 288 289 if keepOriginalArtifact { 290 artifacts = append(artifacts, nil) 291 copy(artifacts[1:], artifacts) 292 artifacts[0] = builderArtifact 293 } else { 294 log.Printf("Deleting original artifact for build '%s'", b.name) 295 if err := builderArtifact.Destroy(); err != nil { 296 errors = append(errors, fmt.Errorf("Error destroying builder artifact: %s", err)) 297 } 298 } 299 300 if len(errors) > 0 { 301 err = &MultiError{errors} 302 } 303 304 return artifacts, err 305 } 306 307 func (b *coreBuild) SetDebug(val bool) { 308 if b.prepareCalled { 309 panic("prepare has already been called") 310 } 311 312 b.debug = val 313 } 314 315 func (b *coreBuild) SetForce(val bool) { 316 if b.prepareCalled { 317 panic("prepare has already been called") 318 } 319 320 b.force = val 321 } 322 323 func (b *coreBuild) SetOnError(val string) { 324 if b.prepareCalled { 325 panic("prepare has already been called") 326 } 327 328 b.onError = val 329 } 330 331 // Cancels the build if it is running. 332 func (b *coreBuild) Cancel() { 333 b.builder.Cancel() 334 }