github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/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 }