|
|
|
@@ -38,6 +38,7 @@ import (
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
|
|
|
@@ -54,11 +55,21 @@ func convert(obj runtime.Object) (runtime.Object, error) {
|
|
|
|
|
return obj, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This creates a fake API version, similar to api/latest.go
|
|
|
|
|
// This creates a fake API version, similar to api/latest.go for a v1beta1 equivalent api. It is distinct
|
|
|
|
|
// from the Kubernetes API versions to allow clients to properly distinguish the two.
|
|
|
|
|
const testVersion = "version"
|
|
|
|
|
|
|
|
|
|
var versions = []string{testVersion}
|
|
|
|
|
var codec = runtime.CodecFor(api.Scheme, testVersion)
|
|
|
|
|
// The equivalent of the Kubernetes v1beta3 API.
|
|
|
|
|
const testVersion2 = "version2"
|
|
|
|
|
|
|
|
|
|
var versions = []string{testVersion, testVersion2}
|
|
|
|
|
var legacyCodec = runtime.CodecFor(api.Scheme, testVersion)
|
|
|
|
|
var codec = runtime.CodecFor(api.Scheme, testVersion2)
|
|
|
|
|
|
|
|
|
|
// these codecs reflect ListOptions/DeleteOptions coming from the serverAPIversion
|
|
|
|
|
var versionServerCodec = runtime.CodecFor(api.Scheme, "v1beta1")
|
|
|
|
|
var version2ServerCodec = runtime.CodecFor(api.Scheme, "v1beta3")
|
|
|
|
|
|
|
|
|
|
var accessor = meta.NewAccessor()
|
|
|
|
|
var versioner runtime.ResourceVersioner = accessor
|
|
|
|
|
var selfLinker runtime.SelfLinker = accessor
|
|
|
|
@@ -69,6 +80,12 @@ var requestContextMapper api.RequestContextMapper
|
|
|
|
|
func interfacesFor(version string) (*meta.VersionInterfaces, error) {
|
|
|
|
|
switch version {
|
|
|
|
|
case testVersion:
|
|
|
|
|
return &meta.VersionInterfaces{
|
|
|
|
|
Codec: legacyCodec,
|
|
|
|
|
ObjectConvertor: api.Scheme,
|
|
|
|
|
MetadataAccessor: accessor,
|
|
|
|
|
}, nil
|
|
|
|
|
case testVersion2:
|
|
|
|
|
return &meta.VersionInterfaces{
|
|
|
|
|
Codec: codec,
|
|
|
|
|
ObjectConvertor: api.Scheme,
|
|
|
|
@@ -100,7 +117,10 @@ func init() {
|
|
|
|
|
api.Scheme.AddKnownTypes("", &Simple{}, &SimpleList{}, &api.Status{}, &api.ListOptions{})
|
|
|
|
|
// "version" version
|
|
|
|
|
// TODO: Use versioned api objects?
|
|
|
|
|
api.Scheme.AddKnownTypes(testVersion, &Simple{}, &SimpleList{}, &v1beta1.DeleteOptions{}, &v1beta1.Status{}, &v1beta1.ListOptions{})
|
|
|
|
|
api.Scheme.AddKnownTypes(testVersion, &Simple{}, &SimpleList{}, &v1beta1.Status{})
|
|
|
|
|
// "version2" version
|
|
|
|
|
// TODO: Use versioned api objects?
|
|
|
|
|
api.Scheme.AddKnownTypes(testVersion2, &Simple{}, &SimpleList{}, &v1beta3.Status{})
|
|
|
|
|
|
|
|
|
|
nsMapper := newMapper()
|
|
|
|
|
legacyNsMapper := newMapper()
|
|
|
|
@@ -118,6 +138,18 @@ func init() {
|
|
|
|
|
namespaceMapper = nsMapper
|
|
|
|
|
admissionControl = admit.NewAlwaysAdmit()
|
|
|
|
|
requestContextMapper = api.NewRequestContextMapper()
|
|
|
|
|
|
|
|
|
|
//mapper.(*meta.DefaultRESTMapper).Add(meta.RESTScopeNamespaceLegacy, "Simple", testVersion, false)
|
|
|
|
|
api.Scheme.AddFieldLabelConversionFunc(testVersion, "Simple",
|
|
|
|
|
func(label, value string) (string, string, error) {
|
|
|
|
|
return label, value, nil
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
api.Scheme.AddFieldLabelConversionFunc(testVersion2, "Simple",
|
|
|
|
|
func(label, value string) (string, string, error) {
|
|
|
|
|
return label, value, nil
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// defaultAPIServer exposes nested objects for testability.
|
|
|
|
@@ -129,46 +161,61 @@ type defaultAPIServer struct {
|
|
|
|
|
|
|
|
|
|
// uses the default settings
|
|
|
|
|
func handle(storage map[string]rest.Storage) http.Handler {
|
|
|
|
|
return handleInternal(storage, admissionControl, mapper, selfLinker)
|
|
|
|
|
return handleInternal(true, storage, admissionControl, selfLinker)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// uses the default settings for a v1beta3 compatible api
|
|
|
|
|
func handleNew(storage map[string]rest.Storage) http.Handler {
|
|
|
|
|
return handleInternal(false, storage, admissionControl, selfLinker)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests with a deny admission controller
|
|
|
|
|
func handleDeny(storage map[string]rest.Storage) http.Handler {
|
|
|
|
|
return handleInternal(storage, deny.NewAlwaysDeny(), mapper, selfLinker)
|
|
|
|
|
return handleInternal(true, storage, deny.NewAlwaysDeny(), selfLinker)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests using the new namespace scope mechanism
|
|
|
|
|
func handleNamespaced(storage map[string]rest.Storage) http.Handler {
|
|
|
|
|
return handleInternal(storage, admissionControl, namespaceMapper, selfLinker)
|
|
|
|
|
return handleInternal(false, storage, admissionControl, selfLinker)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tests using a custom self linker
|
|
|
|
|
func handleLinker(storage map[string]rest.Storage, selfLinker runtime.SelfLinker) http.Handler {
|
|
|
|
|
return handleInternal(storage, admissionControl, mapper, selfLinker)
|
|
|
|
|
return handleInternal(true, storage, admissionControl, selfLinker)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleInternal(storage map[string]rest.Storage, admissionControl admission.Interface, mapper meta.RESTMapper, selfLinker runtime.SelfLinker) http.Handler {
|
|
|
|
|
func handleInternal(legacy bool, storage map[string]rest.Storage, admissionControl admission.Interface, selfLinker runtime.SelfLinker) http.Handler {
|
|
|
|
|
group := &APIGroupVersion{
|
|
|
|
|
Storage: storage,
|
|
|
|
|
|
|
|
|
|
Mapper: mapper,
|
|
|
|
|
|
|
|
|
|
Root: "/api",
|
|
|
|
|
Version: testVersion,
|
|
|
|
|
Root: "/api",
|
|
|
|
|
|
|
|
|
|
Creater: api.Scheme,
|
|
|
|
|
Convertor: api.Scheme,
|
|
|
|
|
Typer: api.Scheme,
|
|
|
|
|
Codec: codec,
|
|
|
|
|
Linker: selfLinker,
|
|
|
|
|
|
|
|
|
|
Admit: admissionControl,
|
|
|
|
|
Context: requestContextMapper,
|
|
|
|
|
}
|
|
|
|
|
if legacy {
|
|
|
|
|
group.Version = testVersion
|
|
|
|
|
group.ServerVersion = "v1beta1"
|
|
|
|
|
group.Codec = legacyCodec
|
|
|
|
|
group.Mapper = legacyNamespaceMapper
|
|
|
|
|
} else {
|
|
|
|
|
group.Version = testVersion2
|
|
|
|
|
group.ServerVersion = "v1beta3"
|
|
|
|
|
group.Codec = codec
|
|
|
|
|
group.Mapper = namespaceMapper
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
container := restful.NewContainer()
|
|
|
|
|
container.Router(restful.CurlyRouter{})
|
|
|
|
|
mux := container.ServeMux
|
|
|
|
|
group.InstallREST(container)
|
|
|
|
|
if err := group.InstallREST(container); err != nil {
|
|
|
|
|
panic(fmt.Sprintf("unable to install container %s: %v", group.Version, err))
|
|
|
|
|
}
|
|
|
|
|
ws := new(restful.WebService)
|
|
|
|
|
InstallSupport(mux, ws)
|
|
|
|
|
container.Add(ws)
|
|
|
|
@@ -557,27 +604,27 @@ func TestList(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
// list items in a namespace, v1beta3+
|
|
|
|
|
{
|
|
|
|
|
url: "/api/version/namespaces/default/simple",
|
|
|
|
|
url: "/api/version2/namespaces/default/simple",
|
|
|
|
|
namespace: "default",
|
|
|
|
|
selfLink: "/api/version/namespaces/default/simple",
|
|
|
|
|
selfLink: "/api/version2/namespaces/default/simple",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
url: "/api/version/namespaces/other/simple",
|
|
|
|
|
url: "/api/version2/namespaces/other/simple",
|
|
|
|
|
namespace: "other",
|
|
|
|
|
selfLink: "/api/version/namespaces/other/simple",
|
|
|
|
|
selfLink: "/api/version2/namespaces/other/simple",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
url: "/api/version/namespaces/other/simple?labels=a%3Db&fields=c%3Dd",
|
|
|
|
|
url: "/api/version2/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
|
|
|
|
|
namespace: "other",
|
|
|
|
|
selfLink: "/api/version/namespaces/other/simple",
|
|
|
|
|
selfLink: "/api/version2/namespaces/other/simple",
|
|
|
|
|
label: "a=b",
|
|
|
|
|
field: "c=d",
|
|
|
|
|
},
|
|
|
|
|
// list items across all namespaces
|
|
|
|
|
{
|
|
|
|
|
url: "/api/version/simple",
|
|
|
|
|
url: "/api/version2/simple",
|
|
|
|
|
namespace: "",
|
|
|
|
|
selfLink: "/api/version/simple",
|
|
|
|
|
selfLink: "/api/version2/simple",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for i, testCase := range testCases {
|
|
|
|
@@ -593,7 +640,7 @@ func TestList(t *testing.T) {
|
|
|
|
|
if testCase.legacy {
|
|
|
|
|
handler = handleLinker(storage, selfLinker)
|
|
|
|
|
} else {
|
|
|
|
|
handler = handleInternal(storage, admissionControl, namespaceMapper, selfLinker)
|
|
|
|
|
handler = handleInternal(false, storage, admissionControl, selfLinker)
|
|
|
|
|
}
|
|
|
|
|
server := httptest.NewServer(handler)
|
|
|
|
|
defer server.Close()
|
|
|
|
@@ -605,6 +652,9 @@ func TestList(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
|
t.Errorf("%d: unexpected status: %d, Expected: %d, %#v", i, resp.StatusCode, http.StatusOK, resp)
|
|
|
|
|
body, _ := ioutil.ReadAll(resp.Body)
|
|
|
|
|
t.Logf("%d: body: %s", string(body))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// TODO: future, restore get links
|
|
|
|
|
if !selfLinker.called {
|
|
|
|
@@ -875,16 +925,16 @@ func TestGetNamespaceSelfLink(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
selfLinker := &setTestSelfLinker{
|
|
|
|
|
t: t,
|
|
|
|
|
expectedSet: "/api/version/namespaces/foo/simple/id",
|
|
|
|
|
expectedSet: "/api/version2/namespaces/foo/simple/id",
|
|
|
|
|
name: "id",
|
|
|
|
|
namespace: "foo",
|
|
|
|
|
}
|
|
|
|
|
storage["simple"] = &simpleStorage
|
|
|
|
|
handler := handleInternal(storage, admissionControl, namespaceMapper, selfLinker)
|
|
|
|
|
handler := handleInternal(false, storage, admissionControl, selfLinker)
|
|
|
|
|
server := httptest.NewServer(handler)
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
resp, err := http.Get(server.URL + "/api/version/namespaces/foo/simple/id")
|
|
|
|
|
resp, err := http.Get(server.URL + "/api/version2/namespaces/foo/simple/id")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
@@ -959,7 +1009,7 @@ func TestDeleteWithOptions(t *testing.T) {
|
|
|
|
|
item := &api.DeleteOptions{
|
|
|
|
|
GracePeriodSeconds: &grace,
|
|
|
|
|
}
|
|
|
|
|
body, err := codec.Encode(item)
|
|
|
|
|
body, err := versionServerCodec.Encode(item)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
@@ -1020,7 +1070,7 @@ func TestLegacyDeleteIgnoresOptions(t *testing.T) {
|
|
|
|
|
defer server.Close()
|
|
|
|
|
|
|
|
|
|
item := api.NewDeleteOptions(300)
|
|
|
|
|
body, err := codec.Encode(item)
|
|
|
|
|
body, err := versionServerCodec.Encode(item)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
@@ -1629,7 +1679,7 @@ func TestCreateInvokesAdmissionControl(t *testing.T) {
|
|
|
|
|
namespace: "other",
|
|
|
|
|
expectedSet: "/api/version/foo/bar?namespace=other",
|
|
|
|
|
}
|
|
|
|
|
handler := handleInternal(map[string]rest.Storage{"foo": &storage}, deny.NewAlwaysDeny(), mapper, selfLinker)
|
|
|
|
|
handler := handleInternal(true, map[string]rest.Storage{"foo": &storage}, deny.NewAlwaysDeny(), selfLinker)
|
|
|
|
|
server := httptest.NewServer(handler)
|
|
|
|
|
defer server.Close()
|
|
|
|
|
client := http.Client{}
|
|
|
|
|