github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/remote/remote_test.go (about)

     1  package remote
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/md5"
     6  	"encoding/base64"
     7  	"encoding/json"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"os"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/hashicorp/terraform/terraform"
    16  )
    17  
    18  func TestEnsureDirectory(t *testing.T) {
    19  	err := EnsureDirectory()
    20  	if err != nil {
    21  		t.Fatalf("Err: %v", err)
    22  	}
    23  
    24  	cwd, _ := os.Getwd()
    25  	path := filepath.Join(cwd, LocalDirectory)
    26  
    27  	_, err = os.Stat(path)
    28  	if err != nil {
    29  		t.Fatalf("err: %v", err)
    30  	}
    31  }
    32  
    33  func TestHiddenStatePath(t *testing.T) {
    34  	path, err := HiddenStatePath()
    35  	if err != nil {
    36  		t.Fatalf("err: %v", err)
    37  	}
    38  
    39  	cwd, _ := os.Getwd()
    40  	expect := filepath.Join(cwd, LocalDirectory, HiddenStateFile)
    41  
    42  	if path != expect {
    43  		t.Fatalf("bad: %v", path)
    44  	}
    45  }
    46  
    47  func TestValidConfig(t *testing.T) {
    48  	conf := &terraform.RemoteState{
    49  		Type:   "",
    50  		Config: map[string]string{},
    51  	}
    52  	if err := ValidConfig(conf); err == nil {
    53  		t.Fatalf("blank should be not be valid: %v", err)
    54  	}
    55  	conf.Config["name"] = "hashicorp/test-remote-state"
    56  	conf.Config["access_token"] = "abcd"
    57  	if err := ValidConfig(conf); err != nil {
    58  		t.Fatalf("should be valid")
    59  	}
    60  	if conf.Type != "atlas" {
    61  		t.Fatalf("should default to atlas")
    62  	}
    63  }
    64  
    65  func TestRefreshState_Init(t *testing.T) {
    66  	defer testFixCwd(testDir(t))
    67  	remote, srv := testRemote(t, nil)
    68  	defer srv.Close()
    69  
    70  	sc, err := RefreshState(remote)
    71  	if err != nil {
    72  		t.Fatalf("err: %v", err)
    73  	}
    74  
    75  	if sc != StateChangeInit {
    76  		t.Fatalf("bad: %s", sc)
    77  	}
    78  
    79  	local := testReadLocal(t)
    80  	if !local.Remote.Equals(remote) {
    81  		t.Fatalf("Bad: %#v", local)
    82  	}
    83  	if local.Serial != 1 {
    84  		t.Fatalf("Bad: %#v", local)
    85  	}
    86  }
    87  
    88  func TestRefreshState_NewVersion(t *testing.T) {
    89  	defer testFixCwd(testDir(t))
    90  
    91  	rs := terraform.NewState()
    92  	rs.Serial = 100
    93  	rs.Version = terraform.StateVersion + 1
    94  	remote, srv := testRemote(t, rs)
    95  	defer srv.Close()
    96  
    97  	local := terraform.NewState()
    98  	local.Serial = 99
    99  	testWriteLocal(t, local)
   100  
   101  	_, err := RefreshState(remote)
   102  	if err == nil {
   103  		t.Fatalf("New version should fail!")
   104  	}
   105  }
   106  
   107  func TestRefreshState_Noop(t *testing.T) {
   108  	defer testFixCwd(testDir(t))
   109  
   110  	rs := terraform.NewState()
   111  	rs.Serial = 100
   112  	remote, srv := testRemote(t, rs)
   113  	defer srv.Close()
   114  
   115  	local := terraform.NewState()
   116  	local.Serial = 100
   117  	testWriteLocal(t, local)
   118  
   119  	sc, err := RefreshState(remote)
   120  	if err != nil {
   121  		t.Fatalf("err: %v", err)
   122  	}
   123  
   124  	if sc != StateChangeNoop {
   125  		t.Fatalf("bad: %s", sc)
   126  	}
   127  }
   128  
   129  func TestRefreshState_UpdateLocal(t *testing.T) {
   130  	defer testFixCwd(testDir(t))
   131  
   132  	rs := terraform.NewState()
   133  	rs.Serial = 100
   134  	remote, srv := testRemote(t, rs)
   135  	defer srv.Close()
   136  
   137  	local := terraform.NewState()
   138  	local.Serial = 99
   139  	testWriteLocal(t, local)
   140  
   141  	sc, err := RefreshState(remote)
   142  	if err != nil {
   143  		t.Fatalf("err: %v", err)
   144  	}
   145  
   146  	if sc != StateChangeUpdateLocal {
   147  		t.Fatalf("bad: %s", sc)
   148  	}
   149  
   150  	// Should update
   151  	local2 := testReadLocal(t)
   152  	if local2.Serial != 100 {
   153  		t.Fatalf("Bad: %#v", local2)
   154  	}
   155  }
   156  
   157  func TestRefreshState_LocalNewer(t *testing.T) {
   158  	defer testFixCwd(testDir(t))
   159  
   160  	rs := terraform.NewState()
   161  	rs.Serial = 99
   162  	remote, srv := testRemote(t, rs)
   163  	defer srv.Close()
   164  
   165  	local := terraform.NewState()
   166  	local.Serial = 100
   167  	testWriteLocal(t, local)
   168  
   169  	sc, err := RefreshState(remote)
   170  	if err != nil {
   171  		t.Fatalf("err: %v", err)
   172  	}
   173  
   174  	if sc != StateChangeLocalNewer {
   175  		t.Fatalf("bad: %s", sc)
   176  	}
   177  }
   178  
   179  func TestRefreshState_Conflict(t *testing.T) {
   180  	defer testFixCwd(testDir(t))
   181  
   182  	rs := terraform.NewState()
   183  	rs.Serial = 50
   184  	rs.RootModule().Outputs["foo"] = "bar"
   185  	remote, srv := testRemote(t, rs)
   186  	defer srv.Close()
   187  
   188  	local := terraform.NewState()
   189  	local.Serial = 50
   190  	local.RootModule().Outputs["foo"] = "baz"
   191  	testWriteLocal(t, local)
   192  
   193  	sc, err := RefreshState(remote)
   194  	if err != nil {
   195  		t.Fatalf("err: %v", err)
   196  	}
   197  
   198  	if sc != StateChangeConflict {
   199  		t.Fatalf("bad: %s", sc)
   200  	}
   201  }
   202  
   203  func TestPushState_NoState(t *testing.T) {
   204  	defer testFixCwd(testDir(t))
   205  
   206  	remote, srv := testRemotePush(t, 200)
   207  	defer srv.Close()
   208  
   209  	sc, err := PushState(remote, false)
   210  	if err.Error() != "No local state to push" {
   211  		t.Fatalf("err: %v", err)
   212  	}
   213  	if sc != StateChangeNoop {
   214  		t.Fatalf("Bad: %v", sc)
   215  	}
   216  }
   217  
   218  func TestPushState_Update(t *testing.T) {
   219  	defer testFixCwd(testDir(t))
   220  
   221  	remote, srv := testRemotePush(t, 200)
   222  	defer srv.Close()
   223  
   224  	local := terraform.NewState()
   225  	testWriteLocal(t, local)
   226  
   227  	sc, err := PushState(remote, false)
   228  	if err != nil {
   229  		t.Fatalf("err: %v", err)
   230  	}
   231  	if sc != StateChangeUpdateRemote {
   232  		t.Fatalf("Bad: %v", sc)
   233  	}
   234  }
   235  
   236  func TestPushState_RemoteNewer(t *testing.T) {
   237  	defer testFixCwd(testDir(t))
   238  
   239  	remote, srv := testRemotePush(t, 412)
   240  	defer srv.Close()
   241  
   242  	local := terraform.NewState()
   243  	testWriteLocal(t, local)
   244  
   245  	sc, err := PushState(remote, false)
   246  	if err != nil {
   247  		t.Fatalf("err: %v", err)
   248  	}
   249  	if sc != StateChangeRemoteNewer {
   250  		t.Fatalf("Bad: %v", sc)
   251  	}
   252  }
   253  
   254  func TestPushState_Conflict(t *testing.T) {
   255  	defer testFixCwd(testDir(t))
   256  
   257  	remote, srv := testRemotePush(t, 409)
   258  	defer srv.Close()
   259  
   260  	local := terraform.NewState()
   261  	testWriteLocal(t, local)
   262  
   263  	sc, err := PushState(remote, false)
   264  	if err != nil {
   265  		t.Fatalf("err: %v", err)
   266  	}
   267  	if sc != StateChangeConflict {
   268  		t.Fatalf("Bad: %v", sc)
   269  	}
   270  }
   271  
   272  func TestPushState_Error(t *testing.T) {
   273  	defer testFixCwd(testDir(t))
   274  
   275  	remote, srv := testRemotePush(t, 500)
   276  	defer srv.Close()
   277  
   278  	local := terraform.NewState()
   279  	testWriteLocal(t, local)
   280  
   281  	sc, err := PushState(remote, false)
   282  	if err != ErrRemoteInternal {
   283  		t.Fatalf("err: %v", err)
   284  	}
   285  	if sc != StateChangeNoop {
   286  		t.Fatalf("Bad: %v", sc)
   287  	}
   288  }
   289  
   290  func TestDeleteState(t *testing.T) {
   291  	defer testFixCwd(testDir(t))
   292  
   293  	remote, srv := testRemotePush(t, 200)
   294  	defer srv.Close()
   295  
   296  	local := terraform.NewState()
   297  	testWriteLocal(t, local)
   298  
   299  	err := DeleteState(remote)
   300  	if err != nil {
   301  		t.Fatalf("err: %v", err)
   302  	}
   303  }
   304  
   305  func TestBlankState(t *testing.T) {
   306  	remote := &terraform.RemoteState{
   307  		Type: "http",
   308  		Config: map[string]string{
   309  			"address": "http://foo.com/",
   310  		},
   311  	}
   312  	r, err := blankState(remote)
   313  	if err != nil {
   314  		t.Fatalf("err: %v", err)
   315  	}
   316  	s, err := terraform.ReadState(bytes.NewReader(r))
   317  	if err != nil {
   318  		t.Fatalf("err: %v", err)
   319  	}
   320  	if !remote.Equals(s.Remote) {
   321  		t.Fatalf("remote mismatch")
   322  	}
   323  }
   324  
   325  func TestPersist(t *testing.T) {
   326  	tmp, cwd := testDir(t)
   327  	defer testFixCwd(tmp, cwd)
   328  
   329  	EnsureDirectory()
   330  
   331  	// Place old state file, should backup
   332  	old := filepath.Join(tmp, LocalDirectory, HiddenStateFile)
   333  	ioutil.WriteFile(old, []byte("test"), 0777)
   334  
   335  	remote := &terraform.RemoteState{
   336  		Type: "http",
   337  		Config: map[string]string{
   338  			"address": "http://foo.com/",
   339  		},
   340  	}
   341  	blank, _ := blankState(remote)
   342  	if err := Persist(bytes.NewReader(blank)); err != nil {
   343  		t.Fatalf("err: %v", err)
   344  	}
   345  
   346  	// Check for backup
   347  	backup := filepath.Join(tmp, LocalDirectory, BackupHiddenStateFile)
   348  	out, err := ioutil.ReadFile(backup)
   349  	if err != nil {
   350  		t.Fatalf("Err: %v", err)
   351  	}
   352  	if string(out) != "test" {
   353  		t.Fatalf("bad: %v", out)
   354  	}
   355  
   356  	// Read the state
   357  	out, err = ioutil.ReadFile(old)
   358  	if err != nil {
   359  		t.Fatalf("Err: %v", err)
   360  	}
   361  	s, err := terraform.ReadState(bytes.NewReader(out))
   362  	if err != nil {
   363  		t.Fatalf("Err: %v", err)
   364  	}
   365  
   366  	// Check the remote
   367  	if !remote.Equals(s.Remote) {
   368  		t.Fatalf("remote mismatch")
   369  	}
   370  }
   371  
   372  // testRemote is used to make a test HTTP server to
   373  // return a given state file
   374  func testRemote(t *testing.T, s *terraform.State) (*terraform.RemoteState, *httptest.Server) {
   375  	var b64md5 string
   376  	buf := bytes.NewBuffer(nil)
   377  
   378  	if s != nil {
   379  		enc := json.NewEncoder(buf)
   380  		if err := enc.Encode(s); err != nil {
   381  			t.Fatalf("err: %v", err)
   382  		}
   383  		md5 := md5.Sum(buf.Bytes())
   384  		b64md5 = base64.StdEncoding.EncodeToString(md5[:16])
   385  	}
   386  
   387  	cb := func(resp http.ResponseWriter, req *http.Request) {
   388  		if s == nil {
   389  			resp.WriteHeader(404)
   390  			return
   391  		}
   392  		resp.Header().Set("Content-MD5", b64md5)
   393  		resp.Write(buf.Bytes())
   394  	}
   395  	srv := httptest.NewServer(http.HandlerFunc(cb))
   396  	remote := &terraform.RemoteState{
   397  		Type: "http",
   398  		Config: map[string]string{
   399  			"address": srv.URL,
   400  		},
   401  	}
   402  	return remote, srv
   403  }
   404  
   405  // testRemotePush is used to make a test HTTP server to
   406  // return a given status code on push
   407  func testRemotePush(t *testing.T, c int) (*terraform.RemoteState, *httptest.Server) {
   408  	cb := func(resp http.ResponseWriter, req *http.Request) {
   409  		resp.WriteHeader(c)
   410  	}
   411  	srv := httptest.NewServer(http.HandlerFunc(cb))
   412  	remote := &terraform.RemoteState{
   413  		Type: "http",
   414  		Config: map[string]string{
   415  			"address": srv.URL,
   416  		},
   417  	}
   418  	return remote, srv
   419  }
   420  
   421  // testDir is used to change the current working directory
   422  // into a test directory that should be remoted after
   423  func testDir(t *testing.T) (string, string) {
   424  	tmp, err := ioutil.TempDir("", "remote")
   425  	if err != nil {
   426  		t.Fatalf("err: %v", err)
   427  	}
   428  	cwd, err := os.Getwd()
   429  	if err != nil {
   430  		t.Fatalf("err: %v", err)
   431  	}
   432  	os.Chdir(tmp)
   433  	if err := EnsureDirectory(); err != nil {
   434  		t.Fatalf("err: %v", err)
   435  	}
   436  	return tmp, cwd
   437  }
   438  
   439  // testFixCwd is used to as a defer to testDir
   440  func testFixCwd(tmp, cwd string) {
   441  	os.Chdir(cwd)
   442  	os.RemoveAll(tmp)
   443  }
   444  
   445  // testReadLocal is used to just get the local state
   446  func testReadLocal(t *testing.T) *terraform.State {
   447  	path, err := HiddenStatePath()
   448  	if err != nil {
   449  		t.Fatalf("err: %v", err)
   450  	}
   451  	raw, err := ioutil.ReadFile(path)
   452  	if err != nil && !os.IsNotExist(err) {
   453  		t.Fatalf("err: %v", err)
   454  	}
   455  	if raw == nil {
   456  		return nil
   457  	}
   458  	s, err := terraform.ReadState(bytes.NewReader(raw))
   459  	if err != nil {
   460  		t.Fatalf("err: %v", err)
   461  	}
   462  	return s
   463  }
   464  
   465  // testWriteLocal is used to write the local state
   466  func testWriteLocal(t *testing.T, s *terraform.State) {
   467  	path, err := HiddenStatePath()
   468  	if err != nil {
   469  		t.Fatalf("err: %v", err)
   470  	}
   471  	buf := bytes.NewBuffer(nil)
   472  	enc := json.NewEncoder(buf)
   473  	if err := enc.Encode(s); err != nil {
   474  		t.Fatalf("err: %v", err)
   475  	}
   476  	err = ioutil.WriteFile(path, buf.Bytes(), 0777)
   477  	if err != nil {
   478  		t.Fatalf("err: %v", err)
   479  	}
   480  }