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