kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/command/workspace_command_test.go (about)

     1  package command
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"testing"
     9  
    10  	"kubeform.dev/terraform-backend-sdk/addrs"
    11  	"kubeform.dev/terraform-backend-sdk/backend"
    12  	"kubeform.dev/terraform-backend-sdk/backend/local"
    13  	"kubeform.dev/terraform-backend-sdk/backend/remote-state/inmem"
    14  	"kubeform.dev/terraform-backend-sdk/states"
    15  	"kubeform.dev/terraform-backend-sdk/states/statemgr"
    16  	"github.com/mitchellh/cli"
    17  
    18  	legacy "kubeform.dev/terraform-backend-sdk/legacy/terraform"
    19  )
    20  
    21  func TestWorkspace_createAndChange(t *testing.T) {
    22  	// Create a temporary working directory that is empty
    23  	td := tempDir(t)
    24  	os.MkdirAll(td, 0755)
    25  	defer os.RemoveAll(td)
    26  	defer testChdir(t, td)()
    27  
    28  	newCmd := &WorkspaceNewCommand{}
    29  
    30  	current, _ := newCmd.Workspace()
    31  	if current != backend.DefaultStateName {
    32  		t.Fatal("current workspace should be 'default'")
    33  	}
    34  
    35  	args := []string{"test"}
    36  	ui := new(cli.MockUi)
    37  	view, _ := testView(t)
    38  	newCmd.Meta = Meta{Ui: ui, View: view}
    39  	if code := newCmd.Run(args); code != 0 {
    40  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
    41  	}
    42  
    43  	current, _ = newCmd.Workspace()
    44  	if current != "test" {
    45  		t.Fatalf("current workspace should be 'test', got %q", current)
    46  	}
    47  
    48  	selCmd := &WorkspaceSelectCommand{}
    49  	args = []string{backend.DefaultStateName}
    50  	ui = new(cli.MockUi)
    51  	selCmd.Meta = Meta{Ui: ui, View: view}
    52  	if code := selCmd.Run(args); code != 0 {
    53  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
    54  	}
    55  
    56  	current, _ = newCmd.Workspace()
    57  	if current != backend.DefaultStateName {
    58  		t.Fatal("current workspace should be 'default'")
    59  	}
    60  
    61  }
    62  
    63  // Create some workspaces and test the list output.
    64  // This also ensures we switch to the correct env after each call
    65  func TestWorkspace_createAndList(t *testing.T) {
    66  	// Create a temporary working directory that is empty
    67  	td := tempDir(t)
    68  	os.MkdirAll(td, 0755)
    69  	defer os.RemoveAll(td)
    70  	defer testChdir(t, td)()
    71  
    72  	// make sure a vars file doesn't interfere
    73  	err := ioutil.WriteFile(
    74  		DefaultVarsFilename,
    75  		[]byte(`foo = "bar"`),
    76  		0644,
    77  	)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	envs := []string{"test_a", "test_b", "test_c"}
    83  
    84  	// create multiple workspaces
    85  	for _, env := range envs {
    86  		ui := new(cli.MockUi)
    87  		view, _ := testView(t)
    88  		newCmd := &WorkspaceNewCommand{
    89  			Meta: Meta{Ui: ui, View: view},
    90  		}
    91  		if code := newCmd.Run([]string{env}); code != 0 {
    92  			t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
    93  		}
    94  	}
    95  
    96  	listCmd := &WorkspaceListCommand{}
    97  	ui := new(cli.MockUi)
    98  	view, _ := testView(t)
    99  	listCmd.Meta = Meta{Ui: ui, View: view}
   100  
   101  	if code := listCmd.Run(nil); code != 0 {
   102  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
   103  	}
   104  
   105  	actual := strings.TrimSpace(ui.OutputWriter.String())
   106  	expected := "default\n  test_a\n  test_b\n* test_c"
   107  
   108  	if actual != expected {
   109  		t.Fatalf("\nexpected: %q\nactual:  %q", expected, actual)
   110  	}
   111  }
   112  
   113  // Create some workspaces and test the show output.
   114  func TestWorkspace_createAndShow(t *testing.T) {
   115  	// Create a temporary working directory that is empty
   116  	td := tempDir(t)
   117  	os.MkdirAll(td, 0755)
   118  	defer os.RemoveAll(td)
   119  	defer testChdir(t, td)()
   120  
   121  	// make sure a vars file doesn't interfere
   122  	err := ioutil.WriteFile(
   123  		DefaultVarsFilename,
   124  		[]byte(`foo = "bar"`),
   125  		0644,
   126  	)
   127  	if err != nil {
   128  		t.Fatal(err)
   129  	}
   130  
   131  	// make sure current workspace show outputs "default"
   132  	showCmd := &WorkspaceShowCommand{}
   133  	ui := new(cli.MockUi)
   134  	view, _ := testView(t)
   135  	showCmd.Meta = Meta{Ui: ui, View: view}
   136  
   137  	if code := showCmd.Run(nil); code != 0 {
   138  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
   139  	}
   140  
   141  	actual := strings.TrimSpace(ui.OutputWriter.String())
   142  	expected := "default"
   143  
   144  	if actual != expected {
   145  		t.Fatalf("\nexpected: %q\nactual:  %q", expected, actual)
   146  	}
   147  
   148  	newCmd := &WorkspaceNewCommand{}
   149  
   150  	env := []string{"test_a"}
   151  
   152  	// create test_a workspace
   153  	ui = new(cli.MockUi)
   154  	newCmd.Meta = Meta{Ui: ui, View: view}
   155  	if code := newCmd.Run(env); code != 0 {
   156  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
   157  	}
   158  
   159  	selCmd := &WorkspaceSelectCommand{}
   160  	ui = new(cli.MockUi)
   161  	selCmd.Meta = Meta{Ui: ui, View: view}
   162  	if code := selCmd.Run(env); code != 0 {
   163  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
   164  	}
   165  
   166  	showCmd = &WorkspaceShowCommand{}
   167  	ui = new(cli.MockUi)
   168  	showCmd.Meta = Meta{Ui: ui, View: view}
   169  
   170  	if code := showCmd.Run(nil); code != 0 {
   171  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
   172  	}
   173  
   174  	actual = strings.TrimSpace(ui.OutputWriter.String())
   175  	expected = "test_a"
   176  
   177  	if actual != expected {
   178  		t.Fatalf("\nexpected: %q\nactual:  %q", expected, actual)
   179  	}
   180  }
   181  
   182  // Don't allow names that aren't URL safe
   183  func TestWorkspace_createInvalid(t *testing.T) {
   184  	// Create a temporary working directory that is empty
   185  	td := tempDir(t)
   186  	os.MkdirAll(td, 0755)
   187  	defer os.RemoveAll(td)
   188  	defer testChdir(t, td)()
   189  
   190  	envs := []string{"test_a*", "test_b/foo", "../../../test_c", "好_d"}
   191  
   192  	// create multiple workspaces
   193  	for _, env := range envs {
   194  		ui := new(cli.MockUi)
   195  		view, _ := testView(t)
   196  		newCmd := &WorkspaceNewCommand{
   197  			Meta: Meta{Ui: ui, View: view},
   198  		}
   199  		if code := newCmd.Run([]string{env}); code == 0 {
   200  			t.Fatalf("expected failure: \n%s", ui.OutputWriter)
   201  		}
   202  	}
   203  
   204  	// list workspaces to make sure none were created
   205  	listCmd := &WorkspaceListCommand{}
   206  	ui := new(cli.MockUi)
   207  	view, _ := testView(t)
   208  	listCmd.Meta = Meta{Ui: ui, View: view}
   209  
   210  	if code := listCmd.Run(nil); code != 0 {
   211  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
   212  	}
   213  
   214  	actual := strings.TrimSpace(ui.OutputWriter.String())
   215  	expected := "* default"
   216  
   217  	if actual != expected {
   218  		t.Fatalf("\nexpected: %q\nactual:  %q", expected, actual)
   219  	}
   220  }
   221  
   222  func TestWorkspace_createWithState(t *testing.T) {
   223  	td := tempDir(t)
   224  	testCopyDir(t, testFixturePath("inmem-backend"), td)
   225  	defer os.RemoveAll(td)
   226  	defer testChdir(t, td)()
   227  	defer inmem.Reset()
   228  
   229  	// init the backend
   230  	ui := new(cli.MockUi)
   231  	view, _ := testView(t)
   232  	initCmd := &InitCommand{
   233  		Meta: Meta{Ui: ui, View: view},
   234  	}
   235  	if code := initCmd.Run([]string{}); code != 0 {
   236  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   237  	}
   238  
   239  	originalState := states.BuildState(func(s *states.SyncState) {
   240  		s.SetResourceInstanceCurrent(
   241  			addrs.Resource{
   242  				Mode: addrs.ManagedResourceMode,
   243  				Type: "test_instance",
   244  				Name: "foo",
   245  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   246  			&states.ResourceInstanceObjectSrc{
   247  				AttrsJSON: []byte(`{"id":"bar"}`),
   248  				Status:    states.ObjectReady,
   249  			},
   250  			addrs.AbsProviderConfig{
   251  				Provider: addrs.NewDefaultProvider("test"),
   252  				Module:   addrs.RootModule,
   253  			},
   254  		)
   255  	})
   256  
   257  	err := statemgr.NewFilesystem("test.tfstate").WriteState(originalState)
   258  	if err != nil {
   259  		t.Fatal(err)
   260  	}
   261  
   262  	workspace := "test_workspace"
   263  
   264  	args := []string{"-state", "test.tfstate", workspace}
   265  	ui = new(cli.MockUi)
   266  	newCmd := &WorkspaceNewCommand{
   267  		Meta: Meta{Ui: ui, View: view},
   268  	}
   269  	if code := newCmd.Run(args); code != 0 {
   270  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter)
   271  	}
   272  
   273  	newPath := filepath.Join(local.DefaultWorkspaceDir, "test", DefaultStateFilename)
   274  	envState := statemgr.NewFilesystem(newPath)
   275  	err = envState.RefreshState()
   276  	if err != nil {
   277  		t.Fatal(err)
   278  	}
   279  
   280  	b := backend.TestBackendConfig(t, inmem.New(), nil)
   281  	sMgr, err := b.StateMgr(workspace)
   282  	if err != nil {
   283  		t.Fatal(err)
   284  	}
   285  
   286  	newState := sMgr.State()
   287  
   288  	if got, want := newState.String(), originalState.String(); got != want {
   289  		t.Fatalf("states not equal\ngot: %s\nwant: %s", got, want)
   290  	}
   291  }
   292  
   293  func TestWorkspace_delete(t *testing.T) {
   294  	td := tempDir(t)
   295  	os.MkdirAll(td, 0755)
   296  	defer os.RemoveAll(td)
   297  	defer testChdir(t, td)()
   298  
   299  	// create the workspace directories
   300  	if err := os.MkdirAll(filepath.Join(local.DefaultWorkspaceDir, "test"), 0755); err != nil {
   301  		t.Fatal(err)
   302  	}
   303  
   304  	// create the workspace file
   305  	if err := os.MkdirAll(DefaultDataDir, 0755); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  	if err := ioutil.WriteFile(filepath.Join(DefaultDataDir, local.DefaultWorkspaceFile), []byte("test"), 0644); err != nil {
   309  		t.Fatal(err)
   310  	}
   311  
   312  	ui := new(cli.MockUi)
   313  	view, _ := testView(t)
   314  	delCmd := &WorkspaceDeleteCommand{
   315  		Meta: Meta{Ui: ui, View: view},
   316  	}
   317  
   318  	current, _ := delCmd.Workspace()
   319  	if current != "test" {
   320  		t.Fatal("wrong workspace:", current)
   321  	}
   322  
   323  	// we can't delete our current workspace
   324  	args := []string{"test"}
   325  	if code := delCmd.Run(args); code == 0 {
   326  		t.Fatal("expected error deleting current workspace")
   327  	}
   328  
   329  	// change back to default
   330  	if err := delCmd.SetWorkspace(backend.DefaultStateName); err != nil {
   331  		t.Fatal(err)
   332  	}
   333  
   334  	// try the delete again
   335  	ui = new(cli.MockUi)
   336  	delCmd.Meta.Ui = ui
   337  	if code := delCmd.Run(args); code != 0 {
   338  		t.Fatalf("error deleting workspace: %s", ui.ErrorWriter)
   339  	}
   340  
   341  	current, _ = delCmd.Workspace()
   342  	if current != backend.DefaultStateName {
   343  		t.Fatalf("wrong workspace: %q", current)
   344  	}
   345  }
   346  
   347  func TestWorkspace_deleteInvalid(t *testing.T) {
   348  	td := tempDir(t)
   349  	os.MkdirAll(td, 0755)
   350  	defer os.RemoveAll(td)
   351  	defer testChdir(t, td)()
   352  
   353  	// choose an invalid workspace name
   354  	workspace := "test workspace"
   355  	path := filepath.Join(local.DefaultWorkspaceDir, workspace)
   356  
   357  	// create the workspace directories
   358  	if err := os.MkdirAll(path, 0755); err != nil {
   359  		t.Fatal(err)
   360  	}
   361  
   362  	ui := new(cli.MockUi)
   363  	view, _ := testView(t)
   364  	delCmd := &WorkspaceDeleteCommand{
   365  		Meta: Meta{Ui: ui, View: view},
   366  	}
   367  
   368  	// delete the workspace
   369  	if code := delCmd.Run([]string{workspace}); code != 0 {
   370  		t.Fatalf("error deleting workspace: %s", ui.ErrorWriter)
   371  	}
   372  
   373  	if _, err := os.Stat(path); err == nil {
   374  		t.Fatalf("should have deleted workspace, but %s still exists", path)
   375  	} else if !os.IsNotExist(err) {
   376  		t.Fatalf("unexpected error for workspace path: %s", err)
   377  	}
   378  }
   379  
   380  func TestWorkspace_deleteWithState(t *testing.T) {
   381  	td := tempDir(t)
   382  	os.MkdirAll(td, 0755)
   383  	defer os.RemoveAll(td)
   384  	defer testChdir(t, td)()
   385  
   386  	// create the workspace directories
   387  	if err := os.MkdirAll(filepath.Join(local.DefaultWorkspaceDir, "test"), 0755); err != nil {
   388  		t.Fatal(err)
   389  	}
   390  
   391  	// create a non-empty state
   392  	originalState := &legacy.State{
   393  		Modules: []*legacy.ModuleState{
   394  			&legacy.ModuleState{
   395  				Path: []string{"root"},
   396  				Resources: map[string]*legacy.ResourceState{
   397  					"test_instance.foo": &legacy.ResourceState{
   398  						Type: "test_instance",
   399  						Primary: &legacy.InstanceState{
   400  							ID: "bar",
   401  						},
   402  					},
   403  				},
   404  			},
   405  		},
   406  	}
   407  
   408  	f, err := os.Create(filepath.Join(local.DefaultWorkspaceDir, "test", "terraform.tfstate"))
   409  	if err != nil {
   410  		t.Fatal(err)
   411  	}
   412  	defer f.Close()
   413  	if err := legacy.WriteState(originalState, f); err != nil {
   414  		t.Fatal(err)
   415  	}
   416  
   417  	ui := new(cli.MockUi)
   418  	view, _ := testView(t)
   419  	delCmd := &WorkspaceDeleteCommand{
   420  		Meta: Meta{Ui: ui, View: view},
   421  	}
   422  	args := []string{"test"}
   423  	if code := delCmd.Run(args); code == 0 {
   424  		t.Fatalf("expected failure without -force.\noutput: %s", ui.OutputWriter)
   425  	}
   426  
   427  	ui = new(cli.MockUi)
   428  	delCmd.Meta.Ui = ui
   429  
   430  	args = []string{"-force", "test"}
   431  	if code := delCmd.Run(args); code != 0 {
   432  		t.Fatalf("failure: %s", ui.ErrorWriter)
   433  	}
   434  
   435  	if _, err := os.Stat(filepath.Join(local.DefaultWorkspaceDir, "test")); !os.IsNotExist(err) {
   436  		t.Fatal("env 'test' still exists!")
   437  	}
   438  }