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 }