github.com/zakuro9715/packer@v0.8.2-0.20150707220638-da749e247094/config.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "io" 6 "log" 7 "os/exec" 8 "path/filepath" 9 "runtime" 10 "strings" 11 12 "github.com/mitchellh/osext" 13 "github.com/mitchellh/packer/packer" 14 "github.com/mitchellh/packer/packer/plugin" 15 ) 16 17 type config struct { 18 DisableCheckpoint bool `json:"disable_checkpoint"` 19 DisableCheckpointSignature bool `json:"disable_checkpoint_signature"` 20 PluginMinPort uint 21 PluginMaxPort uint 22 23 Builders 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 // Search the directory of the executable, then the plugins directory, and 51 // finally the CWD, in that order. Any conflicts will overwrite previously 52 // found plugins, in that order. 53 // Hence, the priority order is the reverse of the search order - i.e., the 54 // CWD has the highest priority. 55 func (c *config) Discover() error { 56 // First, look in the same directory as the executable. 57 exePath, err := osext.Executable() 58 if err != nil { 59 log.Printf("[ERR] Error loading exe directory: %s", err) 60 } else { 61 if err := c.discover(filepath.Dir(exePath)); err != nil { 62 return err 63 } 64 } 65 66 // Next, look in the plugins directory. 67 dir, err := ConfigDir() 68 if err != nil { 69 log.Printf("[ERR] Error loading config directory: %s", err) 70 } else { 71 if err := c.discover(filepath.Join(dir, "plugins")); err != nil { 72 return err 73 } 74 } 75 76 // Last, look in the CWD. 77 if err := c.discover("."); err != nil { 78 return err 79 } 80 81 return nil 82 } 83 84 // This is a proper packer.BuilderFunc that can be used to load packer.Builder 85 // implementations from the defined plugins. 86 func (c *config) LoadBuilder(name string) (packer.Builder, error) { 87 log.Printf("Loading builder: %s\n", name) 88 bin, ok := c.Builders[name] 89 if !ok { 90 log.Printf("Builder not found: %s\n", name) 91 return nil, nil 92 } 93 94 return c.pluginClient(bin).Builder() 95 } 96 97 // This is a proper implementation of packer.HookFunc that can be used 98 // to load packer.Hook implementations from the defined plugins. 99 func (c *config) LoadHook(name string) (packer.Hook, error) { 100 log.Printf("Loading hook: %s\n", name) 101 return c.pluginClient(name).Hook() 102 } 103 104 // This is a proper packer.PostProcessorFunc that can be used to load 105 // packer.PostProcessor implementations from defined plugins. 106 func (c *config) LoadPostProcessor(name string) (packer.PostProcessor, error) { 107 log.Printf("Loading post-processor: %s", name) 108 bin, ok := c.PostProcessors[name] 109 if !ok { 110 log.Printf("Post-processor not found: %s", name) 111 return nil, nil 112 } 113 114 return c.pluginClient(bin).PostProcessor() 115 } 116 117 // This is a proper packer.ProvisionerFunc that can be used to load 118 // packer.Provisioner implementations from defined plugins. 119 func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) { 120 log.Printf("Loading provisioner: %s\n", name) 121 bin, ok := c.Provisioners[name] 122 if !ok { 123 log.Printf("Provisioner not found: %s\n", name) 124 return nil, nil 125 } 126 127 return c.pluginClient(bin).Provisioner() 128 } 129 130 func (c *config) discover(path string) error { 131 var err error 132 133 if !filepath.IsAbs(path) { 134 path, err = filepath.Abs(path) 135 if err != nil { 136 return err 137 } 138 } 139 140 err = c.discoverSingle( 141 filepath.Join(path, "packer-builder-*"), &c.Builders) 142 if err != nil { 143 return err 144 } 145 146 err = c.discoverSingle( 147 filepath.Join(path, "packer-post-processor-*"), &c.PostProcessors) 148 if err != nil { 149 return err 150 } 151 152 err = c.discoverSingle( 153 filepath.Join(path, "packer-provisioner-*"), &c.Provisioners) 154 if err != nil { 155 return err 156 } 157 158 return nil 159 } 160 161 func (c *config) discoverSingle(glob string, m *map[string]string) error { 162 matches, err := filepath.Glob(glob) 163 if err != nil { 164 return err 165 } 166 167 if *m == nil { 168 *m = make(map[string]string) 169 } 170 171 prefix := filepath.Base(glob) 172 prefix = prefix[:strings.Index(prefix, "*")] 173 for _, match := range matches { 174 file := filepath.Base(match) 175 176 // One Windows, ignore any plugins that don't end in .exe. 177 // We could do a full PATHEXT parse, but this is probably good enough. 178 if runtime.GOOS == "windows" && strings.ToLower(filepath.Ext(file)) != ".exe" { 179 log.Printf( 180 "[DEBUG] Ignoring plugin match %s, no exe extension", 181 match) 182 continue 183 } 184 185 // If the filename has a ".", trim up to there 186 if idx := strings.Index(file, "."); idx >= 0 { 187 file = file[:idx] 188 } 189 190 // Look for foo-bar-baz. The plugin name is "baz" 191 plugin := file[len(prefix):] 192 log.Printf("[DEBUG] Discovered plugin: %s = %s", plugin, match) 193 (*m)[plugin] = match 194 } 195 196 return nil 197 } 198 199 func (c *config) pluginClient(path string) *plugin.Client { 200 originalPath := path 201 202 // First attempt to find the executable by consulting the PATH. 203 path, err := exec.LookPath(path) 204 if err != nil { 205 // If that doesn't work, look for it in the same directory 206 // as the `packer` executable (us). 207 log.Printf("Plugin could not be found. Checking same directory as executable.") 208 exePath, err := osext.Executable() 209 if err != nil { 210 log.Printf("Couldn't get current exe path: %s", err) 211 } else { 212 log.Printf("Current exe path: %s", exePath) 213 path = filepath.Join(filepath.Dir(exePath), filepath.Base(originalPath)) 214 } 215 } 216 217 // If everything failed, just use the original path and let the error 218 // bubble through. 219 if path == "" { 220 path = originalPath 221 } 222 223 log.Printf("Creating plugin client for path: %s", path) 224 var config plugin.ClientConfig 225 config.Cmd = exec.Command(path) 226 config.Managed = true 227 config.MinPort = c.PluginMinPort 228 config.MaxPort = c.PluginMaxPort 229 return plugin.NewClient(&config) 230 }