Support for multiple providers of the same type
Adds an "alias" field to the provider which allows creating multiple instances of a provider under different names. This provides support for configurations such as multiple AWS providers for different regions. In each resource, the provider can be set with the "provider" field. (thanks to Cisco Cloud for their support)
This commit is contained in:
parent
ff224dec13
commit
21b0a03d70
|
@ -237,6 +237,38 @@ func TestAccAWSInstance_vpc(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAWSInstance_multipleRegions(t *testing.T) {
|
||||
var v ec2.Instance
|
||||
|
||||
// record the initialized providers so that we can use them to
|
||||
// check for the instances in each region
|
||||
var providers []*schema.Provider
|
||||
providerFactories := map[string]terraform.ResourceProviderFactory{
|
||||
"aws": func() (terraform.ResourceProvider, error) {
|
||||
p := Provider()
|
||||
providers = append(providers, p.(*schema.Provider))
|
||||
return p, nil
|
||||
},
|
||||
}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
ProviderFactories: providerFactories,
|
||||
CheckDestroy: testAccCheckInstanceDestroyWithProviders(&providers),
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccInstanceConfigMultipleRegions,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckInstanceExistsWithProviders(
|
||||
"aws_instance.foo", &v, &providers),
|
||||
testAccCheckInstanceExistsWithProviders(
|
||||
"aws_instance.bar", &v, &providers),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSInstance_NetworkInstanceSecurityGroups(t *testing.T) {
|
||||
var v ec2.Instance
|
||||
|
||||
|
@ -364,7 +396,25 @@ func TestAccAWSInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
|||
}
|
||||
|
||||
func testAccCheckInstanceDestroy(s *terraform.State) error {
|
||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||
return testAccCheckInstanceDestroyWithProvider(s, testAccProvider)
|
||||
}
|
||||
|
||||
func testAccCheckInstanceDestroyWithProviders(providers *[]*schema.Provider) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
for _, provider := range *providers {
|
||||
if provider.Meta() == nil {
|
||||
continue
|
||||
}
|
||||
if err := testAccCheckInstanceDestroyWithProvider(s, provider); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckInstanceDestroyWithProvider(s *terraform.State, provider *schema.Provider) error {
|
||||
conn := provider.Meta().(*AWSClient).ec2conn
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "aws_instance" {
|
||||
|
@ -397,6 +447,11 @@ func testAccCheckInstanceDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFunc {
|
||||
providers := []*schema.Provider{testAccProvider}
|
||||
return testAccCheckInstanceExistsWithProviders(n, i, &providers)
|
||||
}
|
||||
|
||||
func testAccCheckInstanceExistsWithProviders(n string, i *ec2.Instance, providers *[]*schema.Provider) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
@ -406,21 +461,25 @@ func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFun
|
|||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
}
|
||||
for _, provider := range *providers {
|
||||
conn := provider.Meta().(*AWSClient).ec2conn
|
||||
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{
|
||||
InstanceIDs: []*string{aws.String(rs.Primary.ID)},
|
||||
})
|
||||
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{
|
||||
InstanceIDs: []*string{aws.String(rs.Primary.ID)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.Reservations) == 0 {
|
||||
return fmt.Errorf("Instance not found")
|
||||
if len(resp.Reservations) > 0 {
|
||||
*i = *resp.Reservations[0].Instances[0]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
*i = *resp.Reservations[0].Instances[0]
|
||||
|
||||
return nil
|
||||
return fmt.Errorf("Instance not found")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -563,6 +622,32 @@ resource "aws_instance" "foo" {
|
|||
}
|
||||
`
|
||||
|
||||
const testAccInstanceConfigMultipleRegions = `
|
||||
provider "aws" {
|
||||
alias = "west"
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
alias = "east"
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
resource "aws_instance" "foo" {
|
||||
# us-west-2
|
||||
provider = "aws.west"
|
||||
ami = "ami-4fccb37f"
|
||||
instance_type = "m1.small"
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
# us-east-1
|
||||
provider = "aws.east"
|
||||
ami = "ami-8c6ea9e4"
|
||||
instance_type = "m1.small"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccCheckInstanceConfigTags = `
|
||||
resource "aws_instance" "foo" {
|
||||
ami = "ami-4fccb37f"
|
||||
|
|
|
@ -63,6 +63,7 @@ type Module struct {
|
|||
// resource provider.
|
||||
type ProviderConfig struct {
|
||||
Name string
|
||||
Alias string
|
||||
RawConfig *RawConfig
|
||||
}
|
||||
|
||||
|
@ -75,6 +76,7 @@ type Resource struct {
|
|||
RawCount *RawConfig
|
||||
RawConfig *RawConfig
|
||||
Provisioners []*Provisioner
|
||||
Provider string
|
||||
DependsOn []string
|
||||
Lifecycle ResourceLifecycle
|
||||
}
|
||||
|
|
|
@ -325,13 +325,13 @@ func loadOutputsHcl(os *hclobj.Object) ([]*Output, error) {
|
|||
// LoadProvidersHcl recurses into the given HCL object and turns
|
||||
// it into a mapping of provider configs.
|
||||
func loadProvidersHcl(os *hclobj.Object) ([]*ProviderConfig, error) {
|
||||
objects := make(map[string]*hclobj.Object)
|
||||
var objects []*hclobj.Object
|
||||
|
||||
// Iterate over all the "provider" blocks and get the keys along with
|
||||
// their raw configuration objects. We'll parse those later.
|
||||
for _, o1 := range os.Elem(false) {
|
||||
for _, o2 := range o1.Elem(true) {
|
||||
objects[o2.Key] = o2
|
||||
objects = append(objects, o2)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,23 +341,38 @@ func loadProvidersHcl(os *hclobj.Object) ([]*ProviderConfig, error) {
|
|||
|
||||
// Go through each object and turn it into an actual result.
|
||||
result := make([]*ProviderConfig, 0, len(objects))
|
||||
for n, o := range objects {
|
||||
for _, o := range objects {
|
||||
var config map[string]interface{}
|
||||
|
||||
if err := hcl.DecodeObject(&config, o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(config, "alias")
|
||||
|
||||
rawConfig, err := NewRawConfig(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading config for provider config %s: %s",
|
||||
n,
|
||||
o.Key,
|
||||
err)
|
||||
}
|
||||
|
||||
// If we have an alias field, then add those in
|
||||
var alias string
|
||||
if a := o.Get("alias", false); a != nil {
|
||||
err := hcl.DecodeObject(&alias, a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading alias for provider[%s]: %s",
|
||||
o.Key,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, &ProviderConfig{
|
||||
Name: n,
|
||||
Name: o.Key,
|
||||
Alias: alias,
|
||||
RawConfig: rawConfig,
|
||||
})
|
||||
}
|
||||
|
@ -417,6 +432,7 @@ func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) {
|
|||
delete(config, "count")
|
||||
delete(config, "depends_on")
|
||||
delete(config, "provisioner")
|
||||
delete(config, "provider")
|
||||
delete(config, "lifecycle")
|
||||
|
||||
rawConfig, err := NewRawConfig(config)
|
||||
|
@ -488,6 +504,19 @@ func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// If we have a provider, then parse it out
|
||||
var provider string
|
||||
if o := obj.Get("provider", false); o != nil {
|
||||
err := hcl.DecodeObject(&provider, o)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading provider for %s[%s]: %s",
|
||||
t.Key,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the resource should be re-created before
|
||||
// destroying the existing instance
|
||||
var lifecycle ResourceLifecycle
|
||||
|
@ -508,6 +537,7 @@ func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) {
|
|||
RawCount: countConfig,
|
||||
RawConfig: rawConfig,
|
||||
Provisioners: provisioners,
|
||||
Provider: provider,
|
||||
DependsOn: dependsOn,
|
||||
Lifecycle: lifecycle,
|
||||
})
|
||||
|
|
|
@ -35,7 +35,8 @@ type TestCase struct {
|
|||
PreCheck func()
|
||||
|
||||
// Provider is the ResourceProvider that will be under test.
|
||||
Providers map[string]terraform.ResourceProvider
|
||||
Providers map[string]terraform.ResourceProvider
|
||||
ProviderFactories map[string]terraform.ResourceProviderFactory
|
||||
|
||||
// CheckDestroy is called after the resource is finally destroyed
|
||||
// to allow the tester to test that the resource is truly gone.
|
||||
|
@ -102,9 +103,12 @@ func Test(t TestT, c TestCase) {
|
|||
}
|
||||
|
||||
// Build our context options that we can
|
||||
ctxProviders := make(map[string]terraform.ResourceProviderFactory)
|
||||
for k, p := range c.Providers {
|
||||
ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p)
|
||||
ctxProviders := c.ProviderFactories
|
||||
if ctxProviders == nil {
|
||||
ctxProviders = make(map[string]terraform.ResourceProviderFactory)
|
||||
for k, p := range c.Providers {
|
||||
ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p)
|
||||
}
|
||||
}
|
||||
opts := terraform.ContextOpts{Providers: ctxProviders}
|
||||
|
||||
|
|
|
@ -3286,6 +3286,39 @@ func TestContext2Apply(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_providerAlias(t *testing.T) {
|
||||
m := testModule(t, "apply-provider-alias")
|
||||
p := testProvider("aws")
|
||||
p.ApplyFn = testApplyFn
|
||||
p.DiffFn = testDiffFn
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
if _, err := ctx.Plan(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
state, err := ctx.Apply()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
mod := state.RootModule()
|
||||
if len(mod.Resources) < 2 {
|
||||
t.Fatalf("bad: %#v", mod.Resources)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(state.String())
|
||||
expected := strings.TrimSpace(testTerraformApplyProviderAliasStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad: \n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_emptyModule(t *testing.T) {
|
||||
m := testModule(t, "apply-empty-module")
|
||||
p := testProvider("aws")
|
||||
|
|
|
@ -3,6 +3,7 @@ package terraform
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
|
@ -68,9 +69,11 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
|
|||
ctx.ProviderLock.Lock()
|
||||
defer ctx.ProviderLock.Unlock()
|
||||
|
||||
f, ok := ctx.Providers[n]
|
||||
typeName := strings.SplitN(n, ".", 2)[0]
|
||||
|
||||
f, ok := ctx.Providers[typeName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Provider '%s' not found", n)
|
||||
return nil, fmt.Errorf("Provider '%s' not found", typeName)
|
||||
}
|
||||
|
||||
p, err := f()
|
||||
|
|
|
@ -154,12 +154,13 @@ func (n *EvalUpdateStateHook) Eval(ctx EvalContext) (interface{}, error) {
|
|||
type EvalWriteState struct {
|
||||
Name string
|
||||
ResourceType string
|
||||
Provider string
|
||||
Dependencies []string
|
||||
State **InstanceState
|
||||
}
|
||||
|
||||
func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
|
||||
return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies,
|
||||
return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies,
|
||||
func(rs *ResourceState) error {
|
||||
rs.Primary = *n.State
|
||||
return nil
|
||||
|
@ -172,6 +173,7 @@ func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
|
|||
type EvalWriteStateTainted struct {
|
||||
Name string
|
||||
ResourceType string
|
||||
Provider string
|
||||
Dependencies []string
|
||||
State **InstanceState
|
||||
// Index indicates which instance in the Tainted list to target, or -1 to append.
|
||||
|
@ -181,7 +183,7 @@ type EvalWriteStateTainted struct {
|
|||
// EvalWriteStateTainted is an EvalNode implementation that writes the
|
||||
// one of the tainted InstanceStates for a specific resource out of the state.
|
||||
func (n *EvalWriteStateTainted) Eval(ctx EvalContext) (interface{}, error) {
|
||||
return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies,
|
||||
return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies,
|
||||
func(rs *ResourceState) error {
|
||||
if n.Index == -1 {
|
||||
rs.Tainted = append(rs.Tainted, *n.State)
|
||||
|
@ -198,6 +200,7 @@ func (n *EvalWriteStateTainted) Eval(ctx EvalContext) (interface{}, error) {
|
|||
type EvalWriteStateDeposed struct {
|
||||
Name string
|
||||
ResourceType string
|
||||
Provider string
|
||||
Dependencies []string
|
||||
State **InstanceState
|
||||
// Index indicates which instance in the Deposed list to target, or -1 to append.
|
||||
|
@ -205,7 +208,7 @@ type EvalWriteStateDeposed struct {
|
|||
}
|
||||
|
||||
func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) {
|
||||
return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies,
|
||||
return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies,
|
||||
func(rs *ResourceState) error {
|
||||
if n.Index == -1 {
|
||||
rs.Deposed = append(rs.Deposed, *n.State)
|
||||
|
@ -225,6 +228,7 @@ func writeInstanceToState(
|
|||
ctx EvalContext,
|
||||
resourceName string,
|
||||
resourceType string,
|
||||
provider string,
|
||||
dependencies []string,
|
||||
writerFn func(*ResourceState) error,
|
||||
) (*InstanceState, error) {
|
||||
|
@ -252,6 +256,7 @@ func writeInstanceToState(
|
|||
}
|
||||
rs.Type = resourceType
|
||||
rs.Dependencies = dependencies
|
||||
rs.Provider = provider
|
||||
|
||||
if err := writerFn(rs); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -110,7 +110,7 @@ func (n *GraphNodeConfigModule) ProvidedBy() []string {
|
|||
providers[p.Name] = struct{}{}
|
||||
}
|
||||
for _, r := range config.Resources {
|
||||
providers[resourceProvider(r.Type)] = struct{}{}
|
||||
providers[resourceProvider(r.Type, r.Provider)] = struct{}{}
|
||||
}
|
||||
|
||||
// Turn the map into a string. This makes sure that the list is
|
||||
|
@ -176,7 +176,7 @@ type GraphNodeConfigProvider struct {
|
|||
}
|
||||
|
||||
func (n *GraphNodeConfigProvider) Name() string {
|
||||
return fmt.Sprintf("provider.%s", n.Provider.Name)
|
||||
return fmt.Sprintf("provider.%s", n.ProviderName())
|
||||
}
|
||||
|
||||
func (n *GraphNodeConfigProvider) ConfigType() GraphNodeConfigType {
|
||||
|
@ -201,12 +201,16 @@ func (n *GraphNodeConfigProvider) DependentOn() []string {
|
|||
|
||||
// GraphNodeEvalable impl.
|
||||
func (n *GraphNodeConfigProvider) EvalTree() EvalNode {
|
||||
return ProviderEvalTree(n.Provider.Name, n.Provider.RawConfig)
|
||||
return ProviderEvalTree(n.ProviderName(), n.Provider.RawConfig)
|
||||
}
|
||||
|
||||
// GraphNodeProvider implementation
|
||||
func (n *GraphNodeConfigProvider) ProviderName() string {
|
||||
return n.Provider.Name
|
||||
if n.Provider.Alias == "" {
|
||||
return n.Provider.Name
|
||||
} else {
|
||||
return fmt.Sprintf("%s.%s", n.Provider.Name, n.Provider.Alias)
|
||||
}
|
||||
}
|
||||
|
||||
// GraphNodeProvider implementation
|
||||
|
@ -396,7 +400,7 @@ func (n *GraphNodeConfigResource) EvalTree() EvalNode {
|
|||
|
||||
// GraphNodeProviderConsumer
|
||||
func (n *GraphNodeConfigResource) ProvidedBy() []string {
|
||||
return []string{resourceProvider(n.Resource.Type)}
|
||||
return []string{resourceProvider(n.Resource.Type, n.Resource.Provider)}
|
||||
}
|
||||
|
||||
// GraphNodeProvisionerConsumer
|
||||
|
|
|
@ -58,6 +58,36 @@ func TestGraphNodeConfigProvider_ProviderName(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGraphNodeConfigProvider_ProviderName_alias(t *testing.T) {
|
||||
n := &GraphNodeConfigProvider{
|
||||
Provider: &config.ProviderConfig{Name: "foo", Alias: "bar"},
|
||||
}
|
||||
|
||||
if v := n.ProviderName(); v != "foo.bar" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraphNodeConfigProvider_Name(t *testing.T) {
|
||||
n := &GraphNodeConfigProvider{
|
||||
Provider: &config.ProviderConfig{Name: "foo"},
|
||||
}
|
||||
|
||||
if v := n.Name(); v != "provider.foo" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraphNodeConfigProvider_Name_alias(t *testing.T) {
|
||||
n := &GraphNodeConfigProvider{
|
||||
Provider: &config.ProviderConfig{Name: "foo", Alias: "bar"},
|
||||
}
|
||||
|
||||
if v := n.Name(); v != "provider.foo.bar" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraphNodeConfigResource_impl(t *testing.T) {
|
||||
var _ dag.Vertex = new(GraphNodeConfigResource)
|
||||
var _ dag.NamedVertex = new(GraphNodeConfigResource)
|
||||
|
@ -76,6 +106,16 @@ func TestGraphNodeConfigResource_ProvidedBy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGraphNodeConfigResource_ProvidedBy_alias(t *testing.T) {
|
||||
n := &GraphNodeConfigResource{
|
||||
Resource: &config.Resource{Type: "aws_instance", Provider: "aws.bar"},
|
||||
}
|
||||
|
||||
if v := n.ProvidedBy(); v[0] != "aws.bar" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGraphNodeConfigResource_ProvisionedBy(t *testing.T) {
|
||||
n := &GraphNodeConfigResource{
|
||||
Resource: &config.Resource{
|
||||
|
|
|
@ -572,6 +572,9 @@ func (m *ModuleState) String() string {
|
|||
|
||||
buf.WriteString(fmt.Sprintf("%s:%s%s\n", k, taintStr, deposedStr))
|
||||
buf.WriteString(fmt.Sprintf(" ID = %s\n", id))
|
||||
if rs.Provider != "" {
|
||||
buf.WriteString(fmt.Sprintf(" provider = %s\n", rs.Provider))
|
||||
}
|
||||
|
||||
var attributes map[string]string
|
||||
if rs.Primary != nil {
|
||||
|
@ -680,6 +683,12 @@ type ResourceState struct {
|
|||
// similar to Tainted instances in that Terraform is only tracking them in
|
||||
// order to remember to destroy them.
|
||||
Deposed []*InstanceState `json:"deposed,omitempty"`
|
||||
|
||||
// Provider is used when a resource is connected to a provider with an alias.
|
||||
// If this string is empty, the resource is connected to the default provider,
|
||||
// e.g. "aws_instance" goes with the "aws" provider.
|
||||
// If the resource block contained a "provider" key, that value will be set here.
|
||||
Provider string `json:"provider,omitempty"`
|
||||
}
|
||||
|
||||
// Equal tests whether two ResourceStates are equal.
|
||||
|
@ -688,6 +697,10 @@ func (s *ResourceState) Equal(other *ResourceState) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if s.Provider != other.Provider {
|
||||
return false
|
||||
}
|
||||
|
||||
// Dependencies must be equal
|
||||
sort.Strings(s.Dependencies)
|
||||
sort.Strings(other.Dependencies)
|
||||
|
@ -769,6 +782,7 @@ func (r *ResourceState) deepcopy() *ResourceState {
|
|||
Dependencies: nil,
|
||||
Primary: r.Primary.deepcopy(),
|
||||
Tainted: nil,
|
||||
Provider: r.Provider,
|
||||
}
|
||||
if r.Dependencies != nil {
|
||||
n.Dependencies = make([]string, len(r.Dependencies))
|
||||
|
|
|
@ -182,6 +182,18 @@ aws_instance.foo:
|
|||
type = aws_instance
|
||||
`
|
||||
|
||||
const testTerraformApplyProviderAliasStr = `
|
||||
aws_instance.bar:
|
||||
ID = foo
|
||||
provider = aws.bar
|
||||
foo = bar
|
||||
type = aws_instance
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
num = 2
|
||||
type = aws_instance
|
||||
`
|
||||
|
||||
const testTerraformApplyEmptyModuleStr = `
|
||||
<no state>
|
||||
Outputs:
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
provider "aws" {
|
||||
alias = "bar"
|
||||
}
|
||||
|
||||
resource "aws_instance" "foo" {
|
||||
num = "2"
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
foo = "bar"
|
||||
provider = "aws.bar"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
provider "aws" {
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
alias = "foo"
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
alias = "bar"
|
||||
}
|
|
@ -86,6 +86,20 @@ func TestConfigTransformer_outputs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfigTransformer_providerAlias(t *testing.T) {
|
||||
g := Graph{Path: RootModulePath}
|
||||
tf := &ConfigTransformer{Module: testModule(t, "graph-provider-alias")}
|
||||
if err := tf.Transform(&g); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(g.String())
|
||||
expected := strings.TrimSpace(testGraphProviderAliasStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n\n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigTransformer_errMissingDeps(t *testing.T) {
|
||||
g := Graph{Path: RootModulePath}
|
||||
tf := &ConfigTransformer{Module: testModule(t, "graph-missing-deps")}
|
||||
|
@ -126,3 +140,9 @@ aws_instance.foo
|
|||
output.foo
|
||||
aws_instance.foo
|
||||
`
|
||||
|
||||
const testGraphProviderAliasStr = `
|
||||
provider.aws
|
||||
provider.aws.bar
|
||||
provider.aws.foo
|
||||
`
|
||||
|
|
|
@ -40,6 +40,7 @@ func (t *DeposedTransformer) Transform(g *Graph) error {
|
|||
Index: i,
|
||||
ResourceName: k,
|
||||
ResourceType: rs.Type,
|
||||
Provider: rs.Provider,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +53,7 @@ type graphNodeDeposedResource struct {
|
|||
Index int
|
||||
ResourceName string
|
||||
ResourceType string
|
||||
Provider string
|
||||
}
|
||||
|
||||
func (n *graphNodeDeposedResource) Name() string {
|
||||
|
@ -59,7 +61,7 @@ func (n *graphNodeDeposedResource) Name() string {
|
|||
}
|
||||
|
||||
func (n *graphNodeDeposedResource) ProvidedBy() []string {
|
||||
return []string{resourceProvider(n.ResourceName)}
|
||||
return []string{resourceProvider(n.ResourceName, n.Provider)}
|
||||
}
|
||||
|
||||
// GraphNodeEvalable impl.
|
||||
|
@ -96,6 +98,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
|
|||
&EvalWriteStateDeposed{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Provider: n.Provider,
|
||||
State: &state,
|
||||
Index: n.Index,
|
||||
},
|
||||
|
@ -138,6 +141,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
|
|||
&EvalWriteStateDeposed{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Provider: n.Provider,
|
||||
State: &state,
|
||||
Index: n.Index,
|
||||
},
|
||||
|
|
|
@ -89,6 +89,7 @@ func (t *OrphanTransformer) Transform(g *Graph) error {
|
|||
resourceVertexes[i] = g.Add(&graphNodeOrphanResource{
|
||||
ResourceName: k,
|
||||
ResourceType: rs.Type,
|
||||
Provider: rs.Provider,
|
||||
dependentOn: rs.Dependencies,
|
||||
})
|
||||
}
|
||||
|
@ -162,6 +163,7 @@ func (n *graphNodeOrphanModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error
|
|||
type graphNodeOrphanResource struct {
|
||||
ResourceName string
|
||||
ResourceType string
|
||||
Provider string
|
||||
|
||||
dependentOn []string
|
||||
}
|
||||
|
@ -179,7 +181,7 @@ func (n *graphNodeOrphanResource) Name() string {
|
|||
}
|
||||
|
||||
func (n *graphNodeOrphanResource) ProvidedBy() []string {
|
||||
return []string{resourceProvider(n.ResourceName)}
|
||||
return []string{resourceProvider(n.ResourceName, n.Provider)}
|
||||
}
|
||||
|
||||
// GraphNodeEvalable impl.
|
||||
|
@ -215,6 +217,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||
&EvalWriteState{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Provider: n.Provider,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
|
@ -272,6 +275,7 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||
&EvalWriteState{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Provider: n.Provider,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
|
|
|
@ -342,6 +342,13 @@ func TestGraphNodeOrphanResource_ProvidedBy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGraphNodeOrphanResource_ProvidedBy_alias(t *testing.T) {
|
||||
n := &graphNodeOrphanResource{ResourceName: "aws_instance.foo", Provider: "aws.bar"}
|
||||
if v := n.ProvidedBy(); v[0] != "aws.bar" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
const testTransformOrphanBasicStr = `
|
||||
aws_instance.db (orphan)
|
||||
aws_instance.web
|
||||
|
|
|
@ -140,7 +140,7 @@ func (n *graphNodeExpandedResource) DependentOn() []string {
|
|||
|
||||
// GraphNodeProviderConsumer
|
||||
func (n *graphNodeExpandedResource) ProvidedBy() []string {
|
||||
return []string{resourceProvider(n.Resource.Type)}
|
||||
return []string{resourceProvider(n.Resource.Type, n.Resource.Provider)}
|
||||
}
|
||||
|
||||
// GraphNodeEvalable impl.
|
||||
|
@ -230,6 +230,7 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Provider: n.Resource.Provider,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
|
@ -270,6 +271,7 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Provider: n.Resource.Provider,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
|
@ -416,6 +418,7 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Provider: n.Resource.Provider,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
|
@ -459,6 +462,7 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
&EvalWriteStateTainted{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Provider: n.Resource.Provider,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
Index: -1,
|
||||
|
@ -476,6 +480,7 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
Else: &EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Provider: n.Resource.Provider,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
|
@ -586,6 +591,7 @@ func (n *graphNodeExpandedResourceDestroy) EvalTree() EvalNode {
|
|||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Provider: n.Resource.Provider,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
|
|
|
@ -45,6 +45,7 @@ func (t *TaintedTransformer) Transform(g *Graph) error {
|
|||
Index: i,
|
||||
ResourceName: k,
|
||||
ResourceType: rs.Type,
|
||||
Provider: rs.Provider,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +58,7 @@ type graphNodeTaintedResource struct {
|
|||
Index int
|
||||
ResourceName string
|
||||
ResourceType string
|
||||
Provider string
|
||||
}
|
||||
|
||||
func (n *graphNodeTaintedResource) Name() string {
|
||||
|
@ -64,7 +66,7 @@ func (n *graphNodeTaintedResource) Name() string {
|
|||
}
|
||||
|
||||
func (n *graphNodeTaintedResource) ProvidedBy() []string {
|
||||
return []string{resourceProvider(n.ResourceName)}
|
||||
return []string{resourceProvider(n.ResourceName, n.Provider)}
|
||||
}
|
||||
|
||||
// GraphNodeEvalable impl.
|
||||
|
@ -101,6 +103,7 @@ func (n *graphNodeTaintedResource) EvalTree() EvalNode {
|
|||
&EvalWriteStateTainted{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Provider: n.Provider,
|
||||
State: &state,
|
||||
Index: n.Index,
|
||||
},
|
||||
|
@ -138,6 +141,7 @@ func (n *graphNodeTaintedResource) EvalTree() EvalNode {
|
|||
&EvalWriteStateTainted{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Provider: n.Provider,
|
||||
State: &state,
|
||||
Index: n.Index,
|
||||
},
|
||||
|
|
|
@ -58,6 +58,13 @@ func TestGraphNodeTaintedResource_ProvidedBy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGraphNodeTaintedResource_ProvidedBy_alias(t *testing.T) {
|
||||
n := &graphNodeTaintedResource{ResourceName: "aws_instance.foo", Provider: "aws.bar"}
|
||||
if v := n.ProvidedBy(); v[0] != "aws.bar" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
||||
const testTransformTaintedBasicStr = `
|
||||
aws_instance.web
|
||||
aws_instance.web (tainted #1)
|
||||
|
|
|
@ -47,7 +47,11 @@ func (s Semaphore) Release() {
|
|||
}
|
||||
|
||||
// resourceProvider returns the provider name for the given type.
|
||||
func resourceProvider(t string) string {
|
||||
func resourceProvider(t, alias string) string {
|
||||
if alias != "" {
|
||||
return alias
|
||||
}
|
||||
|
||||
idx := strings.IndexRune(t, '_')
|
||||
if idx == -1 {
|
||||
return ""
|
||||
|
|
Loading…
Reference in New Issue