diff --git a/federation/apis/core/install/install.go b/federation/apis/core/install/install.go new file mode 100644 index 0000000000..5659821109 --- /dev/null +++ b/federation/apis/core/install/install.go @@ -0,0 +1,160 @@ +/* +Copyright 2016 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 install + +import ( + "fmt" + + "github.com/golang/glog" + + core "k8s.io/kubernetes/federation/apis/core" + core_v1 "k8s.io/kubernetes/federation/apis/core/v1" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/conversion" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/sets" +) + +const importPrefix = "k8s.io/kubernetes/federation/api" + +var accessor = meta.NewAccessor() + +// availableVersions lists all known external versions for this group from most preferred to least preferred +var availableVersions = []unversioned.GroupVersion{core_v1.SchemeGroupVersion} + +func init() { + registered.RegisterVersions(availableVersions) + externalVersions := []unversioned.GroupVersion{} + for _, v := range availableVersions { + if registered.IsAllowedVersion(v) { + externalVersions = append(externalVersions, v) + } + } + if len(externalVersions) == 0 { + glog.V(4).Infof("No version is registered for group %v", core.GroupName) + return + } + + if err := registered.EnableVersions(externalVersions...); err != nil { + glog.V(4).Infof("%v", err) + return + } + if err := enableVersions(externalVersions); err != nil { + glog.V(4).Infof("%v", err) + return + } +} + +// TODO: enableVersions should be centralized rather than spread in each API +// group. +// We can combine registered.RegisterVersions, registered.EnableVersions and +// registered.RegisterGroup once we have moved enableVersions there. +func enableVersions(externalVersions []unversioned.GroupVersion) error { + addVersionsToScheme(externalVersions...) + preferredExternalVersion := externalVersions[0] + + groupMeta := apimachinery.GroupMeta{ + GroupVersion: preferredExternalVersion, + GroupVersions: externalVersions, + RESTMapper: newRESTMapper(externalVersions), + SelfLinker: runtime.SelfLinker(accessor), + InterfacesFor: interfacesFor, + } + + if err := registered.RegisterGroup(groupMeta); err != nil { + return err + } + api.RegisterRESTMapper(groupMeta.RESTMapper) + return nil +} + +// userResources is a group of resources mostly used by a kubectl user +var userResources = []string{"svc"} + +func newRESTMapper(externalVersions []unversioned.GroupVersion) meta.RESTMapper { + // the list of kinds that are scoped at the root of the api hierarchy + // if a kind is not enumerated here, it is assumed to have a namespace scope + rootScoped := sets.NewString() + + // these kinds should be excluded from the list of resources + ignoredKinds := sets.NewString( + "ListOptions", + "DeleteOptions", + "Status") + + mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped) + // setup aliases for groups of resources + mapper.AddResourceAlias("all", userResources...) + + return mapper +} + +// InterfacesFor returns the default Codec and ResourceVersioner for a given version +// string, or an error if the version is not known. +func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { + switch version { + case core_v1.SchemeGroupVersion: + return &meta.VersionInterfaces{ + ObjectConvertor: core.Scheme, + MetadataAccessor: accessor, + }, nil + default: + g, _ := registered.Group(core.GroupName) + return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions) + } +} + +func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) { + // add the internal version to Scheme + core.AddToScheme(core.Scheme) + // add the enabled external versions to Scheme + for _, v := range externalVersions { + if !registered.IsEnabledVersion(v) { + glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v) + continue + } + switch v { + case core_v1.SchemeGroupVersion: + core_v1.AddToScheme(core.Scheme) + } + } + + // This is a "fast-path" that avoids reflection for common types. It focuses on the objects that are + // converted the most in the cluster. + // TODO: generate one of these for every external API group - this is to prove the impact + core.Scheme.AddGenericConversionFunc(func(objA, objB interface{}, s conversion.Scope) (bool, error) { + switch a := objA.(type) { + case *v1.Service: + switch b := objB.(type) { + case *api.Service: + return true, v1.Convert_v1_Service_To_api_Service(a, b, s) + } + case *api.Service: + switch b := objB.(type) { + case *v1.Service: + return true, v1.Convert_api_Service_To_v1_Service(a, b, s) + } + + } + return false, nil + }) +} diff --git a/federation/apis/core/register.go b/federation/apis/core/register.go new file mode 100644 index 0000000000..609a4dbbfd --- /dev/null +++ b/federation/apis/core/register.go @@ -0,0 +1,75 @@ +/* +Copyright 2016 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 core + +import ( + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" +) + +// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered. +var Scheme = runtime.NewScheme() + +// Codecs provides access to encoding and decoding for the scheme +var Codecs = serializer.NewCodecFactory(Scheme) + +// GroupName is the group name use in this package +const GroupName = "" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Unversiond is group version for unversioned API objects +// TODO: this should be v1 probably +var Unversioned = unversioned.GroupVersion{Group: "", Version: "v1"} + +// ParameterCodec handles versioning of objects that are converted to query parameters. +var ParameterCodec = runtime.NewParameterCodec(Scheme) + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) unversioned.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns back a Group qualified GroupResource +func Resource(resource string) unversioned.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +func AddToScheme(scheme *runtime.Scheme) { + if err := Scheme.AddIgnoredConversionType(&unversioned.TypeMeta{}, &unversioned.TypeMeta{}); err != nil { + panic(err) + } + scheme.AddKnownTypes(SchemeGroupVersion, + &api.ServiceList{}, + &api.Service{}, + &api.ListOptions{}, + &api.DeleteOptions{}, + ) + + // Register Unversioned types under their own special group + Scheme.AddUnversionedTypes(Unversioned, + &unversioned.ExportOptions{}, + &unversioned.Status{}, + &unversioned.APIVersions{}, + &unversioned.APIGroupList{}, + &unversioned.APIGroup{}, + &unversioned.APIResourceList{}, + ) +} diff --git a/federation/apis/core/v1/conversion.go b/federation/apis/core/v1/conversion.go new file mode 100644 index 0000000000..e04cdd97f8 --- /dev/null +++ b/federation/apis/core/v1/conversion.go @@ -0,0 +1,58 @@ +/* +Copyright 2016 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 v1 + +import ( + "fmt" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" +) + +func addConversionFuncs(scheme *runtime.Scheme) { + // Add non-generated conversion functions + err := scheme.AddConversionFuncs( + v1.Convert_api_ServiceSpec_To_v1_ServiceSpec, + v1.Convert_v1_ServiceSpec_To_api_ServiceSpec, + ) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } + + // Add field label conversions for kinds having selectable nothing but ObjectMeta fields. + for _, kind := range []string{ + "Service", + } { + err = api.Scheme.AddFieldLabelConversionFunc("v1", kind, + func(label, value string) (string, string, error) { + switch label { + case "metadata.namespace", + "metadata.name": + return label, value, nil + default: + return "", "", fmt.Errorf("field label %q not supported for %q", label, kind) + } + }) + if err != nil { + // If one of the conversion functions is malformed, detect it immediately. + panic(err) + } + } + +} diff --git a/federation/apis/core/v1/defaults.go b/federation/apis/core/v1/defaults.go new file mode 100644 index 0000000000..5e03961883 --- /dev/null +++ b/federation/apis/core/v1/defaults.go @@ -0,0 +1,28 @@ +/* +Copyright 2016 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 v1 + +import ( + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) { + scheme.AddDefaultingFuncs( + v1.SetDefaults_ServiceSpec, + ) +} diff --git a/federation/apis/core/v1/register.go b/federation/apis/core/v1/register.go new file mode 100644 index 0000000000..d942d976d8 --- /dev/null +++ b/federation/apis/core/v1/register.go @@ -0,0 +1,53 @@ +/* +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 v1 + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" + versionedwatch "k8s.io/kubernetes/pkg/watch/versioned" +) + +// GroupName is the group name use in this package +const GroupName = "" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"} + +func AddToScheme(scheme *runtime.Scheme) { + // Add the API to Scheme. + addKnownTypes(scheme) + addConversionFuncs(scheme) + addDefaultingFuncs(scheme) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) { + scheme.AddKnownTypes(SchemeGroupVersion, + &v1.Service{}, + &v1.ServiceList{}, + &v1.ListOptions{}, + &v1.DeleteOptions{}, + ) + + // Add common types + scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.Status{}) + + // Add the watch version that applies + versionedwatch.AddToGroupVersion(scheme, SchemeGroupVersion) +} diff --git a/federation/cmd/federated-apiserver/app/core.go b/federation/cmd/federated-apiserver/app/core.go new file mode 100644 index 0000000000..f91884b36a --- /dev/null +++ b/federation/cmd/federated-apiserver/app/core.go @@ -0,0 +1,53 @@ +/* +Copyright 2016 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 app + +import ( + "github.com/golang/glog" + "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/genericapiserver" + + "k8s.io/kubernetes/federation/apis/core" + _ "k8s.io/kubernetes/federation/apis/core/install" + "k8s.io/kubernetes/federation/apis/core/v1" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/rest" + serviceetcd "k8s.io/kubernetes/pkg/registry/service/etcd" +) + +func installCoreAPIs(s *genericapiserver.ServerRunOptions, g *genericapiserver.GenericAPIServer, f genericapiserver.StorageFactory) { + serviceStore, serviceStatusStorage := serviceetcd.NewREST(createRESTOptionsOrDie(s, g, f, api.Resource("service"))) + coreResources := map[string]rest.Storage{ + "services": serviceStore, + "services/status": serviceStatusStorage, + } + coreGroupMeta := registered.GroupOrDie(core.GroupName) + apiGroupInfo := genericapiserver.APIGroupInfo{ + GroupMeta: *coreGroupMeta, + VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ + v1.SchemeGroupVersion.Version: coreResources, + }, + OptionsExternalVersion: ®istered.GroupOrDie(core.GroupName).GroupVersion, + IsLegacyGroup: true, + Scheme: core.Scheme, + ParameterCodec: core.ParameterCodec, + NegotiatedSerializer: core.Codecs, + } + if err := g.InstallAPIGroup(&apiGroupInfo); err != nil { + glog.Fatalf("Error in registering group version: %v", err) + } +} diff --git a/federation/cmd/federated-apiserver/app/federation.go b/federation/cmd/federated-apiserver/app/federation.go index 0a4460721b..5fcbd3ba10 100644 --- a/federation/cmd/federated-apiserver/app/federation.go +++ b/federation/cmd/federated-apiserver/app/federation.go @@ -24,22 +24,13 @@ import ( "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/genericapiserver" - "k8s.io/kubernetes/pkg/registry/generic" _ "k8s.io/kubernetes/federation/apis/federation/install" clusteretcd "k8s.io/kubernetes/federation/registry/cluster/etcd" ) func installFederationAPIs(s *genericapiserver.ServerRunOptions, g *genericapiserver.GenericAPIServer, f genericapiserver.StorageFactory) { - storage, err := f.New(federation.Resource("clusters")) - if err != nil { - glog.Fatalf("Unable to find storage destination for %v, due to %v", "clusters", err.Error()) - } - clusterStorage, clusterStatusStorage := clusteretcd.NewREST(generic.RESTOptions{ - Storage: storage, - Decorator: g.StorageDecorator(), - DeleteCollectionWorkers: s.DeleteCollectionWorkers, - }) + clusterStorage, clusterStatusStorage := clusteretcd.NewREST(createRESTOptionsOrDie(s, g, f, federation.Resource("clusters"))) federationResources := map[string]rest.Storage{ "clusters": clusterStorage, "clusters/status": clusterStatusStorage, diff --git a/federation/cmd/federated-apiserver/app/server.go b/federation/cmd/federated-apiserver/app/server.go index 4990936dd3..0bb0de8f64 100644 --- a/federation/cmd/federated-apiserver/app/server.go +++ b/federation/cmd/federated-apiserver/app/server.go @@ -33,6 +33,7 @@ import ( "k8s.io/kubernetes/pkg/apiserver/authenticator" "k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/registry/cachesize" + "k8s.io/kubernetes/pkg/registry/generic" ) // NewAPIServerCommand creates a *cobra.Command object with default parameters @@ -141,7 +142,20 @@ func Run(s *genericapiserver.ServerRunOptions) error { } installFederationAPIs(s, m, storageFactory) + installCoreAPIs(s, m, storageFactory) m.Run(s) return nil } + +func createRESTOptionsOrDie(s *genericapiserver.ServerRunOptions, g *genericapiserver.GenericAPIServer, f genericapiserver.StorageFactory, resource unversioned.GroupResource) generic.RESTOptions { + storage, err := f.New(resource) + if err != nil { + glog.Fatalf("Unable to find storage destination for %v, due to %v", resource, err.Error()) + } + return generic.RESTOptions{ + Storage: storage, + Decorator: g.StorageDecorator(), + DeleteCollectionWorkers: s.DeleteCollectionWorkers, + } +} diff --git a/federation/cmd/federated-apiserver/app/server_test.go b/federation/cmd/federated-apiserver/app/server_test.go index 07f469fa90..5b9ea45f83 100644 --- a/federation/cmd/federated-apiserver/app/server_test.go +++ b/federation/cmd/federated-apiserver/app/server_test.go @@ -30,6 +30,7 @@ import ( "github.com/stretchr/testify/assert" fed_v1a1 "k8s.io/kubernetes/federation/apis/federation/v1alpha1" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/genericapiserver" ) @@ -76,7 +77,9 @@ func TestLongRunningRequestRegexp(t *testing.T) { var insecurePort = 8082 var serverIP = fmt.Sprintf("http://localhost:%v", insecurePort) -var groupVersion = fed_v1a1.SchemeGroupVersion +var groupVersions = []unversioned.GroupVersion{ + fed_v1a1.SchemeGroupVersion, +} func TestRun(t *testing.T) { s := genericapiserver.NewServerRunOptions() @@ -151,9 +154,12 @@ func findGroup(groups []unversioned.APIGroup, groupName string) *unversioned.API } func testAPIGroupList(t *testing.T) { - var groupVersionForDiscovery = unversioned.GroupVersionForDiscovery{ - GroupVersion: groupVersion.String(), - Version: groupVersion.Version, + groupVersionForDiscoveryMap := make(map[string]unversioned.GroupVersionForDiscovery) + for _, groupVersion := range groupVersions { + groupVersionForDiscoveryMap[groupVersion.Group] = unversioned.GroupVersionForDiscovery{ + GroupVersion: groupVersion.String(), + Version: groupVersion.Version, + } } serverURL := serverIP + "/apis" @@ -167,31 +173,59 @@ func testAPIGroupList(t *testing.T) { t.Fatalf("Error in unmarshalling response from server %s: %v", serverURL, err) } - found := findGroup(apiGroupList.Groups, groupVersion.Group) - assert.NotNil(t, found) - assert.Equal(t, found.Name, groupVersion.Group) - assert.Equal(t, 1, len(found.Versions)) - assert.Equal(t, found.Versions[0], groupVersionForDiscovery) - assert.Equal(t, found.PreferredVersion, groupVersionForDiscovery) + for _, groupVersion := range groupVersions { + found := findGroup(apiGroupList.Groups, groupVersion.Group) + assert.NotNil(t, found) + assert.Equal(t, groupVersion.Group, found.Name) + assert.Equal(t, 1, len(found.Versions)) + groupVersionForDiscovery := groupVersionForDiscoveryMap[groupVersion.Group] + assert.Equal(t, groupVersionForDiscovery, found.Versions[0]) + assert.Equal(t, groupVersionForDiscovery, found.PreferredVersion) + } } func testAPIGroup(t *testing.T) { - serverURL := serverIP + "/apis/federation" + for _, groupVersion := range groupVersions { + serverURL := serverIP + "/apis/" + groupVersion.Group + contents, err := readResponse(serverURL) + if err != nil { + t.Fatalf("%v", err) + } + var apiGroup unversioned.APIGroup + err = json.Unmarshal(contents, &apiGroup) + if err != nil { + t.Fatalf("Error in unmarshalling response from server %s: %v", serverURL, err) + } + // empty APIVersion for extensions group + if groupVersion.Group == "extensions" { + assert.Equal(t, "", apiGroup.APIVersion) + } else { + assert.Equal(t, "v1", apiGroup.APIVersion) + } + assert.Equal(t, apiGroup.Name, groupVersion.Group) + assert.Equal(t, 1, len(apiGroup.Versions)) + assert.Equal(t, groupVersion.String(), apiGroup.Versions[0].GroupVersion) + assert.Equal(t, groupVersion.Version, apiGroup.Versions[0].Version) + assert.Equal(t, apiGroup.PreferredVersion, apiGroup.Versions[0]) + } + + testCoreAPIGroup(t) +} + +func testCoreAPIGroup(t *testing.T) { + serverURL := serverIP + "/api" contents, err := readResponse(serverURL) if err != nil { t.Fatalf("%v", err) } - var apiGroup unversioned.APIGroup - err = json.Unmarshal(contents, &apiGroup) + var apiVersions unversioned.APIVersions + err = json.Unmarshal(contents, &apiVersions) if err != nil { t.Fatalf("Error in unmarshalling response from server %s: %v", serverURL, err) } - assert.Equal(t, apiGroup.APIVersion, "v1") - assert.Equal(t, apiGroup.Name, groupVersion.Group) - assert.Equal(t, 1, len(apiGroup.Versions)) - assert.Equal(t, apiGroup.Versions[0].GroupVersion, groupVersion.String()) - assert.Equal(t, apiGroup.Versions[0].Version, groupVersion.Version) - assert.Equal(t, apiGroup.Versions[0], apiGroup.PreferredVersion) + assert.Equal(t, 1, len(apiVersions.Versions)) + assert.Equal(t, "v1", apiVersions.Versions[0]) + assert.NotEmpty(t, apiVersions.ServerAddressByClientCIDRs) } func findResource(resources []unversioned.APIResource, resourceName string) *unversioned.APIResource { @@ -204,7 +238,12 @@ func findResource(resources []unversioned.APIResource, resourceName string) *unv } func testAPIResourceList(t *testing.T) { - serverURL := serverIP + "/apis/federation/v1alpha1" + testFederationResourceList(t) + testCoreResourceList(t) +} + +func testFederationResourceList(t *testing.T) { + serverURL := serverIP + "/apis/" + fed_v1a1.SchemeGroupVersion.String() contents, err := readResponse(serverURL) if err != nil { t.Fatalf("%v", err) @@ -214,8 +253,8 @@ func testAPIResourceList(t *testing.T) { if err != nil { t.Fatalf("Error in unmarshalling response from server %s: %v", serverURL, err) } - assert.Equal(t, apiResourceList.APIVersion, "v1") - assert.Equal(t, apiResourceList.GroupVersion, groupVersion.String()) + assert.Equal(t, "v1", apiResourceList.APIVersion) + assert.Equal(t, fed_v1a1.SchemeGroupVersion.String(), apiResourceList.GroupVersion) found := findResource(apiResourceList.APIResources, "clusters") assert.NotNil(t, found) @@ -224,3 +263,25 @@ func testAPIResourceList(t *testing.T) { assert.NotNil(t, found) assert.False(t, found.Namespaced) } + +func testCoreResourceList(t *testing.T) { + serverURL := serverIP + "/api/" + v1.SchemeGroupVersion.String() + contents, err := readResponse(serverURL) + if err != nil { + t.Fatalf("%v", err) + } + var apiResourceList unversioned.APIResourceList + err = json.Unmarshal(contents, &apiResourceList) + if err != nil { + t.Fatalf("Error in unmarshalling response from server %s: %v", serverURL, err) + } + assert.Equal(t, "", apiResourceList.APIVersion) + assert.Equal(t, v1.SchemeGroupVersion.String(), apiResourceList.GroupVersion) + + found := findResource(apiResourceList.APIResources, "services") + assert.NotNil(t, found) + assert.True(t, found.Namespaced) + found = findResource(apiResourceList.APIResources, "services/status") + assert.NotNil(t, found) + assert.True(t, found.Namespaced) +}