mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			228 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2014 The Kubernetes Authors All rights reserved.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package apiserver
 | 
						|
 | 
						|
import (
 | 
						|
	"compress/gzip"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"net/http"
 | 
						|
	"net/http/httptest"
 | 
						|
	"net/url"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"golang.org/x/net/websocket"
 | 
						|
	"k8s.io/kubernetes/pkg/api/rest"
 | 
						|
)
 | 
						|
 | 
						|
func TestProxy(t *testing.T) {
 | 
						|
	table := []struct {
 | 
						|
		method          string
 | 
						|
		path            string
 | 
						|
		reqBody         string
 | 
						|
		respBody        string
 | 
						|
		respContentType string
 | 
						|
		reqNamespace    string
 | 
						|
	}{
 | 
						|
		{"GET", "/some/dir", "", "answer", "text/css", "default"},
 | 
						|
		{"GET", "/some/dir", "", "<html><head></head><body>answer</body></html>", "text/html", "default"},
 | 
						|
		{"POST", "/some/other/dir", "question", "answer", "text/css", "default"},
 | 
						|
		{"PUT", "/some/dir/id", "different question", "answer", "text/css", "default"},
 | 
						|
		{"DELETE", "/some/dir/id", "", "ok", "text/css", "default"},
 | 
						|
		{"GET", "/some/dir/id", "", "answer", "text/css", "other"},
 | 
						|
		{"GET", "/trailing/slash/", "", "answer", "text/css", "default"},
 | 
						|
		{"GET", "/", "", "answer", "text/css", "default"},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, item := range table {
 | 
						|
		proxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
						|
			gotBody, err := ioutil.ReadAll(req.Body)
 | 
						|
			if err != nil {
 | 
						|
				t.Errorf("%v - unexpected error %v", item.method, err)
 | 
						|
			}
 | 
						|
			if e, a := item.reqBody, string(gotBody); e != a {
 | 
						|
				t.Errorf("%v - expected %v, got %v", item.method, e, a)
 | 
						|
			}
 | 
						|
			if e, a := item.path, req.URL.Path; e != a {
 | 
						|
				t.Errorf("%v - expected %v, got %v", item.method, e, a)
 | 
						|
			}
 | 
						|
			w.Header().Set("Content-Type", item.respContentType)
 | 
						|
			var out io.Writer = w
 | 
						|
			if strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
 | 
						|
				// The proxier can ask for gzip'd data; we need to provide it with that
 | 
						|
				// in order to test our processing of that data.
 | 
						|
				w.Header().Set("Content-Encoding", "gzip")
 | 
						|
				gzw := gzip.NewWriter(w)
 | 
						|
				out = gzw
 | 
						|
				defer gzw.Close()
 | 
						|
			}
 | 
						|
			fmt.Fprint(out, item.respBody)
 | 
						|
		}))
 | 
						|
		defer proxyServer.Close()
 | 
						|
 | 
						|
		serverURL, _ := url.Parse(proxyServer.URL)
 | 
						|
		simpleStorage := &SimpleRESTStorage{
 | 
						|
			errors:                    map[string]error{},
 | 
						|
			resourceLocation:          serverURL,
 | 
						|
			expectedResourceNamespace: item.reqNamespace,
 | 
						|
		}
 | 
						|
 | 
						|
		namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
 | 
						|
		namespaceServer := httptest.NewServer(namespaceHandler)
 | 
						|
		defer namespaceServer.Close()
 | 
						|
 | 
						|
		// test each supported URL pattern for finding the redirection resource in the proxy in a particular namespace
 | 
						|
		serverPatterns := []struct {
 | 
						|
			server           *httptest.Server
 | 
						|
			proxyTestPattern string
 | 
						|
		}{
 | 
						|
			{namespaceServer, "/api/version2/proxy/namespaces/" + item.reqNamespace + "/foo/id" + item.path},
 | 
						|
		}
 | 
						|
 | 
						|
		for _, serverPattern := range serverPatterns {
 | 
						|
			server := serverPattern.server
 | 
						|
			proxyTestPattern := serverPattern.proxyTestPattern
 | 
						|
			req, err := http.NewRequest(
 | 
						|
				item.method,
 | 
						|
				server.URL+proxyTestPattern,
 | 
						|
				strings.NewReader(item.reqBody),
 | 
						|
			)
 | 
						|
			if err != nil {
 | 
						|
				t.Errorf("%v - unexpected error %v", item.method, err)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			resp, err := http.DefaultClient.Do(req)
 | 
						|
			if err != nil {
 | 
						|
				t.Errorf("%v - unexpected error %v", item.method, err)
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			gotResp, err := ioutil.ReadAll(resp.Body)
 | 
						|
			if err != nil {
 | 
						|
				t.Errorf("%v - unexpected error %v", item.method, err)
 | 
						|
			}
 | 
						|
			resp.Body.Close()
 | 
						|
			if e, a := item.respBody, string(gotResp); e != a {
 | 
						|
				t.Errorf("%v - expected %v, got %v. url: %#v", item.method, e, a, req.URL)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestProxyUpgrade(t *testing.T) {
 | 
						|
	backendServer := httptest.NewServer(websocket.Handler(func(ws *websocket.Conn) {
 | 
						|
		defer ws.Close()
 | 
						|
		body := make([]byte, 5)
 | 
						|
		ws.Read(body)
 | 
						|
		ws.Write([]byte("hello " + string(body)))
 | 
						|
	}))
 | 
						|
	defer backendServer.Close()
 | 
						|
 | 
						|
	serverURL, _ := url.Parse(backendServer.URL)
 | 
						|
	simpleStorage := &SimpleRESTStorage{
 | 
						|
		errors:                    map[string]error{},
 | 
						|
		resourceLocation:          serverURL,
 | 
						|
		expectedResourceNamespace: "myns",
 | 
						|
	}
 | 
						|
 | 
						|
	namespaceHandler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
 | 
						|
 | 
						|
	server := httptest.NewServer(namespaceHandler)
 | 
						|
	defer server.Close()
 | 
						|
 | 
						|
	ws, err := websocket.Dial("ws://"+server.Listener.Addr().String()+"/api/version2/proxy/namespaces/myns/foo/123", "", "http://127.0.0.1/")
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("websocket dial err: %s", err)
 | 
						|
	}
 | 
						|
	defer ws.Close()
 | 
						|
 | 
						|
	if _, err := ws.Write([]byte("world")); err != nil {
 | 
						|
		t.Fatalf("write err: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	response := make([]byte, 20)
 | 
						|
	n, err := ws.Read(response)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("read err: %s", err)
 | 
						|
	}
 | 
						|
	if e, a := "hello world", string(response[0:n]); e != a {
 | 
						|
		t.Fatalf("expected '%#v', got '%#v'", e, a)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestRedirectOnMissingTrailingSlash(t *testing.T) {
 | 
						|
	table := []struct {
 | 
						|
		// The requested path
 | 
						|
		path string
 | 
						|
		// The path requested on the proxy server.
 | 
						|
		proxyServerPath string
 | 
						|
		// query string
 | 
						|
		query string
 | 
						|
	}{
 | 
						|
		{"/trailing/slash/", "/trailing/slash/", ""},
 | 
						|
		{"/", "/", "test1=value1&test2=value2"},
 | 
						|
		// "/" should be added at the end.
 | 
						|
		{"", "/", "test1=value1&test2=value2"},
 | 
						|
		// "/" should not be added at a non-root path.
 | 
						|
		{"/some/path", "/some/path", ""},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, item := range table {
 | 
						|
		proxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
						|
			if req.URL.Path != item.proxyServerPath {
 | 
						|
				t.Errorf("Unexpected request on path: %s, expected path: %s, item: %v", req.URL.Path, item.proxyServerPath, item)
 | 
						|
			}
 | 
						|
			if req.URL.RawQuery != item.query {
 | 
						|
				t.Errorf("Unexpected query on url: %s, expected: %s", req.URL.RawQuery, item.query)
 | 
						|
			}
 | 
						|
		}))
 | 
						|
		defer proxyServer.Close()
 | 
						|
 | 
						|
		serverURL, _ := url.Parse(proxyServer.URL)
 | 
						|
		simpleStorage := &SimpleRESTStorage{
 | 
						|
			errors:                    map[string]error{},
 | 
						|
			resourceLocation:          serverURL,
 | 
						|
			expectedResourceNamespace: "ns",
 | 
						|
		}
 | 
						|
 | 
						|
		handler := handleNamespaced(map[string]rest.Storage{"foo": simpleStorage})
 | 
						|
		server := httptest.NewServer(handler)
 | 
						|
		defer server.Close()
 | 
						|
 | 
						|
		proxyTestPattern := "/api/version2/proxy/namespaces/ns/foo/id" + item.path
 | 
						|
		req, err := http.NewRequest(
 | 
						|
			"GET",
 | 
						|
			server.URL+proxyTestPattern+"?"+item.query,
 | 
						|
			strings.NewReader(""),
 | 
						|
		)
 | 
						|
		if err != nil {
 | 
						|
			t.Errorf("unexpected error %v", err)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		// Note: We are using a default client here, that follows redirects.
 | 
						|
		resp, err := http.DefaultClient.Do(req)
 | 
						|
		if err != nil {
 | 
						|
			t.Errorf("unexpected error %v", err)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if resp.StatusCode != http.StatusOK {
 | 
						|
			t.Errorf("Unexpected errorCode: %v, expected: 200. Response: %v, item: %v", resp.StatusCode, resp, item)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |