Merge pull request #17636 from hashicorp/jbardin/windows-refresh-lock
Don't open a new file descriptor for a locked state.
This commit is contained in:
commit
6b6b92f6b3
|
@ -119,8 +119,20 @@ func (s *LocalState) RefreshState() error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
if s.PathOut == "" {
|
||||||
|
s.PathOut = s.Path
|
||||||
|
}
|
||||||
|
|
||||||
var reader io.Reader
|
var reader io.Reader
|
||||||
if !s.written {
|
|
||||||
|
// The s.Path file is only OK to read if we have not written any state out
|
||||||
|
// (in which case the same state needs to be read in), and no state output file
|
||||||
|
// has been opened (possibly via a lock) or the input path is different
|
||||||
|
// than the output path.
|
||||||
|
// This is important for Windows, as if the input file is the same as the
|
||||||
|
// output file, and the output file has been locked already, we can't open
|
||||||
|
// the file again.
|
||||||
|
if !s.written && (s.stateFileOut == nil || s.Path != s.PathOut) {
|
||||||
// we haven't written a state file yet, so load from Path
|
// we haven't written a state file yet, so load from Path
|
||||||
f, err := os.Open(s.Path)
|
f, err := os.Open(s.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -166,3 +166,42 @@ func testLocalState(t *testing.T) *LocalState {
|
||||||
|
|
||||||
return ls
|
return ls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure we can refresh while the state is locked
|
||||||
|
func TestLocalState_refreshWhileLocked(t *testing.T) {
|
||||||
|
f, err := ioutil.TempFile("", "tf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = terraform.WriteState(TestStateInitial(), f)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &LocalState{Path: f.Name()}
|
||||||
|
defer os.Remove(s.Path)
|
||||||
|
|
||||||
|
// lock first
|
||||||
|
info := NewLockInfo()
|
||||||
|
info.Operation = "test"
|
||||||
|
lockID, err := s.Lock(info)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := s.Unlock(lockID); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := s.RefreshState(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
readState := s.State()
|
||||||
|
if readState == nil || readState.Lineage == "" {
|
||||||
|
t.Fatal("missing state")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -1876,13 +1877,21 @@ var ErrNoState = errors.New("no state")
|
||||||
// ReadState reads a state structure out of a reader in the format that
|
// ReadState reads a state structure out of a reader in the format that
|
||||||
// was written by WriteState.
|
// was written by WriteState.
|
||||||
func ReadState(src io.Reader) (*State, error) {
|
func ReadState(src io.Reader) (*State, error) {
|
||||||
buf := bufio.NewReader(src)
|
// check for a nil file specifically, since that produces a platform
|
||||||
if _, err := buf.Peek(1); err != nil {
|
// specific error if we try to use it in a bufio.Reader.
|
||||||
// the error is either io.EOF or "invalid argument", and both are from
|
if f, ok := src.(*os.File); ok && f == nil {
|
||||||
// an empty state.
|
|
||||||
return nil, ErrNoState
|
return nil, ErrNoState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf := bufio.NewReader(src)
|
||||||
|
|
||||||
|
if _, err := buf.Peek(1); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil, ErrNoState
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := testForV0State(buf); err != nil {
|
if err := testForV0State(buf); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue