github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/backend/local/backend_local_test.go (about) 1 package local 2 3 import ( 4 "os" 5 "path/filepath" 6 "testing" 7 8 "github.com/iaas-resource-provision/iaas-rpc/internal/backend" 9 "github.com/iaas-resource-provision/iaas-rpc/internal/command/arguments" 10 "github.com/iaas-resource-provision/iaas-rpc/internal/command/clistate" 11 "github.com/iaas-resource-provision/iaas-rpc/internal/command/views" 12 "github.com/iaas-resource-provision/iaas-rpc/internal/configs/configload" 13 "github.com/iaas-resource-provision/iaas-rpc/internal/initwd" 14 "github.com/iaas-resource-provision/iaas-rpc/internal/plans" 15 "github.com/iaas-resource-provision/iaas-rpc/internal/plans/planfile" 16 "github.com/iaas-resource-provision/iaas-rpc/internal/states" 17 "github.com/iaas-resource-provision/iaas-rpc/internal/states/statefile" 18 "github.com/iaas-resource-provision/iaas-rpc/internal/terminal" 19 "github.com/zclconf/go-cty/cty" 20 ) 21 22 func TestLocalContext(t *testing.T) { 23 configDir := "./testdata/empty" 24 b, cleanup := TestLocal(t) 25 defer cleanup() 26 27 _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) 28 defer configCleanup() 29 30 streams, _ := terminal.StreamsForTesting(t) 31 view := views.NewView(streams) 32 stateLocker := clistate.NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view)) 33 34 op := &backend.Operation{ 35 ConfigDir: configDir, 36 ConfigLoader: configLoader, 37 Workspace: backend.DefaultStateName, 38 StateLocker: stateLocker, 39 } 40 41 _, _, diags := b.Context(op) 42 if diags.HasErrors() { 43 t.Fatalf("unexpected error: %s", diags.Err().Error()) 44 } 45 46 // Context() retains a lock on success 47 assertBackendStateLocked(t, b) 48 } 49 50 func TestLocalContext_error(t *testing.T) { 51 configDir := "./testdata/apply" 52 b, cleanup := TestLocal(t) 53 defer cleanup() 54 55 _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) 56 defer configCleanup() 57 58 streams, _ := terminal.StreamsForTesting(t) 59 view := views.NewView(streams) 60 stateLocker := clistate.NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view)) 61 62 op := &backend.Operation{ 63 ConfigDir: configDir, 64 ConfigLoader: configLoader, 65 Workspace: backend.DefaultStateName, 66 StateLocker: stateLocker, 67 } 68 69 _, _, diags := b.Context(op) 70 if !diags.HasErrors() { 71 t.Fatal("unexpected success") 72 } 73 74 // Context() unlocks the state on failure 75 assertBackendStateUnlocked(t, b) 76 } 77 78 func TestLocalContext_stalePlan(t *testing.T) { 79 configDir := "./testdata/apply" 80 b, cleanup := TestLocal(t) 81 defer cleanup() 82 83 _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) 84 defer configCleanup() 85 86 // Write an empty state file with serial 3 87 sf, err := os.Create(b.StatePath) 88 if err != nil { 89 t.Fatalf("unexpected error creating state file %s: %s", b.StatePath, err) 90 } 91 if err := statefile.Write(statefile.New(states.NewState(), "boop", 3), sf); err != nil { 92 t.Fatalf("unexpected error writing state file: %s", err) 93 } 94 95 // Refresh the state 96 sm, err := b.StateMgr("") 97 if err != nil { 98 t.Fatalf("unexpected error: %s", err) 99 } 100 if err := sm.RefreshState(); err != nil { 101 t.Fatalf("unexpected error refreshing state: %s", err) 102 } 103 104 // Create a minimal plan which also has state file serial 2, so is stale 105 backendConfig := cty.ObjectVal(map[string]cty.Value{ 106 "path": cty.NullVal(cty.String), 107 "workspace_dir": cty.NullVal(cty.String), 108 }) 109 backendConfigRaw, err := plans.NewDynamicValue(backendConfig, backendConfig.Type()) 110 if err != nil { 111 t.Fatal(err) 112 } 113 plan := &plans.Plan{ 114 UIMode: plans.NormalMode, 115 Changes: plans.NewChanges(), 116 Backend: plans.Backend{ 117 Type: "local", 118 Config: backendConfigRaw, 119 }, 120 PrevRunState: states.NewState(), 121 PriorState: states.NewState(), 122 } 123 prevStateFile := statefile.New(plan.PrevRunState, "boop", 1) 124 stateFile := statefile.New(plan.PriorState, "boop", 2) 125 126 // Roundtrip through serialization as expected by the operation 127 outDir := testTempDir(t) 128 defer os.RemoveAll(outDir) 129 planPath := filepath.Join(outDir, "plan.tfplan") 130 if err := planfile.Create(planPath, configload.NewEmptySnapshot(), prevStateFile, stateFile, plan); err != nil { 131 t.Fatalf("unexpected error writing planfile: %s", err) 132 } 133 planFile, err := planfile.Open(planPath) 134 if err != nil { 135 t.Fatalf("unexpected error reading planfile: %s", err) 136 } 137 138 streams, _ := terminal.StreamsForTesting(t) 139 view := views.NewView(streams) 140 stateLocker := clistate.NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view)) 141 142 op := &backend.Operation{ 143 ConfigDir: configDir, 144 ConfigLoader: configLoader, 145 PlanFile: planFile, 146 Workspace: backend.DefaultStateName, 147 StateLocker: stateLocker, 148 } 149 150 _, _, diags := b.Context(op) 151 if !diags.HasErrors() { 152 t.Fatal("unexpected success") 153 } 154 155 // Context() unlocks the state on failure 156 assertBackendStateUnlocked(t, b) 157 }