2014-12-04 05:05:29 +01:00
|
|
|
package remote
|
|
|
|
|
2014-12-05 01:53:18 +01:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/md5"
|
|
|
|
"encoding/base64"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
)
|
2014-12-04 05:05:29 +01:00
|
|
|
|
|
|
|
func TestHTTPRemote_Interface(t *testing.T) {
|
|
|
|
var client interface{} = &HTTPRemoteClient{}
|
|
|
|
if _, ok := client.(RemoteClient); !ok {
|
|
|
|
t.Fatalf("does not implement interface")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-05 01:53:18 +01:00
|
|
|
func TestHTTPRemote_GetState(t *testing.T) {
|
|
|
|
type tcase struct {
|
|
|
|
Code int
|
|
|
|
Header http.Header
|
|
|
|
Body []byte
|
|
|
|
ExpectMD5 []byte
|
|
|
|
ExpectErr string
|
|
|
|
}
|
|
|
|
inp := []byte("testing")
|
|
|
|
inpMD5 := md5.Sum(inp)
|
|
|
|
hash := inpMD5[:16]
|
|
|
|
cases := []*tcase{
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusOK,
|
|
|
|
Body: inp,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusNoContent,
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusNotFound,
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusInternalServerError,
|
|
|
|
ExpectErr: "Remote server reporting internal error",
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: 418,
|
|
|
|
ExpectErr: "Unexpected HTTP response code 418",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
cb := func(resp http.ResponseWriter, req *http.Request) {
|
|
|
|
for k, v := range tc.Header {
|
|
|
|
resp.Header()[k] = v
|
|
|
|
}
|
|
|
|
resp.WriteHeader(tc.Code)
|
|
|
|
if tc.Body != nil {
|
|
|
|
resp.Write(tc.Body)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s := httptest.NewServer(http.HandlerFunc(cb))
|
|
|
|
defer s.Close()
|
|
|
|
|
|
|
|
remote := &terraform.RemoteState{
|
|
|
|
Type: "http",
|
|
|
|
Config: map[string]string{
|
|
|
|
"url": s.URL,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
r, err := NewClientByState(remote)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
payload, err := r.GetState()
|
|
|
|
errStr := ""
|
|
|
|
if err != nil {
|
|
|
|
errStr = err.Error()
|
|
|
|
}
|
|
|
|
if errStr != tc.ExpectErr {
|
|
|
|
t.Fatalf("bad err: %v %v", errStr, tc.ExpectErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
if tc.ExpectMD5 != nil {
|
|
|
|
if payload == nil || !bytes.Equal(payload.MD5, tc.ExpectMD5) {
|
|
|
|
t.Fatalf("bad: %#v", payload)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if tc.Body != nil {
|
|
|
|
if !bytes.Equal(payload.State, tc.Body) {
|
|
|
|
t.Fatalf("bad: %#v", payload)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestHTTPRemote_PutState(t *testing.T) {
|
|
|
|
type tcase struct {
|
|
|
|
Code int
|
|
|
|
Path string
|
|
|
|
Header http.Header
|
|
|
|
Body []byte
|
|
|
|
ExpectMD5 []byte
|
|
|
|
Force bool
|
|
|
|
ExpectErr string
|
|
|
|
}
|
|
|
|
inp := []byte("testing")
|
|
|
|
inpMD5 := md5.Sum(inp)
|
|
|
|
hash := inpMD5[:16]
|
|
|
|
cases := []*tcase{
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusOK,
|
|
|
|
Path: "/foobar",
|
|
|
|
Body: inp,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusOK,
|
|
|
|
Path: "/foobar?force=true",
|
|
|
|
Body: inp,
|
|
|
|
Force: true,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusConflict,
|
|
|
|
Path: "/foobar",
|
|
|
|
Body: inp,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
ExpectErr: ErrConflict.Error(),
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusPreconditionFailed,
|
|
|
|
Path: "/foobar",
|
|
|
|
Body: inp,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
ExpectErr: ErrServerNewer.Error(),
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusUnauthorized,
|
|
|
|
Path: "/foobar",
|
|
|
|
Body: inp,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
ExpectErr: ErrRequireAuth.Error(),
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusForbidden,
|
|
|
|
Path: "/foobar",
|
|
|
|
Body: inp,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
ExpectErr: ErrInvalidAuth.Error(),
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusInternalServerError,
|
|
|
|
Path: "/foobar",
|
|
|
|
Body: inp,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
ExpectErr: ErrRemoteInternal.Error(),
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: 418,
|
|
|
|
Path: "/foobar",
|
|
|
|
Body: inp,
|
|
|
|
ExpectMD5: hash,
|
|
|
|
ExpectErr: "Unexpected HTTP response code 418",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
cb := func(resp http.ResponseWriter, req *http.Request) {
|
|
|
|
for k, v := range tc.Header {
|
|
|
|
resp.Header()[k] = v
|
|
|
|
}
|
|
|
|
resp.WriteHeader(tc.Code)
|
|
|
|
|
|
|
|
// Verify the body
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
io.Copy(buf, req.Body)
|
|
|
|
if !bytes.Equal(buf.Bytes(), tc.Body) {
|
|
|
|
t.Fatalf("bad body: %v", buf.Bytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the path
|
|
|
|
req.URL.Host = ""
|
|
|
|
if req.URL.String() != tc.Path {
|
|
|
|
t.Fatalf("Bad path: %v %v", req.URL.String(), tc.Path)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the content length
|
|
|
|
if req.ContentLength != int64(len(tc.Body)) {
|
|
|
|
t.Fatalf("bad content length: %d", req.ContentLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the Content-MD5
|
|
|
|
b64 := req.Header.Get("Content-MD5")
|
|
|
|
raw, _ := base64.StdEncoding.DecodeString(b64)
|
|
|
|
if !bytes.Equal(raw, tc.ExpectMD5) {
|
|
|
|
t.Fatalf("bad md5: %v", raw)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s := httptest.NewServer(http.HandlerFunc(cb))
|
|
|
|
defer s.Close()
|
|
|
|
|
|
|
|
remote := &terraform.RemoteState{
|
|
|
|
Type: "http",
|
|
|
|
Config: map[string]string{
|
|
|
|
"url": s.URL + "/foobar",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
r, err := NewClientByState(remote)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = r.PutState(tc.Body, tc.Force)
|
|
|
|
errStr := ""
|
|
|
|
if err != nil {
|
|
|
|
errStr = err.Error()
|
|
|
|
}
|
|
|
|
if errStr != tc.ExpectErr {
|
|
|
|
t.Fatalf("bad err: %v %v", errStr, tc.ExpectErr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestHTTPRemote_DeleteState(t *testing.T) {
|
|
|
|
type tcase struct {
|
|
|
|
Code int
|
|
|
|
Path string
|
|
|
|
Header http.Header
|
|
|
|
ExpectErr string
|
|
|
|
}
|
|
|
|
cases := []*tcase{
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusOK,
|
|
|
|
Path: "/foobar",
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusNoContent,
|
|
|
|
Path: "/foobar",
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusNotFound,
|
|
|
|
Path: "/foobar",
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusUnauthorized,
|
|
|
|
Path: "/foobar",
|
|
|
|
ExpectErr: ErrRequireAuth.Error(),
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusForbidden,
|
|
|
|
Path: "/foobar",
|
|
|
|
ExpectErr: ErrInvalidAuth.Error(),
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: http.StatusInternalServerError,
|
|
|
|
Path: "/foobar",
|
|
|
|
ExpectErr: ErrRemoteInternal.Error(),
|
|
|
|
},
|
|
|
|
&tcase{
|
|
|
|
Code: 418,
|
|
|
|
Path: "/foobar",
|
|
|
|
ExpectErr: "Unexpected HTTP response code 418",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
cb := func(resp http.ResponseWriter, req *http.Request) {
|
|
|
|
for k, v := range tc.Header {
|
|
|
|
resp.Header()[k] = v
|
|
|
|
}
|
|
|
|
resp.WriteHeader(tc.Code)
|
|
|
|
|
|
|
|
// Verify the path
|
|
|
|
req.URL.Host = ""
|
|
|
|
if req.URL.String() != tc.Path {
|
|
|
|
t.Fatalf("Bad path: %v %v", req.URL.String(), tc.Path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s := httptest.NewServer(http.HandlerFunc(cb))
|
|
|
|
defer s.Close()
|
|
|
|
|
|
|
|
remote := &terraform.RemoteState{
|
|
|
|
Type: "http",
|
|
|
|
Config: map[string]string{
|
|
|
|
"url": s.URL + "/foobar",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
r, err := NewClientByState(remote)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = r.DeleteState()
|
|
|
|
errStr := ""
|
|
|
|
if err != nil {
|
|
|
|
errStr = err.Error()
|
|
|
|
}
|
|
|
|
if errStr != tc.ExpectErr {
|
|
|
|
t.Fatalf("bad err: %v %v", errStr, tc.ExpectErr)
|
|
|
|
}
|
|
|
|
}
|
2014-12-04 05:05:29 +01:00
|
|
|
}
|