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