github.com/hs0210/hashicorp-terraform@v0.11.12-beta1/svchost/disco/host_test.go (about) 1 package disco 2 3 import ( 4 "fmt" 5 "net/http" 6 "net/http/httptest" 7 "net/url" 8 "os" 9 "path" 10 "reflect" 11 "strconv" 12 "strings" 13 "testing" 14 ) 15 16 func TestHostServiceURL(t *testing.T) { 17 baseURL, _ := url.Parse("https://example.com/disco/foo.json") 18 host := Host{ 19 discoURL: baseURL, 20 hostname: "test-server", 21 services: map[string]interface{}{ 22 "absolute.v1": "http://example.net/foo/bar", 23 "absolutewithport.v1": "http://example.net:8080/foo/bar", 24 "relative.v1": "./stu/", 25 "rootrelative.v1": "/baz", 26 "protorelative.v1": "//example.net/", 27 "withfragment.v1": "http://example.org/#foo", 28 "querystring.v1": "https://example.net/baz?foo=bar", 29 "nothttp.v1": "ftp://127.0.0.1/pub/", 30 "invalid.v1": "***not A URL at all!:/<@@@@>***", 31 }, 32 } 33 34 tests := []struct { 35 ID string 36 want string 37 err string 38 }{ 39 {"absolute.v1", "http://example.net/foo/bar", ""}, 40 {"absolutewithport.v1", "http://example.net:8080/foo/bar", ""}, 41 {"relative.v1", "https://example.com/disco/stu/", ""}, 42 {"rootrelative.v1", "https://example.com/baz", ""}, 43 {"protorelative.v1", "https://example.net/", ""}, 44 {"withfragment.v1", "http://example.org/", ""}, 45 {"querystring.v1", "https://example.net/baz?foo=bar", ""}, 46 {"nothttp.v1", "<nil>", "unsupported scheme"}, 47 {"invalid.v1", "<nil>", "Failed to parse service URL"}, 48 } 49 50 for _, test := range tests { 51 t.Run(test.ID, func(t *testing.T) { 52 url, err := host.ServiceURL(test.ID) 53 if (err != nil || test.err != "") && 54 (err == nil || !strings.Contains(err.Error(), test.err)) { 55 t.Fatalf("unexpected service URL error: %s", err) 56 } 57 58 var got string 59 if url != nil { 60 got = url.String() 61 } else { 62 got = "<nil>" 63 } 64 65 if got != test.want { 66 t.Errorf("wrong result\ngot: %s\nwant: %s", got, test.want) 67 } 68 }) 69 } 70 } 71 72 func TestVersionConstrains(t *testing.T) { 73 baseURL, _ := url.Parse("https://example.com/disco/foo.json") 74 75 t.Run("exact service version is provided", func(t *testing.T) { 76 portStr, close := testVersionsServer(func(w http.ResponseWriter, r *http.Request) { 77 resp := []byte(` 78 { 79 "service": "%s", 80 "product": "%s", 81 "minimum": "0.11.8", 82 "maximum": "0.12.0" 83 }`) 84 // Add the requested service and product to the response. 85 service := path.Base(r.URL.Path) 86 product := r.URL.Query().Get("product") 87 resp = []byte(fmt.Sprintf(string(resp), service, product)) 88 89 w.Header().Add("Content-Type", "application/json") 90 w.Header().Add("Content-Length", strconv.Itoa(len(resp))) 91 w.Write(resp) 92 }) 93 defer close() 94 95 host := Host{ 96 discoURL: baseURL, 97 hostname: "test-server", 98 transport: httpTransport, 99 services: map[string]interface{}{ 100 "thingy.v1": "/api/v1/", 101 "thingy.v2": "/api/v2/", 102 "versions.v1": "https://localhost" + portStr + "/v1/versions/", 103 }, 104 } 105 106 expected := &Constraints{ 107 Service: "thingy.v1", 108 Product: "terraform", 109 Minimum: "0.11.8", 110 Maximum: "0.12.0", 111 } 112 113 actual, err := host.VersionConstraints("thingy.v1", "terraform") 114 if err != nil { 115 t.Fatalf("unexpected version constraints error: %s", err) 116 } 117 118 if !reflect.DeepEqual(actual, expected) { 119 t.Fatalf("expected %#v, got: %#v", expected, actual) 120 } 121 }) 122 123 t.Run("service provided with different versions", func(t *testing.T) { 124 portStr, close := testVersionsServer(func(w http.ResponseWriter, r *http.Request) { 125 resp := []byte(` 126 { 127 "service": "%s", 128 "product": "%s", 129 "minimum": "0.11.8", 130 "maximum": "0.12.0" 131 }`) 132 // Add the requested service and product to the response. 133 service := path.Base(r.URL.Path) 134 product := r.URL.Query().Get("product") 135 resp = []byte(fmt.Sprintf(string(resp), service, product)) 136 137 w.Header().Add("Content-Type", "application/json") 138 w.Header().Add("Content-Length", strconv.Itoa(len(resp))) 139 w.Write(resp) 140 }) 141 defer close() 142 143 host := Host{ 144 discoURL: baseURL, 145 hostname: "test-server", 146 transport: httpTransport, 147 services: map[string]interface{}{ 148 "thingy.v2": "/api/v2/", 149 "thingy.v3": "/api/v3/", 150 "versions.v1": "https://localhost" + portStr + "/v1/versions/", 151 }, 152 } 153 154 expected := &Constraints{ 155 Service: "thingy.v3", 156 Product: "terraform", 157 Minimum: "0.11.8", 158 Maximum: "0.12.0", 159 } 160 161 actual, err := host.VersionConstraints("thingy.v1", "terraform") 162 if err != nil { 163 t.Fatalf("unexpected version constraints error: %s", err) 164 } 165 166 if !reflect.DeepEqual(actual, expected) { 167 t.Fatalf("expected %#v, got: %#v", expected, actual) 168 } 169 }) 170 171 t.Run("service not provided", func(t *testing.T) { 172 host := Host{ 173 discoURL: baseURL, 174 hostname: "test-server", 175 transport: httpTransport, 176 services: map[string]interface{}{ 177 "versions.v1": "https://localhost/v1/versions/", 178 }, 179 } 180 181 _, err := host.VersionConstraints("thingy.v1", "terraform") 182 if _, ok := err.(*ErrServiceNotProvided); !ok { 183 t.Fatalf("expected service not provided error, got: %v", err) 184 } 185 }) 186 187 t.Run("versions service returns a 404", func(t *testing.T) { 188 portStr, close := testVersionsServer(nil) 189 defer close() 190 191 host := Host{ 192 discoURL: baseURL, 193 hostname: "test-server", 194 transport: httpTransport, 195 services: map[string]interface{}{ 196 "thingy.v1": "/api/v1/", 197 "versions.v1": "https://localhost" + portStr + "/v1/non-existent/", 198 }, 199 } 200 201 _, err := host.VersionConstraints("thingy.v1", "terraform") 202 if _, ok := err.(*ErrNoVersionConstraints); !ok { 203 t.Fatalf("expected service not provided error, got: %v", err) 204 } 205 }) 206 207 t.Run("checkpoint is disabled", func(t *testing.T) { 208 if err := os.Setenv("CHECKPOINT_DISABLE", "1"); err != nil { 209 t.Fatalf("unexpected error: %v", err) 210 } 211 defer os.Unsetenv("CHECKPOINT_DISABLE") 212 213 host := Host{ 214 discoURL: baseURL, 215 hostname: "test-server", 216 transport: httpTransport, 217 services: map[string]interface{}{ 218 "thingy.v1": "/api/v1/", 219 "versions.v1": "https://localhost/v1/versions/", 220 }, 221 } 222 223 _, err := host.VersionConstraints("thingy.v1", "terraform") 224 if _, ok := err.(*ErrNoVersionConstraints); !ok { 225 t.Fatalf("expected service not provided error, got: %v", err) 226 } 227 }) 228 229 t.Run("versions service not discovered", func(t *testing.T) { 230 host := Host{ 231 discoURL: baseURL, 232 hostname: "test-server", 233 transport: httpTransport, 234 services: map[string]interface{}{ 235 "thingy.v1": "/api/v1/", 236 }, 237 } 238 239 _, err := host.VersionConstraints("thingy.v1", "terraform") 240 if _, ok := err.(*ErrServiceNotProvided); !ok { 241 t.Fatalf("expected service not provided error, got: %v", err) 242 } 243 }) 244 245 t.Run("versions service version not discovered", func(t *testing.T) { 246 host := Host{ 247 discoURL: baseURL, 248 hostname: "test-server", 249 transport: httpTransport, 250 services: map[string]interface{}{ 251 "thingy.v1": "/api/v1/", 252 "versions.v2": "https://localhost/v2/versions/", 253 }, 254 } 255 256 _, err := host.VersionConstraints("thingy.v1", "terraform") 257 if _, ok := err.(*ErrVersionNotSupported); !ok { 258 t.Fatalf("expected service not provided error, got: %v", err) 259 } 260 }) 261 } 262 263 func testVersionsServer(h func(w http.ResponseWriter, r *http.Request)) (portStr string, close func()) { 264 server := httptest.NewTLSServer(http.HandlerFunc( 265 func(w http.ResponseWriter, r *http.Request) { 266 // Test server always returns 404 if the URL isn't what we expect 267 if !strings.HasPrefix(r.URL.Path, "/v1/versions/") { 268 w.WriteHeader(404) 269 w.Write([]byte("not found")) 270 return 271 } 272 273 // If the URL is correct then the given hander decides the response 274 h(w, r) 275 }, 276 )) 277 278 serverURL, _ := url.Parse(server.URL) 279 280 portStr = serverURL.Port() 281 if portStr != "" { 282 portStr = ":" + portStr 283 } 284 285 close = func() { 286 server.Close() 287 } 288 289 return portStr, close 290 }