Merge pull request #12405 from uluyol/kubectlexp

Add experimental api support to kubectl
This commit is contained in:
CJ Cullen 2015-08-12 14:24:56 -07:00
commit 59da05efdf
17 changed files with 210 additions and 70 deletions

View File

@ -97,7 +97,7 @@ func init() {
"PodProxyOptions",
"Daemon")
mapper := api.NewDefaultRESTMapper(versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
mapper := api.NewDefaultRESTMapper("api", versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
// setup aliases for groups of resources
mapper.AddResourceAlias("all", userResources...)
RESTMapper = mapper

View File

@ -33,11 +33,12 @@ func RegisterRESTMapper(m meta.RESTMapper) {
RESTMapper = append(RESTMapper.(meta.MultiRESTMapper), m)
}
func NewDefaultRESTMapper(versions []string, interfacesFunc meta.VersionInterfacesFunc, importPathPrefix string,
ignoredKinds, rootScoped util.StringSet) *meta.DefaultRESTMapper {
func NewDefaultRESTMapper(group string, versions []string, interfacesFunc meta.VersionInterfacesFunc,
importPathPrefix string, ignoredKinds, rootScoped util.StringSet) *meta.DefaultRESTMapper {
mapper := meta.NewDefaultRESTMapper(versions, interfacesFunc)
// enumerate all supported versions, get the kinds, and register with the mapper how to address our resources.
mapper := meta.NewDefaultRESTMapper(group, versions, interfacesFunc)
// enumerate all supported versions, get the kinds, and register with the mapper how to address
// our resources.
for _, version := range versions {
for kind, oType := range Scheme.KnownTypes(version) {
// TODO: Remove import path prefix check.

View File

@ -142,8 +142,18 @@ type RESTMapping struct {
// RESTMapper allows clients to map resources to kind, and map kind and version
// to interfaces for manipulating those objects. It is primarily intended for
// consumers of Kubernetes compatible REST APIs as defined in docs/api-conventions.md.
//
// The Kubernetes API provides versioned resources and object kinds which are scoped
// to API groups. In other words, kinds and resources should not be assumed to be
// unique across groups.
//
// TODO(caesarxuchao): Add proper multi-group support so that kinds & resources are
// scoped to groups. See http://issues.k8s.io/12413 and http://issues.k8s.io/10009.
type RESTMapper interface {
VersionAndKindForResource(resource string) (defaultVersion, kind string, err error)
// TODO(caesarxuchao): Remove GroupForResource when multi-group support is in (since
// group will be part of the version).
GroupForResource(resource string) (string, error)
RESTMapping(kind string, versions ...string) (*RESTMapping, error)
AliasesForResource(resource string) ([]string, bool)
ResourceSingularizer(resource string) (singular string, err error)

View File

@ -76,6 +76,7 @@ type DefaultRESTMapper struct {
mapping map[string]typeMeta
reverse map[typeMeta]string
scopes map[typeMeta]RESTScope
group string
versions []string
plurals map[string]string
singulars map[string]string
@ -88,10 +89,10 @@ type VersionInterfacesFunc func(apiVersion string) (*VersionInterfaces, error)
// NewDefaultRESTMapper initializes a mapping between Kind and APIVersion
// to a resource name and back based on the objects in a runtime.Scheme
// and the Kubernetes API conventions. Takes a priority list of the versions to
// search when an object has no default version (set empty to return an error)
// and the Kubernetes API conventions. Takes a group name, a priority list of the versions
// to search when an object has no default version (set empty to return an error),
// and a function that retrieves the correct codec and metadata for a given version.
func NewDefaultRESTMapper(versions []string, f VersionInterfacesFunc) *DefaultRESTMapper {
func NewDefaultRESTMapper(group string, versions []string, f VersionInterfacesFunc) *DefaultRESTMapper {
mapping := make(map[string]typeMeta)
reverse := make(map[typeMeta]string)
scopes := make(map[typeMeta]RESTScope)
@ -103,6 +104,7 @@ func NewDefaultRESTMapper(versions []string, f VersionInterfacesFunc) *DefaultRE
mapping: mapping,
reverse: reverse,
scopes: scopes,
group: group,
versions: versions,
plurals: plurals,
singulars: singulars,
@ -174,6 +176,13 @@ func (m *DefaultRESTMapper) VersionAndKindForResource(resource string) (defaultV
return meta.APIVersion, meta.Kind, nil
}
func (m *DefaultRESTMapper) GroupForResource(resource string) (string, error) {
if _, ok := m.mapping[strings.ToLower(resource)]; !ok {
return "", fmt.Errorf("no resource %q has been defined", resource)
}
return m.group, nil
}
// RESTMapping returns a struct representing the resource path and conversion interfaces a
// RESTClient should use to operate on the provided kind in order of versions. If a version search
// order is not provided, the search order provided to DefaultRESTMapper will be used to resolve which
@ -292,6 +301,18 @@ func (m MultiRESTMapper) VersionAndKindForResource(resource string) (defaultVers
return
}
// GroupForResource provides the Group mappings for the REST resources. This
// implementation supports multiple REST schemas and returns the first match.
func (m MultiRESTMapper) GroupForResource(resource string) (group string, err error) {
for _, t := range m {
group, err = t.GroupForResource(resource)
if err == nil {
return
}
}
return
}
// RESTMapping provides the REST mapping for the resource based on the resource
// kind and version. This implementation supports multiple REST schemas and
// return the first match.

View File

@ -93,7 +93,7 @@ func TestRESTMapperVersionAndKindForResource(t *testing.T) {
{Resource: "internalObjects", MixedCase: true, Kind: "InternalObject", APIVersion: "test"},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
mapper := NewDefaultRESTMapper("tgroup", []string{"test"}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
v, k, err := mapper.VersionAndKindForResource(testCase.Resource)
hasErr := err != nil
@ -107,6 +107,33 @@ func TestRESTMapperVersionAndKindForResource(t *testing.T) {
}
}
func TestRESTMapperGroupForResource(t *testing.T) {
testCases := []struct {
Resource string
Kind, APIVersion, Group string
Err bool
}{
{Resource: "myObject", Kind: "MyObject", APIVersion: "test", Group: "testapi"},
{Resource: "myobject", Kind: "MyObject", APIVersion: "test", Group: "testapi2"},
{Resource: "myObje", Err: true, Kind: "MyObject", APIVersion: "test", Group: "testapi"},
{Resource: "myobje", Err: true, Kind: "MyObject", APIVersion: "test", Group: "testapi"},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper(testCase.Group, []string{"test"}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, false)
g, err := mapper.GroupForResource(testCase.Resource)
if testCase.Err {
if err == nil {
t.Errorf("%d: expected error", i)
}
} else if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
} else if g != testCase.Group {
t.Errorf("%d: expected group %q, got %q", i, testCase.Group, g)
}
}
}
func TestKindToResource(t *testing.T) {
testCases := []struct {
Kind string
@ -159,7 +186,7 @@ func TestRESTMapperResourceSingularizer(t *testing.T) {
{Kind: "lowercases", APIVersion: "test", MixedCase: false, Plural: "lowercases", Singular: "lowercases"},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper([]string{"test"}, fakeInterfaces)
mapper := NewDefaultRESTMapper("tgroup", []string{"test"}, fakeInterfaces)
// create singular/plural mapping
mapper.Add(RESTScopeNamespace, testCase.Kind, testCase.APIVersion, testCase.MixedCase)
singular, _ := mapper.ResourceSingularizer(testCase.Plural)
@ -198,7 +225,7 @@ func TestRESTMapperRESTMapping(t *testing.T) {
// TODO: add test for a resource that exists in one version but not another
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
mapper := NewDefaultRESTMapper("tgroup", testCase.DefaultVersions, fakeInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", "test", testCase.MixedCase)
mapping, err := mapper.RESTMapping(testCase.Kind, testCase.APIVersions...)
hasErr := err != nil
@ -225,7 +252,7 @@ func TestRESTMapperRESTMapping(t *testing.T) {
}
func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, fakeInterfaces)
mapper := NewDefaultRESTMapper("tgroup", []string{"test1", "test2"}, fakeInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
mapper.Add(RESTScopeNamespace, "OtherObject", "test2", false)
@ -278,7 +305,7 @@ func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
}
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
mapper := NewDefaultRESTMapper([]string{"test1", "test2"}, unmatchedVersionInterfaces)
mapper := NewDefaultRESTMapper("tgroup", []string{"test1", "test2"}, unmatchedVersionInterfaces)
mapper.Add(RESTScopeNamespace, "InternalObject", "test1", false)
_, err := mapper.RESTMapping("InternalObject", "test1")
if err == nil {

View File

@ -88,7 +88,7 @@ func interfacesFor(version string) (*meta.VersionInterfaces, error) {
}
func newMapper() *meta.DefaultRESTMapper {
return meta.NewDefaultRESTMapper(versions, interfacesFor)
return meta.NewDefaultRESTMapper("testgroup", versions, interfacesFor)
}
func addTestTypes() {

View File

@ -55,7 +55,7 @@ func init() {
ignoredKinds := util.NewStringSet()
RESTMapper = api.NewDefaultRESTMapper(Versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
RESTMapper = api.NewDefaultRESTMapper("experimental", Versions, InterfacesFor, importPrefix, ignoredKinds, rootScoped)
api.RegisterRESTMapper(RESTMapper)
}

View File

@ -17,12 +17,13 @@ limitations under the License.
package cmd
import (
"fmt"
"io"
"os"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/api"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
@ -40,7 +41,7 @@ func NewCmdApiVersions(f *cmdutil.Factory, out io.Writer) *cobra.Command {
return cmd
}
func RunApiVersions(f *cmdutil.Factory, out io.Writer) error {
func RunApiVersions(f *cmdutil.Factory, w io.Writer) error {
if len(os.Args) > 1 && os.Args[1] == "apiversions" {
printDeprecationWarning("api-versions", "apiversions")
}
@ -50,6 +51,24 @@ func RunApiVersions(f *cmdutil.Factory, out io.Writer) error {
return err
}
kubectl.GetApiVersions(out, client)
apiVersions, err := client.ServerAPIVersions()
if err != nil {
fmt.Printf("Couldn't get available api versions from server: %v\n", err)
os.Exit(1)
}
var expAPIVersions *api.APIVersions
showExpVersions := false
expClient, err := f.ExperimentalClient()
if err == nil {
expAPIVersions, err = expClient.ServerAPIVersions()
showExpVersions = err == nil
}
fmt.Fprintf(w, "Available Server Api Versions: %#v\n", *apiVersions)
if showExpVersions {
fmt.Fprintf(w, "Available Server Experimental Api Versions: %#v\n", *expAPIVersions)
}
return nil
}

View File

@ -83,7 +83,7 @@ func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
codec := runtime.CodecFor(scheme, "unlikelyversion")
validVersion := testapi.Version()
mapper := meta.NewDefaultRESTMapper([]string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, error) {
mapper := meta.NewDefaultRESTMapper("apitest", []string{"unlikelyversion", validVersion}, func(version string) (*meta.VersionInterfaces, error) {
return &meta.VersionInterfaces{
Codec: runtime.CodecFor(scheme, version),
ObjectConvertor: scheme,

View File

@ -22,17 +22,17 @@ import (
"k8s.io/kubernetes/pkg/client/clientcmd"
)
func NewClientCache(loader clientcmd.ClientConfig) *clientCache {
return &clientCache{
func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
return &ClientCache{
clients: make(map[string]*client.Client),
configs: make(map[string]*client.Config),
loader: loader,
}
}
// clientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
// ClientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
// is invoked only once
type clientCache struct {
type ClientCache struct {
loader clientcmd.ClientConfig
clients map[string]*client.Client
configs map[string]*client.Config
@ -42,7 +42,7 @@ type clientCache struct {
}
// ClientConfigForVersion returns the correct config for a server
func (c *clientCache) ClientConfigForVersion(version string) (*client.Config, error) {
func (c *ClientCache) ClientConfigForVersion(version string) (*client.Config, error) {
if c.defaultConfig == nil {
config, err := c.loader.ClientConfig()
if err != nil {
@ -73,7 +73,7 @@ func (c *clientCache) ClientConfigForVersion(version string) (*client.Config, er
// ClientForVersion initializes or reuses a client for the specified version, or returns an
// error if that is not possible
func (c *clientCache) ClientForVersion(version string) (*client.Client, error) {
func (c *ClientCache) ClientForVersion(version string) (*client.Client, error) {
if client, ok := c.clients[version]; ok {
return client, nil
}

View File

@ -17,6 +17,7 @@ limitations under the License.
package util
import (
"errors"
"flag"
"fmt"
"io"
@ -27,7 +28,6 @@ import (
"github.com/spf13/pflag"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/latest"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/registered"
"k8s.io/kubernetes/pkg/api/validation"
@ -49,7 +49,7 @@ const (
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
// commands are decoupled from the factory).
type Factory struct {
clients *clientCache
clients *ClientCache
flags *pflag.FlagSet
generators map[string]kubectl.Generator
@ -57,6 +57,8 @@ type Factory struct {
Object func() (meta.RESTMapper, runtime.ObjectTyper)
// Returns a client for accessing Kubernetes resources or an error.
Client func() (*client.Client, error)
// Returns a client for accessing experimental Kubernetes resources or an error.
ExperimentalClient func() (*client.ExperimentalClient, error)
// Returns a client.Config for accessing the Kubernetes server.
ClientConfig func() (*client.Config, error)
// Returns a RESTClient for working with the specified RESTMapping or an error. This is intended
@ -90,7 +92,7 @@ type Factory struct {
// if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig.
// if optionalClientConfig is not nil, then this factory will make use of it.
func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
mapper := kubectl.ShortcutExpander{RESTMapper: latest.RESTMapper}
mapper := kubectl.ShortcutExpander{RESTMapper: api.RESTMapper}
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags
@ -109,6 +111,25 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
clients := NewClientCache(clientConfig)
// Initialize the experimental client (if possible). Failing here is non-fatal, errors
// will be returned when an experimental client is explicitly requested.
var experimentalClient *client.ExperimentalClient
cfg, experimentalClientErr := clientConfig.ClientConfig()
if experimentalClientErr == nil {
experimentalClient, experimentalClientErr = client.NewExperimental(cfg)
}
noClientErr := errors.New("could not get client")
getBothClients := func(group string, version string) (client *client.Client, expClient *client.ExperimentalClient, err error) {
err = noClientErr
switch group {
case "api":
client, err = clients.ClientForVersion(version)
case "experimental":
expClient, err = experimentalClient, experimentalClientErr
}
return
}
return &Factory{
clients: clients,
flags: flags,
@ -124,26 +145,46 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
Client: func() (*client.Client, error) {
return clients.ClientForVersion("")
},
ExperimentalClient: func() (*client.ExperimentalClient, error) {
return experimentalClient, experimentalClientErr
},
ClientConfig: func() (*client.Config, error) {
return clients.ClientConfigForVersion("")
},
RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
group, err := api.RESTMapper.GroupForResource(mapping.Resource)
if err != nil {
return nil, err
}
return client.RESTClient, nil
switch group {
case "api":
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
return client.RESTClient, nil
case "experimental":
client, err := experimentalClient, experimentalClientErr
if err != nil {
return nil, err
}
return client.RESTClient, nil
}
return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource)
},
Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
group, err := api.RESTMapper.GroupForResource(mapping.Resource)
if err != nil {
return nil, err
}
describer, ok := kubectl.DescriberFor(mapping.Kind, client)
if !ok {
return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
client, expClient, err := getBothClients(group, mapping.APIVersion)
if err != nil {
return nil, err
}
return describer, nil
if describer, ok := kubectl.DescriberFor(mapping.Kind, client, expClient); ok {
return describer, nil
}
return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
},
Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, columnLabels), nil
@ -192,18 +233,26 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return meta.NewAccessor().Labels(object)
},
Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
group, err := api.RESTMapper.GroupForResource(mapping.Resource)
if err != nil {
return nil, err
}
client, _, err := getBothClients(group, mapping.APIVersion)
if err != nil {
return nil, err
}
return kubectl.ScalerFor(mapping.Kind, kubectl.NewScalerClient(client))
},
Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
group, err := api.RESTMapper.GroupForResource(mapping.Resource)
if err != nil {
return nil, err
}
return kubectl.ReaperFor(mapping.Kind, client)
client, expClient, err := getBothClients(group, mapping.APIVersion)
if err != nil {
return nil, err
}
return kubectl.ReaperFor(mapping.Kind, client, expClient)
},
Validator: func() (validation.Schema, error) {
if flags.Lookup("validate").Value.String() == "true" {
@ -211,7 +260,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
if err != nil {
return nil, err
}
return &clientSwaggerSchema{client, api.Scheme}, nil
return &clientSwaggerSchema{client, experimentalClient, api.Scheme}, nil
}
return validation.NullSchema{}, nil
},
@ -274,20 +323,14 @@ func getServicePorts(spec api.ServiceSpec) []string {
}
type clientSwaggerSchema struct {
c *client.Client
t runtime.ObjectTyper
c *client.Client
ec *client.ExperimentalClient
t runtime.ObjectTyper
}
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
version, _, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data)
if err != nil {
return err
}
if ok := registered.IsRegisteredAPIVersion(version); !ok {
return fmt.Errorf("API version %q isn't supported, only supports API versions %q", version, registered.RegisteredVersions)
}
schemaData, err := c.c.RESTClient.Get().
AbsPath("/swaggerapi/api", version).
func getSchemaAndValidate(c *client.RESTClient, data []byte, group, version string) error {
schemaData, err := c.Get().
AbsPath("/swaggerapi", group, version).
Do().
Raw()
if err != nil {
@ -300,6 +343,28 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
return schema.ValidateBytes(data)
}
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
version, _, err := runtime.UnstructuredJSONScheme.DataVersionAndKind(data)
if err != nil {
return err
}
if ok := registered.IsRegisteredAPIVersion(version); !ok {
return fmt.Errorf("API version %q isn't supported, only supports API versions %q", version, registered.RegisteredVersions)
}
// First try stable api, if we can't validate using that, try experimental.
// If experimental fails, return error from stable api.
// TODO: Figure out which group to try once multiple group support is merged
// instead of trying everything.
err = getSchemaAndValidate(c.c.RESTClient, data, "api", version)
if err != nil && c.ec != nil {
errExp := getSchemaAndValidate(c.ec.RESTClient, data, "experimental", version)
if errExp == nil {
return nil
}
}
return err
}
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
// 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me.
// 1. Merge together the kubeconfig itself. This is done with the following hierarchy rules:

View File

@ -82,6 +82,10 @@ func describerMap(c *client.Client) map[string]Describer {
return m
}
func expDescriberMap(c *client.ExperimentalClient) map[string]Describer {
return map[string]Describer{}
}
// List of all resource types we can describe
func DescribableResources() []string {
keys := make([]string, 0)
@ -95,12 +99,15 @@ func DescribableResources() []string {
// Describer returns the default describe functions for each of the standard
// Kubernetes types.
func DescriberFor(kind string, c *client.Client) (Describer, bool) {
f, ok := describerMap(c)[kind]
if ok {
return f, true
func DescriberFor(kind string, c *client.Client, ec *client.ExperimentalClient) (Describer, bool) {
var f Describer
var ok bool
if c != nil {
f, ok = describerMap(c)[kind]
} else if ec != nil {
f, ok = expDescriberMap(ec)[kind]
}
return nil, false
return f, ok
}
// DefaultObjectDescriber can describe the default Kubernetes objects.

View File

@ -52,7 +52,7 @@ func IsNoSuchReaperError(err error) bool {
return ok
}
func ReaperFor(kind string, c client.Interface) (Reaper, error) {
func ReaperFor(kind string, c client.Interface, ec *client.ExperimentalClient) (Reaper, error) {
switch kind {
case "ReplicationController":
return &ReplicationControllerReaper{c, Interval, Timeout}, nil

View File

@ -156,7 +156,7 @@ func TestSimpleStop(t *testing.T) {
}
for _, test := range tests {
fake := test.fake
reaper, err := ReaperFor(test.kind, fake)
reaper, err := ReaperFor(test.kind, fake, nil)
if err != nil {
t.Errorf("unexpected error: %v (%s)", err, test.test)
}

View File

@ -40,13 +40,3 @@ func GetVersion(w io.Writer, kubeClient client.Interface) {
func GetClientVersion(w io.Writer) {
fmt.Fprintf(w, "Client Version: %#v\n", version.Get())
}
func GetApiVersions(w io.Writer, kubeClient client.Interface) {
apiVersions, err := kubeClient.ServerAPIVersions()
if err != nil {
fmt.Printf("Couldn't get available api versions from server: %v\n", err)
os.Exit(1)
}
fmt.Fprintf(w, "Available Server Api Versions: %#v\n", *apiVersions)
}

View File

@ -89,7 +89,7 @@ func ServeImageOrFail(f *Framework, test string, image string) {
defer func() {
// Resize the replication controller to zero to get rid of pods.
By("Cleaning up the replication controller")
rcReaper, err := kubectl.ReaperFor("ReplicationController", f.Client)
rcReaper, err := kubectl.ReaperFor("ReplicationController", f.Client, nil)
if err != nil {
Logf("Failed to cleanup replication controller %v: %v.", controller.Name, err)
}

View File

@ -190,7 +190,7 @@ func RCFromManifest(fileName string) *api.ReplicationController {
// StopRC stops the rc via kubectl's stop library
func StopRC(rc *api.ReplicationController, restClient *client.Client) error {
reaper, err := kubectl.ReaperFor("ReplicationController", restClient)
reaper, err := kubectl.ReaperFor("ReplicationController", restClient, nil)
if err != nil || reaper == nil {
return err
}