github.com/ezbercih/terraform@v0.1.1-0.20140729011846-3c33865e0839/config.go (about)

     1  package main
     2  
     3  import (
     4  	"os/exec"
     5  	"path/filepath"
     6  
     7  	"github.com/hashicorp/terraform/plugin"
     8  	"github.com/hashicorp/terraform/rpc"
     9  	"github.com/hashicorp/terraform/terraform"
    10  	"github.com/mitchellh/go-libucl"
    11  	"github.com/mitchellh/osext"
    12  )
    13  
    14  // Config is the structure of the configuration for the Terraform CLI.
    15  //
    16  // This is not the configuration for Terraform itself. That is in the
    17  // "config" package.
    18  type Config struct {
    19  	Providers    map[string]string
    20  	Provisioners map[string]string
    21  }
    22  
    23  // BuiltinConfig is the built-in defaults for the configuration. These
    24  // can be overridden by user configurations.
    25  var BuiltinConfig Config
    26  
    27  // ContextOpts are the global ContextOpts we use to initialize the CLI.
    28  var ContextOpts terraform.ContextOpts
    29  
    30  // Put the parse flags we use for libucl in a constant so we can get
    31  // equally behaving parsing everywhere.
    32  const libuclParseFlags = libucl.ParserNoTime
    33  
    34  func init() {
    35  	BuiltinConfig.Providers = map[string]string{
    36  		"aws":          "terraform-provider-aws",
    37  		"digitalocean": "terraform-provider-digitalocean",
    38  		"heroku":       "terraform-provider-heroku",
    39  		"dnsimple":     "terraform-provider-dnsimple",
    40  		"consul":       "terraform-provider-consul",
    41  		"cloudflare":   "terraform-provider-cloudflare",
    42  	}
    43  	BuiltinConfig.Provisioners = map[string]string{
    44  		"local-exec":  "terraform-provisioner-local-exec",
    45  		"remote-exec": "terraform-provisioner-remote-exec",
    46  		"file":        "terraform-provisioner-file",
    47  	}
    48  }
    49  
    50  // LoadConfig loads the CLI configuration from ".terraformrc" files.
    51  func LoadConfig(path string) (*Config, error) {
    52  	var obj *libucl.Object
    53  
    54  	// Parse the file and get the root object.
    55  	parser := libucl.NewParser(libuclParseFlags)
    56  	err := parser.AddFile(path)
    57  	if err == nil {
    58  		obj = parser.Object()
    59  		defer obj.Close()
    60  	}
    61  	defer parser.Close()
    62  
    63  	// If there was an error parsing, return now.
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	// Build up the result
    69  	var result Config
    70  
    71  	if err := obj.Decode(&result); err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return &result, nil
    76  }
    77  
    78  // Merge merges two configurations and returns a third entirely
    79  // new configuration with the two merged.
    80  func (c1 *Config) Merge(c2 *Config) *Config {
    81  	var result Config
    82  	result.Providers = make(map[string]string)
    83  	result.Provisioners = make(map[string]string)
    84  	for k, v := range c1.Providers {
    85  		result.Providers[k] = v
    86  	}
    87  	for k, v := range c2.Providers {
    88  		result.Providers[k] = v
    89  	}
    90  	for k, v := range c1.Provisioners {
    91  		result.Provisioners[k] = v
    92  	}
    93  	for k, v := range c2.Provisioners {
    94  		result.Provisioners[k] = v
    95  	}
    96  
    97  	return &result
    98  }
    99  
   100  // ProviderFactories returns the mapping of prefixes to
   101  // ResourceProviderFactory that can be used to instantiate a
   102  // binary-based plugin.
   103  func (c *Config) ProviderFactories() map[string]terraform.ResourceProviderFactory {
   104  	result := make(map[string]terraform.ResourceProviderFactory)
   105  	for k, v := range c.Providers {
   106  		result[k] = c.providerFactory(v)
   107  	}
   108  
   109  	return result
   110  }
   111  
   112  func (c *Config) providerFactory(path string) terraform.ResourceProviderFactory {
   113  	return func() (terraform.ResourceProvider, error) {
   114  		// Build the plugin client configuration and init the plugin
   115  		var config plugin.ClientConfig
   116  		config.Cmd = pluginCmd(path)
   117  		config.Managed = true
   118  		client := plugin.NewClient(&config)
   119  
   120  		// Request the RPC client and service name from the client
   121  		// so we can build the actual RPC-implemented provider.
   122  		rpcClient, err := client.Client()
   123  		if err != nil {
   124  			return nil, err
   125  		}
   126  
   127  		service, err := client.Service()
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  
   132  		return &rpc.ResourceProvider{
   133  			Client: rpcClient,
   134  			Name:   service,
   135  		}, nil
   136  	}
   137  }
   138  
   139  // ProvisionerFactories returns the mapping of prefixes to
   140  // ResourceProvisionerFactory that can be used to instantiate a
   141  // binary-based plugin.
   142  func (c *Config) ProvisionerFactories() map[string]terraform.ResourceProvisionerFactory {
   143  	result := make(map[string]terraform.ResourceProvisionerFactory)
   144  	for k, v := range c.Provisioners {
   145  		result[k] = c.provisionerFactory(v)
   146  	}
   147  
   148  	return result
   149  }
   150  
   151  func (c *Config) provisionerFactory(path string) terraform.ResourceProvisionerFactory {
   152  	return func() (terraform.ResourceProvisioner, error) {
   153  		// Build the plugin client configuration and init the plugin
   154  		var config plugin.ClientConfig
   155  		config.Cmd = pluginCmd(path)
   156  		config.Managed = true
   157  		client := plugin.NewClient(&config)
   158  
   159  		// Request the RPC client and service name from the client
   160  		// so we can build the actual RPC-implemented provider.
   161  		rpcClient, err := client.Client()
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  
   166  		service, err := client.Service()
   167  		if err != nil {
   168  			return nil, err
   169  		}
   170  
   171  		return &rpc.ResourceProvisioner{
   172  			Client: rpcClient,
   173  			Name:   service,
   174  		}, nil
   175  	}
   176  }
   177  
   178  func pluginCmd(path string) *exec.Cmd {
   179  	originalPath := path
   180  
   181  	// First look for the provider on the PATH.
   182  	path, err := exec.LookPath(path)
   183  	if err != nil {
   184  		// If that doesn't work, look for it in the same directory
   185  		// as the executable that is running.
   186  		exePath, err := osext.Executable()
   187  		if err == nil {
   188  			path = filepath.Join(
   189  				filepath.Dir(exePath),
   190  				filepath.Base(originalPath))
   191  		}
   192  	}
   193  
   194  	// If we still don't have a path set, then set it to the
   195  	// original path and let any errors that happen bubble out.
   196  	if path == "" {
   197  		path = originalPath
   198  	}
   199  
   200  	// Build the command to execute the plugin
   201  	return exec.Command(path)
   202  }