244 lines
7.9 KiB
Go
244 lines
7.9 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
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 server
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/http/httputil"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"k8s.io/apimachinery/pkg/util/json"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/apiserver/pkg/server/healthz"
|
|
"k8s.io/client-go/informers"
|
|
"k8s.io/client-go/kubernetes/fake"
|
|
"k8s.io/client-go/rest"
|
|
)
|
|
|
|
func TestAuthorizeClientBearerTokenNoops(t *testing.T) {
|
|
// All of these should do nothing (not panic, no side-effects)
|
|
cfgGens := []func() *rest.Config{
|
|
func() *rest.Config { return nil },
|
|
func() *rest.Config { return &rest.Config{} },
|
|
func() *rest.Config { return &rest.Config{BearerToken: "mu"} },
|
|
}
|
|
authcGens := []func() *AuthenticationInfo{
|
|
func() *AuthenticationInfo { return nil },
|
|
func() *AuthenticationInfo { return &AuthenticationInfo{} },
|
|
}
|
|
authzGens := []func() *AuthorizationInfo{
|
|
func() *AuthorizationInfo { return nil },
|
|
func() *AuthorizationInfo { return &AuthorizationInfo{} },
|
|
}
|
|
for _, cfgGen := range cfgGens {
|
|
for _, authcGen := range authcGens {
|
|
for _, authzGen := range authzGens {
|
|
pConfig := cfgGen()
|
|
pAuthc := authcGen()
|
|
pAuthz := authzGen()
|
|
AuthorizeClientBearerToken(pConfig, pAuthc, pAuthz)
|
|
if before, after := authcGen(), pAuthc; !reflect.DeepEqual(before, after) {
|
|
t.Errorf("AuthorizeClientBearerToken(%v, %#+v, %v) changed %#+v", pConfig, pAuthc, pAuthz, *before)
|
|
}
|
|
if before, after := authzGen(), pAuthz; !reflect.DeepEqual(before, after) {
|
|
t.Errorf("AuthorizeClientBearerToken(%v, %v, %#+v) changed %#+v", pConfig, pAuthc, pAuthz, *before)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNewWithDelegate(t *testing.T) {
|
|
delegateConfig := NewConfig(codecs)
|
|
delegateConfig.ExternalAddress = "192.168.10.4:443"
|
|
delegateConfig.PublicAddress = net.ParseIP("192.168.10.4")
|
|
delegateConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
|
|
delegateConfig.LoopbackClientConfig = &rest.Config{}
|
|
clientset := fake.NewSimpleClientset()
|
|
if clientset == nil {
|
|
t.Fatal("unable to create fake client set")
|
|
}
|
|
|
|
delegateConfig.HealthzChecks = append(delegateConfig.HealthzChecks, healthz.NamedCheck("delegate-health", func(r *http.Request) error {
|
|
return fmt.Errorf("delegate failed healthcheck")
|
|
}))
|
|
|
|
sharedInformers := informers.NewSharedInformerFactory(clientset, delegateConfig.LoopbackClientConfig.Timeout)
|
|
delegateServer, err := delegateConfig.Complete(sharedInformers).New("test", NewEmptyDelegate())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
delegateServer.Handler.NonGoRestfulMux.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
})
|
|
|
|
delegatePostStartHookChan := make(chan struct{})
|
|
delegateServer.AddPostStartHookOrDie("delegate-post-start-hook", func(context PostStartHookContext) error {
|
|
defer close(delegatePostStartHookChan)
|
|
return nil
|
|
})
|
|
|
|
// this wires up swagger
|
|
delegateServer.PrepareRun()
|
|
|
|
wrappingConfig := NewConfig(codecs)
|
|
wrappingConfig.ExternalAddress = "192.168.10.4:443"
|
|
wrappingConfig.PublicAddress = net.ParseIP("192.168.10.4")
|
|
wrappingConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
|
|
wrappingConfig.LoopbackClientConfig = &rest.Config{}
|
|
|
|
wrappingConfig.HealthzChecks = append(wrappingConfig.HealthzChecks, healthz.NamedCheck("wrapping-health", func(r *http.Request) error {
|
|
return fmt.Errorf("wrapping failed healthcheck")
|
|
}))
|
|
|
|
sharedInformers = informers.NewSharedInformerFactory(clientset, wrappingConfig.LoopbackClientConfig.Timeout)
|
|
wrappingServer, err := wrappingConfig.Complete(sharedInformers).New("test", delegateServer)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wrappingServer.Handler.NonGoRestfulMux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
})
|
|
|
|
wrappingPostStartHookChan := make(chan struct{})
|
|
wrappingServer.AddPostStartHookOrDie("wrapping-post-start-hook", func(context PostStartHookContext) error {
|
|
defer close(wrappingPostStartHookChan)
|
|
return nil
|
|
})
|
|
|
|
stopCh := make(chan struct{})
|
|
defer close(stopCh)
|
|
wrappingServer.PrepareRun()
|
|
wrappingServer.RunPostStartHooks(stopCh)
|
|
|
|
server := httptest.NewServer(wrappingServer.Handler)
|
|
defer server.Close()
|
|
|
|
// Wait for the hooks to finish before checking the response
|
|
<-delegatePostStartHookChan
|
|
<-wrappingPostStartHookChan
|
|
expectedPaths := []string{
|
|
"/apis",
|
|
"/bar",
|
|
"/foo",
|
|
"/healthz",
|
|
"/healthz/delegate-health",
|
|
"/healthz/log",
|
|
"/healthz/ping",
|
|
"/healthz/poststarthook/delegate-post-start-hook",
|
|
"/healthz/poststarthook/generic-apiserver-start-informers",
|
|
"/healthz/poststarthook/wrapping-post-start-hook",
|
|
"/healthz/wrapping-health",
|
|
"/livez",
|
|
"/livez/delegate-health",
|
|
"/livez/log",
|
|
"/livez/ping",
|
|
"/livez/poststarthook/delegate-post-start-hook",
|
|
"/livez/poststarthook/generic-apiserver-start-informers",
|
|
"/livez/poststarthook/wrapping-post-start-hook",
|
|
"/metrics",
|
|
"/readyz",
|
|
"/readyz/delegate-health",
|
|
"/readyz/log",
|
|
"/readyz/ping",
|
|
"/readyz/poststarthook/delegate-post-start-hook",
|
|
"/readyz/poststarthook/generic-apiserver-start-informers",
|
|
"/readyz/poststarthook/wrapping-post-start-hook",
|
|
"/readyz/shutdown",
|
|
}
|
|
checkExpectedPathsAtRoot(server.URL, expectedPaths, t)
|
|
checkPath(server.URL+"/healthz", http.StatusInternalServerError, `[+]ping ok
|
|
[+]log ok
|
|
[-]wrapping-health failed: reason withheld
|
|
[-]delegate-health failed: reason withheld
|
|
[+]poststarthook/generic-apiserver-start-informers ok
|
|
[+]poststarthook/delegate-post-start-hook ok
|
|
[+]poststarthook/wrapping-post-start-hook ok
|
|
healthz check failed
|
|
`, t)
|
|
|
|
checkPath(server.URL+"/healthz/delegate-health", http.StatusInternalServerError, `internal server error: delegate failed healthcheck
|
|
`, t)
|
|
checkPath(server.URL+"/healthz/wrapping-health", http.StatusInternalServerError, `internal server error: wrapping failed healthcheck
|
|
`, t)
|
|
checkPath(server.URL+"/healthz/poststarthook/delegate-post-start-hook", http.StatusOK, `ok`, t)
|
|
checkPath(server.URL+"/healthz/poststarthook/wrapping-post-start-hook", http.StatusOK, `ok`, t)
|
|
checkPath(server.URL+"/foo", http.StatusForbidden, ``, t)
|
|
checkPath(server.URL+"/bar", http.StatusUnauthorized, ``, t)
|
|
}
|
|
|
|
func checkPath(url string, expectedStatusCode int, expectedBody string, t *testing.T) {
|
|
t.Run(url, func(t *testing.T) {
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dump, _ := httputil.DumpResponse(resp, true)
|
|
t.Log(string(dump))
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if e, a := expectedBody, string(body); e != a {
|
|
t.Errorf("%q expected %v, got %v", url, e, a)
|
|
}
|
|
if e, a := expectedStatusCode, resp.StatusCode; e != a {
|
|
t.Errorf("%q expected %v, got %v", url, e, a)
|
|
}
|
|
})
|
|
}
|
|
|
|
func checkExpectedPathsAtRoot(url string, expectedPaths []string, t *testing.T) {
|
|
t.Run(url, func(t *testing.T) {
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dump, _ := httputil.DumpResponse(resp, true)
|
|
t.Log(string(dump))
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var result map[string]interface{}
|
|
json.Unmarshal(body, &result)
|
|
paths, ok := result["paths"].([]interface{})
|
|
if !ok {
|
|
t.Errorf("paths not found")
|
|
}
|
|
pathset := sets.NewString()
|
|
for _, p := range paths {
|
|
pathset.Insert(p.(string))
|
|
}
|
|
expectedset := sets.NewString(expectedPaths...)
|
|
for _, p := range pathset.Difference(expectedset) {
|
|
t.Errorf("Got %v path, which we did not expect", p)
|
|
}
|
|
for _, p := range expectedset.Difference(pathset) {
|
|
t.Errorf(" Expected %v path which we did not get", p)
|
|
}
|
|
})
|
|
}
|