github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/command/taint_test.go (about)

     1  package command
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/mitchellh/cli"
    10  
    11  	"github.com/iaas-resource-provision/iaas-rpc/internal/addrs"
    12  	"github.com/iaas-resource-provision/iaas-rpc/internal/states"
    13  )
    14  
    15  func TestTaint(t *testing.T) {
    16  	state := states.BuildState(func(s *states.SyncState) {
    17  		s.SetResourceInstanceCurrent(
    18  			addrs.Resource{
    19  				Mode: addrs.ManagedResourceMode,
    20  				Type: "test_instance",
    21  				Name: "foo",
    22  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    23  			&states.ResourceInstanceObjectSrc{
    24  				AttrsJSON: []byte(`{"id":"bar"}`),
    25  				Status:    states.ObjectReady,
    26  			},
    27  			addrs.AbsProviderConfig{
    28  				Provider: addrs.NewDefaultProvider("test"),
    29  				Module:   addrs.RootModule,
    30  			},
    31  		)
    32  	})
    33  	statePath := testStateFile(t, state)
    34  
    35  	ui := new(cli.MockUi)
    36  	view, _ := testView(t)
    37  	c := &TaintCommand{
    38  		Meta: Meta{
    39  			Ui:   ui,
    40  			View: view,
    41  		},
    42  	}
    43  
    44  	args := []string{
    45  		"-state", statePath,
    46  		"test_instance.foo",
    47  	}
    48  	if code := c.Run(args); code != 0 {
    49  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    50  	}
    51  
    52  	testStateOutput(t, statePath, testTaintStr)
    53  }
    54  
    55  func TestTaint_lockedState(t *testing.T) {
    56  	state := states.BuildState(func(s *states.SyncState) {
    57  		s.SetResourceInstanceCurrent(
    58  			addrs.Resource{
    59  				Mode: addrs.ManagedResourceMode,
    60  				Type: "test_instance",
    61  				Name: "foo",
    62  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    63  			&states.ResourceInstanceObjectSrc{
    64  				AttrsJSON: []byte(`{"id":"bar"}`),
    65  				Status:    states.ObjectReady,
    66  			},
    67  			addrs.AbsProviderConfig{
    68  				Provider: addrs.NewDefaultProvider("test"),
    69  				Module:   addrs.RootModule,
    70  			},
    71  		)
    72  	})
    73  	statePath := testStateFile(t, state)
    74  
    75  	unlock, err := testLockState(testDataDir, statePath)
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  	defer unlock()
    80  	ui := new(cli.MockUi)
    81  	view, _ := testView(t)
    82  	c := &TaintCommand{
    83  		Meta: Meta{
    84  			Ui:   ui,
    85  			View: view,
    86  		},
    87  	}
    88  
    89  	args := []string{
    90  		"-state", statePath,
    91  		"test_instance.foo",
    92  	}
    93  	if code := c.Run(args); code == 0 {
    94  		t.Fatal("expected error")
    95  	}
    96  
    97  	output := ui.ErrorWriter.String()
    98  	if !strings.Contains(output, "lock") {
    99  		t.Fatal("command output does not look like a lock error:", output)
   100  	}
   101  }
   102  
   103  func TestTaint_backup(t *testing.T) {
   104  	// Get a temp cwd
   105  	tmp, cwd := testCwd(t)
   106  	defer testFixCwd(t, tmp, cwd)
   107  
   108  	// Write the temp state
   109  	state := states.BuildState(func(s *states.SyncState) {
   110  		s.SetResourceInstanceCurrent(
   111  			addrs.Resource{
   112  				Mode: addrs.ManagedResourceMode,
   113  				Type: "test_instance",
   114  				Name: "foo",
   115  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   116  			&states.ResourceInstanceObjectSrc{
   117  				AttrsJSON: []byte(`{"id":"bar"}`),
   118  				Status:    states.ObjectReady,
   119  			},
   120  			addrs.AbsProviderConfig{
   121  				Provider: addrs.NewDefaultProvider("test"),
   122  				Module:   addrs.RootModule,
   123  			},
   124  		)
   125  	})
   126  	testStateFileDefault(t, state)
   127  
   128  	ui := new(cli.MockUi)
   129  	view, _ := testView(t)
   130  	c := &TaintCommand{
   131  		Meta: Meta{
   132  			Ui:   ui,
   133  			View: view,
   134  		},
   135  	}
   136  
   137  	args := []string{
   138  		"test_instance.foo",
   139  	}
   140  	if code := c.Run(args); code != 0 {
   141  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   142  	}
   143  
   144  	testStateOutput(t, DefaultStateFilename+".backup", testTaintDefaultStr)
   145  	testStateOutput(t, DefaultStateFilename, testTaintStr)
   146  }
   147  
   148  func TestTaint_backupDisable(t *testing.T) {
   149  	// Get a temp cwd
   150  	tmp, cwd := testCwd(t)
   151  	defer testFixCwd(t, tmp, cwd)
   152  
   153  	// Write the temp state
   154  	state := states.BuildState(func(s *states.SyncState) {
   155  		s.SetResourceInstanceCurrent(
   156  			addrs.Resource{
   157  				Mode: addrs.ManagedResourceMode,
   158  				Type: "test_instance",
   159  				Name: "foo",
   160  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   161  			&states.ResourceInstanceObjectSrc{
   162  				AttrsJSON: []byte(`{"id":"bar"}`),
   163  				Status:    states.ObjectReady,
   164  			},
   165  			addrs.AbsProviderConfig{
   166  				Provider: addrs.NewDefaultProvider("test"),
   167  				Module:   addrs.RootModule,
   168  			},
   169  		)
   170  	})
   171  	testStateFileDefault(t, state)
   172  
   173  	ui := new(cli.MockUi)
   174  	view, _ := testView(t)
   175  	c := &TaintCommand{
   176  		Meta: Meta{
   177  			Ui:   ui,
   178  			View: view,
   179  		},
   180  	}
   181  
   182  	args := []string{
   183  		"-backup", "-",
   184  		"test_instance.foo",
   185  	}
   186  	if code := c.Run(args); code != 0 {
   187  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   188  	}
   189  
   190  	if _, err := os.Stat(DefaultStateFilename + ".backup"); err == nil {
   191  		t.Fatal("backup path should not exist")
   192  	}
   193  
   194  	testStateOutput(t, DefaultStateFilename, testTaintStr)
   195  }
   196  
   197  func TestTaint_badState(t *testing.T) {
   198  	ui := new(cli.MockUi)
   199  	view, _ := testView(t)
   200  	c := &TaintCommand{
   201  		Meta: Meta{
   202  			Ui:   ui,
   203  			View: view,
   204  		},
   205  	}
   206  
   207  	args := []string{
   208  		"-state", "i-should-not-exist-ever",
   209  		"foo",
   210  	}
   211  	if code := c.Run(args); code != 1 {
   212  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   213  	}
   214  }
   215  
   216  func TestTaint_defaultState(t *testing.T) {
   217  	// Get a temp cwd
   218  	tmp, cwd := testCwd(t)
   219  	defer testFixCwd(t, tmp, cwd)
   220  
   221  	// Write the temp state
   222  	state := states.BuildState(func(s *states.SyncState) {
   223  		s.SetResourceInstanceCurrent(
   224  			addrs.Resource{
   225  				Mode: addrs.ManagedResourceMode,
   226  				Type: "test_instance",
   227  				Name: "foo",
   228  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   229  			&states.ResourceInstanceObjectSrc{
   230  				AttrsJSON: []byte(`{"id":"bar"}`),
   231  				Status:    states.ObjectReady,
   232  			},
   233  			addrs.AbsProviderConfig{
   234  				Provider: addrs.NewDefaultProvider("test"),
   235  				Module:   addrs.RootModule,
   236  			},
   237  		)
   238  	})
   239  	testStateFileDefault(t, state)
   240  
   241  	ui := new(cli.MockUi)
   242  	view, _ := testView(t)
   243  	c := &TaintCommand{
   244  		Meta: Meta{
   245  			Ui:   ui,
   246  			View: view,
   247  		},
   248  	}
   249  
   250  	args := []string{
   251  		"test_instance.foo",
   252  	}
   253  	if code := c.Run(args); code != 0 {
   254  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   255  	}
   256  
   257  	testStateOutput(t, DefaultStateFilename, testTaintStr)
   258  }
   259  
   260  func TestTaint_defaultWorkspaceState(t *testing.T) {
   261  	// Get a temp cwd
   262  	tmp, cwd := testCwd(t)
   263  	defer testFixCwd(t, tmp, cwd)
   264  
   265  	state := states.BuildState(func(s *states.SyncState) {
   266  		s.SetResourceInstanceCurrent(
   267  			addrs.Resource{
   268  				Mode: addrs.ManagedResourceMode,
   269  				Type: "test_instance",
   270  				Name: "foo",
   271  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   272  			&states.ResourceInstanceObjectSrc{
   273  				AttrsJSON: []byte(`{"id":"bar"}`),
   274  				Status:    states.ObjectReady,
   275  			},
   276  			addrs.AbsProviderConfig{
   277  				Provider: addrs.NewDefaultProvider("test"),
   278  				Module:   addrs.RootModule,
   279  			},
   280  		)
   281  	})
   282  	testWorkspace := "development"
   283  	path := testStateFileWorkspaceDefault(t, testWorkspace, state)
   284  
   285  	ui := new(cli.MockUi)
   286  	view, _ := testView(t)
   287  	meta := Meta{Ui: ui, View: view}
   288  	meta.SetWorkspace(testWorkspace)
   289  	c := &TaintCommand{
   290  		Meta: meta,
   291  	}
   292  
   293  	args := []string{
   294  		"test_instance.foo",
   295  	}
   296  	if code := c.Run(args); code != 0 {
   297  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   298  	}
   299  
   300  	testStateOutput(t, path, testTaintStr)
   301  }
   302  
   303  func TestTaint_missing(t *testing.T) {
   304  	state := states.BuildState(func(s *states.SyncState) {
   305  		s.SetResourceInstanceCurrent(
   306  			addrs.Resource{
   307  				Mode: addrs.ManagedResourceMode,
   308  				Type: "test_instance",
   309  				Name: "foo",
   310  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   311  			&states.ResourceInstanceObjectSrc{
   312  				AttrsJSON: []byte(`{"id":"bar"}`),
   313  				Status:    states.ObjectReady,
   314  			},
   315  			addrs.AbsProviderConfig{
   316  				Provider: addrs.NewDefaultProvider("test"),
   317  				Module:   addrs.RootModule,
   318  			},
   319  		)
   320  	})
   321  	statePath := testStateFile(t, state)
   322  
   323  	ui := new(cli.MockUi)
   324  	view, _ := testView(t)
   325  	c := &TaintCommand{
   326  		Meta: Meta{
   327  			Ui:   ui,
   328  			View: view,
   329  		},
   330  	}
   331  
   332  	args := []string{
   333  		"-state", statePath,
   334  		"test_instance.bar",
   335  	}
   336  	if code := c.Run(args); code == 0 {
   337  		t.Fatalf("bad: %d\n\n%s", code, ui.OutputWriter.String())
   338  	}
   339  }
   340  
   341  func TestTaint_missingAllow(t *testing.T) {
   342  	state := states.BuildState(func(s *states.SyncState) {
   343  		s.SetResourceInstanceCurrent(
   344  			addrs.Resource{
   345  				Mode: addrs.ManagedResourceMode,
   346  				Type: "test_instance",
   347  				Name: "foo",
   348  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   349  			&states.ResourceInstanceObjectSrc{
   350  				AttrsJSON: []byte(`{"id":"bar"}`),
   351  				Status:    states.ObjectReady,
   352  			},
   353  			addrs.AbsProviderConfig{
   354  				Provider: addrs.NewDefaultProvider("test"),
   355  				Module:   addrs.RootModule,
   356  			},
   357  		)
   358  	})
   359  	statePath := testStateFile(t, state)
   360  
   361  	ui := new(cli.MockUi)
   362  	view, _ := testView(t)
   363  	c := &TaintCommand{
   364  		Meta: Meta{
   365  			Ui:   ui,
   366  			View: view,
   367  		},
   368  	}
   369  
   370  	args := []string{
   371  		"-allow-missing",
   372  		"-state", statePath,
   373  		"test_instance.bar",
   374  	}
   375  	if code := c.Run(args); code != 0 {
   376  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   377  	}
   378  
   379  	// Check for the warning
   380  	actual := strings.TrimSpace(ui.ErrorWriter.String())
   381  	expected := strings.TrimSpace(`
   382  Warning: No such resource instance
   383  
   384  Resource instance test_instance.bar was not found, but this is not an error
   385  because -allow-missing was set.
   386  
   387  `)
   388  	if diff := cmp.Diff(expected, actual); diff != "" {
   389  		t.Fatalf("wrong output\n%s", diff)
   390  	}
   391  }
   392  
   393  func TestTaint_stateOut(t *testing.T) {
   394  	// Get a temp cwd
   395  	tmp, cwd := testCwd(t)
   396  	defer testFixCwd(t, tmp, cwd)
   397  
   398  	// Write the temp state
   399  	state := states.BuildState(func(s *states.SyncState) {
   400  		s.SetResourceInstanceCurrent(
   401  			addrs.Resource{
   402  				Mode: addrs.ManagedResourceMode,
   403  				Type: "test_instance",
   404  				Name: "foo",
   405  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   406  			&states.ResourceInstanceObjectSrc{
   407  				AttrsJSON: []byte(`{"id":"bar"}`),
   408  				Status:    states.ObjectReady,
   409  			},
   410  			addrs.AbsProviderConfig{
   411  				Provider: addrs.NewDefaultProvider("test"),
   412  				Module:   addrs.RootModule,
   413  			},
   414  		)
   415  	})
   416  	testStateFileDefault(t, state)
   417  
   418  	ui := new(cli.MockUi)
   419  	view, _ := testView(t)
   420  	c := &TaintCommand{
   421  		Meta: Meta{
   422  			Ui:   ui,
   423  			View: view,
   424  		},
   425  	}
   426  
   427  	args := []string{
   428  		"-state-out", "foo",
   429  		"test_instance.foo",
   430  	}
   431  	if code := c.Run(args); code != 0 {
   432  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   433  	}
   434  
   435  	testStateOutput(t, DefaultStateFilename, testTaintDefaultStr)
   436  	testStateOutput(t, "foo", testTaintStr)
   437  }
   438  
   439  func TestTaint_module(t *testing.T) {
   440  	state := states.BuildState(func(s *states.SyncState) {
   441  		s.SetResourceInstanceCurrent(
   442  			addrs.Resource{
   443  				Mode: addrs.ManagedResourceMode,
   444  				Type: "test_instance",
   445  				Name: "foo",
   446  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   447  			&states.ResourceInstanceObjectSrc{
   448  				AttrsJSON: []byte(`{"id":"bar"}`),
   449  				Status:    states.ObjectReady,
   450  			},
   451  			addrs.AbsProviderConfig{
   452  				Provider: addrs.NewDefaultProvider("test"),
   453  				Module:   addrs.RootModule,
   454  			},
   455  		)
   456  		s.SetResourceInstanceCurrent(
   457  			addrs.Resource{
   458  				Mode: addrs.ManagedResourceMode,
   459  				Type: "test_instance",
   460  				Name: "blah",
   461  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey)),
   462  			&states.ResourceInstanceObjectSrc{
   463  				AttrsJSON: []byte(`{"id":"blah"}`),
   464  				Status:    states.ObjectReady,
   465  			},
   466  			addrs.AbsProviderConfig{
   467  				Provider: addrs.NewDefaultProvider("test"),
   468  				Module:   addrs.RootModule,
   469  			},
   470  		)
   471  	})
   472  	statePath := testStateFile(t, state)
   473  
   474  	ui := new(cli.MockUi)
   475  	view, _ := testView(t)
   476  	c := &TaintCommand{
   477  		Meta: Meta{
   478  			Ui:   ui,
   479  			View: view,
   480  		},
   481  	}
   482  
   483  	args := []string{
   484  		"-state", statePath,
   485  		"module.child.test_instance.blah",
   486  	}
   487  	if code := c.Run(args); code != 0 {
   488  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   489  	}
   490  
   491  	testStateOutput(t, statePath, testTaintModuleStr)
   492  }
   493  
   494  func TestTaint_checkRequiredVersion(t *testing.T) {
   495  	// Create a temporary working directory that is empty
   496  	td := tempDir(t)
   497  	testCopyDir(t, testFixturePath("taint-check-required-version"), td)
   498  	defer os.RemoveAll(td)
   499  	defer testChdir(t, td)()
   500  
   501  	// Write the temp state
   502  	state := states.BuildState(func(s *states.SyncState) {
   503  		s.SetResourceInstanceCurrent(
   504  			addrs.Resource{
   505  				Mode: addrs.ManagedResourceMode,
   506  				Type: "test_instance",
   507  				Name: "foo",
   508  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   509  			&states.ResourceInstanceObjectSrc{
   510  				AttrsJSON: []byte(`{"id":"bar"}`),
   511  				Status:    states.ObjectReady,
   512  			},
   513  			addrs.AbsProviderConfig{
   514  				Provider: addrs.NewDefaultProvider("test"),
   515  				Module:   addrs.RootModule,
   516  			},
   517  		)
   518  	})
   519  	path := testStateFile(t, state)
   520  
   521  	ui := cli.NewMockUi()
   522  	view, _ := testView(t)
   523  	c := &TaintCommand{
   524  		Meta: Meta{
   525  			testingOverrides: metaOverridesForProvider(testProvider()),
   526  			Ui:               ui,
   527  			View:             view,
   528  		},
   529  	}
   530  
   531  	args := []string{"test_instance.foo"}
   532  	if code := c.Run(args); code != 1 {
   533  		t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
   534  	}
   535  
   536  	// State is unchanged
   537  	testStateOutput(t, path, testTaintDefaultStr)
   538  
   539  	// Required version diags are correct
   540  	errStr := ui.ErrorWriter.String()
   541  	if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) {
   542  		t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr)
   543  	}
   544  	if strings.Contains(errStr, `required_version = ">= 0.13.0"`) {
   545  		t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr)
   546  	}
   547  }
   548  
   549  const testTaintStr = `
   550  test_instance.foo: (tainted)
   551    ID = bar
   552    provider = provider["registry.terraform.io/hashicorp/test"]
   553  `
   554  
   555  const testTaintDefaultStr = `
   556  test_instance.foo:
   557    ID = bar
   558    provider = provider["registry.terraform.io/hashicorp/test"]
   559  `
   560  
   561  const testTaintModuleStr = `
   562  test_instance.foo:
   563    ID = bar
   564    provider = provider["registry.terraform.io/hashicorp/test"]
   565  
   566  module.child:
   567    test_instance.blah: (tainted)
   568      ID = blah
   569      provider = provider["registry.terraform.io/hashicorp/test"]
   570  `