mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 18:24:07 +00:00
Merge pull request #2116 from erictune/improve_auth_integ
Improve integration test
This commit is contained in:
commit
c92e15679a
@ -32,32 +32,43 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
requireEtcd()
|
requireEtcd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
AliceToken string = "abc123" // username: alice. Present in token file.
|
||||||
|
BobToken string = "xyz987" // username: bob. Present in token file.
|
||||||
|
UnknownToken string = "qwerty" // Not present in token file.
|
||||||
|
// Keep file in sync with above constants.
|
||||||
|
TokenfileCSV string = `
|
||||||
|
abc123,alice,1
|
||||||
|
xyz987,bob,2
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeTestTokenFile() string {
|
||||||
|
// Write a token file.
|
||||||
|
f, err := ioutil.TempFile("", "auth_integration_test")
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
glog.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(f.Name(), []byte(TokenfileCSV), 0700); err != nil {
|
||||||
|
glog.Fatalf("unexpected error writing tokenfile: %v", err)
|
||||||
|
}
|
||||||
|
return f.Name()
|
||||||
|
}
|
||||||
|
|
||||||
// TestWhoAmI passes a known Bearer Token to the master's /_whoami endpoint and checks that
|
// TestWhoAmI passes a known Bearer Token to the master's /_whoami endpoint and checks that
|
||||||
// the master authenticates the user.
|
// the master authenticates the user.
|
||||||
func TestWhoAmI(t *testing.T) {
|
func TestWhoAmI(t *testing.T) {
|
||||||
deleteAllEtcdKeys()
|
deleteAllEtcdKeys()
|
||||||
|
|
||||||
// Write a token file.
|
|
||||||
json := `
|
|
||||||
abc123,alice,1
|
|
||||||
xyz987,bob,2
|
|
||||||
`
|
|
||||||
f, err := ioutil.TempFile("", "auth_integration_test")
|
|
||||||
f.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(f.Name())
|
|
||||||
if err := ioutil.WriteFile(f.Name(), []byte(json), 0700); err != nil {
|
|
||||||
t.Fatalf("unexpected error writing tokenfile: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up a master
|
// Set up a master
|
||||||
|
|
||||||
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1")
|
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1")
|
||||||
@ -65,12 +76,14 @@ xyz987,bob,2
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokenFilename := writeTestTokenFile()
|
||||||
|
defer os.Remove(tokenFilename)
|
||||||
m := master.New(&master.Config{
|
m := master.New(&master.Config{
|
||||||
EtcdHelper: helper,
|
EtcdHelper: helper,
|
||||||
EnableLogsSupport: false,
|
EnableLogsSupport: false,
|
||||||
EnableUISupport: false,
|
EnableUISupport: false,
|
||||||
APIPrefix: "/api",
|
APIPrefix: "/api",
|
||||||
TokenAuthFile: f.Name(),
|
TokenAuthFile: tokenFilename,
|
||||||
AuthorizationMode: "AlwaysAllow",
|
AuthorizationMode: "AlwaysAllow",
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -86,8 +99,8 @@ xyz987,bob,2
|
|||||||
expected string
|
expected string
|
||||||
succeeds bool
|
succeeds bool
|
||||||
}{
|
}{
|
||||||
{"Valid token", "abc123", "AUTHENTICATED AS alice", true},
|
{"Valid token", AliceToken, "AUTHENTICATED AS alice", true},
|
||||||
{"Unknown token", "456jkl", "", false},
|
{"Unknown token", UnknownToken, "", false},
|
||||||
{"No token", "", "", false},
|
{"No token", "", "", false},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@ -96,8 +109,9 @@ xyz987,bob,2
|
|||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tc.token))
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tc.token))
|
||||||
|
{
|
||||||
resp, err := transport.RoundTrip(req)
|
resp, err := transport.RoundTrip(req)
|
||||||
|
defer resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -119,6 +133,7 @@ xyz987,bob,2
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bodies for requests used in subsequent tests.
|
// Bodies for requests used in subsequent tests.
|
||||||
@ -181,15 +196,16 @@ var aMinion string = `
|
|||||||
|
|
||||||
var aEvent string = `
|
var aEvent string = `
|
||||||
{
|
{
|
||||||
"kind": "Binding",
|
"kind": "Event",
|
||||||
"apiVersion": "v1beta1",
|
"apiVersion": "v1beta1",
|
||||||
"id": "a",
|
"id": "a",
|
||||||
"involvedObject": {
|
"involvedObject": {
|
||||||
{
|
{
|
||||||
"kind": "Minion",
|
"kind": "Minion",
|
||||||
"name": "a"
|
"name": "a",
|
||||||
"apiVersion": "v1beta1",
|
"apiVersion": "v1beta1",
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -215,113 +231,116 @@ var aEndpoints string = `
|
|||||||
// Requests to try. Each one should be forbidden or not forbidden
|
// Requests to try. Each one should be forbidden or not forbidden
|
||||||
// depending on the authentication and authorization setup of the master.
|
// depending on the authentication and authorization setup of the master.
|
||||||
|
|
||||||
|
var code200or202 = map[int]bool{200: true, 202: true} // Unpredicatable which will be returned.
|
||||||
|
var code404 = map[int]bool{404: true}
|
||||||
|
var code409 = map[int]bool{409: true}
|
||||||
|
var code422 = map[int]bool{422: true}
|
||||||
|
var code500 = map[int]bool{500: true}
|
||||||
|
|
||||||
func getTestRequests() []struct {
|
func getTestRequests() []struct {
|
||||||
verb string
|
verb string
|
||||||
URL string
|
URL string
|
||||||
body string
|
body string
|
||||||
|
statusCodes map[int]bool // allowed status codes.
|
||||||
} {
|
} {
|
||||||
requests := []struct {
|
requests := []struct {
|
||||||
verb string
|
verb string
|
||||||
URL string
|
URL string
|
||||||
body string
|
body string
|
||||||
|
statusCodes map[int]bool // Set of expected resp.StatusCode if all goes well.
|
||||||
}{
|
}{
|
||||||
// Normal methods on pods
|
// Normal methods on pods
|
||||||
{"GET", "/api/v1beta1/pods", ""},
|
{"GET", "/api/v1beta1/pods", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/pods/a", ""},
|
{"POST", "/api/v1beta1/pods", aPod, code200or202},
|
||||||
{"POST", "/api/v1beta1/pods", aPod},
|
{"PUT", "/api/v1beta1/pods/a", aPod, code500}, // See #2114 about why 500
|
||||||
{"PUT", "/api/v1beta1/pods", aPod},
|
{"GET", "/api/v1beta1/pods", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/pods", ""},
|
{"GET", "/api/v1beta1/pods/a", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/pods/a", ""},
|
{"DELETE", "/api/v1beta1/pods/a", "", code200or202},
|
||||||
{"DELETE", "/api/v1beta1/pods", ""},
|
|
||||||
|
|
||||||
// Non-standard methods (not expected to work,
|
// Non-standard methods (not expected to work,
|
||||||
// but expected to pass/fail authorization prior to
|
// but expected to pass/fail authorization prior to
|
||||||
// failing validation.
|
// failing validation.
|
||||||
{"PATCH", "/api/v1beta1/pods/a", ""},
|
{"PATCH", "/api/v1beta1/pods/a", "", code404},
|
||||||
{"OPTIONS", "/api/v1beta1/pods", ""},
|
{"OPTIONS", "/api/v1beta1/pods", "", code404},
|
||||||
{"OPTIONS", "/api/v1beta1/pods/a", ""},
|
{"OPTIONS", "/api/v1beta1/pods/a", "", code404},
|
||||||
{"HEAD", "/api/v1beta1/pods", ""},
|
{"HEAD", "/api/v1beta1/pods", "", code404},
|
||||||
{"HEAD", "/api/v1beta1/pods/a", ""},
|
{"HEAD", "/api/v1beta1/pods/a", "", code404},
|
||||||
{"TRACE", "/api/v1beta1/pods", ""},
|
{"TRACE", "/api/v1beta1/pods", "", code404},
|
||||||
{"TRACE", "/api/v1beta1/pods/a", ""},
|
{"TRACE", "/api/v1beta1/pods/a", "", code404},
|
||||||
{"NOSUCHVERB", "/api/v1beta1/pods", ""},
|
{"NOSUCHVERB", "/api/v1beta1/pods", "", code404},
|
||||||
|
|
||||||
// Normal methods on services
|
// Normal methods on services
|
||||||
{"GET", "/api/v1beta1/services", ""},
|
{"GET", "/api/v1beta1/services", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/services/a", ""},
|
{"POST", "/api/v1beta1/services", aService, code200or202},
|
||||||
{"POST", "/api/v1beta1/services", aService},
|
{"PUT", "/api/v1beta1/services/a", aService, code422}, // TODO: GET and put back server-provided fields to avoid a 422
|
||||||
{"PUT", "/api/v1beta1/services", aService},
|
{"GET", "/api/v1beta1/services", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/services", ""},
|
{"GET", "/api/v1beta1/services/a", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/services/a", ""},
|
{"DELETE", "/api/v1beta1/services/a", "", code200or202},
|
||||||
{"DELETE", "/api/v1beta1/services", ""},
|
|
||||||
|
|
||||||
// Normal methods on replicationControllers
|
// Normal methods on replicationControllers
|
||||||
{"GET", "/api/v1beta1/replicationControllers", ""},
|
{"GET", "/api/v1beta1/replicationControllers", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/replicationControllers/a", ""},
|
{"POST", "/api/v1beta1/replicationControllers", aRC, code200or202},
|
||||||
{"POST", "/api/v1beta1/replicationControllers", aRC},
|
{"PUT", "/api/v1beta1/replicationControllers/a", aRC, code409}, // See #2115 about why 409
|
||||||
{"PUT", "/api/v1beta1/replicationControllers", aRC},
|
{"GET", "/api/v1beta1/replicationControllers", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/replicationControllers", ""},
|
{"GET", "/api/v1beta1/replicationControllers/a", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/replicationControllers/a", ""},
|
{"DELETE", "/api/v1beta1/replicationControllers/a", "", code200or202},
|
||||||
{"DELETE", "/api/v1beta1/replicationControllers", ""},
|
|
||||||
|
|
||||||
// Normal methods on endpoints
|
// Normal methods on endpoints
|
||||||
{"GET", "/api/v1beta1/endpoints", ""},
|
{"GET", "/api/v1beta1/endpoints", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/endpoints/a", ""},
|
{"POST", "/api/v1beta1/endpoints", aEndpoints, code200or202},
|
||||||
{"POST", "/api/v1beta1/endpoints", aEndpoints},
|
{"PUT", "/api/v1beta1/endpoints/a", aEndpoints, code200or202},
|
||||||
{"PUT", "/api/v1beta1/endpoints", aEndpoints},
|
{"GET", "/api/v1beta1/endpoints", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/endpoints", ""},
|
{"GET", "/api/v1beta1/endpoints/a", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/endpoints/a", ""},
|
{"DELETE", "/api/v1beta1/endpoints/a", "", code500}, // Issue #2113.
|
||||||
{"DELETE", "/api/v1beta1/endpoints", ""},
|
|
||||||
|
|
||||||
// Normal methods on minions
|
// Normal methods on minions
|
||||||
{"GET", "/api/v1beta1/minions", ""},
|
{"GET", "/api/v1beta1/minions", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/minions/a", ""},
|
{"POST", "/api/v1beta1/minions", aMinion, code200or202},
|
||||||
{"POST", "/api/v1beta1/minions", aMinion},
|
{"PUT", "/api/v1beta1/minions/a", aMinion, code500}, // See #2114 about why 500
|
||||||
{"PUT", "/api/v1beta1/minions", aMinion},
|
{"GET", "/api/v1beta1/minions", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/minions", ""},
|
{"GET", "/api/v1beta1/minions/a", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/minions/a", ""},
|
{"DELETE", "/api/v1beta1/minions/a", "", code200or202},
|
||||||
{"DELETE", "/api/v1beta1/minions", ""},
|
|
||||||
|
|
||||||
// Normal methods on events
|
// Normal methods on events
|
||||||
{"GET", "/api/v1beta1/events", ""},
|
{"GET", "/api/v1beta1/events", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/events/a", ""},
|
{"POST", "/api/v1beta1/events", aEvent, code200or202},
|
||||||
{"POST", "/api/v1beta1/events", aEvent},
|
{"PUT", "/api/v1beta1/events/a", aEvent, code500}, // See #2114 about why 500
|
||||||
{"PUT", "/api/v1beta1/events", aEvent},
|
{"GET", "/api/v1beta1/events", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/events", ""},
|
{"GET", "/api/v1beta1/events", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/events/a", ""},
|
{"GET", "/api/v1beta1/events/a", "", code200or202},
|
||||||
{"DELETE", "/api/v1beta1/events", ""},
|
{"DELETE", "/api/v1beta1/events/a", "", code200or202},
|
||||||
|
|
||||||
// Normal methods on bindings
|
// Normal methods on bindings
|
||||||
{"GET", "/api/v1beta1/events", ""},
|
{"GET", "/api/v1beta1/bindings", "", code404}, // Bindings are write-only, so 404
|
||||||
{"GET", "/api/v1beta1/events/a", ""},
|
{"POST", "/api/v1beta1/pods", aPod, code200or202}, // Need a pod to bind or you get a 404
|
||||||
{"POST", "/api/v1beta1/events", aBinding},
|
{"POST", "/api/v1beta1/bindings", aBinding, code200or202},
|
||||||
{"PUT", "/api/v1beta1/events", aBinding},
|
{"PUT", "/api/v1beta1/bindings/a", aBinding, code500}, // See #2114 about why 500
|
||||||
{"GET", "/api/v1beta1/events", ""},
|
{"GET", "/api/v1beta1/bindings", "", code404},
|
||||||
{"GET", "/api/v1beta1/events/a", ""},
|
{"GET", "/api/v1beta1/bindings/a", "", code404},
|
||||||
{"DELETE", "/api/v1beta1/events", ""},
|
{"DELETE", "/api/v1beta1/bindings/a", "", code404},
|
||||||
|
|
||||||
// Non-existent object type.
|
// Non-existent object type.
|
||||||
{"GET", "/api/v1beta1/foo", ""},
|
{"GET", "/api/v1beta1/foo", "", code404},
|
||||||
{"GET", "/api/v1beta1/foo/a", ""},
|
{"POST", "/api/v1beta1/foo", `{"foo": "foo"}`, code404},
|
||||||
{"POST", "/api/v1beta1/foo", `{"foo": "foo"}`},
|
{"PUT", "/api/v1beta1/foo/a", `{"foo": "foo"}`, code404},
|
||||||
{"PUT", "/api/v1beta1/foo", `{"foo": "foo"}`},
|
{"GET", "/api/v1beta1/foo", "", code404},
|
||||||
{"GET", "/api/v1beta1/foo", ""},
|
{"GET", "/api/v1beta1/foo/a", "", code404},
|
||||||
{"GET", "/api/v1beta1/foo/a", ""},
|
{"DELETE", "/api/v1beta1/foo", "", code404},
|
||||||
{"DELETE", "/api/v1beta1/foo", ""},
|
|
||||||
|
|
||||||
// Operations
|
// Operations
|
||||||
{"GET", "/api/v1beta1/operations", ""},
|
{"GET", "/api/v1beta1/operations", "", code200or202},
|
||||||
{"GET", "/api/v1beta1/operations/1234567890", ""},
|
{"GET", "/api/v1beta1/operations/1234567890", "", code404},
|
||||||
|
|
||||||
// Special verbs on pods
|
// Special verbs on pods
|
||||||
{"GET", "/api/v1beta1/proxy/pods/a", ""},
|
{"GET", "/api/v1beta1/proxy/minions/a", "", code404},
|
||||||
{"GET", "/api/v1beta1/redirect/pods/a", ""},
|
{"GET", "/api/v1beta1/redirect/minions/a", "", code404},
|
||||||
// TODO: test .../watch/..., which doesn't end before the test timeout.
|
// TODO: test .../watch/..., which doesn't end before the test timeout.
|
||||||
|
// TODO: figure out how to create a minion so that it can successfully proxy/redirect.
|
||||||
|
|
||||||
// Non-object endpoints
|
// Non-object endpoints
|
||||||
{"GET", "/", ""},
|
{"GET", "/", "", code200or202},
|
||||||
{"GET", "/healthz", ""},
|
{"GET", "/healthz", "", code200or202},
|
||||||
{"GET", "/versions", ""},
|
{"GET", "/version", "", code200or202},
|
||||||
}
|
}
|
||||||
return requests
|
return requests
|
||||||
}
|
}
|
||||||
@ -361,12 +380,17 @@ func TestAuthModeAlwaysAllow(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
resp, err := transport.RoundTrip(req)
|
resp, err := transport.RoundTrip(req)
|
||||||
|
defer resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if resp.StatusCode == http.StatusForbidden {
|
if _, ok := r.statusCodes[resp.StatusCode]; !ok {
|
||||||
t.Errorf("Expected status other than Forbidden")
|
t.Errorf("Expected status one of %v, but got %v", r.statusCodes, resp.StatusCode)
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
t.Errorf("Body: %v", string(b))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -400,8 +424,9 @@ func TestAuthModeAlwaysDeny(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
resp, err := transport.RoundTrip(req)
|
resp, err := transport.RoundTrip(req)
|
||||||
|
defer resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -409,4 +434,5 @@ func TestAuthModeAlwaysDeny(t *testing.T) {
|
|||||||
t.Errorf("Expected status Forbidden but got status %v", resp.Status)
|
t.Errorf("Expected status Forbidden but got status %v", resp.Status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user