vendor updates

This commit is contained in:
Serguei Bezverkhi
2018-03-06 17:33:18 -05:00
parent 4b3ebc171b
commit e9033989a0
5854 changed files with 248382 additions and 119809 deletions

View File

@ -30,7 +30,6 @@ go_library(
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/unversioned:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/categories:go_default_library",
@ -40,6 +39,7 @@ go_library(
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util/transport:go_default_library",
"//pkg/kubectl/validation:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
@ -53,6 +53,7 @@ go_library(
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
"//vendor/k8s.io/api/batch/v1:go_default_library",
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
@ -76,6 +77,7 @@ go_library(
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/scale:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/util/homedir:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
@ -94,8 +96,7 @@ go_test(
data = [
"//api/swagger-spec",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util",
library = ":go_default_library",
embed = [":go_default_library"],
visibility = [
"//build/visible_to:COMMON_testing",
],

View File

@ -24,8 +24,8 @@ import (
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
oldclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/version"
)
@ -150,7 +150,7 @@ func (c *ClientCache) clientConfigForVersion(requiredVersion *schema.GroupVersio
}
// TODO this isn't what we want. Each clientset should be setting defaults as it sees fit.
oldclient.SetKubernetesDefaults(&config)
setKubernetesDefaults(&config)
if requiredVersion != nil {
c.configs[*requiredVersion] = copyConfig(&config)
@ -165,6 +165,22 @@ func (c *ClientCache) clientConfigForVersion(requiredVersion *schema.GroupVersio
return copyConfig(&config), nil
}
// setKubernetesDefaults sets default values on the provided client config for accessing the
// Kubernetes API or returns an error if any of the defaults are impossible or invalid.
func setKubernetesDefaults(config *restclient.Config) error {
if config.APIPath == "" {
config.APIPath = "/api"
}
// TODO chase down uses and tolerate nil
if config.GroupVersion == nil {
config.GroupVersion = &schema.GroupVersion{}
}
if config.NegotiatedSerializer == nil {
config.NegotiatedSerializer = legacyscheme.Codecs
}
return restclient.SetKubernetesDefaults(config)
}
func copyConfig(in *restclient.Config) *restclient.Config {
configCopy := *in
copyGroupVersion := *configCopy.GroupVersion

View File

@ -42,8 +42,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["editor_test.go"],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor",
library = ":go_default_library",
embed = [":go_default_library"],
)
filegroup(

View File

@ -60,10 +60,8 @@ type EditOptions struct {
cmdutil.ValidateOptions
Mapper meta.RESTMapper
ResourceMapper *resource.Mapper
OriginalResult *resource.Result
Encoder runtime.Encoder
EditMode EditMode
@ -107,7 +105,6 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []
if err != nil {
return err
}
mapper, _ := f.Object()
b := f.NewBuilder().
Unstructured()
if o.EditMode == NormalEditMode || o.EditMode == ApplyEditMode {
@ -138,9 +135,7 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []
Do()
}
o.Mapper = mapper
o.CmdNamespace = cmdNamespace
o.Encoder = f.JSONEncoder()
o.f = f
// Set up writer
@ -397,25 +392,25 @@ func (o *EditOptions) visitToApplyEditPatch(originalInfos []*resource.Info, patc
return fmt.Errorf("no original object found for %#v", info.Object)
}
originalJS, err := encodeToJson(o.Encoder, originalInfo.Object)
originalJS, err := encodeToJson(cmdutil.InternalVersionJSONEncoder(), originalInfo.Object)
if err != nil {
return err
}
editedJS, err := encodeToJson(o.Encoder, info.Object)
editedJS, err := encodeToJson(cmdutil.InternalVersionJSONEncoder(), info.Object)
if err != nil {
return err
}
if reflect.DeepEqual(originalJS, editedJS) {
o.f.PrintSuccess(o.Mapper, false, o.Out, info.Mapping.Resource, info.Name, false, "skipped")
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "skipped")
return nil
} else {
err := o.annotationPatch(info)
if err != nil {
return err
}
o.f.PrintSuccess(o.Mapper, false, o.Out, info.Mapping.Resource, info.Name, false, "edited")
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "edited")
return nil
}
})
@ -423,7 +418,7 @@ func (o *EditOptions) visitToApplyEditPatch(originalInfos []*resource.Info, patc
}
func (o *EditOptions) annotationPatch(update *resource.Info) error {
patch, _, patchType, err := GetApplyPatch(update.Object, o.Encoder)
patch, _, patchType, err := GetApplyPatch(update.Object, cmdutil.InternalVersionJSONEncoder())
if err != nil {
return err
}
@ -522,19 +517,19 @@ func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor
return fmt.Errorf("no original object found for %#v", info.Object)
}
originalJS, err := encodeToJson(o.Encoder, originalInfo.Object)
originalJS, err := encodeToJson(cmdutil.InternalVersionJSONEncoder(), originalInfo.Object)
if err != nil {
return err
}
editedJS, err := encodeToJson(o.Encoder, info.Object)
editedJS, err := encodeToJson(cmdutil.InternalVersionJSONEncoder(), info.Object)
if err != nil {
return err
}
if reflect.DeepEqual(originalJS, editedJS) {
// no edit, so just skip it.
o.f.PrintSuccess(o.Mapper, false, o.Out, info.Mapping.Resource, info.Name, false, "skipped")
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "skipped")
return nil
}
@ -588,7 +583,7 @@ func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor
return nil
}
info.Refresh(patched, true)
o.f.PrintSuccess(o.Mapper, false, o.Out, info.Mapping.Resource, info.Name, false, "edited")
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "edited")
return nil
})
return err
@ -599,7 +594,7 @@ func (o *EditOptions) visitToCreate(createVisitor resource.Visitor) error {
if err := resource.CreateAndRefresh(info); err != nil {
return err
}
o.f.PrintSuccess(o.Mapper, false, o.Out, info.Mapping.Resource, info.Name, false, "created")
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "created")
return nil
})
return err
@ -610,7 +605,7 @@ func (o *EditOptions) visitAnnotation(annotationVisitor resource.Visitor) error
err := annotationVisitor.Visit(func(info *resource.Info, incomingErr error) error {
// put configuration annotation in "updates"
if o.ApplyAnnotation {
if err := kubectl.CreateOrUpdateAnnotation(true, info, o.Encoder); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(true, info, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
}

View File

@ -37,6 +37,5 @@ filegroup(
go_test(
name = "go_default_test",
srcs = ["env_parse_test.go"],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/env",
library = ":go_default_library",
embed = [":go_default_library"],
)

View File

@ -46,7 +46,7 @@ func getSecretRefValue(client kubernetes.Interface, namespace string, store *Res
secret, ok := store.SecretStore[secretSelector.Name]
if !ok {
var err error
secret, err = client.Core().Secrets(namespace).Get(secretSelector.Name, metav1.GetOptions{})
secret, err = client.CoreV1().Secrets(namespace).Get(secretSelector.Name, metav1.GetOptions{})
if err != nil {
return "", err
}
@ -64,7 +64,7 @@ func getConfigMapRefValue(client kubernetes.Interface, namespace string, store *
configMap, ok := store.ConfigMapStore[configMapSelector.Name]
if !ok {
var err error
configMap, err = client.Core().ConfigMaps(namespace).Get(configMapSelector.Name, metav1.GetOptions{})
configMap, err = client.CoreV1().ConfigMaps(namespace).Get(configMapSelector.Name, metav1.GetOptions{})
if err != nil {
return "", err
}

View File

@ -18,7 +18,6 @@ package util
import (
"fmt"
"io"
"sort"
"strconv"
"strings"
@ -60,10 +59,10 @@ var (
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
// of resources and different API sets.
// The rings are here for a reason. In order for composers to be able to provide alternative factory implementations
// The rings are here for a reason. In order for composers to be able to provide alternative factory implementations
// they need to provide low level pieces of *certain* functions so that when the factory calls back into itself
// it uses the custom version of the function. Rather than try to enumerate everything that someone would want to override
// we split the factory into rings, where each ring can depend on methods an earlier ring, but cannot depend
// it uses the custom version of the function. Rather than try to enumerate everything that someone would want to override
// we split the factory into rings, where each ring can depend on methods in an earlier ring, but cannot depend
// upon peer methods in its own ring.
// TODO: make the functions interfaces
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
@ -103,18 +102,6 @@ type ClientAccessFactory interface {
// just directions to the server. People use this to build RESTMappers on top of
BareClientConfig() (*restclient.Config, error)
// TODO remove. This should be rolled into `ClientSet`
ClientSetForVersion(requiredVersion *schema.GroupVersion) (internalclientset.Interface, error)
// TODO remove. This should be rolled into `ClientConfig`
ClientConfigForVersion(requiredVersion *schema.GroupVersion) (*restclient.Config, error)
// Returns interfaces for decoding objects - if toInternal is set, decoded objects will be converted
// into their internal form (if possible). Eventually the internal form will be removed as an option,
// and only versioned objects will be returned.
Decoder(toInternal bool) runtime.Decoder
// Returns an encoder capable of encoding a provided object into JSON in the default desired version.
JSONEncoder() runtime.Encoder
// UpdatePodSpecForObject will call the provided function on the pod spec this object supports,
// return false if no pod spec is supported, or return an error.
UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
@ -146,14 +133,12 @@ type ClientAccessFactory interface {
// SuggestedPodTemplateResources returns a list of resource types that declare a pod template
SuggestedPodTemplateResources() []schema.GroupResource
// Returns a Printer for formatting objects of the given type or an error.
Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error)
// Pauser marks the object in the info as paused. Currently supported only for Deployments.
// Returns the patched object in bytes and any error that occured during the encoding or
// Returns the patched object in bytes and any error that occurred during the encoding or
// in case the object is already paused.
Pauser(info *resource.Info) ([]byte, error)
// Resumer resumes a paused object inside the info. Currently supported only for Deployments.
// Returns the patched object in bytes and any error that occured during the encoding or
// Returns the patched object in bytes and any error that occurred during the encoding or
// in case the object is already resumed.
Resumer(info *resource.Info) ([]byte, error)
@ -177,12 +162,9 @@ type ClientAccessFactory interface {
// can range over in order to determine if the user has specified an editor
// of their choice.
EditorEnvs() []string
// PrintObjectSpecificMessage prints object-specific messages on the provided writer
PrintObjectSpecificMessage(obj runtime.Object, out io.Writer)
}
// ObjectMappingFactory holds the second level of factory methods. These functions depend upon ClientAccessFactory methods.
// ObjectMappingFactory holds the second level of factory methods. These functions depend upon ClientAccessFactory methods.
// Generally they provide object typing and functions that build requests based on the negotiated clients.
type ObjectMappingFactory interface {
// Returns interfaces for dealing with arbitrary runtime.Objects.
@ -220,31 +202,13 @@ type ObjectMappingFactory interface {
// Returns a schema that can validate objects stored on disk.
Validator(validate bool) (validation.Schema, error)
// OpenAPISchema returns the schema openapi schema definiton
// OpenAPISchema returns the schema openapi schema definition
OpenAPISchema() (openapi.Resources, error)
}
// BuilderFactory holds the second level of factory methods. These functions depend upon ObjectMappingFactory and ClientAccessFactory methods.
// BuilderFactory holds the third level of factory methods. These functions depend upon ObjectMappingFactory and ClientAccessFactory methods.
// Generally they depend upon client mapper functions
type BuilderFactory interface {
// PrinterForCommand returns the default printer for the command. It requires that certain options
// are declared on the command (see AddPrinterFlags). Returns a printer, or an error if a printer
// could not be found.
PrinterForOptions(options *printers.PrintOptions) (printers.ResourcePrinter, error)
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
// Returns a printer, true if the printer is generic (is not internal), or
// an error if a printer could not be found.
PrinterForMapping(options *printers.PrintOptions, mapping *meta.RESTMapping) (printers.ResourcePrinter, error)
// PrintObject prints an api object given command line flags to modify the output format
PrintObject(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
// PrintResourceInfoForCommand receives a *cobra.Command and a *resource.Info and
// attempts to print an info object based on the specified output format. If the
// object passed is non-generic, it attempts to print the object using a HumanReadablePrinter.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, out io.Writer) error
// PrintSuccess prints message after finishing mutating operations
PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource, name string, dryRun bool, operation string)
// NewBuilder returns an object that assists in loading objects from both disk and the server
// and which implements the common patterns for CLI interactions with generic resources.
NewBuilder() *resource.Builder
@ -254,16 +218,6 @@ type BuilderFactory interface {
PluginRunner() plugins.PluginRunner
}
func getGroupVersionKinds(gvks []schema.GroupVersionKind, group string) []schema.GroupVersionKind {
result := []schema.GroupVersionKind{}
for ix := range gvks {
if gvks[ix].Group == group {
result = append(result, gvks[ix])
}
}
return result
}
type factory struct {
ClientAccessFactory
ObjectMappingFactory

View File

@ -19,18 +19,11 @@ limitations under the License.
package util
import (
"fmt"
"io"
"os"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl/plugins"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
)
type ring2Factory struct {
@ -47,105 +40,6 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac
return f
}
func (f *ring2Factory) PrinterForOptions(options *printers.PrintOptions) (printers.ResourcePrinter, error) {
var mapper meta.RESTMapper
var typer runtime.ObjectTyper
mapper, typer = f.objectMappingFactory.Object()
// TODO: used by the custom column implementation and the name implementation, break this dependency
decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme}
encoder := f.clientAccessFactory.JSONEncoder()
return PrinterForOptions(mapper, typer, encoder, decoders, options)
}
func (f *ring2Factory) PrinterForMapping(options *printers.PrintOptions, mapping *meta.RESTMapping) (printers.ResourcePrinter, error) {
printer, err := f.PrinterForOptions(options)
if err != nil {
return nil, err
}
// Make sure we output versioned data for generic printers
if printer.IsGeneric() {
if mapping == nil {
return nil, fmt.Errorf("no serialization format found")
}
version := mapping.GroupVersionKind.GroupVersion()
if version.Empty() {
return nil, fmt.Errorf("no serialization format found")
}
printer = printers.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion())
}
return printer, nil
}
func (f *ring2Factory) PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource, name string, dryRun bool, operation string) {
resource, _ = mapper.ResourceSingularizer(resource)
dryRunMsg := ""
if dryRun {
dryRunMsg = " (dry run)"
}
if shortOutput {
// -o name: prints resource/name
if len(resource) > 0 {
fmt.Fprintf(out, "%s/%s\n", resource, name)
} else {
fmt.Fprintf(out, "%s\n", name)
}
} else {
// understandable output by default
if len(resource) > 0 {
fmt.Fprintf(out, "%s \"%s\" %s%s\n", resource, name, operation, dryRunMsg)
} else {
fmt.Fprintf(out, "\"%s\" %s%s\n", name, operation, dryRunMsg)
}
}
}
func (f *ring2Factory) PrintObject(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error {
// try to get a typed object
_, typer := f.objectMappingFactory.Object()
gvks, _, err := typer.ObjectKinds(obj)
if err != nil {
return err
}
// Prefer the existing external version if specified
var preferredVersion []string
if gvks[0].Version != "" && gvks[0].Version != runtime.APIVersionInternal {
preferredVersion = []string{gvks[0].Version}
}
mapping, err := mapper.RESTMapping(gvks[0].GroupKind(), preferredVersion...)
if err != nil {
return err
}
printer, err := f.PrinterForMapping(ExtractCmdPrintOptions(cmd, false), mapping)
if err != nil {
return err
}
return printer.PrintObj(obj, out)
}
func (f *ring2Factory) PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, out io.Writer) error {
printOpts := ExtractCmdPrintOptions(cmd, false)
printer, err := f.PrinterForOptions(printOpts)
if err != nil {
return err
}
if !printer.IsGeneric() {
printer, err = f.PrinterForMapping(printOpts, nil)
if err != nil {
return err
}
}
return printer.PrintObj(info.Object, out)
}
// NewBuilder returns a new resource builder for structured api objects.
func (f *ring2Factory) NewBuilder() *resource.Builder {
clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping)
@ -160,7 +54,7 @@ func (f *ring2Factory) NewBuilder() *resource.Builder {
RESTMapper: mapper,
ObjectTyper: typer,
ClientMapper: clientMapperFunc,
Decoder: f.clientAccessFactory.Decoder(true),
Decoder: InternalVersionDecoder(),
},
&resource.Mapper{
RESTMapper: mapper,

View File

@ -23,6 +23,7 @@ import (
"flag"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"regexp"
@ -38,6 +39,7 @@ import (
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -58,8 +60,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
"k8s.io/kubernetes/pkg/kubectl/util/transport"
)
type ring0Factory struct {
@ -108,7 +109,15 @@ func (f *discoveryFactory) DiscoveryClient() (discovery.CachedDiscoveryInterface
return nil, err
}
cfg.CacheDir = f.cacheDir
if f.cacheDir != "" {
wt := cfg.WrapTransport
cfg.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
if wt != nil {
rt = wt(rt)
}
return transport.NewCacheRoundTripper(f.cacheDir, rt)
}
}
discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil {
@ -194,10 +203,6 @@ func (f *ring0Factory) ClientSet() (internalclientset.Interface, error) {
return f.clientCache.ClientSetForVersion(nil)
}
func (f *ring0Factory) ClientSetForVersion(requiredVersion *schema.GroupVersion) (internalclientset.Interface, error) {
return f.clientCache.ClientSetForVersion(requiredVersion)
}
func (f *ring0Factory) ClientConfig() (*restclient.Config, error) {
return f.clientCache.ClientConfigForVersion(nil)
}
@ -205,10 +210,6 @@ func (f *ring0Factory) BareClientConfig() (*restclient.Config, error) {
return f.clientConfig.ClientConfig()
}
func (f *ring0Factory) ClientConfigForVersion(requiredVersion *schema.GroupVersion) (*restclient.Config, error) {
return f.clientCache.ClientConfigForVersion(nil)
}
func (f *ring0Factory) RESTClient() (*restclient.RESTClient, error) {
clientConfig, err := f.clientCache.ClientConfigForVersion(nil)
if err != nil {
@ -217,20 +218,6 @@ func (f *ring0Factory) RESTClient() (*restclient.RESTClient, error) {
return restclient.RESTClientFor(clientConfig)
}
func (f *ring0Factory) Decoder(toInternal bool) runtime.Decoder {
var decoder runtime.Decoder
if toInternal {
decoder = legacyscheme.Codecs.UniversalDecoder()
} else {
decoder = legacyscheme.Codecs.UniversalDeserializer()
}
return decoder
}
func (f *ring0Factory) JSONEncoder() runtime.Encoder {
return legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...)
}
func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error) {
// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
switch t := obj.(type) {
@ -275,6 +262,11 @@ func (f *ring0Factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*v1.Po
// Job
case *batchv1.Job:
return true, fn(&t.Spec.Template.Spec)
// CronJob
case *batchv1beta1.CronJob:
return true, fn(&t.Spec.JobTemplate.Spec.Template.Spec)
case *batchv2alpha1.CronJob:
return true, fn(&t.Spec.JobTemplate.Spec.Template.Spec)
default:
return false, fmt.Errorf("the object is not a pod or does not have a pod template")
}
@ -425,12 +417,6 @@ func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource {
}
}
func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) {
p := printers.NewHumanReadablePrinter(f.JSONEncoder(), f.Decoder(true), options)
printersinternal.AddHandlers(p)
return p, nil
}
func (f *ring0Factory) Pauser(info *resource.Info) ([]byte, error) {
switch obj := info.Object.(type) {
case *extensions.Deployment:
@ -438,7 +424,7 @@ func (f *ring0Factory) Pauser(info *resource.Info) ([]byte, error) {
return nil, errors.New("is already paused")
}
obj.Spec.Paused = true
return runtime.Encode(f.JSONEncoder(), info.Object)
return runtime.Encode(InternalVersionJSONEncoder(), info.Object)
default:
return nil, fmt.Errorf("pausing is not supported")
}
@ -455,7 +441,7 @@ func (f *ring0Factory) Resumer(info *resource.Info) ([]byte, error) {
return nil, errors.New("is not paused")
}
obj.Spec.Paused = false
return runtime.Encode(f.JSONEncoder(), info.Object)
return runtime.Encode(InternalVersionJSONEncoder(), info.Object)
default:
return nil, fmt.Errorf("resuming is not supported")
}
@ -538,10 +524,6 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
CronJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{},
CronJobV1Beta1GeneratorName: kubectl.CronJobV1Beta1{},
}
case "autoscale":
generator = map[string]kubectl.Generator{
HorizontalPodAutoscalerV1GeneratorName: kubectl.HorizontalPodAutoscalerV1{},
}
case "namespace":
generator = map[string]kubectl.Generator{
NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{},
@ -665,34 +647,6 @@ func (f *ring0Factory) EditorEnvs() []string {
return []string{"KUBE_EDITOR", "EDITOR"}
}
func (f *ring0Factory) PrintObjectSpecificMessage(obj runtime.Object, out io.Writer) {
switch obj := obj.(type) {
case *api.Service:
if obj.Spec.Type == api.ServiceTypeNodePort {
msg := fmt.Sprintf(
`You have exposed your service on an external port on all nodes in your
cluster. If you want to expose this service to the external internet, you may
need to set up firewall rules for the service port(s) (%s) to serve traffic.
See http://kubernetes.io/docs/user-guide/services-firewalls for more details.
`,
makePortsString(obj.Spec.Ports, true))
out.Write([]byte(msg))
}
if _, ok := obj.Annotations[api.AnnotationLoadBalancerSourceRangesKey]; ok {
msg := fmt.Sprintf(
`You are using service annotation [service.beta.kubernetes.io/load-balancer-source-ranges].
It has been promoted to field [loadBalancerSourceRanges] in service spec. This annotation will be deprecated in the future.
Please use the loadBalancerSourceRanges field instead.
See http://kubernetes.io/docs/user-guide/services-firewalls for more details.
`)
out.Write([]byte(msg))
}
}
}
// overlyCautiousIllegalFileCharacters matches characters that *might* not be supported. Windows is really restrictive, so this is really restrictive
var overlyCautiousIllegalFileCharacters = regexp.MustCompile(`[^(\w/\.)]`)
@ -705,3 +659,12 @@ func computeDiscoverCacheDir(parentDir, host string) string {
return filepath.Join(parentDir, safeHost)
}
// this method exists to help us find the points still relying on internal types.
func InternalVersionDecoder() runtime.Decoder {
return legacyscheme.Codecs.UniversalDecoder()
}
func InternalVersionJSONEncoder() runtime.Encoder {
return legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...)
}

View File

@ -37,12 +37,12 @@ import (
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
restclient "k8s.io/client-go/rest"
scaleclient "k8s.io/client-go/scale"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/categories"
@ -131,7 +131,7 @@ func (f *ring1Factory) ClientForMapping(mapping *meta.RESTMapping) (resource.RES
if err != nil {
return nil, err
}
if err := client.SetKubernetesDefaults(cfg); err != nil {
if err := setKubernetesDefaults(cfg); err != nil {
return nil, err
}
gvk := mapping.GroupVersionKind
@ -165,9 +165,7 @@ func (f *ring1Factory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (
}
func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (printers.Describer, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
clientset, err := f.clientAccessFactory.ClientSet()
if err != nil {
// if we can't make a client for this group/version, go generic if possible
if genericDescriber, genericErr := genericDescriber(f.clientAccessFactory, mapping); genericErr == nil {
@ -218,7 +216,7 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES
}
func (f *ring1Factory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {
clientset, err := f.clientAccessFactory.ClientSetForVersion(nil)
clientset, err := f.clientAccessFactory.ClientSet()
if err != nil {
return nil, err
}
@ -281,17 +279,31 @@ func (f *ring1Factory) LogsForObject(object, options runtime.Object, timeout tim
}
func (f *ring1Factory) Scaler(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
clientset, err := f.clientAccessFactory.ClientSet()
if err != nil {
return nil, err
}
return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), clientset)
// create scales getter
// TODO(p0lyn0mial): put scalesGetter to a factory
discoClient, err := f.clientAccessFactory.DiscoveryClient()
if err != nil {
return nil, err
}
restClient, err := f.clientAccessFactory.RESTClient()
if err != nil {
return nil, err
}
mapper, _ := f.Object()
resolver := scaleclient.NewDiscoveryScaleKindResolver(discoClient)
scalesGetter := scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver)
gvk := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource)
return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), clientset.Batch(), scalesGetter, gvk.GroupResource()), nil
}
func (f *ring1Factory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
clientset, clientsetErr := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
clientset, clientsetErr := f.clientAccessFactory.ClientSet()
reaper, reaperErr := kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), clientset)
if kubectl.IsNoSuchReaperError(reaperErr) {
@ -350,7 +362,7 @@ func (f *ring1Factory) ApproximatePodTemplateForObject(object runtime.Object) (*
}
func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout time.Duration) (*api.Pod, error) {
clientset, err := f.clientAccessFactory.ClientSetForVersion(nil)
clientset, err := f.clientAccessFactory.ClientSet()
if err != nil {
return nil, err
}
@ -386,6 +398,13 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout tim
return nil, fmt.Errorf("invalid label selector: %v", err)
}
case *api.Service:
namespace = t.Namespace
if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 {
return nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
}
selector = labels.SelectorFromSet(t.Spec.Selector)
case *api.Pod:
return t, nil

View File

@ -40,7 +40,7 @@ type fakeClientAccessFactory struct {
fakeClientset *fake.Clientset
}
func (f *fakeClientAccessFactory) ClientSetForVersion(requiredVersion *schema.GroupVersion) (internalclientset.Interface, error) {
func (f *fakeClientAccessFactory) ClientSet() (internalclientset.Interface, error) {
return f.fakeClientset, nil
}

View File

@ -17,7 +17,6 @@ limitations under the License.
package util
import (
"bytes"
"fmt"
"sort"
"strings"
@ -418,41 +417,6 @@ func TestGetFirstPod(t *testing.T) {
}
}
func TestPrintObjectSpecificMessage(t *testing.T) {
f := NewFactory(nil)
tests := []struct {
obj runtime.Object
expectOutput bool
}{
{
obj: &api.Service{},
expectOutput: false,
},
{
obj: &api.Pod{},
expectOutput: false,
},
{
obj: &api.Service{Spec: api.ServiceSpec{Type: api.ServiceTypeLoadBalancer}},
expectOutput: false,
},
{
obj: &api.Service{Spec: api.ServiceSpec{Type: api.ServiceTypeNodePort}},
expectOutput: true,
},
}
for _, test := range tests {
buff := &bytes.Buffer{}
f.PrintObjectSpecificMessage(test.obj, buff)
if test.expectOutput && buff.Len() == 0 {
t.Errorf("Expected output, saw none for %v", test.obj)
}
if !test.expectOutput && buff.Len() > 0 {
t.Errorf("Expected no output, saw %s for %v", buff.String(), test.obj)
}
}
}
func TestMakePortsString(t *testing.T) {
tests := []struct {
ports []api.ServicePort

View File

@ -401,7 +401,7 @@ func AddValidateFlags(cmd *cobra.Command) {
}
func AddValidateOptionFlags(cmd *cobra.Command, options *ValidateOptions) {
cmd.Flags().BoolVar(&options.EnableValidation, "validate", true, "If true, use a schema to validate the input before sending it")
cmd.Flags().BoolVar(&options.EnableValidation, "validate", options.EnableValidation, "If true, use a schema to validate the input before sending it")
}
func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
@ -427,7 +427,7 @@ func AddApplyAnnotationFlags(cmd *cobra.Command) {
}
func AddApplyAnnotationVarFlags(cmd *cobra.Command, applyAnnotation *bool) {
cmd.Flags().BoolVar(applyAnnotation, ApplyAnnotationsFlag, false, "If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future.")
cmd.Flags().BoolVar(applyAnnotation, ApplyAnnotationsFlag, *applyAnnotation, "If true, the configuration of current object will be saved in its annotation. Otherwise, the annotation will be unchanged. This flag is useful when you want to perform kubectl apply on this object in the future.")
}
// AddGeneratorFlags adds flags common to resource generation commands
@ -524,7 +524,7 @@ func AddRecordFlag(cmd *cobra.Command) {
}
func AddRecordVarFlag(cmd *cobra.Command, record *bool) {
cmd.Flags().BoolVar(record, "record", false, "Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists.")
cmd.Flags().BoolVar(record, "record", *record, "Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists.")
}
func GetRecordFlag(cmd *cobra.Command) bool {
@ -601,7 +601,7 @@ func AddInclude3rdPartyFlags(cmd *cobra.Command) {
}
func AddInclude3rdPartyVarFlags(cmd *cobra.Command, include3rdParty *bool) {
cmd.Flags().BoolVar(include3rdParty, "include-extended-apis", true, "If true, include definitions of new APIs via calls to the API server. [default true]")
cmd.Flags().BoolVar(include3rdParty, "include-extended-apis", *include3rdParty, "If true, include definitions of new APIs via calls to the API server. [default true]")
cmd.Flags().MarkDeprecated("include-extended-apis", "No longer required.")
}

View File

@ -33,7 +33,6 @@ go_test(
"openapi_test.go",
],
data = ["//api/openapi-spec:swagger-spec"],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi_test",
deps = [
":go_default_library",
"//pkg/kubectl/cmd/util/openapi/testing:go_default_library",

View File

@ -56,9 +56,11 @@ func NewOpenAPIData(doc *openapi_v2.Document) (Resources, error) {
if model == nil {
panic("ListModels returns a model that can't be looked-up.")
}
gvk := parseGroupVersionKind(model)
if len(gvk.Kind) > 0 {
resources[gvk] = modelName
gvkList := parseGroupVersionKind(model)
for _, gvk := range gvkList {
if len(gvk.Kind) > 0 {
resources[gvk] = modelName
}
}
}
@ -77,48 +79,49 @@ func (d *document) LookupResource(gvk schema.GroupVersionKind) proto.Schema {
}
// Get and parse GroupVersionKind from the extension. Returns empty if it doesn't have one.
func parseGroupVersionKind(s proto.Schema) schema.GroupVersionKind {
func parseGroupVersionKind(s proto.Schema) []schema.GroupVersionKind {
extensions := s.GetExtensions()
gvkListResult := []schema.GroupVersionKind{}
// Get the extensions
gvkExtension, ok := extensions[groupVersionKindExtensionKey]
if !ok {
return schema.GroupVersionKind{}
return []schema.GroupVersionKind{}
}
// gvk extension must be a list of 1 element.
// gvk extension must be a list of at least 1 element.
gvkList, ok := gvkExtension.([]interface{})
if !ok {
return schema.GroupVersionKind{}
}
if len(gvkList) != 1 {
return schema.GroupVersionKind{}
}
gvk := gvkList[0]
// gvk extension list must be a map with group, version, and
// kind fields
gvkMap, ok := gvk.(map[interface{}]interface{})
if !ok {
return schema.GroupVersionKind{}
}
group, ok := gvkMap["group"].(string)
if !ok {
return schema.GroupVersionKind{}
}
version, ok := gvkMap["version"].(string)
if !ok {
return schema.GroupVersionKind{}
}
kind, ok := gvkMap["kind"].(string)
if !ok {
return schema.GroupVersionKind{}
return []schema.GroupVersionKind{}
}
return schema.GroupVersionKind{
Group: group,
Version: version,
Kind: kind,
for _, gvk := range gvkList {
// gvk extension list must be a map with group, version, and
// kind fields
gvkMap, ok := gvk.(map[interface{}]interface{})
if !ok {
continue
}
group, ok := gvkMap["group"].(string)
if !ok {
continue
}
version, ok := gvkMap["version"].(string)
if !ok {
continue
}
kind, ok := gvkMap["kind"].(string)
if !ok {
continue
}
gvkListResult = append(gvkListResult, schema.GroupVersionKind{
Group: group,
Version: version,
Kind: kind,
})
}
return gvkListResult
}

View File

@ -27,8 +27,7 @@ go_test(
"validation_test.go",
],
data = ["//api/openapi-spec:swagger-spec"],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/validation",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/api/testapi:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",

View File

@ -18,16 +18,20 @@ package util
import (
"fmt"
"io"
"strings"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
kubectlscheme "k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/kubernetes/pkg/api/legacyscheme"
)
// AddPrinterFlags adds printing related flags to a command (e.g. output format, no headers, template path)
@ -48,7 +52,8 @@ func AddNonDeprecatedPrinterFlags(cmd *cobra.Command) {
cmd.Flags().String("template", "", "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
cmd.MarkFlagFilename("template")
cmd.Flags().String("sort-by", "", "If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.")
cmd.Flags().BoolP("show-all", "a", false, "When printing, show all resources (default hide terminated pods.)")
cmd.Flags().BoolP("show-all", "a", true, "When printing, show all resources (default hide terminated pods.)")
cmd.Flags().MarkDeprecated("show-all", "will be removed in an upcoming release")
}
// AddOutputFlagsForMutation adds output related flags to a command. Used by mutations only.
@ -58,7 +63,7 @@ func AddOutputFlagsForMutation(cmd *cobra.Command) {
// AddOutputVarFlagsForMutation adds output related flags to a command. Used by mutations only.
func AddOutputVarFlagsForMutation(cmd *cobra.Command, output *string) {
cmd.Flags().StringVarP(output, "output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
cmd.Flags().StringVarP(output, "output", "o", *output, "Output mode. Use \"-o name\" for shorter output (resource/name).")
}
// AddOutputFlags adds output related flags to a command.
@ -81,11 +86,59 @@ func ValidateOutputArgs(cmd *cobra.Command) error {
return nil
}
// PrintSuccess prints a success message and can do a "-o name" as "shortOutput"
// TODO this should really just be a printer. It's got just about the exact same signature.
func PrintSuccess(shortOutput bool, out io.Writer, obj runtime.Object, dryRun bool, operation string) {
dryRunMsg := ""
if dryRun {
dryRunMsg = " (dry run)"
}
// match name printer format
name := "<unknown>"
if acc, err := meta.Accessor(obj); err == nil {
if n := acc.GetName(); len(n) > 0 {
name = n
}
}
// legacy scheme to be sure we work ok with internal types.
// TODO internal types aren't supposed to exist here
groupKind := printers.GetObjectGroupKind(obj, legacyscheme.Scheme)
kindString := fmt.Sprintf("%s.%s", strings.ToLower(groupKind.Kind), groupKind.Group)
if len(groupKind.Group) == 0 {
kindString = strings.ToLower(groupKind.Kind)
}
if shortOutput {
// -o name: prints resource/name
fmt.Fprintf(out, "%s/%s\n", kindString, name)
return
}
// understandable output by default
fmt.Fprintf(out, "%s \"%s\" %s%s\n", kindString, name, operation, dryRunMsg)
}
// PrintObject prints a single object based on the default command options
// TODO this should go away once commands can embed the PrintOptions instead
func PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error {
printer, err := PrinterForOptions(ExtractCmdPrintOptions(cmd, false))
if err != nil {
return err
}
return printer.PrintObj(obj, out)
}
// PrinterForOptions returns the printer for the outputOptions (if given) or
// returns the default printer for the command. Requires that printer flags have
// been added to cmd (see AddPrinterFlags).
func PrinterForOptions(mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options *printers.PrintOptions) (printers.ResourcePrinter, error) {
printer, err := printers.GetStandardPrinter(mapper, typer, encoder, decoders, *options)
// returns the default printer for the command.
// TODO this should become a function on the PrintOptions struct
func PrinterForOptions(options *printers.PrintOptions) (printers.ResourcePrinter, error) {
// TODO: used by the custom column implementation and the name implementation, break this dependency
decoders := []runtime.Decoder{kubectlscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}
encoder := kubectlscheme.Codecs.LegacyCodec(kubectlscheme.Registry.EnabledVersions()...)
printer, err := printers.GetStandardPrinter(kubectlscheme.Scheme, encoder, decoders, *options)
if err != nil {
return nil, err
}
@ -93,11 +146,18 @@ func PrinterForOptions(mapper meta.RESTMapper, typer runtime.ObjectTyper, encode
// we try to convert to HumanReadablePrinter, if return ok, it must be no generic
// we execute AddHandlers() here before maybeWrapSortingPrinter so that we don't
// need to convert to delegatePrinter again then invoke AddHandlers()
if humanReadablePrinter, ok := printer.(*printers.HumanReadablePrinter); ok {
// TODO this looks highly questionable. human readable printers are baked into code. This can just live in the definition of the handler itself
// TODO or be registered there
if humanReadablePrinter, ok := printer.(printers.PrintHandler); ok {
printersinternal.AddHandlers(humanReadablePrinter)
}
return maybeWrapSortingPrinter(printer, *options), nil
printer = maybeWrapSortingPrinter(printer, *options)
// wrap the printer in a versioning printer that understands when to convert and when not to convert
printer = printers.NewVersionedPrinter(printer, legacyscheme.Scheme, legacyscheme.Scheme, kubectlscheme.Versions...)
return printer, nil
}
// ExtractCmdPrintOptions parses printer specific commandline args and
@ -223,3 +283,15 @@ func ValidResourceTypeList(f ClientAccessFactory) string {
`)
}
// Retrieve a list of handled resources from printer as valid args
// TODO: This function implementation should be replaced with a real implementation from the discovery service.
func ValidArgList(f ClientAccessFactory) []string {
validArgs := []string{}
humanReadablePrinter := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{})
printersinternal.AddHandlers(humanReadablePrinter)
validArgs = humanReadablePrinter.HandledResources()
return validArgs
}

View File

@ -22,6 +22,7 @@ import (
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/kubernetes/pkg/kubectl"
@ -72,7 +73,7 @@ func (e shortcutExpander) RESTMappings(gk schema.GroupKind, versions ...string)
// First the list of potential resources will be taken from the API server.
// Next we will append the hardcoded list of resources - to be backward compatible with old servers.
// NOTE that the list is ordered by group priority.
func (e shortcutExpander) getShortcutMappings() ([]kubectl.ResourceShortcuts, error) {
func (e shortcutExpander) getShortcutMappings() ([]*metav1.APIResourceList, []kubectl.ResourceShortcuts, error) {
res := []kubectl.ResourceShortcuts{}
// get server resources
// This can return an error *and* the results it was able to find. We don't need to fail on the error.
@ -81,13 +82,13 @@ func (e shortcutExpander) getShortcutMappings() ([]kubectl.ResourceShortcuts, er
glog.V(1).Infof("Error loading discovery information: %v", err)
}
for _, apiResources := range apiResList {
gv, err := schema.ParseGroupVersion(apiResources.GroupVersion)
if err != nil {
glog.V(1).Infof("Unable to parse groupversion = %s due to = %s", apiResources.GroupVersion, err.Error())
continue
}
for _, apiRes := range apiResources.APIResources {
for _, shortName := range apiRes.ShortNames {
gv, err := schema.ParseGroupVersion(apiResources.GroupVersion)
if err != nil {
glog.V(1).Infof("Unable to parse groupversion = %s due to = %s", apiResources.GroupVersion, err.Error())
continue
}
rs := kubectl.ResourceShortcuts{
ShortForm: schema.GroupResource{Group: gv.Group, Resource: shortName},
LongForm: schema.GroupResource{Group: gv.Group, Resource: apiRes.Name},
@ -99,7 +100,7 @@ func (e shortcutExpander) getShortcutMappings() ([]kubectl.ResourceShortcuts, er
// append hardcoded short forms at the end of the list
res = append(res, kubectl.ResourcesShortcutStatic...)
return res, nil
return apiResList, res, nil
}
// expandResourceShortcut will return the expanded version of resource
@ -108,13 +109,33 @@ func (e shortcutExpander) getShortcutMappings() ([]kubectl.ResourceShortcuts, er
// Lastly we will return resource unmodified.
func (e shortcutExpander) expandResourceShortcut(resource schema.GroupVersionResource) schema.GroupVersionResource {
// get the shortcut mappings and return on first match.
if resources, err := e.getShortcutMappings(); err == nil {
for _, item := range resources {
if allResources, shortcutResources, err := e.getShortcutMappings(); err == nil {
// avoid expanding if there's an exact match to a full resource name
for _, apiResources := range allResources {
gv, err := schema.ParseGroupVersion(apiResources.GroupVersion)
if err != nil {
continue
}
if len(resource.Group) != 0 && resource.Group != gv.Group {
continue
}
for _, apiRes := range apiResources.APIResources {
if resource.Resource == apiRes.Name {
return resource
}
if resource.Resource == apiRes.SingularName {
return resource
}
}
}
for _, item := range shortcutResources {
if len(resource.Group) != 0 && resource.Group != item.ShortForm.Group {
continue
}
if resource.Resource == item.ShortForm.Resource {
resource.Resource = item.LongForm.Resource
resource.Group = item.LongForm.Group
return resource
}
}
@ -123,12 +144,13 @@ func (e shortcutExpander) expandResourceShortcut(resource schema.GroupVersionRes
if len(resource.Group) == 0 {
return resource
}
for _, item := range resources {
for _, item := range shortcutResources {
if !strings.HasPrefix(item.ShortForm.Group, resource.Group) {
continue
}
if resource.Resource == item.ShortForm.Resource {
resource.Resource = item.LongForm.Resource
resource.Group = item.LongForm.Group
return resource
}
}

View File

@ -46,7 +46,7 @@ func TestReplaceAliases(t *testing.T) {
{
name: "hpa-priority",
arg: "hpa",
expected: schema.GroupVersionResource{Resource: "superhorizontalpodautoscalers"},
expected: schema.GroupVersionResource{Resource: "superhorizontalpodautoscalers", Group: "autoscaling"},
srvRes: []*metav1.APIResourceList{
{
GroupVersion: "autoscaling/v1",
@ -68,6 +68,61 @@ func TestReplaceAliases(t *testing.T) {
},
},
},
{
name: "resource-override",
arg: "dpl",
expected: schema.GroupVersionResource{Resource: "deployments", Group: "foo"},
srvRes: []*metav1.APIResourceList{
{
GroupVersion: "foo/v1",
APIResources: []metav1.APIResource{
{
Name: "deployments",
ShortNames: []string{"dpl"},
},
},
},
{
GroupVersion: "extension/v1beta1",
APIResources: []metav1.APIResource{
{
Name: "deployments",
ShortNames: []string{"deploy"},
},
},
},
},
},
{
name: "resource-match-preferred",
arg: "pods",
expected: schema.GroupVersionResource{Resource: "pods", Group: ""},
srvRes: []*metav1.APIResourceList{
{
GroupVersion: "v1",
APIResources: []metav1.APIResource{{Name: "pods", SingularName: "pod"}},
},
{
GroupVersion: "acme.com/v1",
APIResources: []metav1.APIResource{{Name: "poddlers", ShortNames: []string{"pods", "pod"}}},
},
},
},
{
name: "resource-match-singular-preferred",
arg: "pod",
expected: schema.GroupVersionResource{Resource: "pod", Group: ""},
srvRes: []*metav1.APIResourceList{
{
GroupVersion: "v1",
APIResources: []metav1.APIResource{{Name: "pods", SingularName: "pod"}},
},
{
GroupVersion: "acme.com/v1",
APIResources: []metav1.APIResource{{Name: "poddlers", ShortNames: []string{"pods", "pod"}}},
},
},
},
}
ds := &fakeDiscoveryClient{}