Updating test/integration to use testapi.Version everywhere

This commit is contained in:
nikhiljindal
2015-04-11 00:56:39 -07:00
parent 86d3072492
commit 8874ef9c06
6 changed files with 528 additions and 245 deletions

View File

@@ -20,6 +20,7 @@ package testapi
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
@@ -76,15 +77,22 @@ func SelfLink(resource, name string) string {
return fmt.Sprintf("/api/%s/%s/%s", Version(), resource, name) return fmt.Sprintf("/api/%s/%s/%s", Version(), resource, name)
} }
// Returns the appropriate path for the given resource, namespace and name. // Returns the appropriate path for the given prefix (watch, proxy, redirect, etc), resource, namespace and name.
// For ex, this is of the form: // For ex, this is of the form:
// /api/v1beta1/pods/pod0 for v1beta1 and // /api/v1beta1/watch/pods/pod0 for v1beta1 and
// /api/v1beta3/namespaces/foo/pods/pod0 for v1beta3. // /api/v1beta3/watch/namespaces/foo/pods/pod0 for v1beta3.
func ResourcePath(resource, namespace, name string) string { func ResourcePathWithPrefix(prefix, resource, namespace, name string) string {
path := "/api/" + Version() path := "/api/" + Version()
if !api.PreV1Beta3(Version()) && namespace != "" { if prefix != "" {
path = path + "/" + prefix
}
if !api.PreV1Beta3(Version()) {
if namespace != "" {
path = path + "/namespaces/" + namespace path = path + "/namespaces/" + namespace
} }
// Resource names in v1beta3 are lower case.
resource = strings.ToLower(resource)
}
if resource != "" { if resource != "" {
path = path + "/" + resource path = path + "/" + resource
} }
@@ -94,6 +102,14 @@ func ResourcePath(resource, namespace, name string) string {
return path return path
} }
// Returns the appropriate path for the given resource, namespace and name.
// For ex, this is of the form:
// /api/v1beta1/pods/pod0 for v1beta1 and
// /api/v1beta3/namespaces/foo/pods/pod0 for v1beta3.
func ResourcePath(resource, namespace, name string) string {
return ResourcePathWithPrefix("", resource, namespace, name)
}
// Returns the appropriate path along with the query params for the given resource, namespace and name. // Returns the appropriate path along with the query params for the given resource, namespace and name.
// For ex, this is of the form: // For ex, this is of the form:
// /api/v1beta1/pods/pod0?namespace=foo for v1beta1 and // /api/v1beta1/pods/pod0?namespace=foo for v1beta1 and
@@ -101,7 +117,7 @@ func ResourcePath(resource, namespace, name string) string {
func ResourcePathWithQueryParams(resource, namespace, name string) string { func ResourcePathWithQueryParams(resource, namespace, name string) string {
path := ResourcePath(resource, namespace, name) path := ResourcePath(resource, namespace, name)
// Add namespace as query param for pre v1beta3. // Add namespace as query param for pre v1beta3.
if api.PreV1Beta3(Version()) { if api.PreV1Beta3(Version()) && namespace != "" {
path = path + "?namespace=" + namespace path = path + "?namespace=" + namespace
} }
return path return path

View File

@@ -0,0 +1,169 @@
/*
Copyright 2015 Google Inc. 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 testapi
import (
"testing"
)
func TestResourcePathWithPrefixForV1Beta3(t *testing.T) {
if Version() != "v1beta3" {
// Skip the test if we are not testing v1beta3.
return
}
testCases := []struct {
prefix string
resource string
namespace string
name string
expected string
}{
{"prefix", "resource", "mynamespace", "myresource", "/api/v1beta3/prefix/namespaces/mynamespace/resource/myresource"},
{"prefix", "resource", "", "myresource", "/api/v1beta3/prefix/resource/myresource"},
{"prefix", "resource", "mynamespace", "", "/api/v1beta3/prefix/namespaces/mynamespace/resource"},
{"prefix", "resource", "", "", "/api/v1beta3/prefix/resource"},
{"", "resource", "mynamespace", "myresource", "/api/v1beta3/namespaces/mynamespace/resource/myresource"},
}
for _, item := range testCases {
if actual := ResourcePathWithPrefix(item.prefix, item.resource, item.namespace, item.name); actual != item.expected {
t.Errorf("Expected: %s, got: %s for prefix: %s, resource: %s, namespace: %s and name: %s", item.expected, actual, item.prefix, item.resource, item.namespace, item.name)
}
}
}
func TestResourcePathWithPrefixForV1Beta1(t *testing.T) {
if Version() != "v1beta1" {
// Skip the test if we are not testing v1beta1.
return
}
testCases := []struct {
prefix string
resource string
namespace string
name string
expected string
}{
{"prefix", "resource", "mynamespace", "myresource", "/api/v1beta1/prefix/resource/myresource"},
{"prefix", "resource", "", "myresource", "/api/v1beta1/prefix/resource/myresource"},
{"prefix", "resource", "mynamespace", "", "/api/v1beta1/prefix/resource"},
{"prefix", "resource", "", "", "/api/v1beta1/prefix/resource"},
{"", "resource", "mynamespace", "myresource", "/api/v1beta1/resource/myresource"},
}
for _, item := range testCases {
if actual := ResourcePathWithPrefix(item.prefix, item.resource, item.namespace, item.name); actual != item.expected {
t.Errorf("Expected: %s, got: %s for prefix: %s, resource: %s, namespace: %s and name: %s", item.expected, actual, item.prefix, item.resource, item.namespace, item.name)
}
}
}
func TestResourcePathForV1Beta3(t *testing.T) {
if Version() != "v1beta3" {
// Skip the test if we are not testing v1beta3.
return
}
testCases := []struct {
resource string
namespace string
name string
expected string
}{
{"resource", "mynamespace", "myresource", "/api/v1beta3/namespaces/mynamespace/resource/myresource"},
{"resource", "", "myresource", "/api/v1beta3/resource/myresource"},
{"resource", "mynamespace", "", "/api/v1beta3/namespaces/mynamespace/resource"},
{"resource", "", "", "/api/v1beta3/resource"},
}
for _, item := range testCases {
if actual := ResourcePath(item.resource, item.namespace, item.name); actual != item.expected {
t.Errorf("Expected: %s, got: %s for resource: %s, namespace: %s and name: %s", item.expected, actual, item.resource, item.namespace, item.name)
}
}
}
func TestResourcePathForV1Beta1(t *testing.T) {
if Version() != "v1beta1" {
// Skip the test if we are not testing v1beta1.
return
}
testCases := []struct {
resource string
namespace string
name string
expected string
}{
{"resource", "mynamespace", "myresource", "/api/v1beta1/resource/myresource"},
{"resource", "", "myresource", "/api/v1beta1/resource/myresource"},
{"resource", "mynamespace", "", "/api/v1beta1/resource"},
{"resource", "", "", "/api/v1beta1/resource"},
}
for _, item := range testCases {
if actual := ResourcePath(item.resource, item.namespace, item.name); actual != item.expected {
t.Errorf("Expected: %s, got: %s for resource: %s, namespace: %s and name: %s", item.expected, actual, item.resource, item.namespace, item.name)
}
}
}
func TestResourcePathWithQueryParamsForV1Beta3(t *testing.T) {
if Version() != "v1beta3" {
// Skip the test if we are not testing v1beta3.
return
}
testCases := []struct {
resource string
namespace string
name string
expected string
}{
{"resource", "mynamespace", "myresource", "/api/v1beta3/namespaces/mynamespace/resource/myresource"},
{"resource", "", "myresource", "/api/v1beta3/resource/myresource"},
{"resource", "mynamespace", "", "/api/v1beta3/namespaces/mynamespace/resource"},
{"resource", "", "", "/api/v1beta3/resource"},
}
for _, item := range testCases {
if actual := ResourcePathWithQueryParams(item.resource, item.namespace, item.name); actual != item.expected {
t.Errorf("Expected: %s, got: %s for resource: %s, namespace: %s and name: %s", item.expected, actual, item.resource, item.namespace, item.name)
}
}
}
func TestResourcePathWithQueryParamsForV1Beta1(t *testing.T) {
if Version() != "v1beta1" {
// Skip the test if we are not testing v1beta1.
return
}
testCases := []struct {
resource string
namespace string
name string
expected string
}{
{"resource", "mynamespace", "myresource", "/api/v1beta1/resource/myresource?namespace=mynamespace"},
{"resource", "", "myresource", "/api/v1beta1/resource/myresource"},
{"resource", "mynamespace", "", "/api/v1beta1/resource?namespace=mynamespace"},
{"resource", "", "", "/api/v1beta1/resource"},
}
for _, item := range testCases {
if actual := ResourcePathWithQueryParams(item.resource, item.namespace, item.name); actual != item.expected {
t.Errorf("Expected: %s, got: %s for resource: %s, namespace: %s and name: %s", item.expected, actual, item.resource, item.namespace, item.name)
}
}
}

View File

@@ -30,10 +30,14 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url"
"os" "os"
"strconv"
"strings" "strings"
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator"
"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator/bearertoken" "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authenticator/bearertoken"
@@ -46,8 +50,15 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/auth/authenticator/token/tokentest" "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/auth/authenticator/token/tokentest"
) )
var nodeResourceName string
func init() { func init() {
requireEtcd() requireEtcd()
if api.PreV1Beta3(testapi.Version()) {
nodeResourceName = "minions"
} else {
nodeResourceName = "nodes"
}
} }
const ( const (
@@ -63,114 +74,176 @@ func getTestTokenAuth() authenticator.Request {
return bearertoken.New(tokenAuthenticator) return bearertoken.New(tokenAuthenticator)
} }
func path(resource, namespace, name string) string {
return testapi.ResourcePath(resource, namespace, name)
}
func pathWithQuery(resource, namespace, name string) string {
return testapi.ResourcePathWithQueryParams(resource, namespace, name)
}
func pathWithPrefix(prefix, resource, namespace, name string) string {
return testapi.ResourcePathWithPrefix(prefix, resource, namespace, name)
}
func timeoutPath(resource, namespace, name string) string {
return addTimeoutFlag(testapi.ResourcePath(resource, namespace, name))
}
func timeoutPathWithQuery(resource, namespace, name string) string {
return addTimeoutFlag(testapi.ResourcePathWithQueryParams(resource, namespace, name))
}
// Bodies for requests used in subsequent tests. // Bodies for requests used in subsequent tests.
var aPod string = ` var aPod string = `
{ {
"kind": "Pod", "kind": "Pod",
"apiVersion": "v1beta1", "apiVersion": "v1beta3",
"id": "a", "metadata": {
"desiredState": { "name": "a",
"manifest": { "creationTimestamp": null%s
"version": "v1beta1", },
"id": "a", "spec": {
"containers": [{ "name": "foo", "image": "bar/foo" }] "containers": [
{
"name": "foo",
"image": "bar/foo"
}
]
} }
}%s
} }
` `
var aRC string = ` var aRC string = `
{ {
"kind": "ReplicationController", "kind": "ReplicationController",
"apiVersion": "v1beta1", "apiVersion": "v1beta3",
"id": "a", "metadata": {
"desiredState": { "name": "a",
"labels": {
"name": "a"
}%s
},
"spec": {
"replicas": 2, "replicas": 2,
"replicaSelector": {"name": "a"}, "selector": {
"podTemplate": { "name": "a"
"desiredState": { },
"manifest": { "template": {
"version": "v1beta1", "metadata": {
"id": "a", "labels": {
"containers": [{ "name": "a"
}
},
"spec": {
"containers": [
{
"name": "foo", "name": "foo",
"image": "bar/foo" "image": "bar/foo"
}]
} }
}, ]
"labels": {"name": "a"} }
}
} }
},
"labels": {"name": "a"}%s
} }
` `
var aService string = ` var aService string = `
{ {
"kind": "Service", "kind": "Service",
"apiVersion": "v1beta1", "apiVersion": "v1beta3",
"id": "a", "metadata": {
"port": 8000, "name": "a",
"portalIP": "10.0.0.100", "labels": {
"labels": { "name": "a" }, "name": "a"
"selector": { "name": "a" }%s }%s
}
`
var aMinion string = `
{
"kind": "Minion",
"apiVersion": "v1beta1",
"id": "a",
"resources": {
"capacity": { "memory": "10", "cpu": "10"}
}, },
"externalID": "external", "spec": {
"hostIP": "10.10.10.10"%s "ports": [
{
"protocol": "TCP",
"port": 8000
}
],
"selector": {
"name": "a"
},
"portalIP": "10.0.0.100"
}
}
`
var aNode string = `
{
"kind": "Node",
"apiVersion": "v1beta3",
"metadata": {
"name": "a"%s
},
"spec": {
"externalID": "external"
}
} }
` `
var aEvent string = ` var aEvent string = `
{ {
"kind": "Event", "kind": "Event",
"apiVersion": "v1beta1", "apiVersion": "v1beta3",
"id": "a", "metadata": {
"name": "a"%s
},
"involvedObject": { "involvedObject": {
"kind": "Minion", "kind": "Node",
"name": "a",
"namespace": "default", "namespace": "default",
"apiVersion": "v1beta1" "name": "a",
}%s "apiVersion": "v1beta3"
}
} }
` `
var aBinding string = ` var aBinding string = `
{ {
"kind": "Binding", "kind": "Binding",
"apiVersion": "v1beta1", "apiVersion": "v1beta3",
"id": "a", "metadata": {
"host": "10.10.10.10", "name": "a"%s
"podID": "a"%s },
"target": {
"name": "10.10.10.10"
}
} }
` `
var aEndpoints string = ` var aEndpoints string = `
{ {
"kind": "Endpoints", "kind": "Endpoints",
"apiVersion": "v1beta1", "apiVersion": "v1beta3",
"id": "a", "metadata": {
"endpoints": ["10.10.1.1:1909"]%s "name": "a"%s
},
"subsets": [
{
"addresses": [
{
"IP": "10.10.1.1"
}
],
"ports": [
{
"port": 1909,
"protocol": "TCP"
}
]
}
]
} }
` `
var deleteNow string = ` var deleteNow string = `
{ {
"kind": "DeleteOptions", "kind": "DeleteOptions",
"apiVersion": "v1beta1", "apiVersion": "v1beta3",
"gracePeriod": 0%s "gracePeriodSeconds": null%s
} }
` `
// To ensure that a POST completes before a dependent GET, set a timeout.
var timeoutFlag = "?timeout=60s"
// 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 code200 = map[int]bool{200: true} var code200 = map[int]bool{200: true}
@@ -183,6 +256,15 @@ var code409 = map[int]bool{409: true}
var code422 = map[int]bool{422: true} var code422 = map[int]bool{422: true}
var code500 = map[int]bool{500: true} var code500 = map[int]bool{500: true}
// To ensure that a POST completes before a dependent GET, set a timeout.
func addTimeoutFlag(URLString string) string {
u, _ := url.Parse(URLString)
values := u.Query()
values.Set("timeout", "60s")
u.RawQuery = values.Encode()
return u.String()
}
func getTestRequests() []struct { func getTestRequests() []struct {
verb string verb string
URL string URL string
@@ -196,86 +278,82 @@ func getTestRequests() []struct {
statusCodes map[int]bool // Set of expected resp.StatusCode if all goes well. 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", "", code200}, {"GET", path("pods", "", ""), "", code200},
{"POST", "/api/v1beta1/pods" + timeoutFlag, aPod, code201}, {"GET", path("pods", api.NamespaceDefault, ""), "", code200},
{"PUT", "/api/v1beta1/pods/a" + timeoutFlag, aPod, code200}, {"POST", timeoutPath("pods", api.NamespaceDefault, ""), aPod, code201},
{"GET", "/api/v1beta1/pods", "", code200}, {"PUT", timeoutPath("pods", api.NamespaceDefault, "a"), aPod, code200},
{"GET", "/api/v1beta1/pods/a", "", code200}, {"GET", path("pods", api.NamespaceDefault, "a"), "", code200},
{"PATCH", "/api/v1beta1/pods/a", "{%v}", code200}, {"PATCH", path("pods", api.NamespaceDefault, "a"), "{%v}", code200},
{"DELETE", "/api/v1beta1/pods/a" + timeoutFlag, deleteNow, code200}, {"DELETE", timeoutPath("pods", api.NamespaceDefault, "a"), deleteNow, code200},
// 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.
{"OPTIONS", "/api/v1beta1/pods", "", code405}, {"OPTIONS", path("pods", api.NamespaceDefault, ""), "", code405},
{"OPTIONS", "/api/v1beta1/pods/a", "", code405}, {"OPTIONS", path("pods", api.NamespaceDefault, "a"), "", code405},
{"HEAD", "/api/v1beta1/pods", "", code405}, {"HEAD", path("pods", api.NamespaceDefault, ""), "", code405},
{"HEAD", "/api/v1beta1/pods/a", "", code405}, {"HEAD", path("pods", api.NamespaceDefault, "a"), "", code405},
{"TRACE", "/api/v1beta1/pods", "", code405}, {"TRACE", path("pods", api.NamespaceDefault, ""), "", code405},
{"TRACE", "/api/v1beta1/pods/a", "", code405}, {"TRACE", path("pods", api.NamespaceDefault, "a"), "", code405},
{"NOSUCHVERB", "/api/v1beta1/pods", "", code405}, {"NOSUCHVERB", path("pods", api.NamespaceDefault, ""), "", code405},
// Normal methods on services // Normal methods on services
{"GET", "/api/v1beta1/services", "", code200}, {"GET", path("services", "", ""), "", code200},
{"POST", "/api/v1beta1/services" + timeoutFlag, aService, code201}, {"GET", path("services", api.NamespaceDefault, ""), "", code200},
{"PUT", "/api/v1beta1/services/a" + timeoutFlag, aService, code200}, {"POST", timeoutPath("services", api.NamespaceDefault, ""), aService, code201},
{"GET", "/api/v1beta1/services", "", code200}, {"PUT", timeoutPath("services", api.NamespaceDefault, "a"), aService, code200},
{"GET", "/api/v1beta1/services/a", "", code200}, {"GET", path("services", api.NamespaceDefault, "a"), "", code200},
{"DELETE", "/api/v1beta1/services/a" + timeoutFlag, "", code200}, {"DELETE", timeoutPath("services", api.NamespaceDefault, "a"), "", code200},
// Normal methods on replicationControllers // Normal methods on replicationControllers
{"GET", "/api/v1beta1/replicationControllers", "", code200}, {"GET", path("replicationControllers", "", ""), "", code200},
{"POST", "/api/v1beta1/replicationControllers" + timeoutFlag, aRC, code201}, {"GET", path("replicationControllers", api.NamespaceDefault, ""), "", code200},
{"PUT", "/api/v1beta1/replicationControllers/a" + timeoutFlag, aRC, code200}, {"POST", timeoutPath("replicationControllers", api.NamespaceDefault, ""), aRC, code201},
{"GET", "/api/v1beta1/replicationControllers", "", code200}, {"PUT", timeoutPath("replicationControllers", api.NamespaceDefault, "a"), aRC, code200},
{"GET", "/api/v1beta1/replicationControllers/a", "", code200}, {"GET", path("replicationControllers", api.NamespaceDefault, "a"), "", code200},
{"DELETE", "/api/v1beta1/replicationControllers/a" + timeoutFlag, "", code200}, {"DELETE", timeoutPath("replicationControllers", api.NamespaceDefault, "a"), "", code200},
// Normal methods on endpoints // Normal methods on endpoints
{"GET", "/api/v1beta1/endpoints", "", code200}, {"GET", path("endpoints", "", ""), "", code200},
{"POST", "/api/v1beta1/endpoints" + timeoutFlag, aEndpoints, code201}, {"GET", path("endpoints", api.NamespaceDefault, ""), "", code200},
{"PUT", "/api/v1beta1/endpoints/a" + timeoutFlag, aEndpoints, code200}, {"POST", timeoutPath("endpoints", api.NamespaceDefault, ""), aEndpoints, code201},
{"GET", "/api/v1beta1/endpoints", "", code200}, {"PUT", timeoutPath("endpoints", api.NamespaceDefault, "a"), aEndpoints, code200},
{"GET", "/api/v1beta1/endpoints/a", "", code200}, {"GET", path("endpoints", api.NamespaceDefault, "a"), "", code200},
{"DELETE", "/api/v1beta1/endpoints/a" + timeoutFlag, "", code200}, {"DELETE", timeoutPath("endpoints", api.NamespaceDefault, "a"), "", code200},
// Normal methods on minions // Normal methods on minions
{"GET", "/api/v1beta1/minions", "", code200}, {"GET", path(nodeResourceName, "", ""), "", code200},
{"POST", "/api/v1beta1/minions" + timeoutFlag, aMinion, code201}, {"POST", timeoutPath(nodeResourceName, "", ""), aNode, code201},
{"PUT", "/api/v1beta1/minions/a" + timeoutFlag, aMinion, code200}, {"PUT", timeoutPath(nodeResourceName, "", "a"), aNode, code200},
{"GET", "/api/v1beta1/minions", "", code200}, {"GET", path(nodeResourceName, "", "a"), "", code200},
{"GET", "/api/v1beta1/minions/a", "", code200}, {"DELETE", timeoutPath(nodeResourceName, "", "a"), "", code200},
{"DELETE", "/api/v1beta1/minions/a" + timeoutFlag, "", code200},
// Normal methods on events // Normal methods on events
{"GET", "/api/v1beta1/events", "", code200}, {"GET", path("events", "", ""), "", code200},
{"POST", "/api/v1beta1/events" + timeoutFlag, aEvent, code201}, {"GET", path("events", api.NamespaceDefault, ""), "", code200},
{"PUT", "/api/v1beta1/events/a" + timeoutFlag, aEvent, code200}, {"POST", timeoutPath("events", api.NamespaceDefault, ""), aEvent, code201},
{"GET", "/api/v1beta1/events", "", code200}, {"PUT", timeoutPath("events", api.NamespaceDefault, "a"), aEvent, code200},
{"GET", "/api/v1beta1/events", "", code200}, {"GET", path("events", api.NamespaceDefault, "a"), "", code200},
{"GET", "/api/v1beta1/events/a", "", code200}, {"DELETE", timeoutPath("events", api.NamespaceDefault, "a"), "", code200},
{"DELETE", "/api/v1beta1/events/a" + timeoutFlag, "", code200},
// Normal methods on bindings // Normal methods on bindings
{"GET", "/api/v1beta1/bindings", "", code405}, // Bindings are write-only {"GET", path("bindings", api.NamespaceDefault, ""), "", code405},
{"POST", "/api/v1beta1/pods" + timeoutFlag, aPod, code201}, // Need a pod to bind or you get a 404 {"POST", timeoutPath("pods", api.NamespaceDefault, ""), aPod, code201}, // Need a pod to bind or you get a 404
{"POST", "/api/v1beta1/bindings" + timeoutFlag, aBinding, code201}, {"POST", timeoutPath("bindings", api.NamespaceDefault, ""), aBinding, code201},
{"PUT", "/api/v1beta1/bindings/a" + timeoutFlag, aBinding, code404}, {"PUT", timeoutPath("bindings", api.NamespaceDefault, "a"), aBinding, code404},
{"GET", "/api/v1beta1/bindings", "", code405}, {"GET", path("bindings", api.NamespaceDefault, "a"), "", code404}, // No bindings instances
{"GET", "/api/v1beta1/bindings/a", "", code404}, // No bindings instances {"DELETE", timeoutPath("bindings", api.NamespaceDefault, "a"), "", code404},
{"DELETE", "/api/v1beta1/bindings/a" + timeoutFlag, "", code404},
// Non-existent object type. // Non-existent object type.
{"GET", "/api/v1beta1/foo", "", code404}, {"GET", path("foo", "", ""), "", code404},
{"POST", "/api/v1beta1/foo", `{"foo": "foo"}`, code404}, {"POST", path("foo", api.NamespaceDefault, ""), `{"foo": "foo"}`, code404},
{"PUT", "/api/v1beta1/foo/a", `{"foo": "foo"}`, code404}, {"PUT", path("foo", api.NamespaceDefault, "a"), `{"foo": "foo"}`, code404},
{"GET", "/api/v1beta1/foo", "", code404}, {"GET", path("foo", api.NamespaceDefault, "a"), "", code404},
{"GET", "/api/v1beta1/foo/a", "", code404}, {"DELETE", timeoutPath("foo", api.NamespaceDefault, ""), "", code404},
{"DELETE", "/api/v1beta1/foo" + timeoutFlag, "", code404},
// Special verbs on nodes // Special verbs on nodes
{"GET", "/api/v1beta1/proxy/minions/a", "", code404}, {"GET", pathWithPrefix("proxy", nodeResourceName, api.NamespaceDefault, "a"), "", code404},
{"GET", "/api/v1beta1/redirect/minions/a", "", code404}, {"GET", pathWithPrefix("redirect", nodeResourceName, api.NamespaceDefault, "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. // TODO: figure out how to create a minion so that it can successfully proxy/redirect.
@@ -299,7 +377,7 @@ func TestAuthModeAlwaysAllow(t *testing.T) {
// Set up a master // Set up a master
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -331,7 +409,7 @@ func TestAuthModeAlwaysAllow(t *testing.T) {
if r.verb == "PUT" { if r.verb == "PUT" {
// For update operations, insert previous resource version // For update operations, insert previous resource version
if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 {
sub += fmt.Sprintf(",\r\n\"resourceVersion\": %v", resVersion) sub += fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
} }
namespace := "default" namespace := "default"
sub += fmt.Sprintf(",\r\n\"namespace\": %q", namespace) sub += fmt.Sprintf(",\r\n\"namespace\": %q", namespace)
@@ -367,6 +445,8 @@ func TestAuthModeAlwaysAllow(t *testing.T) {
if err == nil { if err == nil {
key := getPreviousResourceVersionKey(r.URL, id) key := getPreviousResourceVersionKey(r.URL, id)
previousResourceVersion[key] = currentResourceVersion previousResourceVersion[key] = currentResourceVersion
} else {
t.Logf("error in trying to extract resource version: %s", err)
} }
} }
} }
@@ -380,10 +460,42 @@ func parseResourceVersion(response []byte) (string, float64, error) {
if err != nil { if err != nil {
return "", 0, fmt.Errorf("unexpected error unmarshaling resultBody: %v", err) return "", 0, fmt.Errorf("unexpected error unmarshaling resultBody: %v", err)
} }
apiVersion, ok := resultBodyMap["apiVersion"].(string)
if !ok {
return "", 0, fmt.Errorf("unexpected error, apiVersion not found in JSON response: %v", string(response))
}
if api.PreV1Beta3(apiVersion) {
return parsePreV1Beta3ResourceVersion(resultBodyMap, response)
}
return parseV1Beta3ResourceVersion(resultBodyMap, response)
}
func parseV1Beta3ResourceVersion(resultBodyMap map[string]interface{}, response []byte) (string, float64, error) {
metadata, ok := resultBodyMap["metadata"].(map[string]interface{})
if !ok {
return "", 0, fmt.Errorf("unexpected error, metadata not found in JSON response: %v", string(response))
}
id, ok := metadata["name"].(string)
if !ok {
return "", 0, fmt.Errorf("unexpected error, id not found in JSON response: %v", string(response))
}
resourceVersionString, ok := metadata["resourceVersion"].(string)
if !ok {
return "", 0, fmt.Errorf("unexpected error, resourceVersion not found in JSON response: %v", string(response))
}
resourceVersion, err := strconv.ParseFloat(resourceVersionString, 64)
if err != nil {
return "", 0, fmt.Errorf("unexpected error, could not parse resourceVersion as float64, err: %s. JSON response: %v", err, string(response))
}
return id, resourceVersion, nil
}
func parsePreV1Beta3ResourceVersion(resultBodyMap map[string]interface{}, response []byte) (string, float64, error) {
id, ok := resultBodyMap["id"].(string) id, ok := resultBodyMap["id"].(string)
if !ok { if !ok {
return "", 0, fmt.Errorf("unexpected error, id not found in JSON response: %v", string(response)) return "", 0, fmt.Errorf("unexpected error, id not found in JSON response: %v", string(response))
} }
resourceVersion, ok := resultBodyMap["resourceVersion"].(float64) resourceVersion, ok := resultBodyMap["resourceVersion"].(float64)
if !ok { if !ok {
return "", 0, fmt.Errorf("unexpected error, resourceVersion not found in JSON response: %v", string(response)) return "", 0, fmt.Errorf("unexpected error, resourceVersion not found in JSON response: %v", string(response))
@@ -405,7 +517,7 @@ func TestAuthModeAlwaysDeny(t *testing.T) {
// Set up a master // Set up a master
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -472,7 +584,7 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) {
// Set up a master // Set up a master
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -506,7 +618,7 @@ func TestAliceNotForbiddenOrUnauthorized(t *testing.T) {
if r.verb == "PUT" { if r.verb == "PUT" {
// For update operations, insert previous resource version // For update operations, insert previous resource version
if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 {
sub += fmt.Sprintf(",\r\n\"resourceVersion\": %v", resVersion) sub += fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
} }
namespace := "default" namespace := "default"
sub += fmt.Sprintf(",\r\n\"namespace\": %q", namespace) sub += fmt.Sprintf(",\r\n\"namespace\": %q", namespace)
@@ -561,7 +673,7 @@ func TestBobIsForbidden(t *testing.T) {
// Set up a master // Set up a master
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -622,7 +734,7 @@ func TestUnknownUserIsUnauthorized(t *testing.T) {
// Set up a master // Set up a master
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -699,7 +811,7 @@ func TestNamespaceAuthorization(t *testing.T) {
// This file has alice and bob in it. // This file has alice and bob in it.
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -736,20 +848,20 @@ func TestNamespaceAuthorization(t *testing.T) {
statusCodes map[int]bool // allowed status codes. statusCodes map[int]bool // allowed status codes.
}{ }{
{"POST", "/api/v1beta1/pods" + timeoutFlag + "&namespace=foo", "foo", aPod, code201}, {"POST", timeoutPathWithQuery("pods", "foo", ""), "foo", aPod, code201},
{"GET", "/api/v1beta1/pods?namespace=foo", "foo", "", code200}, {"GET", pathWithQuery("pods", "foo", ""), "foo", "", code200},
{"GET", "/api/v1beta1/pods/a?namespace=foo", "foo", "", code200}, {"GET", pathWithQuery("pods", "foo", "a"), "foo", "", code200},
{"DELETE", "/api/v1beta1/pods/a" + timeoutFlag + "&namespace=foo", "foo", "", code200}, {"DELETE", timeoutPathWithQuery("pods", "foo", "a"), "foo", "", code200},
{"POST", "/api/v1beta1/pods" + timeoutFlag + "&namespace=bar", "bar", aPod, code403}, {"POST", timeoutPath("pods", "bar", ""), "bar", aPod, code403},
{"GET", "/api/v1beta1/pods?namespace=bar", "bar", "", code403}, {"GET", pathWithQuery("pods", "bar", ""), "bar", "", code403},
{"GET", "/api/v1beta1/pods/a?namespace=bar", "bar", "", code403}, {"GET", pathWithQuery("pods", "bar", "a"), "bar", "", code403},
{"DELETE", "/api/v1beta1/pods/a" + timeoutFlag + "&namespace=bar", "bar", "", code403}, {"DELETE", timeoutPathWithQuery("pods", "bar", "a"), "bar", "", code403},
{"POST", "/api/v1beta1/pods" + timeoutFlag, "", aPod, code403}, {"POST", timeoutPath("pods", api.NamespaceDefault, ""), "", aPod, code403},
{"GET", "/api/v1beta1/pods", "", "", code403}, {"GET", path("pods", "", ""), "", "", code403},
{"GET", "/api/v1beta1/pods/a", "", "", code403}, {"GET", path("pods", api.NamespaceDefault, "a"), "", "", code403},
{"DELETE", "/api/v1beta1/pods/a" + timeoutFlag, "", "", code403}, {"DELETE", timeoutPath("pods", api.NamespaceDefault, "a"), "", "", code403},
} }
for _, r := range requests { for _, r := range requests {
@@ -760,7 +872,7 @@ func TestNamespaceAuthorization(t *testing.T) {
if r.verb == "PUT" && r.body != "" { if r.verb == "PUT" && r.body != "" {
// For update operations, insert previous resource version // For update operations, insert previous resource version
if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 {
sub += fmt.Sprintf(",\r\n\"resourceVersion\": %v", resVersion) sub += fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
} }
namespace := r.namespace namespace := r.namespace
if len(namespace) == 0 { if len(namespace) == 0 {
@@ -814,7 +926,7 @@ func TestKindAuthorization(t *testing.T) {
// Set up a master // Set up a master
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -849,15 +961,15 @@ func TestKindAuthorization(t *testing.T) {
body string body string
statusCodes map[int]bool // allowed status codes. statusCodes map[int]bool // allowed status codes.
}{ }{
{"POST", "/api/v1beta1/services" + timeoutFlag, aService, code201}, {"POST", timeoutPath("services", api.NamespaceDefault, ""), aService, code201},
{"GET", "/api/v1beta1/services", "", code200}, {"GET", path("services", api.NamespaceDefault, ""), "", code200},
{"GET", "/api/v1beta1/services/a", "", code200}, {"GET", path("services", api.NamespaceDefault, "a"), "", code200},
{"DELETE", "/api/v1beta1/services/a" + timeoutFlag, "", code200}, {"DELETE", timeoutPath("services", api.NamespaceDefault, "a"), "", code200},
{"POST", "/api/v1beta1/pods" + timeoutFlag, aPod, code403}, {"POST", timeoutPath("pods", api.NamespaceDefault, ""), aPod, code403},
{"GET", "/api/v1beta1/pods", "", code403}, {"GET", path("pods", "", ""), "", code403},
{"GET", "/api/v1beta1/pods/a", "", code403}, {"GET", path("pods", api.NamespaceDefault, "a"), "", code403},
{"DELETE", "/api/v1beta1/pods/a" + timeoutFlag, "", code403}, {"DELETE", timeoutPath("pods", api.NamespaceDefault, "a"), "", code403},
} }
for _, r := range requests { for _, r := range requests {
@@ -868,7 +980,7 @@ func TestKindAuthorization(t *testing.T) {
if r.verb == "PUT" && r.body != "" { if r.verb == "PUT" && r.body != "" {
// For update operations, insert previous resource version // For update operations, insert previous resource version
if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 { if resVersion := previousResourceVersion[getPreviousResourceVersionKey(r.URL, "")]; resVersion != 0 {
resourceVersionJson := fmt.Sprintf(",\r\n\"resourceVersion\": %v", resVersion) resourceVersionJson := fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
bodyStr = fmt.Sprintf(r.body, resourceVersionJson) bodyStr = fmt.Sprintf(r.body, resourceVersionJson)
} }
} }
@@ -917,13 +1029,12 @@ func TestReadOnlyAuthorization(t *testing.T) {
// Set up a master // Set up a master
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
a := newAuthorizerWithContents(t, `{"readonly": true} a := newAuthorizerWithContents(t, `{"readonly": true}`)
`)
var m *master.Master var m *master.Master
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
@@ -951,9 +1062,9 @@ func TestReadOnlyAuthorization(t *testing.T) {
body string body string
statusCodes map[int]bool // allowed status codes. statusCodes map[int]bool // allowed status codes.
}{ }{
{"POST", "/api/v1beta1/pods", aPod, code403}, {"POST", path("pods", "", ""), aPod, code403},
{"GET", "/api/v1beta1/pods", "", code200}, {"GET", path("pods", "", ""), "", code200},
{"GET", "/api/v1beta1/pods/a", "", code404}, {"GET", path("pods", api.NamespaceDefault, "a"), "", code404},
} }
for _, r := range requests { for _, r := range requests {

View File

@@ -30,6 +30,7 @@ import (
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields" "github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
@@ -45,7 +46,7 @@ func init() {
} }
func RunAMaster(t *testing.T) (*master.Master, *httptest.Server) { func RunAMaster(t *testing.T) (*master.Master, *httptest.Server) {
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -73,15 +74,9 @@ func TestClient(t *testing.T) {
_, s := RunAMaster(t) _, s := RunAMaster(t)
defer s.Close() defer s.Close()
testCases := []string{
"v1beta1",
"v1beta2",
"v1beta3",
}
for _, apiVersion := range testCases {
ns := api.NamespaceDefault ns := api.NamespaceDefault
deleteAllEtcdKeys() deleteAllEtcdKeys()
client := client.NewOrDie(&client.Config{Host: s.URL, Version: apiVersion}) client := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Version()})
info, err := client.ServerVersion() info, err := client.ServerVersion()
if err != nil { if err != nil {
@@ -143,7 +138,6 @@ func TestClient(t *testing.T) {
if actual.Spec.Host != "" { if actual.Spec.Host != "" {
t.Errorf("expected pod to be unscheduled, got %#v", actual) t.Errorf("expected pod to be unscheduled, got %#v", actual)
} }
}
} }
func TestMultiWatch(t *testing.T) { func TestMultiWatch(t *testing.T) {
@@ -159,7 +153,7 @@ func TestMultiWatch(t *testing.T) {
defer s.Close() defer s.Close()
ns := api.NamespaceDefault ns := api.NamespaceDefault
client := client.NewOrDie(&client.Config{Host: s.URL, Version: "v1beta1"}) client := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Version()})
dummyEvent := func(i int) *api.Event { dummyEvent := func(i int) *api.Event {
name := fmt.Sprintf("unrelated-%v", i) name := fmt.Sprintf("unrelated-%v", i)

View File

@@ -23,8 +23,7 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@@ -93,9 +92,9 @@ func TestExtractObj(t *testing.T) {
func TestWatch(t *testing.T) { func TestWatch(t *testing.T) {
client := newEtcdClient() client := newEtcdClient()
helper := tools.NewEtcdHelper(client, latest.Codec) helper := tools.NewEtcdHelper(client, testapi.Codec())
withEtcdKey(func(key string) { withEtcdKey(func(key string) {
resp, err := client.Set(key, runtime.EncodeOrDie(v1beta1.Codec, &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}), 0) resp, err := client.Set(key, runtime.EncodeOrDie(testapi.Codec(), &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}), 0)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }

View File

@@ -26,6 +26,7 @@ import (
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/master" "github.com/GoogleCloudPlatform/kubernetes/pkg/master"
@@ -49,7 +50,7 @@ func deleteSecretOrErrorf(t *testing.T, c *client.Client, ns, name string) {
// TestSecrets tests apiserver-side behavior of creation of secret objects and their use by pods. // TestSecrets tests apiserver-side behavior of creation of secret objects and their use by pods.
func TestSecrets(t *testing.T) { func TestSecrets(t *testing.T) {
helper, err := master.NewEtcdHelper(newEtcdClient(), "v1beta1") helper, err := master.NewEtcdHelper(newEtcdClient(), testapi.Version())
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
@@ -71,16 +72,9 @@ func TestSecrets(t *testing.T) {
AdmissionControl: admit.NewAlwaysAdmit(), AdmissionControl: admit.NewAlwaysAdmit(),
}) })
testCases := []string{
"v1beta1",
"v1beta2",
}
for _, apiVersion := range testCases {
deleteAllEtcdKeys() deleteAllEtcdKeys()
client := client.NewOrDie(&client.Config{Host: s.URL, Version: apiVersion}) client := client.NewOrDie(&client.Config{Host: s.URL, Version: testapi.Version()})
DoTestSecrets(t, client, apiVersion) DoTestSecrets(t, client, testapi.Version())
}
} }
// DoTestSecrets test secrets for one api version. // DoTestSecrets test secrets for one api version.