github.com/svenhamers/terraform@v0.11.12-beta1/backend/remote/backend_test.go (about)

     1  package remote
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/terraform/backend"
    10  	"github.com/hashicorp/terraform/config"
    11  	"github.com/hashicorp/terraform/svchost/disco"
    12  	"github.com/hashicorp/terraform/terraform"
    13  	"github.com/hashicorp/terraform/version"
    14  
    15  	backendLocal "github.com/hashicorp/terraform/backend/local"
    16  )
    17  
    18  func TestRemote(t *testing.T) {
    19  	var _ backend.Enhanced = New(nil)
    20  	var _ backend.CLI = New(nil)
    21  }
    22  
    23  func TestRemote_backendDefault(t *testing.T) {
    24  	b := testBackendDefault(t)
    25  	backend.TestBackendStates(t, b)
    26  	backend.TestBackendStateLocks(t, b, b)
    27  	backend.TestBackendStateForceUnlock(t, b, b)
    28  }
    29  
    30  func TestRemote_backendNoDefault(t *testing.T) {
    31  	b := testBackendNoDefault(t)
    32  	backend.TestBackendStates(t, b)
    33  }
    34  
    35  func TestRemote_config(t *testing.T) {
    36  	cases := map[string]struct {
    37  		config map[string]interface{}
    38  		err    error
    39  	}{
    40  		"with_a_name": {
    41  			config: map[string]interface{}{
    42  				"organization": "hashicorp",
    43  				"workspaces": []interface{}{
    44  					map[string]interface{}{
    45  						"name": "prod",
    46  					},
    47  				},
    48  			},
    49  			err: nil,
    50  		},
    51  		"with_a_prefix": {
    52  			config: map[string]interface{}{
    53  				"organization": "hashicorp",
    54  				"workspaces": []interface{}{
    55  					map[string]interface{}{
    56  						"prefix": "my-app-",
    57  					},
    58  				},
    59  			},
    60  			err: nil,
    61  		},
    62  		"without_either_a_name_and_a_prefix": {
    63  			config: map[string]interface{}{
    64  				"organization": "hashicorp",
    65  				"workspaces": []interface{}{
    66  					map[string]interface{}{},
    67  				},
    68  			},
    69  			err: errors.New("either workspace 'name' or 'prefix' is required"),
    70  		},
    71  		"with_both_a_name_and_a_prefix": {
    72  			config: map[string]interface{}{
    73  				"organization": "hashicorp",
    74  				"workspaces": []interface{}{
    75  					map[string]interface{}{
    76  						"name":   "prod",
    77  						"prefix": "my-app-",
    78  					},
    79  				},
    80  			},
    81  			err: errors.New("only one of workspace 'name' or 'prefix' is allowed"),
    82  		},
    83  		"with_an_unknown_host": {
    84  			config: map[string]interface{}{
    85  				"hostname":     "nonexisting.local",
    86  				"organization": "hashicorp",
    87  				"workspaces": []interface{}{
    88  					map[string]interface{}{
    89  						"name": "prod",
    90  					},
    91  				},
    92  			},
    93  			err: errors.New("Failed to request discovery document"),
    94  		},
    95  	}
    96  
    97  	for name, tc := range cases {
    98  		s := testServer(t)
    99  		b := New(testDisco(s))
   100  
   101  		// Get the proper config structure
   102  		rc, err := config.NewRawConfig(tc.config)
   103  		if err != nil {
   104  			t.Fatalf("%s: error creating raw config: %v", name, err)
   105  		}
   106  		conf := terraform.NewResourceConfig(rc)
   107  
   108  		// Validate
   109  		warns, errs := b.Validate(conf)
   110  		if len(warns) > 0 {
   111  			t.Fatalf("%s: validation warnings: %v", name, warns)
   112  		}
   113  		if len(errs) > 0 {
   114  			t.Fatalf("%s: validation errors: %v", name, errs)
   115  		}
   116  
   117  		// Configure
   118  		err = b.Configure(conf)
   119  		if err != tc.err && err != nil && tc.err != nil && !strings.Contains(err.Error(), tc.err.Error()) {
   120  			t.Fatalf("%s: expected error %q, got: %q", name, tc.err, err)
   121  		}
   122  	}
   123  }
   124  
   125  func TestRemote_versionConstraints(t *testing.T) {
   126  	cases := map[string]struct {
   127  		config     map[string]interface{}
   128  		prerelease string
   129  		version    string
   130  		err        error
   131  	}{
   132  		"compatible version": {
   133  			config: map[string]interface{}{
   134  				"organization": "hashicorp",
   135  				"workspaces": []interface{}{
   136  					map[string]interface{}{
   137  						"name": "prod",
   138  					},
   139  				},
   140  			},
   141  			version: "0.11.1",
   142  		},
   143  		"version too old": {
   144  			config: map[string]interface{}{
   145  				"organization": "hashicorp",
   146  				"workspaces": []interface{}{
   147  					map[string]interface{}{
   148  						"name": "prod",
   149  					},
   150  				},
   151  			},
   152  			version: "0.10.1",
   153  			err:     errors.New("upgrade Terraform to >= 0.11.8"),
   154  		},
   155  		"version too new": {
   156  			config: map[string]interface{}{
   157  				"organization": "hashicorp",
   158  				"workspaces": []interface{}{
   159  					map[string]interface{}{
   160  						"name": "prod",
   161  					},
   162  				},
   163  			},
   164  			version: "0.12.0",
   165  			err:     errors.New("downgrade Terraform to <= 0.11.11"),
   166  		},
   167  	}
   168  
   169  	// Save and restore the actual version.
   170  	p := version.Prerelease
   171  	v := version.Version
   172  	defer func() {
   173  		version.Prerelease = p
   174  		version.Version = v
   175  	}()
   176  
   177  	for name, tc := range cases {
   178  		s := testServer(t)
   179  		b := New(testDisco(s))
   180  
   181  		// Set the version for this test.
   182  		version.Prerelease = tc.prerelease
   183  		version.Version = tc.version
   184  
   185  		// Get the proper config structure
   186  		rc, err := config.NewRawConfig(tc.config)
   187  		if err != nil {
   188  			t.Fatalf("%s: error creating raw config: %v", name, err)
   189  		}
   190  		conf := terraform.NewResourceConfig(rc)
   191  
   192  		// Validate
   193  		warns, errs := b.Validate(conf)
   194  		if len(warns) > 0 {
   195  			t.Fatalf("%s: validation warnings: %v", name, warns)
   196  		}
   197  		if len(errs) > 0 {
   198  			t.Fatalf("%s: validation errors: %v", name, errs)
   199  		}
   200  
   201  		// Configure
   202  		err = b.Configure(conf)
   203  		if err != tc.err && err != nil && tc.err != nil && !strings.Contains(err.Error(), tc.err.Error()) {
   204  			t.Fatalf("%s: expected error %q, got: %q", name, tc.err, err)
   205  		}
   206  	}
   207  }
   208  
   209  func TestRemote_localBackend(t *testing.T) {
   210  	b := testBackendDefault(t)
   211  
   212  	local, ok := b.local.(*backendLocal.Local)
   213  	if !ok {
   214  		t.Fatalf("expected b.local to be \"*local.Local\", got: %T", b.local)
   215  	}
   216  
   217  	remote, ok := local.Backend.(*Remote)
   218  	if !ok {
   219  		t.Fatalf("expected local.Backend to be *remote.Remote, got: %T", remote)
   220  	}
   221  }
   222  
   223  func TestRemote_addAndRemoveStatesDefault(t *testing.T) {
   224  	b := testBackendDefault(t)
   225  	if _, err := b.States(); err != backend.ErrNamedStatesNotSupported {
   226  		t.Fatalf("expected error %v, got %v", backend.ErrNamedStatesNotSupported, err)
   227  	}
   228  
   229  	if _, err := b.State(backend.DefaultStateName); err != nil {
   230  		t.Fatalf("expected no error, got %v", err)
   231  	}
   232  
   233  	if _, err := b.State("prod"); err != backend.ErrNamedStatesNotSupported {
   234  		t.Fatalf("expected error %v, got %v", backend.ErrNamedStatesNotSupported, err)
   235  	}
   236  
   237  	if err := b.DeleteState(backend.DefaultStateName); err != nil {
   238  		t.Fatalf("expected no error, got %v", err)
   239  	}
   240  
   241  	if err := b.DeleteState("prod"); err != backend.ErrNamedStatesNotSupported {
   242  		t.Fatalf("expected error %v, got %v", backend.ErrNamedStatesNotSupported, err)
   243  	}
   244  }
   245  
   246  func TestRemote_addAndRemoveStatesNoDefault(t *testing.T) {
   247  	b := testBackendNoDefault(t)
   248  	states, err := b.States()
   249  	if err != nil {
   250  		t.Fatal(err)
   251  	}
   252  
   253  	expectedStates := []string(nil)
   254  	if !reflect.DeepEqual(states, expectedStates) {
   255  		t.Fatalf("expected states %#+v, got %#+v", expectedStates, states)
   256  	}
   257  
   258  	if _, err := b.State(backend.DefaultStateName); err != backend.ErrDefaultStateNotSupported {
   259  		t.Fatalf("expected error %v, got %v", backend.ErrDefaultStateNotSupported, err)
   260  	}
   261  
   262  	expectedA := "test_A"
   263  	if _, err := b.State(expectedA); err != nil {
   264  		t.Fatal(err)
   265  	}
   266  
   267  	states, err = b.States()
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  
   272  	expectedStates = append(expectedStates, expectedA)
   273  	if !reflect.DeepEqual(states, expectedStates) {
   274  		t.Fatalf("expected %#+v, got %#+v", expectedStates, states)
   275  	}
   276  
   277  	expectedB := "test_B"
   278  	if _, err := b.State(expectedB); err != nil {
   279  		t.Fatal(err)
   280  	}
   281  
   282  	states, err = b.States()
   283  	if err != nil {
   284  		t.Fatal(err)
   285  	}
   286  
   287  	expectedStates = append(expectedStates, expectedB)
   288  	if !reflect.DeepEqual(states, expectedStates) {
   289  		t.Fatalf("expected %#+v, got %#+v", expectedStates, states)
   290  	}
   291  
   292  	if err := b.DeleteState(backend.DefaultStateName); err != backend.ErrDefaultStateNotSupported {
   293  		t.Fatalf("expected error %v, got %v", backend.ErrDefaultStateNotSupported, err)
   294  	}
   295  
   296  	if err := b.DeleteState(expectedA); err != nil {
   297  		t.Fatal(err)
   298  	}
   299  
   300  	states, err = b.States()
   301  	if err != nil {
   302  		t.Fatal(err)
   303  	}
   304  
   305  	expectedStates = []string{expectedB}
   306  	if !reflect.DeepEqual(states, expectedStates) {
   307  		t.Fatalf("expected %#+v got %#+v", expectedStates, states)
   308  	}
   309  
   310  	if err := b.DeleteState(expectedB); err != nil {
   311  		t.Fatal(err)
   312  	}
   313  
   314  	states, err = b.States()
   315  	if err != nil {
   316  		t.Fatal(err)
   317  	}
   318  
   319  	expectedStates = []string(nil)
   320  	if !reflect.DeepEqual(states, expectedStates) {
   321  		t.Fatalf("expected %#+v, got %#+v", expectedStates, states)
   322  	}
   323  }
   324  
   325  func TestRemote_checkConstraints(t *testing.T) {
   326  	b := testBackendDefault(t)
   327  
   328  	cases := map[string]struct {
   329  		constraints *disco.Constraints
   330  		prerelease  string
   331  		version     string
   332  		result      string
   333  	}{
   334  		"compatible version": {
   335  			constraints: &disco.Constraints{
   336  				Minimum: "0.11.0",
   337  				Maximum: "0.11.11",
   338  			},
   339  			version: "0.11.1",
   340  			result:  "",
   341  		},
   342  		"version too old": {
   343  			constraints: &disco.Constraints{
   344  				Minimum: "0.11.0",
   345  				Maximum: "0.11.11",
   346  			},
   347  			version: "0.10.1",
   348  			result:  "upgrade Terraform to >= 0.11.0",
   349  		},
   350  		"version too new": {
   351  			constraints: &disco.Constraints{
   352  				Minimum: "0.11.0",
   353  				Maximum: "0.11.11",
   354  			},
   355  			version: "0.12.0",
   356  			result:  "downgrade Terraform to <= 0.11.11",
   357  		},
   358  		"version excluded - ordered": {
   359  			constraints: &disco.Constraints{
   360  				Minimum:   "0.11.0",
   361  				Excluding: []string{"0.11.7", "0.11.8"},
   362  				Maximum:   "0.11.11",
   363  			},
   364  			version: "0.11.7",
   365  			result:  "upgrade Terraform to > 0.11.8",
   366  		},
   367  		"version excluded - unordered": {
   368  			constraints: &disco.Constraints{
   369  				Minimum:   "0.11.0",
   370  				Excluding: []string{"0.11.8", "0.11.6"},
   371  				Maximum:   "0.11.11",
   372  			},
   373  			version: "0.11.6",
   374  			result:  "upgrade Terraform to > 0.11.8",
   375  		},
   376  		"list versions": {
   377  			constraints: &disco.Constraints{
   378  				Minimum: "0.11.0",
   379  				Maximum: "0.11.11",
   380  			},
   381  			version: "0.10.1",
   382  			result:  "versions >= 0.11.0, <= 0.11.11.",
   383  		},
   384  		"list exclusion": {
   385  			constraints: &disco.Constraints{
   386  				Minimum:   "0.11.0",
   387  				Excluding: []string{"0.11.6"},
   388  				Maximum:   "0.11.11",
   389  			},
   390  			version: "0.11.6",
   391  			result:  "excluding version 0.11.6.",
   392  		},
   393  		"list exclusions": {
   394  			constraints: &disco.Constraints{
   395  				Minimum:   "0.11.0",
   396  				Excluding: []string{"0.11.8", "0.11.6"},
   397  				Maximum:   "0.11.11",
   398  			},
   399  			version: "0.11.6",
   400  			result:  "excluding versions 0.11.6, 0.11.8.",
   401  		},
   402  	}
   403  
   404  	for name, tc := range cases {
   405  		version.Prerelease = tc.prerelease
   406  		version.Version = tc.version
   407  
   408  		// Check the constraints.
   409  		err := b.checkConstraints(tc.constraints)
   410  		if (err != nil || tc.result != "") &&
   411  			(err == nil || !strings.Contains(err.Error(), tc.result)) {
   412  			t.Fatalf("%s: unexpected constraints result: %v", name, err)
   413  		}
   414  	}
   415  }