terraform/config.go

207 lines
5.4 KiB
Go
Raw Normal View History

2014-05-26 02:39:44 +02:00
package main
import (
2014-08-19 18:56:50 +02:00
"fmt"
"io/ioutil"
"os/exec"
"path/filepath"
2014-08-19 18:56:50 +02:00
"github.com/hashicorp/hcl"
"github.com/hashicorp/terraform/plugin"
"github.com/hashicorp/terraform/rpc"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/osext"
2014-05-26 02:39:44 +02:00
)
// Config is the structure of the configuration for the Terraform CLI.
//
// This is not the configuration for Terraform itself. That is in the
// "config" package.
type Config struct {
2014-07-09 23:47:37 +02:00
Providers map[string]string
Provisioners map[string]string
2014-05-26 02:39:44 +02:00
}
// BuiltinConfig is the built-in defaults for the configuration. These
// can be overridden by user configurations.
var BuiltinConfig Config
2014-07-03 21:01:20 +02:00
// ContextOpts are the global ContextOpts we use to initialize the CLI.
var ContextOpts terraform.ContextOpts
func init() {
BuiltinConfig.Providers = map[string]string{
2014-07-19 02:20:28 +02:00
"aws": "terraform-provider-aws",
"digitalocean": "terraform-provider-digitalocean",
2014-07-23 06:03:30 +02:00
"heroku": "terraform-provider-heroku",
2014-07-23 23:43:28 +02:00
"dnsimple": "terraform-provider-dnsimple",
2014-07-25 23:03:17 +02:00
"consul": "terraform-provider-consul",
"cloudflare": "terraform-provider-cloudflare",
}
2014-07-09 23:47:37 +02:00
BuiltinConfig.Provisioners = map[string]string{
"local-exec": "terraform-provisioner-local-exec",
"remote-exec": "terraform-provisioner-remote-exec",
2014-07-15 23:37:55 +02:00
"file": "terraform-provisioner-file",
2014-07-09 23:47:37 +02:00
}
}
// ConfigFile returns the default path to the configuration file.
//
// On Unix-like systems this is the ".terraformrc" file in the home directory.
// On Windows, this is the "terraform.rc" file in the application data
// directory.
func ConfigFile() (string, error) {
2014-08-21 07:24:35 +02:00
return configFile()
}
2014-05-26 02:39:44 +02:00
// LoadConfig loads the CLI configuration from ".terraformrc" files.
func LoadConfig(path string) (*Config, error) {
2014-08-19 18:56:50 +02:00
// Read the HCL file and prepare for parsing
d, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf(
"Error reading %s: %s", path, err)
2014-05-26 02:39:44 +02:00
}
2014-08-19 18:56:50 +02:00
// Parse it
obj, err := hcl.Parse(string(d))
2014-05-26 02:39:44 +02:00
if err != nil {
2014-08-19 18:56:50 +02:00
return nil, fmt.Errorf(
"Error parsing %s: %s", path, err)
2014-05-26 02:39:44 +02:00
}
// Build up the result
var result Config
2014-08-19 18:56:50 +02:00
if err := hcl.DecodeObject(&result, obj); err != nil {
2014-05-26 02:39:44 +02:00
return nil, err
}
return &result, nil
}
// Merge merges two configurations and returns a third entirely
// new configuration with the two merged.
func (c1 *Config) Merge(c2 *Config) *Config {
var result Config
result.Providers = make(map[string]string)
2014-07-09 23:47:37 +02:00
result.Provisioners = make(map[string]string)
for k, v := range c1.Providers {
result.Providers[k] = v
}
for k, v := range c2.Providers {
result.Providers[k] = v
}
2014-07-09 23:47:37 +02:00
for k, v := range c1.Provisioners {
result.Provisioners[k] = v
}
for k, v := range c2.Provisioners {
result.Provisioners[k] = v
}
return &result
}
// ProviderFactories returns the mapping of prefixes to
// ResourceProviderFactory that can be used to instantiate a
// binary-based plugin.
func (c *Config) ProviderFactories() map[string]terraform.ResourceProviderFactory {
result := make(map[string]terraform.ResourceProviderFactory)
for k, v := range c.Providers {
result[k] = c.providerFactory(v)
}
return result
}
func (c *Config) providerFactory(path string) terraform.ResourceProviderFactory {
return func() (terraform.ResourceProvider, error) {
// Build the plugin client configuration and init the plugin
var config plugin.ClientConfig
2014-07-24 16:29:53 +02:00
config.Cmd = pluginCmd(path)
config.Managed = true
client := plugin.NewClient(&config)
// Request the RPC client and service name from the client
// so we can build the actual RPC-implemented provider.
rpcClient, err := client.Client()
if err != nil {
return nil, err
}
service, err := client.Service()
if err != nil {
return nil, err
}
return &rpc.ResourceProvider{
Client: rpcClient,
Name: service,
}, nil
}
}
2014-07-09 23:47:37 +02:00
// ProvisionerFactories returns the mapping of prefixes to
// ResourceProvisionerFactory that can be used to instantiate a
// binary-based plugin.
func (c *Config) ProvisionerFactories() map[string]terraform.ResourceProvisionerFactory {
result := make(map[string]terraform.ResourceProvisionerFactory)
for k, v := range c.Provisioners {
result[k] = c.provisionerFactory(v)
}
return result
}
func (c *Config) provisionerFactory(path string) terraform.ResourceProvisionerFactory {
return func() (terraform.ResourceProvisioner, error) {
// Build the plugin client configuration and init the plugin
var config plugin.ClientConfig
2014-07-24 16:29:53 +02:00
config.Cmd = pluginCmd(path)
2014-07-09 23:47:37 +02:00
config.Managed = true
client := plugin.NewClient(&config)
// Request the RPC client and service name from the client
// so we can build the actual RPC-implemented provider.
rpcClient, err := client.Client()
if err != nil {
return nil, err
}
service, err := client.Service()
if err != nil {
return nil, err
}
return &rpc.ResourceProvisioner{
Client: rpcClient,
Name: service,
}, nil
}
}
2014-07-24 16:29:53 +02:00
func pluginCmd(path string) *exec.Cmd {
originalPath := path
// First look for the provider on the PATH.
path, err := exec.LookPath(path)
if err != nil {
// If that doesn't work, look for it in the same directory
// as the executable that is running.
exePath, err := osext.Executable()
if err == nil {
path = filepath.Join(
filepath.Dir(exePath),
filepath.Base(originalPath))
}
}
// If we still don't have a path set, then set it to the
// original path and let any errors that happen bubble out.
if path == "" {
path = originalPath
}
// Build the command to execute the plugin
return exec.Command(path)
}