github.com/HashDataInc/packer@v1.3.2/packer/core.go (about) 1 package packer 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/hashicorp/go-multierror" 8 "github.com/hashicorp/go-version" 9 "github.com/hashicorp/packer/template" 10 "github.com/hashicorp/packer/template/interpolate" 11 ) 12 13 // Core is the main executor of Packer. If Packer is being used as a 14 // library, this is the struct you'll want to instantiate to get anything done. 15 type Core struct { 16 Template *template.Template 17 18 components ComponentFinder 19 variables map[string]string 20 builds map[string]*template.Builder 21 version string 22 secrets []string 23 } 24 25 // CoreConfig is the structure for initializing a new Core. Once a CoreConfig 26 // is used to initialize a Core, it shouldn't be re-used or modified again. 27 type CoreConfig struct { 28 Components ComponentFinder 29 Template *template.Template 30 Variables map[string]string 31 SensitiveVariables []string 32 Version string 33 } 34 35 // The function type used to lookup Builder implementations. 36 type BuilderFunc func(name string) (Builder, error) 37 38 // The function type used to lookup Hook implementations. 39 type HookFunc func(name string) (Hook, error) 40 41 // The function type used to lookup PostProcessor implementations. 42 type PostProcessorFunc func(name string) (PostProcessor, error) 43 44 // The function type used to lookup Provisioner implementations. 45 type ProvisionerFunc func(name string) (Provisioner, error) 46 47 // ComponentFinder is a struct that contains the various function 48 // pointers necessary to look up components of Packer such as builders, 49 // commands, etc. 50 type ComponentFinder struct { 51 Builder BuilderFunc 52 Hook HookFunc 53 PostProcessor PostProcessorFunc 54 Provisioner ProvisionerFunc 55 } 56 57 // NewCore creates a new Core. 58 func NewCore(c *CoreConfig) (*Core, error) { 59 result := &Core{ 60 Template: c.Template, 61 components: c.Components, 62 variables: c.Variables, 63 version: c.Version, 64 } 65 66 if err := result.validate(); err != nil { 67 return nil, err 68 } 69 if err := result.init(); err != nil { 70 return nil, err 71 } 72 for _, secret := range result.secrets { 73 LogSecretFilter.Set(secret) 74 } 75 76 // Go through and interpolate all the build names. We should be able 77 // to do this at this point with the variables. 78 result.builds = make(map[string]*template.Builder) 79 for _, b := range c.Template.Builders { 80 v, err := interpolate.Render(b.Name, result.Context()) 81 if err != nil { 82 return nil, fmt.Errorf( 83 "Error interpolating builder '%s': %s", 84 b.Name, err) 85 } 86 87 result.builds[v] = b 88 } 89 90 return result, nil 91 } 92 93 // BuildNames returns the builds that are available in this configured core. 94 func (c *Core) BuildNames() []string { 95 r := make([]string, 0, len(c.builds)) 96 for n := range c.builds { 97 r = append(r, n) 98 } 99 sort.Strings(r) 100 101 return r 102 } 103 104 // Build returns the Build object for the given name. 105 func (c *Core) Build(n string) (Build, error) { 106 // Setup the builder 107 configBuilder, ok := c.builds[n] 108 if !ok { 109 return nil, fmt.Errorf("no such build found: %s", n) 110 } 111 builder, err := c.components.Builder(configBuilder.Type) 112 if err != nil { 113 return nil, fmt.Errorf( 114 "error initializing builder '%s': %s", 115 configBuilder.Type, err) 116 } 117 if builder == nil { 118 return nil, fmt.Errorf( 119 "builder type not found: %s", configBuilder.Type) 120 } 121 122 // rawName is the uninterpolated name that we use for various lookups 123 rawName := configBuilder.Name 124 125 // Setup the provisioners for this build 126 provisioners := make([]coreBuildProvisioner, 0, len(c.Template.Provisioners)) 127 for _, rawP := range c.Template.Provisioners { 128 // If we're skipping this, then ignore it 129 if rawP.Skip(rawName) { 130 continue 131 } 132 133 // Get the provisioner 134 provisioner, err := c.components.Provisioner(rawP.Type) 135 if err != nil { 136 return nil, fmt.Errorf( 137 "error initializing provisioner '%s': %s", 138 rawP.Type, err) 139 } 140 if provisioner == nil { 141 return nil, fmt.Errorf( 142 "provisioner type not found: %s", rawP.Type) 143 } 144 145 // Get the configuration 146 config := make([]interface{}, 1, 2) 147 config[0] = rawP.Config 148 if rawP.Override != nil { 149 if override, ok := rawP.Override[rawName]; ok { 150 config = append(config, override) 151 } 152 } 153 154 // If we're pausing, we wrap the provisioner in a special pauser. 155 if rawP.PauseBefore > 0 { 156 provisioner = &PausedProvisioner{ 157 PauseBefore: rawP.PauseBefore, 158 Provisioner: provisioner, 159 } 160 } 161 162 provisioners = append(provisioners, coreBuildProvisioner{ 163 pType: rawP.Type, 164 provisioner: provisioner, 165 config: config, 166 }) 167 } 168 169 // Setup the post-processors 170 postProcessors := make([][]coreBuildPostProcessor, 0, len(c.Template.PostProcessors)) 171 for _, rawPs := range c.Template.PostProcessors { 172 current := make([]coreBuildPostProcessor, 0, len(rawPs)) 173 for _, rawP := range rawPs { 174 // If we skip, ignore 175 if rawP.Skip(rawName) { 176 continue 177 } 178 179 // Get the post-processor 180 postProcessor, err := c.components.PostProcessor(rawP.Type) 181 if err != nil { 182 return nil, fmt.Errorf( 183 "error initializing post-processor '%s': %s", 184 rawP.Type, err) 185 } 186 if postProcessor == nil { 187 return nil, fmt.Errorf( 188 "post-processor type not found: %s", rawP.Type) 189 } 190 191 current = append(current, coreBuildPostProcessor{ 192 processor: postProcessor, 193 processorType: rawP.Type, 194 config: rawP.Config, 195 keepInputArtifact: rawP.KeepInputArtifact, 196 }) 197 } 198 199 // If we have no post-processors in this chain, just continue. 200 if len(current) == 0 { 201 continue 202 } 203 204 postProcessors = append(postProcessors, current) 205 } 206 207 // TODO hooks one day 208 209 return &coreBuild{ 210 name: n, 211 builder: builder, 212 builderConfig: configBuilder.Config, 213 builderType: configBuilder.Type, 214 postProcessors: postProcessors, 215 provisioners: provisioners, 216 templatePath: c.Template.Path, 217 variables: c.variables, 218 }, nil 219 } 220 221 // Context returns an interpolation context. 222 func (c *Core) Context() *interpolate.Context { 223 return &interpolate.Context{ 224 TemplatePath: c.Template.Path, 225 UserVariables: c.variables, 226 } 227 } 228 229 // validate does a full validation of the template. 230 // 231 // This will automatically call template.validate() in addition to doing 232 // richer semantic checks around variables and so on. 233 func (c *Core) validate() error { 234 // First validate the template in general, we can't do anything else 235 // unless the template itself is valid. 236 if err := c.Template.Validate(); err != nil { 237 return err 238 } 239 240 // Validate the minimum version is satisfied 241 if c.Template.MinVersion != "" { 242 versionActual, err := version.NewVersion(c.version) 243 if err != nil { 244 // This shouldn't happen since we set it via the compiler 245 panic(err) 246 } 247 248 versionMin, err := version.NewVersion(c.Template.MinVersion) 249 if err != nil { 250 return fmt.Errorf( 251 "min_version is invalid: %s", err) 252 } 253 254 if versionActual.LessThan(versionMin) { 255 return fmt.Errorf( 256 "This template requires Packer version %s or higher; using %s", 257 versionMin, 258 versionActual) 259 } 260 } 261 262 // Validate variables are set 263 var err error 264 for n, v := range c.Template.Variables { 265 if v.Required { 266 if _, ok := c.variables[n]; !ok { 267 err = multierror.Append(err, fmt.Errorf( 268 "required variable not set: %s", n)) 269 } 270 } 271 } 272 273 // TODO: validate all builders exist 274 // TODO: ^^ provisioner 275 // TODO: ^^ post-processor 276 277 return err 278 } 279 280 func (c *Core) init() error { 281 if c.variables == nil { 282 c.variables = make(map[string]string) 283 } 284 285 // Go through the variables and interpolate the environment variables 286 ctx := c.Context() 287 ctx.EnableEnv = true 288 ctx.UserVariables = nil 289 for k, v := range c.Template.Variables { 290 // Ignore variables that are required 291 if v.Required { 292 continue 293 } 294 295 // Ignore variables that have a value 296 if _, ok := c.variables[k]; ok { 297 continue 298 } 299 300 // Interpolate the default 301 def, err := interpolate.Render(v.Default, ctx) 302 if err != nil { 303 return fmt.Errorf( 304 "error interpolating default value for '%s': %s", 305 k, err) 306 } 307 308 c.variables[k] = def 309 } 310 311 for _, v := range c.Template.SensitiveVariables { 312 def, err := interpolate.Render(v.Default, ctx) 313 if err != nil { 314 return fmt.Errorf( 315 "error interpolating default value for '%#v': %s", 316 v, err) 317 } 318 c.secrets = append(c.secrets, def) 319 } 320 321 // Interpolate the push configuration 322 if _, err := interpolate.RenderInterface(&c.Template.Push, c.Context()); err != nil { 323 return fmt.Errorf("Error interpolating 'push': %s", err) 324 } 325 326 return nil 327 }