Allow refresh of local state with no resources
This commit is contained in:
parent
0857b13cb9
commit
ff80e7b245
|
@ -64,8 +64,13 @@ func (s *CacheState) RefreshState() error {
|
||||||
// Cache is newer than remote. Not a big deal, user can just
|
// Cache is newer than remote. Not a big deal, user can just
|
||||||
// persist to get correct state.
|
// persist to get correct state.
|
||||||
s.refreshResult = CacheRefreshLocalNewer
|
s.refreshResult = CacheRefreshLocalNewer
|
||||||
case cached == nil && durable != nil:
|
case !cached.HasResources() && durable != nil:
|
||||||
// Cache should be updated since the remote is set but cache isn't
|
// Cache should be updated since the remote is set but cache isn't
|
||||||
|
//
|
||||||
|
// If local is empty then we'll treat it as missing so that
|
||||||
|
// it can be overwritten by an already-existing remote. This
|
||||||
|
// allows the user to activate remote state for the first time
|
||||||
|
// against an already-existing remote state.
|
||||||
s.refreshResult = CacheRefreshUpdateLocal
|
s.refreshResult = CacheRefreshUpdateLocal
|
||||||
case durable.Serial < cached.Serial:
|
case durable.Serial < cached.Serial:
|
||||||
// Cache is newer than remote. Not a big deal, user can just
|
// Cache is newer than remote. Not a big deal, user can just
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCacheState(t *testing.T) {
|
func TestCacheState(t *testing.T) {
|
||||||
|
@ -50,6 +52,56 @@ func TestCacheState_persistDurable(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCacheState_RefreshState(t *testing.T) {
|
||||||
|
for i, test := range []struct {
|
||||||
|
cacheModules []*terraform.ModuleState
|
||||||
|
expected CacheRefreshResult
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cacheModules: nil,
|
||||||
|
expected: CacheRefreshUpdateLocal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cacheModules: []*terraform.ModuleState{},
|
||||||
|
expected: CacheRefreshUpdateLocal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cacheModules: []*terraform.ModuleState{
|
||||||
|
&terraform.ModuleState{
|
||||||
|
Resources: map[string]*terraform.ResourceState{
|
||||||
|
"foo.foo": &terraform.ResourceState{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: CacheRefreshLocalNewer,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
cache := testLocalState(t)
|
||||||
|
durable := testLocalState(t)
|
||||||
|
defer os.Remove(cache.Path)
|
||||||
|
defer os.Remove(durable.Path)
|
||||||
|
|
||||||
|
cs := &CacheState{
|
||||||
|
Cache: cache,
|
||||||
|
Durable: durable,
|
||||||
|
}
|
||||||
|
|
||||||
|
state := cache.State()
|
||||||
|
state.Modules = test.cacheModules
|
||||||
|
if err := cs.WriteState(state); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cs.RefreshState(); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs.RefreshResult() != test.expected {
|
||||||
|
t.Fatalf("bad %d: %v", i, cs.RefreshResult())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCacheState_impl(t *testing.T) {
|
func TestCacheState_impl(t *testing.T) {
|
||||||
var _ StateReader = new(CacheState)
|
var _ StateReader = new(CacheState)
|
||||||
var _ StateWriter = new(CacheState)
|
var _ StateWriter = new(CacheState)
|
||||||
|
|
|
@ -220,6 +220,24 @@ func (s *State) Empty() bool {
|
||||||
return len(s.Modules) == 0
|
return len(s.Modules) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasResources returns true if the state contains any resources.
|
||||||
|
//
|
||||||
|
// This is similar to !s.Empty, but returns true also in the case where the
|
||||||
|
// state has modules but all of them are devoid of resources.
|
||||||
|
func (s *State) HasResources() bool {
|
||||||
|
if s.Empty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mod := range s.Modules {
|
||||||
|
if len(mod.Resources) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// IsRemote returns true if State represents a state that exists and is
|
// IsRemote returns true if State represents a state that exists and is
|
||||||
// remote.
|
// remote.
|
||||||
func (s *State) IsRemote() bool {
|
func (s *State) IsRemote() bool {
|
||||||
|
|
|
@ -1136,6 +1136,64 @@ func TestStateEmpty(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateHasResources(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
In *State
|
||||||
|
Result bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&State{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&State{
|
||||||
|
Remote: &RemoteState{Type: "foo"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{},
|
||||||
|
&ModuleState{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{},
|
||||||
|
&ModuleState{
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"foo.foo": &ResourceState{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
if tc.In.HasResources() != tc.Result {
|
||||||
|
t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStateFromFutureTerraform(t *testing.T) {
|
func TestStateFromFutureTerraform(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
In string
|
In string
|
||||||
|
|
Loading…
Reference in New Issue