From 60182afb0bc82ff5fddc027ad56a00d26e1f1f5e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 2 Sep 2014 15:29:37 -0700 Subject: [PATCH] Automatically discover plugins --- config.go | 143 +++++++++++++++++++++++++++++++++--------------------- packer.go | 12 +++-- 2 files changed, 94 insertions(+), 61 deletions(-) diff --git a/config.go b/config.go index 44d362d26..26a1c19a7 100644 --- a/config.go +++ b/config.go @@ -2,68 +2,17 @@ package main import ( "encoding/json" - "github.com/mitchellh/osext" - "github.com/mitchellh/packer/packer" - "github.com/mitchellh/packer/packer/plugin" "io" "log" "os/exec" "path/filepath" + "strings" + + "github.com/mitchellh/osext" + "github.com/mitchellh/packer/packer" + "github.com/mitchellh/packer/packer/plugin" ) -// This is the default, built-in configuration that ships with -// Packer. -const defaultConfig = ` -{ - "plugin_min_port": 10000, - "plugin_max_port": 25000, - - "builders": { - "amazon-ebs": "packer-builder-amazon-ebs", - "amazon-chroot": "packer-builder-amazon-chroot", - "amazon-instance": "packer-builder-amazon-instance", - "digitalocean": "packer-builder-digitalocean", - "docker": "packer-builder-docker", - "googlecompute": "packer-builder-googlecompute", - "openstack": "packer-builder-openstack", - "qemu": "packer-builder-qemu", - "virtualbox-iso": "packer-builder-virtualbox-iso", - "virtualbox-ovf": "packer-builder-virtualbox-ovf", - "vmware-iso": "packer-builder-vmware-iso", - "vmware-vmx": "packer-builder-vmware-vmx", - "parallels-iso": "packer-builder-parallels-iso", - "parallels-pvm": "packer-builder-parallels-pvm", - "null": "packer-builder-null" - }, - - "commands": { - "build": "packer-command-build", - "fix": "packer-command-fix", - "inspect": "packer-command-inspect", - "validate": "packer-command-validate" - }, - - "post-processors": { - "vagrant": "packer-post-processor-vagrant", - "vsphere": "packer-post-processor-vsphere", - "docker-push": "packer-post-processor-docker-push", - "docker-import": "packer-post-processor-docker-import", - "vagrant-cloud": "packer-post-processor-vagrant-cloud" - }, - - "provisioners": { - "ansible-local": "packer-provisioner-ansible-local", - "chef-client": "packer-provisioner-chef-client", - "chef-solo": "packer-provisioner-chef-solo", - "file": "packer-provisioner-file", - "puppet-masterless": "packer-provisioner-puppet-masterless", - "puppet-server": "packer-provisioner-puppet-server", - "shell": "packer-provisioner-shell", - "salt-masterless": "packer-provisioner-salt-masterless" - } -} -` - type config struct { PluginMinPort uint PluginMaxPort uint @@ -81,6 +30,30 @@ func decodeConfig(r io.Reader, c *config) error { return decoder.Decode(c) } +// Discover discovers plugins. +// +// This looks in the directory of the executable and the CWD, in that +// order for priority. +func (c *config) Discover() error { + // Look in the cwd. + if err := c.discover("."); err != nil { + return err + } + + // Next, look in the same directory as the executable. Any conflicts + // will overwrite those found in our current directory. + exePath, err := osext.Executable() + if err != nil { + log.Printf("[ERR] Error loading exe directory: %s", err) + } else { + if err := c.discover(filepath.Dir(exePath)); err != nil { + return err + } + } + + return nil +} + // Returns an array of defined command names. func (c *config) CommandNames() (result []string) { result = make([]string, 0, len(c.Commands)) @@ -149,6 +122,64 @@ func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) { return c.pluginClient(bin).Provisioner() } +func (c *config) discover(path string) error { + var err error + err = c.discoverSingle( + filepath.Join(path, "packer-builder-*"), &c.Builders) + if err != nil { + return err + } + + err = c.discoverSingle( + filepath.Join(path, "packer-command-*"), &c.Commands) + if err != nil { + return err + } + + err = c.discoverSingle( + filepath.Join(path, "packer-post-processor-*"), &c.PostProcessors) + if err != nil { + return err + } + + err = c.discoverSingle( + filepath.Join(path, "packer-provisioner-*"), &c.Provisioners) + if err != nil { + return err + } + + return nil +} + +func (c *config) discoverSingle(glob string, m *map[string]string) error { + matches, err := filepath.Glob(glob) + if err != nil { + return err + } + + if *m == nil { + *m = make(map[string]string) + } + + prefix := filepath.Base(glob) + prefix = prefix[:strings.Index(prefix, "*")] + for _, match := range matches { + file := filepath.Base(match) + + // If the filename has a ".", trim up to there + if idx := strings.Index(file, "."); idx >= 0 { + file = file[:idx] + } + + // Look for foo-bar-baz. The plugin name is "baz" + plugin := file[len(prefix):] + log.Printf("[DEBUG] Discoverd plugin: %s = %s", plugin, match) + (*m)[plugin] = match + } + + return nil +} + func (c *config) pluginClient(path string) *plugin.Client { originalPath := path diff --git a/packer.go b/packer.go index 0674e503b..8bccf37cc 100644 --- a/packer.go +++ b/packer.go @@ -2,17 +2,17 @@ package main import ( - "bytes" "fmt" - "github.com/mitchellh/packer/packer" - "github.com/mitchellh/packer/packer/plugin" - "github.com/mitchellh/panicwrap" "io" "io/ioutil" "log" "os" "path/filepath" "runtime" + + "github.com/mitchellh/packer/packer" + "github.com/mitchellh/packer/packer/plugin" + "github.com/mitchellh/panicwrap" ) func main() { @@ -173,7 +173,9 @@ func extractMachineReadable(args []string) ([]string, bool) { func loadConfig() (*config, error) { var config config - if err := decodeConfig(bytes.NewBufferString(defaultConfig), &config); err != nil { + config.PluginMinPort = 10000 + config.PluginMaxPort = 25000 + if err := config.Discover(); err != nil { return nil, err }