main: auto-discover plugins [GH-190]

/cc @pearkes @armon - exe dir and pwd
This commit is contained in:
Mitchell Hashimoto 2014-08-28 17:27:15 -07:00
parent f2f119d0eb
commit 4fd3dff829
3 changed files with 82 additions and 16 deletions

View File

@ -1,5 +1,10 @@
## 0.2.1 (unreleased)
IMPROVEMENTS:
* core: Plugins are automatically discovered in the executable directory
or pwd if named properly. [GH-190]
BUG FIXES:
* core: Configuration parses when identifier and '=' have no space. [GH-243]

View File

@ -3,6 +3,7 @@ package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
@ -31,22 +32,6 @@ var BuiltinConfig Config
// ContextOpts are the global ContextOpts we use to initialize the CLI.
var ContextOpts terraform.ContextOpts
func init() {
BuiltinConfig.Providers = map[string]string{
"aws": "terraform-provider-aws",
"digitalocean": "terraform-provider-digitalocean",
"heroku": "terraform-provider-heroku",
"dnsimple": "terraform-provider-dnsimple",
"consul": "terraform-provider-consul",
"cloudflare": "terraform-provider-cloudflare",
}
BuiltinConfig.Provisioners = map[string]string{
"local-exec": "terraform-provisioner-local-exec",
"remote-exec": "terraform-provisioner-remote-exec",
"file": "terraform-provisioner-file",
}
}
// ConfigFile returns the default path to the configuration file.
//
// On Unix-like systems this is the ".terraformrc" file in the home directory.
@ -81,6 +66,30 @@ func LoadConfig(path string) (*Config, error) {
return &result, nil
}
// 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
}
// Merge merges two configurations and returns a third entirely
// new configuration with the two merged.
func (c1 *Config) Merge(c2 *Config) *Config {
@ -103,6 +112,54 @@ func (c1 *Config) Merge(c2 *Config) *Config {
return &result
}
func (c *Config) discover(path string) error {
var err error
err = c.discoverSingle(
filepath.Join(path, "terraform-provider-*"), &c.Providers)
if err != nil {
return err
}
err = c.discoverSingle(
filepath.Join(path, "terraform-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)
}
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"
parts := strings.SplitN(file, "-", 3)
if len(parts) != 3 {
continue
}
log.Printf("[DEBUG] Discoverd plugin: %s = %s", parts[2], match)
(*m)[parts[2]] = match
}
return nil
}
// ProviderFactories returns the mapping of prefixes to
// ResourceProviderFactory that can be used to instantiate a
// binary-based plugin.

View File

@ -79,6 +79,10 @@ func wrappedMain() int {
// Load the configuration
config := BuiltinConfig
if err := config.Discover(); err != nil {
fmt.Fprintf(os.Stderr, "Error discovering plugins: %s", err)
return 1
}
// Make sure we clean up any managed plugins at the end of this
defer plugin.CleanupClients()