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  }