Add test for apply/refresh on locked state files

Verify that these operations fail when a state file is locked.
This commit is contained in:
James Bardin 2017-02-03 15:32:40 -05:00
parent fb60b6f6f2
commit bd65ddbcaa
3 changed files with 84 additions and 9 deletions

View File

@ -8,12 +8,10 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"syscall"
"testing" "testing"
"time" "time"
@ -65,13 +63,11 @@ func TestApply(t *testing.T) {
func TestApply_lockedState(t *testing.T) { func TestApply_lockedState(t *testing.T) {
statePath := testTempFile(t) statePath := testTempFile(t)
locker := exec.Command("go", "run", "testadata/statelocker.go", statePath) unlock, err := testLockState(statePath)
locker.Stderr = os.Stderr if err != nil {
if err := locker.Start(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer unlock()
defer locker.Process.Signal(syscall.SIGTERM)
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
@ -86,8 +82,13 @@ func TestApply_lockedState(t *testing.T) {
"-state", statePath, "-state", statePath,
testFixturePath("apply"), testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code == 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatal("expected error")
}
output := ui.ErrorWriter.String()
if !strings.Contains(output, "locked") {
t.Fatal("command output does not look like a lock error:", output)
} }
} }

View File

@ -6,14 +6,17 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"testing" "testing"
"github.com/hashicorp/go-getter" "github.com/hashicorp/go-getter"
@ -529,3 +532,37 @@ func testRemoteState(t *testing.T, s *terraform.State, c int) (*terraform.Remote
return remote, srv return remote, srv
} }
// testlockState calls a separate process to the lock the state file at path.
// deferFunc should be called in the caller to properly unlock the file.
func testLockState(path string) (func(), error) {
locker := exec.Command("go", "run", "testdata/statelocker.go", path)
pr, pw, err := os.Pipe()
if err != nil {
return nil, err
}
defer pr.Close()
defer pw.Close()
locker.Stderr = pw
locker.Stdout = pw
if err := locker.Start(); err != nil {
return nil, err
}
deferFunc := func() {
locker.Process.Signal(syscall.SIGTERM)
locker.Wait()
}
// wait for the process to lock
buf := make([]byte, 1024)
n, err := pr.Read(buf)
if err != nil {
return deferFunc, fmt.Errorf("read from statelocker returned: %s", err)
}
if string(buf[:n]) != "LOCKED" {
return deferFunc, fmt.Errorf("statelocker wrote", string(buf[:n]))
}
return deferFunc, nil
}

View File

@ -59,6 +59,43 @@ func TestRefresh(t *testing.T) {
} }
} }
func TestRefresh_lockedState(t *testing.T) {
state := testState()
statePath := testStateFile(t, state)
unlock, err := testLockState(statePath)
if err != nil {
t.Fatal(err)
}
defer unlock()
p := testProvider()
ui := new(cli.MockUi)
c := &RefreshCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
}
p.RefreshFn = nil
p.RefreshReturn = &terraform.InstanceState{ID: "yes"}
args := []string{
"-state", statePath,
testFixturePath("refresh"),
}
if code := c.Run(args); code == 0 {
t.Fatal("expected error")
}
output := ui.ErrorWriter.String()
if !strings.Contains(output, "locked") {
t.Fatal("command output does not look like a lock error:", output)
}
}
func TestRefresh_badState(t *testing.T) { func TestRefresh_badState(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)