github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/builtin/providers/terraform/data_source_state_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"testing"
     7  
     8  	"github.com/apparentlymart/go-dump/dump"
     9  	"github.com/muratcelep/terraform/not-internal/backend"
    10  	"github.com/muratcelep/terraform/not-internal/configs/configschema"
    11  	"github.com/muratcelep/terraform/not-internal/states/statemgr"
    12  	"github.com/muratcelep/terraform/not-internal/tfdiags"
    13  	"github.com/zclconf/go-cty/cty"
    14  )
    15  
    16  func TestResource(t *testing.T) {
    17  	if err := dataSourceRemoteStateGetSchema().Block.InternalValidate(); err != nil {
    18  		t.Fatalf("err: %s", err)
    19  	}
    20  }
    21  
    22  func TestState_basic(t *testing.T) {
    23  	var tests = map[string]struct {
    24  		Config cty.Value
    25  		Want   cty.Value
    26  		Err    bool
    27  	}{
    28  		"basic": {
    29  			cty.ObjectVal(map[string]cty.Value{
    30  				"backend": cty.StringVal("local"),
    31  				"config": cty.ObjectVal(map[string]cty.Value{
    32  					"path": cty.StringVal("./testdata/basic.tfstate"),
    33  				}),
    34  			}),
    35  			cty.ObjectVal(map[string]cty.Value{
    36  				"backend": cty.StringVal("local"),
    37  				"config": cty.ObjectVal(map[string]cty.Value{
    38  					"path": cty.StringVal("./testdata/basic.tfstate"),
    39  				}),
    40  				"outputs": cty.ObjectVal(map[string]cty.Value{
    41  					"foo": cty.StringVal("bar"),
    42  				}),
    43  				"defaults":  cty.NullVal(cty.DynamicPseudoType),
    44  				"workspace": cty.NullVal(cty.String),
    45  			}),
    46  			false,
    47  		},
    48  		"workspace": {
    49  			cty.ObjectVal(map[string]cty.Value{
    50  				"backend":   cty.StringVal("local"),
    51  				"workspace": cty.StringVal(backend.DefaultStateName),
    52  				"config": cty.ObjectVal(map[string]cty.Value{
    53  					"path": cty.StringVal("./testdata/basic.tfstate"),
    54  				}),
    55  			}),
    56  			cty.ObjectVal(map[string]cty.Value{
    57  				"backend":   cty.StringVal("local"),
    58  				"workspace": cty.StringVal(backend.DefaultStateName),
    59  				"config": cty.ObjectVal(map[string]cty.Value{
    60  					"path": cty.StringVal("./testdata/basic.tfstate"),
    61  				}),
    62  				"outputs": cty.ObjectVal(map[string]cty.Value{
    63  					"foo": cty.StringVal("bar"),
    64  				}),
    65  				"defaults": cty.NullVal(cty.DynamicPseudoType),
    66  			}),
    67  			false,
    68  		},
    69  		"_local": {
    70  			cty.ObjectVal(map[string]cty.Value{
    71  				"backend": cty.StringVal("_local"),
    72  				"config": cty.ObjectVal(map[string]cty.Value{
    73  					"path": cty.StringVal("./testdata/basic.tfstate"),
    74  				}),
    75  			}),
    76  			cty.ObjectVal(map[string]cty.Value{
    77  				"backend": cty.StringVal("_local"),
    78  				"config": cty.ObjectVal(map[string]cty.Value{
    79  					"path": cty.StringVal("./testdata/basic.tfstate"),
    80  				}),
    81  				"outputs": cty.ObjectVal(map[string]cty.Value{
    82  					"foo": cty.StringVal("bar"),
    83  				}),
    84  				"defaults":  cty.NullVal(cty.DynamicPseudoType),
    85  				"workspace": cty.NullVal(cty.String),
    86  			}),
    87  			false,
    88  		},
    89  		"complex outputs": {
    90  			cty.ObjectVal(map[string]cty.Value{
    91  				"backend": cty.StringVal("local"),
    92  				"config": cty.ObjectVal(map[string]cty.Value{
    93  					"path": cty.StringVal("./testdata/complex_outputs.tfstate"),
    94  				}),
    95  			}),
    96  			cty.ObjectVal(map[string]cty.Value{
    97  				"backend": cty.StringVal("local"),
    98  				"config": cty.ObjectVal(map[string]cty.Value{
    99  					"path": cty.StringVal("./testdata/complex_outputs.tfstate"),
   100  				}),
   101  				"outputs": cty.ObjectVal(map[string]cty.Value{
   102  					"computed_map": cty.MapVal(map[string]cty.Value{
   103  						"key1": cty.StringVal("value1"),
   104  					}),
   105  					"computed_set": cty.ListVal([]cty.Value{
   106  						cty.StringVal("setval1"),
   107  						cty.StringVal("setval2"),
   108  					}),
   109  					"map": cty.MapVal(map[string]cty.Value{
   110  						"key":  cty.StringVal("test"),
   111  						"test": cty.StringVal("test"),
   112  					}),
   113  					"set": cty.ListVal([]cty.Value{
   114  						cty.StringVal("test1"),
   115  						cty.StringVal("test2"),
   116  					}),
   117  				}),
   118  				"defaults":  cty.NullVal(cty.DynamicPseudoType),
   119  				"workspace": cty.NullVal(cty.String),
   120  			}),
   121  			false,
   122  		},
   123  		"null outputs": {
   124  			cty.ObjectVal(map[string]cty.Value{
   125  				"backend": cty.StringVal("local"),
   126  				"config": cty.ObjectVal(map[string]cty.Value{
   127  					"path": cty.StringVal("./testdata/null_outputs.tfstate"),
   128  				}),
   129  			}),
   130  			cty.ObjectVal(map[string]cty.Value{
   131  				"backend": cty.StringVal("local"),
   132  				"config": cty.ObjectVal(map[string]cty.Value{
   133  					"path": cty.StringVal("./testdata/null_outputs.tfstate"),
   134  				}),
   135  				"outputs": cty.ObjectVal(map[string]cty.Value{
   136  					"map":  cty.NullVal(cty.Map(cty.String)),
   137  					"list": cty.NullVal(cty.List(cty.String)),
   138  				}),
   139  				"defaults":  cty.NullVal(cty.DynamicPseudoType),
   140  				"workspace": cty.NullVal(cty.String),
   141  			}),
   142  			false,
   143  		},
   144  		"defaults": {
   145  			cty.ObjectVal(map[string]cty.Value{
   146  				"backend": cty.StringVal("local"),
   147  				"config": cty.ObjectVal(map[string]cty.Value{
   148  					"path": cty.StringVal("./testdata/empty.tfstate"),
   149  				}),
   150  				"defaults": cty.ObjectVal(map[string]cty.Value{
   151  					"foo": cty.StringVal("bar"),
   152  				}),
   153  			}),
   154  			cty.ObjectVal(map[string]cty.Value{
   155  				"backend": cty.StringVal("local"),
   156  				"config": cty.ObjectVal(map[string]cty.Value{
   157  					"path": cty.StringVal("./testdata/empty.tfstate"),
   158  				}),
   159  				"defaults": cty.ObjectVal(map[string]cty.Value{
   160  					"foo": cty.StringVal("bar"),
   161  				}),
   162  				"outputs": cty.ObjectVal(map[string]cty.Value{
   163  					"foo": cty.StringVal("bar"),
   164  				}),
   165  				"workspace": cty.NullVal(cty.String),
   166  			}),
   167  			false,
   168  		},
   169  		"missing": {
   170  			cty.ObjectVal(map[string]cty.Value{
   171  				"backend": cty.StringVal("local"),
   172  				"config": cty.ObjectVal(map[string]cty.Value{
   173  					"path": cty.StringVal("./testdata/missing.tfstate"), // intentionally not present on disk
   174  				}),
   175  			}),
   176  			cty.ObjectVal(map[string]cty.Value{
   177  				"backend": cty.StringVal("local"),
   178  				"config": cty.ObjectVal(map[string]cty.Value{
   179  					"path": cty.StringVal("./testdata/missing.tfstate"),
   180  				}),
   181  				"defaults":  cty.NullVal(cty.DynamicPseudoType),
   182  				"outputs":   cty.EmptyObjectVal,
   183  				"workspace": cty.NullVal(cty.String),
   184  			}),
   185  			true,
   186  		},
   187  		"wrong type for config": {
   188  			cty.ObjectVal(map[string]cty.Value{
   189  				"backend": cty.StringVal("local"),
   190  				"config":  cty.StringVal("nope"),
   191  			}),
   192  			cty.NilVal,
   193  			true,
   194  		},
   195  		"wrong type for config with unknown backend": {
   196  			cty.ObjectVal(map[string]cty.Value{
   197  				"backend": cty.UnknownVal(cty.String),
   198  				"config":  cty.StringVal("nope"),
   199  			}),
   200  			cty.NilVal,
   201  			true,
   202  		},
   203  		"wrong type for config with unknown config": {
   204  			cty.ObjectVal(map[string]cty.Value{
   205  				"backend": cty.StringVal("local"),
   206  				"config":  cty.UnknownVal(cty.String),
   207  			}),
   208  			cty.NilVal,
   209  			true,
   210  		},
   211  		"wrong type for defaults": {
   212  			cty.ObjectVal(map[string]cty.Value{
   213  				"backend": cty.StringVal("local"),
   214  				"config": cty.ObjectVal(map[string]cty.Value{
   215  					"path": cty.StringVal("./testdata/basic.tfstate"),
   216  				}),
   217  				"defaults": cty.StringVal("nope"),
   218  			}),
   219  			cty.NilVal,
   220  			true,
   221  		},
   222  		"config as map": {
   223  			cty.ObjectVal(map[string]cty.Value{
   224  				"backend": cty.StringVal("local"),
   225  				"config": cty.MapVal(map[string]cty.Value{
   226  					"path": cty.StringVal("./testdata/empty.tfstate"),
   227  				}),
   228  			}),
   229  			cty.ObjectVal(map[string]cty.Value{
   230  				"backend": cty.StringVal("local"),
   231  				"config": cty.MapVal(map[string]cty.Value{
   232  					"path": cty.StringVal("./testdata/empty.tfstate"),
   233  				}),
   234  				"defaults":  cty.NullVal(cty.DynamicPseudoType),
   235  				"outputs":   cty.EmptyObjectVal,
   236  				"workspace": cty.NullVal(cty.String),
   237  			}),
   238  			false,
   239  		},
   240  		"defaults as map": {
   241  			cty.ObjectVal(map[string]cty.Value{
   242  				"backend": cty.StringVal("local"),
   243  				"config": cty.ObjectVal(map[string]cty.Value{
   244  					"path": cty.StringVal("./testdata/basic.tfstate"),
   245  				}),
   246  				"defaults": cty.MapValEmpty(cty.String),
   247  			}),
   248  			cty.ObjectVal(map[string]cty.Value{
   249  				"backend": cty.StringVal("local"),
   250  				"config": cty.ObjectVal(map[string]cty.Value{
   251  					"path": cty.StringVal("./testdata/basic.tfstate"),
   252  				}),
   253  				"defaults": cty.MapValEmpty(cty.String),
   254  				"outputs": cty.ObjectVal(map[string]cty.Value{
   255  					"foo": cty.StringVal("bar"),
   256  				}),
   257  				"workspace": cty.NullVal(cty.String),
   258  			}),
   259  			false,
   260  		},
   261  		"nonexistent backend": {
   262  			cty.ObjectVal(map[string]cty.Value{
   263  				"backend": cty.StringVal("nonexistent"),
   264  				"config": cty.ObjectVal(map[string]cty.Value{
   265  					"path": cty.StringVal("./testdata/basic.tfstate"),
   266  				}),
   267  			}),
   268  			cty.NilVal,
   269  			true,
   270  		},
   271  		"null config": {
   272  			cty.ObjectVal(map[string]cty.Value{
   273  				"backend": cty.StringVal("local"),
   274  				"config":  cty.NullVal(cty.DynamicPseudoType),
   275  			}),
   276  			cty.NilVal,
   277  			true,
   278  		},
   279  	}
   280  	for name, test := range tests {
   281  		t.Run(name, func(t *testing.T) {
   282  			schema := dataSourceRemoteStateGetSchema().Block
   283  			config, err := schema.CoerceValue(test.Config)
   284  			if err != nil {
   285  				t.Fatalf("unexpected error: %s", err)
   286  			}
   287  
   288  			diags := dataSourceRemoteStateValidate(config)
   289  
   290  			var got cty.Value
   291  			if !diags.HasErrors() && config.IsWhollyKnown() {
   292  				var moreDiags tfdiags.Diagnostics
   293  				got, moreDiags = dataSourceRemoteStateRead(config)
   294  				diags = diags.Append(moreDiags)
   295  			}
   296  
   297  			if test.Err {
   298  				if !diags.HasErrors() {
   299  					t.Fatal("succeeded; want error")
   300  				}
   301  			} else if diags.HasErrors() {
   302  				t.Fatalf("unexpected errors: %s", diags.Err())
   303  			}
   304  
   305  			if test.Want != cty.NilVal && !test.Want.RawEquals(got) {
   306  				t.Errorf("wrong result\nconfig: %sgot:    %swant:   %s", dump.Value(config), dump.Value(got), dump.Value(test.Want))
   307  			}
   308  		})
   309  	}
   310  }
   311  
   312  func TestState_validation(t *testing.T) {
   313  	// The main test TestState_basic covers both validation and reading of
   314  	// state snapshots, so this additional test is here only to verify that
   315  	// the validation step in isolation does not attempt to configure
   316  	// the backend.
   317  	overrideBackendFactories = map[string]backend.InitFn{
   318  		"failsconfigure": func() backend.Backend {
   319  			return backendFailsConfigure{}
   320  		},
   321  	}
   322  	defer func() {
   323  		// undo our overrides so we won't affect other tests
   324  		overrideBackendFactories = nil
   325  	}()
   326  
   327  	schema := dataSourceRemoteStateGetSchema().Block
   328  	config, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
   329  		"backend": cty.StringVal("failsconfigure"),
   330  		"config":  cty.EmptyObjectVal,
   331  	}))
   332  	if err != nil {
   333  		t.Fatalf("unexpected error: %s", err)
   334  	}
   335  
   336  	diags := dataSourceRemoteStateValidate(config)
   337  	if diags.HasErrors() {
   338  		t.Fatalf("unexpected errors\n%s", diags.Err().Error())
   339  	}
   340  }
   341  
   342  type backendFailsConfigure struct{}
   343  
   344  func (b backendFailsConfigure) ConfigSchema() *configschema.Block {
   345  	log.Printf("[TRACE] backendFailsConfigure.ConfigSchema")
   346  	return &configschema.Block{} // intentionally empty configuration schema
   347  }
   348  
   349  func (b backendFailsConfigure) PrepareConfig(given cty.Value) (cty.Value, tfdiags.Diagnostics) {
   350  	// No special actions to take here
   351  	return given, nil
   352  }
   353  
   354  func (b backendFailsConfigure) Configure(config cty.Value) tfdiags.Diagnostics {
   355  	log.Printf("[TRACE] backendFailsConfigure.Configure(%#v)", config)
   356  	var diags tfdiags.Diagnostics
   357  	diags = diags.Append(fmt.Errorf("Configure should never be called"))
   358  	return diags
   359  }
   360  
   361  func (b backendFailsConfigure) StateMgr(workspace string) (statemgr.Full, error) {
   362  	return nil, fmt.Errorf("StateMgr not implemented")
   363  }
   364  
   365  func (b backendFailsConfigure) DeleteWorkspace(name string) error {
   366  	return fmt.Errorf("DeleteWorkspace not implemented")
   367  }
   368  
   369  func (b backendFailsConfigure) Workspaces() ([]string, error) {
   370  	return nil, fmt.Errorf("Workspaces not implemented")
   371  }