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