builtin/providers: terraform remote state datasource (#18446)
* builtin/providers: implement terraform remote state datasource as providers.Interface * append and return diags separately (to match the idiomatic usage elsewhere in Terraform) * diagnostic summary style improvements * update tests to pass config to schema.CoerceValue * trust that the schema will be enforced and there is no need to check that a given attribute exists * added dataSourceRemoteStateGetSchema() (effectively replacing a function that was inappropriately removed) for consistency with other terraform providers * builtin/provider terraform test: added InternalValidate() test for dataSourceRemoteStateGetSchema
This commit is contained in:
parent
05936df0e7
commit
52ae93cf97
|
@ -1,142 +1,151 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
backendinit "github.com/hashicorp/terraform/backend/init"
|
||||
"github.com/hashicorp/terraform/config/hcl2shim"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
backendinit "github.com/hashicorp/terraform/backend/init"
|
||||
"github.com/hashicorp/terraform/config/hcl2shim"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func dataSourceRemoteState() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceRemoteStateRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"backend": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||
if vStr, ok := v.(string); ok && vStr == "_local" {
|
||||
ws = append(ws, "Use of the %q backend is now officially "+
|
||||
"supported as %q. Please update your configuration to ensure "+
|
||||
"compatibility with future versions of Terraform.",
|
||||
"_local", "local")
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
},
|
||||
|
||||
"config": {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"defaults": {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"environment": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: backend.DefaultStateName,
|
||||
Deprecated: "Terraform environments are now called workspaces. Please use the workspace key instead.",
|
||||
},
|
||||
|
||||
"workspace": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: backend.DefaultStateName,
|
||||
},
|
||||
|
||||
"__has_dynamic_attributes": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
func dataSourceRemoteStateGetSchema() providers.Schema {
|
||||
return providers.Schema{
|
||||
Block: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"backend": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
"config": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Optional: true,
|
||||
},
|
||||
"defaults": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Optional: true,
|
||||
},
|
||||
"outputs": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Computed: true,
|
||||
},
|
||||
"workspace": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceRemoteStateRead(d *schema.ResourceData, meta interface{}) error {
|
||||
backendType := d.Get("backend").(string)
|
||||
func dataSourceRemoteStateRead(d *cty.Value) (cty.Value, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Don't break people using the old _local syntax - but note warning above
|
||||
if backendType == "_local" {
|
||||
log.Println(`[INFO] Switching old (unsupported) backend "_local" to "local"`)
|
||||
backendType = "local"
|
||||
newState := make(map[string]cty.Value)
|
||||
newState["backend"] = d.GetAttr("backend")
|
||||
|
||||
backendType := d.GetAttr("backend").AsString()
|
||||
|
||||
// Don't break people using the old _local syntax - but note warning above
|
||||
if backendType == "_local" {
|
||||
log.Println(`[INFO] Switching old (unsupported) backend "_local" to "local"`)
|
||||
backendType = "local"
|
||||
}
|
||||
|
||||
// Create the client to access our remote state
|
||||
log.Printf("[DEBUG] Initializing remote state backend: %s", backendType)
|
||||
f := backendinit.Backend(backendType)
|
||||
if f == nil {
|
||||
return fmt.Errorf("Unknown backend type: %s", backendType)
|
||||
diags = diags.Append(tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
"Invalid backend configuration",
|
||||
fmt.Sprintf("Unknown backend type: %s", backendType),
|
||||
cty.Path(nil).GetAttr("backend"),
|
||||
))
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
b := f()
|
||||
|
||||
schema := b.ConfigSchema()
|
||||
rawConfig := d.Get("config")
|
||||
configVal := hcl2shim.HCL2ValueFromConfigValue(rawConfig)
|
||||
config := d.GetAttr("config")
|
||||
newState["config"] = config
|
||||
|
||||
schema := b.ConfigSchema()
|
||||
// Try to coerce the provided value into the desired configuration type.
|
||||
configVal, err := schema.CoerceValue(configVal)
|
||||
configVal, err := schema.CoerceValue(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid %s backend configuration: %s", backendType, tfdiags.FormatError(err))
|
||||
diags = diags.Append(tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
"Invalid backend configuration",
|
||||
fmt.Sprintf("The given configuration is not valid for backend %q: %s.", backendType,
|
||||
tfdiags.FormatError(err)),
|
||||
cty.Path(nil).GetAttr("config"),
|
||||
))
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
validateDiags := b.ValidateConfig(configVal)
|
||||
diags = diags.Append(validateDiags)
|
||||
if validateDiags.HasErrors() {
|
||||
return validateDiags.Err()
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
configureDiags := b.Configure(configVal)
|
||||
if configureDiags.HasErrors() {
|
||||
return configureDiags.Err()
|
||||
diags = diags.Append(configureDiags.Err())
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
// environment is deprecated in favour of workspace.
|
||||
// If both keys are set workspace should win.
|
||||
name := d.Get("environment").(string)
|
||||
if ws, ok := d.GetOk("workspace"); ok && ws != backend.DefaultStateName {
|
||||
name = ws.(string)
|
||||
var name string
|
||||
|
||||
if workspaceVal := d.GetAttr("workspace"); !workspaceVal.IsNull() {
|
||||
newState["workspace"] = workspaceVal
|
||||
ws := workspaceVal.AsString()
|
||||
if ws != backend.DefaultStateName {
|
||||
name = ws
|
||||
}
|
||||
}
|
||||
|
||||
state, err := b.State(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error loading the remote state: %s", err)
|
||||
diags = diags.Append(tfdiags.AttributeValue(
|
||||
tfdiags.Error,
|
||||
"Error loading state error",
|
||||
fmt.Sprintf("error loading the remote state: %s", err),
|
||||
cty.Path(nil).GetAttr("backend"),
|
||||
))
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
|
||||
if err := state.RefreshState(); err != nil {
|
||||
return err
|
||||
diags = diags.Append(err)
|
||||
return cty.NilVal, diags
|
||||
}
|
||||
d.SetId(time.Now().UTC().String())
|
||||
|
||||
outputMap := make(map[string]interface{})
|
||||
outputs := make(map[string]cty.Value)
|
||||
|
||||
defaults := d.Get("defaults").(map[string]interface{})
|
||||
for key, val := range defaults {
|
||||
outputMap[key] = val
|
||||
if defaultsVal := d.GetAttr("defaults"); !defaultsVal.IsNull() {
|
||||
newState["defaults"] = defaultsVal
|
||||
it := defaultsVal.ElementIterator()
|
||||
for it.Next() {
|
||||
k, v := it.Element()
|
||||
outputs[k.AsString()] = v
|
||||
}
|
||||
}
|
||||
|
||||
remoteState := state.State()
|
||||
if remoteState.Empty() {
|
||||
log.Println("[DEBUG] empty remote state")
|
||||
log.Println("[DEBUG] empty remote state")
|
||||
} else {
|
||||
for key, val := range remoteState.RootModule().Outputs {
|
||||
if val.Value != nil {
|
||||
outputMap[key] = val.Value
|
||||
}
|
||||
}
|
||||
for k, os := range remoteState.RootModule().Outputs {
|
||||
outputs[k] = hcl2shim.HCL2ValueFromConfigValue(os.Value)
|
||||
}
|
||||
}
|
||||
|
||||
mappedOutputs := remoteStateFlatten(outputMap)
|
||||
newState["outputs"] = cty.ObjectVal(outputs)
|
||||
|
||||
for key, val := range mappedOutputs {
|
||||
d.UnsafeSetFieldRaw(key, val)
|
||||
}
|
||||
|
||||
return nil
|
||||
return cty.ObjectVal(newState), diags
|
||||
}
|
||||
|
|
|
@ -1,225 +1,135 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
backendInit "github.com/hashicorp/terraform/backend/init"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestResource(t *testing.T) {
|
||||
if err := dataSourceRemoteStateGetSchema().Block.InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestState_basic(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccState_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckStateValue(
|
||||
"data.terraform_remote_state.foo", "foo", "bar"),
|
||||
),
|
||||
},
|
||||
var tests = []struct {
|
||||
Config cty.Value
|
||||
Want cty.Value
|
||||
Err bool
|
||||
}{
|
||||
{ // basic test
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"backend": cty.StringVal("local"),
|
||||
"config": cty.ObjectVal(map[string]cty.Value{
|
||||
"path": cty.StringVal("./test-fixtures/basic.tfstate"),
|
||||
}),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"backend": cty.StringVal("local"),
|
||||
"config": cty.ObjectVal(map[string]cty.Value{
|
||||
"path": cty.StringVal("./test-fixtures/basic.tfstate"),
|
||||
}),
|
||||
"outputs": cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.StringVal("bar"),
|
||||
}),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestState_backends(t *testing.T) {
|
||||
backendInit.Set("_ds_test", backendInit.Backend("local"))
|
||||
defer backendInit.Set("_ds_test", nil)
|
||||
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccState_backend,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckStateValue(
|
||||
"data.terraform_remote_state.foo", "foo", "bar"),
|
||||
),
|
||||
},
|
||||
{ // complex outputs
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"backend": cty.StringVal("local"),
|
||||
"config": cty.ObjectVal(map[string]cty.Value{
|
||||
"path": cty.StringVal("./test-fixtures/complex_outputs.tfstate"),
|
||||
}),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"backend": cty.StringVal("local"),
|
||||
"config": cty.ObjectVal(map[string]cty.Value{
|
||||
"path": cty.StringVal("./test-fixtures/complex_outputs.tfstate"),
|
||||
}),
|
||||
"outputs": cty.ObjectVal(map[string]cty.Value{
|
||||
"computed_map": cty.ObjectVal(map[string]cty.Value{
|
||||
"key1": cty.StringVal("value1"),
|
||||
}),
|
||||
"computed_set": cty.TupleVal([]cty.Value{
|
||||
cty.StringVal("setval1"),
|
||||
cty.StringVal("setval2"),
|
||||
}),
|
||||
"map": cty.ObjectVal(map[string]cty.Value{
|
||||
"key": cty.StringVal("test"),
|
||||
"test": cty.StringVal("test"),
|
||||
}),
|
||||
"set": cty.TupleVal([]cty.Value{
|
||||
cty.StringVal("test1"),
|
||||
cty.StringVal("test2"),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestState_complexOutputs(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccState_complexOutputs,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckStateValue("terraform_remote_state.foo", "backend", "local"),
|
||||
// This (adding the hash) should be reverted when merged into 0.12.
|
||||
// testAccCheckStateValue("terraform_remote_state.foo", "config.path", "./test-fixtures/complex_outputs.tfstate"),
|
||||
testAccCheckStateValue("terraform_remote_state.foo", "config.1590222752.path", "./test-fixtures/complex_outputs.tfstate"),
|
||||
testAccCheckStateValue("terraform_remote_state.foo", "computed_set.#", "2"),
|
||||
testAccCheckStateValue("terraform_remote_state.foo", `map.%`, "2"),
|
||||
testAccCheckStateValue("terraform_remote_state.foo", `map.key`, "test"),
|
||||
),
|
||||
},
|
||||
{ // null outputs
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"backend": cty.StringVal("local"),
|
||||
"config": cty.ObjectVal(map[string]cty.Value{
|
||||
"path": cty.StringVal("./test-fixtures/null_outputs.tfstate"),
|
||||
}),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"backend": cty.StringVal("local"),
|
||||
"config": cty.ObjectVal(map[string]cty.Value{
|
||||
"path": cty.StringVal("./test-fixtures/null_outputs.tfstate"),
|
||||
}),
|
||||
"outputs": cty.ObjectVal(map[string]cty.Value{
|
||||
"map": cty.NullVal(cty.DynamicPseudoType),
|
||||
"list": cty.NullVal(cty.DynamicPseudoType),
|
||||
}),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// outputs should never have a null value, but don't crash if we ever encounter
|
||||
// them.
|
||||
func TestState_nullOutputs(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccState_nullOutputs,
|
||||
},
|
||||
{ // defaults
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"backend": cty.StringVal("local"),
|
||||
"config": cty.ObjectVal(map[string]cty.Value{
|
||||
"path": cty.StringVal("./test-fixtures/empty.tfstate"),
|
||||
}),
|
||||
"defaults": cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.StringVal("bar"),
|
||||
}),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"backend": cty.StringVal("local"),
|
||||
"config": cty.ObjectVal(map[string]cty.Value{
|
||||
"path": cty.StringVal("./test-fixtures/empty.tfstate"),
|
||||
}),
|
||||
"defaults": cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.StringVal("bar"),
|
||||
}),
|
||||
"outputs": cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.StringVal("bar"),
|
||||
}),
|
||||
}),
|
||||
false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestEmptyState_defaults(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccEmptyState_defaults,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckStateValue(
|
||||
"data.terraform_remote_state.foo", "foo", "bar"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestState_defaults(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccEmptyState_defaults,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckStateValue(
|
||||
"data.terraform_remote_state.foo", "foo", "bar"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckStateValue(id, name, value string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[id]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", id)
|
||||
}
|
||||
for _, test := range tests {
|
||||
schema := dataSourceRemoteStateGetSchema().Block
|
||||
config, err := schema.CoerceValue(test.Config)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No ID is set")
|
||||
got, diags := dataSourceRemoteStateRead(&config)
|
||||
|
||||
if test.Err {
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("succeeded; want error")
|
||||
}
|
||||
} else if diags.HasErrors() {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
v := rs.Primary.Attributes[name]
|
||||
if v != value {
|
||||
return fmt.Errorf(
|
||||
"Value for %s is %s, not %s", name, v, value)
|
||||
if !got.RawEquals(test.Want) {
|
||||
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// make sure that the deprecated environment field isn't overridden by the
|
||||
// default value for workspace.
|
||||
func TestState_deprecatedEnvironment(t *testing.T) {
|
||||
resource.UnitTest(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccState_deprecatedEnvironment,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckStateValue(
|
||||
// if the workspace default value overrides the
|
||||
// environment, this will get the foo value from the
|
||||
// default state.
|
||||
"data.terraform_remote_state.foo", "foo", ""),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const testAccState_basic = `
|
||||
data "terraform_remote_state" "foo" {
|
||||
backend = "local"
|
||||
|
||||
config {
|
||||
path = "./test-fixtures/basic.tfstate"
|
||||
}
|
||||
}`
|
||||
|
||||
const testAccState_backend = `
|
||||
data "terraform_remote_state" "foo" {
|
||||
backend = "_ds_test"
|
||||
|
||||
config {
|
||||
path = "./test-fixtures/basic.tfstate"
|
||||
}
|
||||
}`
|
||||
|
||||
const testAccState_complexOutputs = `
|
||||
resource "terraform_remote_state" "foo" {
|
||||
backend = "local"
|
||||
|
||||
config {
|
||||
path = "./test-fixtures/complex_outputs.tfstate"
|
||||
}
|
||||
}`
|
||||
|
||||
const testAccState_nullOutputs = `
|
||||
resource "terraform_remote_state" "foo" {
|
||||
backend = "local"
|
||||
|
||||
config {
|
||||
path = "./test-fixtures/null_outputs.tfstate"
|
||||
}
|
||||
}`
|
||||
|
||||
const testAccEmptyState_defaults = `
|
||||
data "terraform_remote_state" "foo" {
|
||||
backend = "local"
|
||||
|
||||
config {
|
||||
path = "./test-fixtures/empty.tfstate"
|
||||
}
|
||||
|
||||
defaults {
|
||||
foo = "bar"
|
||||
}
|
||||
}`
|
||||
|
||||
const testAccState_defaults = `
|
||||
data "terraform_remote_state" "foo" {
|
||||
backend = "local"
|
||||
|
||||
config {
|
||||
path = "./test-fixtures/basic.tfstate"
|
||||
}
|
||||
|
||||
defaults {
|
||||
foo = "not bar"
|
||||
}
|
||||
}`
|
||||
|
||||
const testAccState_deprecatedEnvironment = `
|
||||
data "terraform_remote_state" "foo" {
|
||||
backend = "local"
|
||||
environment = "deprecated"
|
||||
|
||||
config {
|
||||
path = "./test-fixtures/basic.tfstate"
|
||||
}
|
||||
}`
|
||||
|
|
|
@ -1,21 +1,124 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
)
|
||||
|
||||
// Provider returns a terraform.ResourceProvider.
|
||||
func Provider() terraform.ResourceProvider {
|
||||
return &schema.Provider{
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"terraform_remote_state": schema.DataSourceResourceShim(
|
||||
"terraform_remote_state",
|
||||
dataSourceRemoteState(),
|
||||
),
|
||||
},
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"terraform_remote_state": dataSourceRemoteState(),
|
||||
// Provider is an implementation of providers.Interface
|
||||
type Provider struct {
|
||||
// Provider is the schema for the provider itself.
|
||||
Schema providers.Schema
|
||||
|
||||
// DataSources maps the data source name to that data source's schema.
|
||||
DataSources map[string]providers.Schema
|
||||
}
|
||||
|
||||
// NewProvider returns a new terraform provider
|
||||
func NewProvider() *Provider {
|
||||
return &Provider{}
|
||||
}
|
||||
|
||||
// GetSchema returns the complete schema for the provider.
|
||||
func (p *Provider) GetSchema() providers.GetSchemaResponse {
|
||||
return providers.GetSchemaResponse{
|
||||
DataSources: map[string]providers.Schema{
|
||||
"terraform_remote_state": dataSourceRemoteStateGetSchema(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateProviderConfig is used to validate the configuration values.
|
||||
func (p *Provider) ValidateProviderConfig(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse {
|
||||
// At this moment there is nothing to configure for the terraform provider,
|
||||
// so we will happily return without taking any action
|
||||
var res providers.ValidateProviderConfigResponse
|
||||
return res
|
||||
}
|
||||
|
||||
// ValidateDataSourceConfig is used to validate the data source configuration values.
|
||||
func (p *Provider) ValidateDataSourceConfig(providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse {
|
||||
// FIXME: move the backend configuration validate call that's currently
|
||||
// inside the read method into here so that we can catch provider configuration
|
||||
// errors in terraform validate as well as during terraform plan.
|
||||
var res providers.ValidateDataSourceConfigResponse
|
||||
return res
|
||||
}
|
||||
|
||||
// Configure configures and initializes the provider.
|
||||
func (p *Provider) Configure(providers.ConfigureRequest) providers.ConfigureResponse {
|
||||
// At this moment there is nothing to configure for the terraform provider,
|
||||
// so we will happily return without taking any action
|
||||
var res providers.ConfigureResponse
|
||||
return res
|
||||
}
|
||||
|
||||
// ReadDataSource returns the data source's current state.
|
||||
func (p *Provider) ReadDataSource(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
|
||||
// call function
|
||||
var res providers.ReadDataSourceResponse
|
||||
|
||||
// This should not happen
|
||||
if req.TypeName != "terraform_remote_state" {
|
||||
res.Diagnostics.Append(fmt.Errorf("Error: unsupported data source %s", req.TypeName))
|
||||
return res
|
||||
}
|
||||
|
||||
newState, diags := dataSourceRemoteStateRead(&req.Config)
|
||||
|
||||
res.State = newState
|
||||
res.Diagnostics = diags
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Stop is called when the provider should halt any in-flight actions.
|
||||
func (p *Provider) Stop() error {
|
||||
log.Println("[DEBUG] terraform provider cannot Stop")
|
||||
return nil
|
||||
}
|
||||
|
||||
// All the Resource-specific functions are below.
|
||||
// The terraform provider supplies a single data source, `terraform_remote_state`
|
||||
// and no resources.
|
||||
|
||||
// UpgradeResourceState is called when the state loader encounters an
|
||||
// instance state whose schema version is less than the one reported by the
|
||||
// currently-used version of the corresponding provider, and the upgraded
|
||||
// result is used for any further processing.
|
||||
func (p *Provider) UpgradeResourceState(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
|
||||
panic("unimplemented - terraform_remote_state has no resources")
|
||||
}
|
||||
|
||||
// ReadResource refreshes a resource and returns its current state.
|
||||
func (p *Provider) ReadResource(providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||
panic("unimplemented - terraform_remote_state has no resources")
|
||||
}
|
||||
|
||||
// PlanResourceChange takes the current state and proposed state of a
|
||||
// resource, and returns the planned final state.
|
||||
func (p *Provider) PlanResourceChange(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
||||
panic("unimplemented - terraform_remote_state has no resources")
|
||||
}
|
||||
|
||||
// ApplyResourceChange takes the planned state for a resource, which may
|
||||
// yet contain unknown computed values, and applies the changes returning
|
||||
// the final state.
|
||||
func (p *Provider) ApplyResourceChange(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
|
||||
panic("unimplemented - terraform_remote_state has no resources")
|
||||
}
|
||||
|
||||
// ImportResourceState requests that the given resource be imported.
|
||||
func (p *Provider) ImportResourceState(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
|
||||
panic("unimplemented - terraform_remote_state has no resources")
|
||||
}
|
||||
|
||||
// ValidateResourceTypeConfig is used to to validate the resource configuration values.
|
||||
func (p *Provider) ValidateResourceTypeConfig(providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse {
|
||||
// At this moment there is nothing to configure for the terraform provider,
|
||||
// so we will happily return without taking any action
|
||||
var res providers.ValidateResourceTypeConfigResponse
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -3,32 +3,21 @@ package terraform
|
|||
import (
|
||||
"testing"
|
||||
|
||||
backendInit "github.com/hashicorp/terraform/backend/init"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
var testAccProviders map[string]*Provider
|
||||
var testAccProvider *Provider
|
||||
|
||||
func init() {
|
||||
// Initialize the backends
|
||||
backendInit.Init(nil)
|
||||
|
||||
testAccProvider = Provider().(*schema.Provider)
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
testAccProvider = NewProvider()
|
||||
testAccProviders = map[string]*Provider{
|
||||
"terraform": testAccProvider,
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider_impl(t *testing.T) {
|
||||
var _ terraform.ResourceProvider = Provider()
|
||||
var _ providers.Interface = NewProvider()
|
||||
}
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue