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

     1  package remote
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/md5"
     6  	"encoding/base64"
     7  	"io"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"testing"
    11  
    12  	"github.com/hashicorp/terraform/terraform"
    13  )
    14  
    15  func TestHTTPRemote_Interface(t *testing.T) {
    16  	var client interface{} = &HTTPRemoteClient{}
    17  	if _, ok := client.(RemoteClient); !ok {
    18  		t.Fatalf("does not implement interface")
    19  	}
    20  }
    21  
    22  func TestHTTPRemote_Validate(t *testing.T) {
    23  	conf := map[string]string{}
    24  	if _, err := NewHTTPRemoteClient(conf); err == nil {
    25  		t.Fatalf("expect error")
    26  	}
    27  
    28  	conf["address"] = ""
    29  	if _, err := NewHTTPRemoteClient(conf); err == nil {
    30  		t.Fatalf("expect error")
    31  	}
    32  
    33  	conf["address"] = "*"
    34  	if _, err := NewHTTPRemoteClient(conf); err == nil {
    35  		t.Fatalf("expect error")
    36  	}
    37  
    38  	conf["address"] = "http://cool.com"
    39  	if _, err := NewHTTPRemoteClient(conf); err != nil {
    40  		t.Fatalf("err: %v", err)
    41  	}
    42  }
    43  
    44  func TestHTTPRemote_GetState(t *testing.T) {
    45  	type tcase struct {
    46  		Code      int
    47  		Header    http.Header
    48  		Body      []byte
    49  		ExpectMD5 []byte
    50  		ExpectErr string
    51  	}
    52  	inp := []byte("testing")
    53  	inpMD5 := md5.Sum(inp)
    54  	hash := inpMD5[:16]
    55  	cases := []*tcase{
    56  		&tcase{
    57  			Code:      http.StatusOK,
    58  			Body:      inp,
    59  			ExpectMD5: hash,
    60  		},
    61  		&tcase{
    62  			Code: http.StatusNoContent,
    63  		},
    64  		&tcase{
    65  			Code: http.StatusNotFound,
    66  		},
    67  		&tcase{
    68  			Code:      http.StatusInternalServerError,
    69  			ExpectErr: "Remote server reporting internal error",
    70  		},
    71  		&tcase{
    72  			Code:      418,
    73  			ExpectErr: "Unexpected HTTP response code 418",
    74  		},
    75  	}
    76  
    77  	for _, tc := range cases {
    78  		cb := func(resp http.ResponseWriter, req *http.Request) {
    79  			for k, v := range tc.Header {
    80  				resp.Header()[k] = v
    81  			}
    82  			resp.WriteHeader(tc.Code)
    83  			if tc.Body != nil {
    84  				resp.Write(tc.Body)
    85  			}
    86  		}
    87  		s := httptest.NewServer(http.HandlerFunc(cb))
    88  		defer s.Close()
    89  
    90  		remote := &terraform.RemoteState{
    91  			Type: "http",
    92  			Config: map[string]string{
    93  				"address": s.URL,
    94  			},
    95  		}
    96  		r, err := NewClientByState(remote)
    97  		if err != nil {
    98  			t.Fatalf("Err: %v", err)
    99  		}
   100  
   101  		payload, err := r.GetState()
   102  		errStr := ""
   103  		if err != nil {
   104  			errStr = err.Error()
   105  		}
   106  		if errStr != tc.ExpectErr {
   107  			t.Fatalf("bad err: %v %v", errStr, tc.ExpectErr)
   108  		}
   109  
   110  		if tc.ExpectMD5 != nil {
   111  			if payload == nil || !bytes.Equal(payload.MD5, tc.ExpectMD5) {
   112  				t.Fatalf("bad: %#v", payload)
   113  			}
   114  		}
   115  
   116  		if tc.Body != nil {
   117  			if !bytes.Equal(payload.State, tc.Body) {
   118  				t.Fatalf("bad: %#v", payload)
   119  			}
   120  		}
   121  	}
   122  
   123  }
   124  
   125  func TestHTTPRemote_PutState(t *testing.T) {
   126  	type tcase struct {
   127  		Code      int
   128  		Path      string
   129  		Header    http.Header
   130  		Body      []byte
   131  		ExpectMD5 []byte
   132  		Force     bool
   133  		ExpectErr string
   134  	}
   135  	inp := []byte("testing")
   136  	inpMD5 := md5.Sum(inp)
   137  	hash := inpMD5[:16]
   138  	cases := []*tcase{
   139  		&tcase{
   140  			Code:      http.StatusOK,
   141  			Path:      "/foobar",
   142  			Body:      inp,
   143  			ExpectMD5: hash,
   144  		},
   145  		&tcase{
   146  			Code:      http.StatusOK,
   147  			Path:      "/foobar?force=true",
   148  			Body:      inp,
   149  			Force:     true,
   150  			ExpectMD5: hash,
   151  		},
   152  		&tcase{
   153  			Code:      http.StatusConflict,
   154  			Path:      "/foobar",
   155  			Body:      inp,
   156  			ExpectMD5: hash,
   157  			ExpectErr: ErrConflict.Error(),
   158  		},
   159  		&tcase{
   160  			Code:      http.StatusPreconditionFailed,
   161  			Path:      "/foobar",
   162  			Body:      inp,
   163  			ExpectMD5: hash,
   164  			ExpectErr: ErrServerNewer.Error(),
   165  		},
   166  		&tcase{
   167  			Code:      http.StatusUnauthorized,
   168  			Path:      "/foobar",
   169  			Body:      inp,
   170  			ExpectMD5: hash,
   171  			ExpectErr: ErrRequireAuth.Error(),
   172  		},
   173  		&tcase{
   174  			Code:      http.StatusForbidden,
   175  			Path:      "/foobar",
   176  			Body:      inp,
   177  			ExpectMD5: hash,
   178  			ExpectErr: ErrInvalidAuth.Error(),
   179  		},
   180  		&tcase{
   181  			Code:      http.StatusInternalServerError,
   182  			Path:      "/foobar",
   183  			Body:      inp,
   184  			ExpectMD5: hash,
   185  			ExpectErr: ErrRemoteInternal.Error(),
   186  		},
   187  		&tcase{
   188  			Code:      418,
   189  			Path:      "/foobar",
   190  			Body:      inp,
   191  			ExpectMD5: hash,
   192  			ExpectErr: "Unexpected HTTP response code 418",
   193  		},
   194  	}
   195  
   196  	for _, tc := range cases {
   197  		cb := func(resp http.ResponseWriter, req *http.Request) {
   198  			for k, v := range tc.Header {
   199  				resp.Header()[k] = v
   200  			}
   201  			resp.WriteHeader(tc.Code)
   202  
   203  			// Verify the body
   204  			buf := bytes.NewBuffer(nil)
   205  			io.Copy(buf, req.Body)
   206  			if !bytes.Equal(buf.Bytes(), tc.Body) {
   207  				t.Fatalf("bad body: %v", buf.Bytes())
   208  			}
   209  
   210  			// Verify the path
   211  			req.URL.Host = ""
   212  			if req.URL.String() != tc.Path {
   213  				t.Fatalf("Bad path: %v %v", req.URL.String(), tc.Path)
   214  			}
   215  
   216  			// Verify the content length
   217  			if req.ContentLength != int64(len(tc.Body)) {
   218  				t.Fatalf("bad content length: %d", req.ContentLength)
   219  			}
   220  
   221  			// Verify the Content-MD5
   222  			b64 := req.Header.Get("Content-MD5")
   223  			raw, _ := base64.StdEncoding.DecodeString(b64)
   224  			if !bytes.Equal(raw, tc.ExpectMD5) {
   225  				t.Fatalf("bad md5: %v", raw)
   226  			}
   227  		}
   228  		s := httptest.NewServer(http.HandlerFunc(cb))
   229  		defer s.Close()
   230  
   231  		remote := &terraform.RemoteState{
   232  			Type: "http",
   233  			Config: map[string]string{
   234  				"address": s.URL + "/foobar",
   235  			},
   236  		}
   237  		r, err := NewClientByState(remote)
   238  		if err != nil {
   239  			t.Fatalf("Err: %v", err)
   240  		}
   241  
   242  		err = r.PutState(tc.Body, tc.Force)
   243  		errStr := ""
   244  		if err != nil {
   245  			errStr = err.Error()
   246  		}
   247  		if errStr != tc.ExpectErr {
   248  			t.Fatalf("bad err: %v %v", errStr, tc.ExpectErr)
   249  		}
   250  	}
   251  }
   252  
   253  func TestHTTPRemote_DeleteState(t *testing.T) {
   254  	type tcase struct {
   255  		Code      int
   256  		Path      string
   257  		Header    http.Header
   258  		ExpectErr string
   259  	}
   260  	cases := []*tcase{
   261  		&tcase{
   262  			Code: http.StatusOK,
   263  			Path: "/foobar",
   264  		},
   265  		&tcase{
   266  			Code: http.StatusNoContent,
   267  			Path: "/foobar",
   268  		},
   269  		&tcase{
   270  			Code: http.StatusNotFound,
   271  			Path: "/foobar",
   272  		},
   273  		&tcase{
   274  			Code:      http.StatusUnauthorized,
   275  			Path:      "/foobar",
   276  			ExpectErr: ErrRequireAuth.Error(),
   277  		},
   278  		&tcase{
   279  			Code:      http.StatusForbidden,
   280  			Path:      "/foobar",
   281  			ExpectErr: ErrInvalidAuth.Error(),
   282  		},
   283  		&tcase{
   284  			Code:      http.StatusInternalServerError,
   285  			Path:      "/foobar",
   286  			ExpectErr: ErrRemoteInternal.Error(),
   287  		},
   288  		&tcase{
   289  			Code:      418,
   290  			Path:      "/foobar",
   291  			ExpectErr: "Unexpected HTTP response code 418",
   292  		},
   293  	}
   294  
   295  	for _, tc := range cases {
   296  		cb := func(resp http.ResponseWriter, req *http.Request) {
   297  			for k, v := range tc.Header {
   298  				resp.Header()[k] = v
   299  			}
   300  			resp.WriteHeader(tc.Code)
   301  
   302  			// Verify the path
   303  			req.URL.Host = ""
   304  			if req.URL.String() != tc.Path {
   305  				t.Fatalf("Bad path: %v %v", req.URL.String(), tc.Path)
   306  			}
   307  		}
   308  		s := httptest.NewServer(http.HandlerFunc(cb))
   309  		defer s.Close()
   310  
   311  		remote := &terraform.RemoteState{
   312  			Type: "http",
   313  			Config: map[string]string{
   314  				"address": s.URL + "/foobar",
   315  			},
   316  		}
   317  		r, err := NewClientByState(remote)
   318  		if err != nil {
   319  			t.Fatalf("Err: %v", err)
   320  		}
   321  
   322  		err = r.DeleteState()
   323  		errStr := ""
   324  		if err != nil {
   325  			errStr = err.Error()
   326  		}
   327  		if errStr != tc.ExpectErr {
   328  			t.Fatalf("bad err: %v %v", errStr, tc.ExpectErr)
   329  		}
   330  	}
   331  }