github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/config.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "io" 6 "log" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 11 "github.com/mitchellh/osext" 12 "github.com/mitchellh/packer/packer" 13 "github.com/mitchellh/packer/packer/plugin" 14 ) 15 16 type config struct { 17 DisableCheckpoint bool `json:"disable_checkpoint"` 18 DisableCheckpointSignature bool `json:"disable_checkpoint_signature"` 19 PluginMinPort uint 20 PluginMaxPort uint 21 22 Builders map[string]string 23 Commands map[string]string 24 PostProcessors map[string]string `json:"post-processors"` 25 Provisioners map[string]string 26 } 27 28 // ConfigFile returns the default path to the configuration file. On 29 // Unix-like systems this is the ".packerconfig" file in the home directory. 30 // On Windows, this is the "packer.config" file in the application data 31 // directory. 32 func ConfigFile() (string, error) { 33 return configFile() 34 } 35 36 // ConfigDir returns the configuration directory for Packer. 37 func ConfigDir() (string, error) { 38 return configDir() 39 } 40 41 // Decodes configuration in JSON format from the given io.Reader into 42 // the config object pointed to. 43 func decodeConfig(r io.Reader, c *config) error { 44 decoder := json.NewDecoder(r) 45 return decoder.Decode(c) 46 } 47 48 // Discover discovers plugins. 49 // 50 // This looks in the directory of the executable and the CWD, in that 51 // order for priority. 52 func (c *config) Discover() error { 53 // Next, look in the same directory as the executable. Any conflicts 54 // will overwrite those found in our current directory. 55 exePath, err := osext.Executable() 56 if err != nil { 57 log.Printf("[ERR] Error loading exe directory: %s", err) 58 } else { 59 if err := c.discover(filepath.Dir(exePath)); err != nil { 60 return err 61 } 62 } 63 64 // Look in the plugins directory 65 dir, err := ConfigDir() 66 if err != nil { 67 log.Printf("[ERR] Error loading config directory: %s", err) 68 } else { 69 if err := c.discover(filepath.Join(dir, "plugins")); err != nil { 70 return err 71 } 72 } 73 74 // Look in the cwd. 75 cwd, err := filepath.Abs(".") 76 if err != nil { 77 log.Printf("[ERR] Error getting path for working directory") 78 } else { 79 if err := c.discover(cwd); err != nil { 80 return err 81 } 82 } 83 return nil 84 } 85 86 // Returns an array of defined command names. 87 func (c *config) CommandNames() (result []string) { 88 result = make([]string, 0, len(c.Commands)) 89 for name := range c.Commands { 90 result = append(result, name) 91 } 92 return 93 } 94 95 // This is a proper packer.BuilderFunc that can be used to load packer.Builder 96 // implementations from the defined plugins. 97 func (c *config) LoadBuilder(name string) (packer.Builder, error) { 98 log.Printf("Loading builder: %s\n", name) 99 bin, ok := c.Builders[name] 100 if !ok { 101 log.Printf("Builder not found: %s\n", name) 102 return nil, nil 103 } 104 105 return c.pluginClient(bin).Builder() 106 } 107 108 // This is a proper packer.CommandFunc that can be used to load packer.Command 109 // implementations from the defined plugins. 110 func (c *config) LoadCommand(name string) (packer.Command, error) { 111 log.Printf("Loading command: %s\n", name) 112 bin, ok := c.Commands[name] 113 if !ok { 114 log.Printf("Command not found: %s\n", name) 115 return nil, nil 116 } 117 118 return c.pluginClient(bin).Command() 119 } 120 121 // This is a proper implementation of packer.HookFunc that can be used 122 // to load packer.Hook implementations from the defined plugins. 123 func (c *config) LoadHook(name string) (packer.Hook, error) { 124 log.Printf("Loading hook: %s\n", name) 125 return c.pluginClient(name).Hook() 126 } 127 128 // This is a proper packer.PostProcessorFunc that can be used to load 129 // packer.PostProcessor implementations from defined plugins. 130 func (c *config) LoadPostProcessor(name string) (packer.PostProcessor, error) { 131 log.Printf("Loading post-processor: %s", name) 132 bin, ok := c.PostProcessors[name] 133 if !ok { 134 log.Printf("Post-processor not found: %s", name) 135 return nil, nil 136 } 137 138 return c.pluginClient(bin).PostProcessor() 139 } 140 141 // This is a proper packer.ProvisionerFunc that can be used to load 142 // packer.Provisioner implementations from defined plugins. 143 func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) { 144 log.Printf("Loading provisioner: %s\n", name) 145 bin, ok := c.Provisioners[name] 146 if !ok { 147 log.Printf("Provisioner not found: %s\n", name) 148 return nil, nil 149 } 150 151 return c.pluginClient(bin).Provisioner() 152 } 153 154 func (c *config) discover(path string) error { 155 var err error 156 err = c.discoverSingle( 157 filepath.Join(path, "packer-builder-*"), &c.Builders) 158 if err != nil { 159 return err 160 } 161 162 err = c.discoverSingle( 163 filepath.Join(path, "packer-command-*"), &c.Commands) 164 if err != nil { 165 return err 166 } 167 168 err = c.discoverSingle( 169 filepath.Join(path, "packer-post-processor-*"), &c.PostProcessors) 170 if err != nil { 171 return err 172 } 173 174 err = c.discoverSingle( 175 filepath.Join(path, "packer-provisioner-*"), &c.Provisioners) 176 if err != nil { 177 return err 178 } 179 180 return nil 181 } 182 183 func (c *config) discoverSingle(glob string, m *map[string]string) error { 184 matches, err := filepath.Glob(glob) 185 if err != nil { 186 return err 187 } 188 189 if *m == nil { 190 *m = make(map[string]string) 191 } 192 193 prefix := filepath.Base(glob) 194 prefix = prefix[:strings.Index(prefix, "*")] 195 for _, match := range matches { 196 file := filepath.Base(match) 197 198 // If the filename has a ".", trim up to there 199 if idx := strings.Index(file, "."); idx >= 0 { 200 file = file[:idx] 201 } 202 203 // Look for foo-bar-baz. The plugin name is "baz" 204 plugin := file[len(prefix):] 205 log.Printf("[DEBUG] Discoverd plugin: %s = %s", plugin, match) 206 (*m)[plugin] = match 207 } 208 209 return nil 210 } 211 212 func (c *config) pluginClient(path string) *plugin.Client { 213 originalPath := path 214 215 // First attempt to find the executable by consulting the PATH. 216 path, err := exec.LookPath(path) 217 if err != nil { 218 // If that doesn't work, look for it in the same directory 219 // as the `packer` executable (us). 220 log.Printf("Plugin could not be found. Checking same directory as executable.") 221 exePath, err := osext.Executable() 222 if err != nil { 223 log.Printf("Couldn't get current exe path: %s", err) 224 } else { 225 log.Printf("Current exe path: %s", exePath) 226 path = filepath.Join(filepath.Dir(exePath), filepath.Base(originalPath)) 227 } 228 } 229 230 // If everything failed, just use the original path and let the error 231 // bubble through. 232 if path == "" { 233 path = originalPath 234 } 235 236 log.Printf("Creating plugin client for path: %s", path) 237 var config plugin.ClientConfig 238 config.Cmd = exec.Command(path) 239 config.Managed = true 240 config.MinPort = c.PluginMinPort 241 config.MaxPort = c.PluginMaxPort 242 return plugin.NewClient(&config) 243 }