2015-04-07 18:21:25 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
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 util
|
|
|
|
|
|
|
|
import (
|
2015-09-11 03:54:22 +00:00
|
|
|
"bytes"
|
2015-08-27 20:45:29 +00:00
|
|
|
"errors"
|
2015-04-24 03:50:35 +00:00
|
|
|
"flag"
|
2015-04-07 18:21:25 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2015-09-10 21:58:09 +00:00
|
|
|
"io/ioutil"
|
2015-04-07 18:21:25 +00:00
|
|
|
"os"
|
2015-09-16 23:31:45 +00:00
|
|
|
"os/user"
|
2015-09-10 21:58:09 +00:00
|
|
|
"path"
|
2015-04-07 18:21:25 +00:00
|
|
|
"strconv"
|
2015-10-22 17:34:19 +00:00
|
|
|
"time"
|
2015-04-07 18:21:25 +00:00
|
|
|
|
2015-12-11 22:15:41 +00:00
|
|
|
"github.com/emicklei/go-restful/swagger"
|
2015-04-07 18:21:25 +00:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/pflag"
|
|
|
|
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api"
|
|
|
|
"k8s.io/kubernetes/pkg/api/meta"
|
2015-11-16 16:42:09 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/validation"
|
2016-01-13 22:40:56 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
2015-11-13 01:07:21 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
2016-01-20 23:48:52 +00:00
|
|
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_1"
|
2015-08-13 19:01:50 +00:00
|
|
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
|
|
|
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/kubectl"
|
|
|
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
2015-10-22 17:34:19 +00:00
|
|
|
"k8s.io/kubernetes/pkg/labels"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/runtime"
|
2015-12-21 05:37:49 +00:00
|
|
|
"k8s.io/kubernetes/pkg/runtime/serializer/json"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util"
|
2015-04-07 18:21:25 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
FlagMatchBinaryVersion = "match-server-version"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
|
|
|
|
// of resources and different API sets.
|
|
|
|
// TODO: make the functions interfaces
|
|
|
|
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
|
|
|
|
// commands are decoupled from the factory).
|
|
|
|
type Factory struct {
|
2015-11-19 18:14:10 +00:00
|
|
|
clients *ClientCache
|
|
|
|
flags *pflag.FlagSet
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
// Returns interfaces for dealing with arbitrary runtime.Objects.
|
|
|
|
Object func() (meta.RESTMapper, runtime.ObjectTyper)
|
2015-12-21 05:37:49 +00:00
|
|
|
// 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 func(toInternal bool) runtime.Decoder
|
|
|
|
// Returns an encoder capable of encoding a provided object into JSON in the default desired version.
|
|
|
|
JSONEncoder func() runtime.Encoder
|
2015-04-07 18:21:25 +00:00
|
|
|
// Returns a client for accessing Kubernetes resources or an error.
|
|
|
|
Client func() (*client.Client, 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
|
|
|
|
// for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer.
|
2015-12-21 05:37:49 +00:00
|
|
|
ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
|
2015-04-07 18:21:25 +00:00
|
|
|
// Returns a Describer for displaying the specified RESTMapping type or an error.
|
|
|
|
Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error)
|
|
|
|
// Returns a Printer for formatting objects of the given type or an error.
|
2015-11-10 11:34:28 +00:00
|
|
|
Printer func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error)
|
2015-05-21 21:10:25 +00:00
|
|
|
// Returns a Scaler for changing the size of the specified RESTMapping type or an error
|
|
|
|
Scaler func(mapping *meta.RESTMapping) (kubectl.Scaler, error)
|
2015-04-07 18:21:25 +00:00
|
|
|
// Returns a Reaper for gracefully shutting down resources.
|
|
|
|
Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
|
2016-01-20 23:48:52 +00:00
|
|
|
// Returns a HistoryViewer for viewing change history
|
|
|
|
HistoryViewer func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error)
|
2015-05-07 15:30:28 +00:00
|
|
|
// PodSelectorForObject returns the pod selector associated with the provided object
|
|
|
|
PodSelectorForObject func(object runtime.Object) (string, error)
|
|
|
|
// PortsForObject returns the ports associated with the provided object
|
|
|
|
PortsForObject func(object runtime.Object) ([]string, error)
|
2015-05-13 08:52:25 +00:00
|
|
|
// LabelsForObject returns the labels associated with the provided object
|
|
|
|
LabelsForObject func(object runtime.Object) (map[string]string, error)
|
2015-10-18 14:20:28 +00:00
|
|
|
// LogsForObject returns a request for the logs associated with the provided object
|
|
|
|
LogsForObject func(object, options runtime.Object) (*client.Request, error)
|
2016-01-25 11:34:48 +00:00
|
|
|
// PauseObject marks the provided object as paused ie. it will not be reconciled by its controller.
|
|
|
|
PauseObject func(object runtime.Object) (bool, error)
|
|
|
|
// ResumeObject resumes a paused object ie. it will be reconciled by its controller.
|
|
|
|
ResumeObject func(object runtime.Object) (bool, error)
|
2015-04-07 18:21:25 +00:00
|
|
|
// Returns a schema that can validate objects stored on disk.
|
2015-09-11 03:54:22 +00:00
|
|
|
Validator func(validate bool, cacheDir string) (validation.Schema, error)
|
2015-12-11 22:15:41 +00:00
|
|
|
// SwaggerSchema returns the schema declaration for the provided group version.
|
|
|
|
SwaggerSchema func(unversioned.GroupVersion) (*swagger.ApiDeclaration, error)
|
2015-06-26 20:49:34 +00:00
|
|
|
// Returns the default namespace to use in cases where no
|
|
|
|
// other namespace is specified and whether the namespace was
|
|
|
|
// overriden.
|
|
|
|
DefaultNamespace func() (string, bool, error)
|
2015-11-19 18:14:10 +00:00
|
|
|
// Generators returns the generators for the provided command
|
|
|
|
Generators func(cmdName string) map[string]kubectl.Generator
|
2015-08-26 08:06:40 +00:00
|
|
|
// Check whether the kind of resources could be exposed
|
2015-12-01 16:52:11 +00:00
|
|
|
CanBeExposed func(kind unversioned.GroupKind) error
|
2015-10-15 22:15:13 +00:00
|
|
|
// Check whether the kind of resources could be autoscaled
|
2015-12-01 16:52:11 +00:00
|
|
|
CanBeAutoscaled func(kind unversioned.GroupKind) error
|
2015-10-22 17:34:19 +00:00
|
|
|
// AttachablePodForObject returns the pod to which to attach given an object.
|
|
|
|
AttachablePodForObject func(object runtime.Object) (*api.Pod, error)
|
2015-11-11 11:29:52 +00:00
|
|
|
// EditorEnvs returns a group of environment variables that the edit command
|
|
|
|
// can range over in order to determine if the user has specified an editor
|
|
|
|
// of their choice.
|
|
|
|
EditorEnvs func() []string
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
|
2015-10-21 14:41:42 +00:00
|
|
|
const (
|
|
|
|
RunV1GeneratorName = "run/v1"
|
|
|
|
RunPodV1GeneratorName = "run-pod/v1"
|
|
|
|
ServiceV1GeneratorName = "service/v1"
|
|
|
|
ServiceV2GeneratorName = "service/v2"
|
|
|
|
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
|
|
|
|
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
|
|
|
|
JobV1Beta1GeneratorName = "job/v1beta1"
|
|
|
|
NamespaceV1GeneratorName = "namespace/v1"
|
|
|
|
SecretV1GeneratorName = "secret/v1"
|
|
|
|
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DefaultGenerators returns the set of default generators for use in Factory instances
|
2015-11-19 18:14:10 +00:00
|
|
|
func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
|
|
|
|
generators := map[string]map[string]kubectl.Generator{}
|
|
|
|
generators["expose"] = map[string]kubectl.Generator{
|
|
|
|
ServiceV1GeneratorName: kubectl.ServiceGeneratorV1{},
|
|
|
|
ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{},
|
|
|
|
}
|
|
|
|
generators["run"] = map[string]kubectl.Generator{
|
|
|
|
RunV1GeneratorName: kubectl.BasicReplicationController{},
|
|
|
|
RunPodV1GeneratorName: kubectl.BasicPod{},
|
|
|
|
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
|
|
|
|
JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
|
|
|
|
}
|
|
|
|
generators["autoscale"] = map[string]kubectl.Generator{
|
2015-10-21 14:41:42 +00:00
|
|
|
HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},
|
|
|
|
}
|
2015-11-19 18:14:10 +00:00
|
|
|
generators["namespace"] = map[string]kubectl.Generator{
|
|
|
|
NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{},
|
|
|
|
}
|
|
|
|
generators["secret"] = map[string]kubectl.Generator{
|
|
|
|
SecretV1GeneratorName: kubectl.SecretGeneratorV1{},
|
|
|
|
}
|
|
|
|
generators["secret-for-docker-registry"] = map[string]kubectl.Generator{
|
|
|
|
SecretForDockerRegistryV1GeneratorName: kubectl.SecretForDockerRegistryGeneratorV1{},
|
|
|
|
}
|
|
|
|
return generators[cmdName]
|
2015-10-21 14:41:42 +00:00
|
|
|
}
|
|
|
|
|
2015-04-07 18:21:25 +00:00
|
|
|
// NewFactory creates a factory with the default Kubernetes resources defined
|
|
|
|
// 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 {
|
2015-08-10 20:08:34 +00:00
|
|
|
mapper := kubectl.ShortcutExpander{RESTMapper: api.RESTMapper}
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
2015-05-16 16:44:42 +00:00
|
|
|
flags.SetNormalizeFunc(util.WarnWordSepNormalizeFunc) // Warn for "_" flags
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
clientConfig := optionalClientConfig
|
|
|
|
if optionalClientConfig == nil {
|
|
|
|
clientConfig = DefaultClientConfig(flags)
|
|
|
|
}
|
|
|
|
|
2015-06-13 02:06:18 +00:00
|
|
|
clients := NewClientCache(clientConfig)
|
2015-08-10 20:08:34 +00:00
|
|
|
|
2015-04-07 18:21:25 +00:00
|
|
|
return &Factory{
|
2015-11-19 18:14:10 +00:00
|
|
|
clients: clients,
|
|
|
|
flags: flags,
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
|
|
|
|
cfg, err := clientConfig.ClientConfig()
|
|
|
|
CheckErr(err)
|
2015-12-01 16:52:11 +00:00
|
|
|
cmdApiVersion := unversioned.GroupVersion{}
|
2015-11-13 21:20:54 +00:00
|
|
|
if cfg.GroupVersion != nil {
|
2015-12-01 16:52:11 +00:00
|
|
|
cmdApiVersion = *cfg.GroupVersion
|
2015-11-13 21:20:54 +00:00
|
|
|
}
|
2015-04-07 18:21:25 +00:00
|
|
|
|
2015-12-01 16:52:11 +00:00
|
|
|
return kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}, api.Scheme
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
|
|
|
Client: func() (*client.Client, error) {
|
2015-12-08 20:14:38 +00:00
|
|
|
return clients.ClientForVersion(nil)
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
|
|
|
ClientConfig: func() (*client.Config, error) {
|
2015-12-08 20:14:38 +00:00
|
|
|
return clients.ClientConfigForVersion(nil)
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
2015-12-21 05:37:49 +00:00
|
|
|
ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
2015-12-08 20:14:38 +00:00
|
|
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
|
|
|
client, err := clients.ClientForVersion(&mappingVersion)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-12-13 02:02:47 +00:00
|
|
|
switch mapping.GroupVersionKind.Group {
|
2015-12-08 14:21:04 +00:00
|
|
|
case api.GroupName:
|
2015-08-10 20:08:34 +00:00
|
|
|
return client.RESTClient, nil
|
2015-12-08 14:21:04 +00:00
|
|
|
case extensions.GroupName:
|
2015-10-12 18:06:42 +00:00
|
|
|
return client.ExtensionsClient.RESTClient, nil
|
2015-08-10 20:08:34 +00:00
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("unable to get RESTClient for resource '%s'", mapping.Resource)
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
|
|
|
Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
|
2015-12-08 20:14:38 +00:00
|
|
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
|
|
|
client, err := clients.ClientForVersion(&mappingVersion)
|
2015-08-10 20:08:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
2015-11-25 21:30:41 +00:00
|
|
|
if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), client); ok {
|
2015-08-10 20:08:34 +00:00
|
|
|
return describer, nil
|
|
|
|
}
|
2015-12-09 10:31:35 +00:00
|
|
|
return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind)
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
2015-12-21 05:37:49 +00:00
|
|
|
Decoder: func(toInternal bool) runtime.Decoder {
|
|
|
|
if toInternal {
|
|
|
|
return api.Codecs.UniversalDecoder()
|
|
|
|
}
|
|
|
|
return api.Codecs.UniversalDeserializer()
|
|
|
|
},
|
|
|
|
JSONEncoder: func() runtime.Encoder {
|
|
|
|
return api.Codecs.LegacyCodec(registered.EnabledVersions()...)
|
|
|
|
},
|
2015-11-10 11:34:28 +00:00
|
|
|
Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
|
|
|
|
return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, absoluteTimestamps, columnLabels), nil
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
2015-05-07 15:30:28 +00:00
|
|
|
PodSelectorForObject: func(object runtime.Object) (string, error) {
|
2015-04-07 18:21:25 +00:00
|
|
|
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
|
2015-05-07 15:30:28 +00:00
|
|
|
switch t := object.(type) {
|
|
|
|
case *api.ReplicationController:
|
|
|
|
return kubectl.MakeLabels(t.Spec.Selector), nil
|
|
|
|
case *api.Pod:
|
|
|
|
if len(t.Labels) == 0 {
|
2015-04-07 18:21:25 +00:00
|
|
|
return "", fmt.Errorf("the pod has no labels and cannot be exposed")
|
|
|
|
}
|
2015-05-07 15:30:28 +00:00
|
|
|
return kubectl.MakeLabels(t.Labels), nil
|
|
|
|
case *api.Service:
|
|
|
|
if t.Spec.Selector == nil {
|
2015-04-07 18:21:25 +00:00
|
|
|
return "", fmt.Errorf("the service has no pod selector set")
|
|
|
|
}
|
2015-05-07 15:30:28 +00:00
|
|
|
return kubectl.MakeLabels(t.Spec.Selector), nil
|
2016-01-15 13:47:11 +00:00
|
|
|
case *extensions.Deployment:
|
|
|
|
return kubectl.MakeLabels(t.Spec.Selector), nil
|
2015-04-07 18:21:25 +00:00
|
|
|
default:
|
2015-11-20 12:38:32 +00:00
|
|
|
gvk, err := api.Scheme.ObjectKind(object)
|
2015-05-07 15:30:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2015-11-20 12:38:32 +00:00
|
|
|
return "", fmt.Errorf("cannot extract pod selector from %v", gvk)
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
},
|
2015-05-07 15:30:28 +00:00
|
|
|
PortsForObject: func(object runtime.Object) ([]string, error) {
|
2015-04-07 18:21:25 +00:00
|
|
|
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
|
2015-05-07 15:30:28 +00:00
|
|
|
switch t := object.(type) {
|
|
|
|
case *api.ReplicationController:
|
|
|
|
return getPorts(t.Spec.Template.Spec), nil
|
|
|
|
case *api.Pod:
|
|
|
|
return getPorts(t.Spec), nil
|
2015-07-17 01:02:36 +00:00
|
|
|
case *api.Service:
|
|
|
|
return getServicePorts(t.Spec), nil
|
2016-01-15 13:47:11 +00:00
|
|
|
case *extensions.Deployment:
|
|
|
|
return getPorts(t.Spec.Template.Spec), nil
|
2015-05-07 15:30:28 +00:00
|
|
|
default:
|
2015-11-20 12:38:32 +00:00
|
|
|
gvk, err := api.Scheme.ObjectKind(object)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-11-20 12:38:32 +00:00
|
|
|
return nil, fmt.Errorf("cannot extract ports from %v", gvk)
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
},
|
2015-05-13 08:52:25 +00:00
|
|
|
LabelsForObject: func(object runtime.Object) (map[string]string, error) {
|
|
|
|
return meta.NewAccessor().Labels(object)
|
|
|
|
},
|
2015-10-18 14:20:28 +00:00
|
|
|
LogsForObject: func(object, options runtime.Object) (*client.Request, error) {
|
2015-12-08 20:14:38 +00:00
|
|
|
c, err := clients.ClientForVersion(nil)
|
2015-10-18 14:20:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t := object.(type) {
|
|
|
|
case *api.Pod:
|
|
|
|
opts, ok := options.(*api.PodLogOptions)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("provided options object is not a PodLogOptions")
|
|
|
|
}
|
|
|
|
return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil
|
|
|
|
default:
|
2015-11-20 12:38:32 +00:00
|
|
|
gvk, err := api.Scheme.ObjectKind(object)
|
2015-10-18 14:20:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-11-20 12:38:32 +00:00
|
|
|
return nil, fmt.Errorf("cannot get the logs from %v", gvk)
|
2015-10-18 14:20:28 +00:00
|
|
|
}
|
|
|
|
},
|
2016-01-25 11:34:48 +00:00
|
|
|
PauseObject: func(object runtime.Object) (bool, error) {
|
|
|
|
c, err := clients.ClientForVersion(nil)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t := object.(type) {
|
|
|
|
case *extensions.Deployment:
|
|
|
|
if t.Spec.Paused {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
t.Spec.Paused = true
|
|
|
|
_, err := c.Extensions().Deployments(t.Namespace).Update(t)
|
|
|
|
return false, err
|
|
|
|
default:
|
|
|
|
gvk, err := api.Scheme.ObjectKind(object)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return false, fmt.Errorf("cannot pause %v", gvk)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ResumeObject: func(object runtime.Object) (bool, error) {
|
|
|
|
c, err := clients.ClientForVersion(nil)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t := object.(type) {
|
|
|
|
case *extensions.Deployment:
|
|
|
|
if !t.Spec.Paused {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
t.Spec.Paused = false
|
|
|
|
_, err := c.Extensions().Deployments(t.Namespace).Update(t)
|
|
|
|
return false, err
|
|
|
|
default:
|
|
|
|
gvk, err := api.Scheme.ObjectKind(object)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return false, fmt.Errorf("cannot resume %v", gvk)
|
|
|
|
}
|
|
|
|
},
|
2015-05-21 21:10:25 +00:00
|
|
|
Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
|
2015-12-08 20:14:38 +00:00
|
|
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
|
|
|
client, err := clients.ClientForVersion(&mappingVersion)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-11-25 21:30:41 +00:00
|
|
|
return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), client)
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
|
|
|
Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
|
2015-12-08 20:14:38 +00:00
|
|
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
|
|
|
client, err := clients.ClientForVersion(&mappingVersion)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-11-25 21:30:41 +00:00
|
|
|
return kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), client)
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
2016-01-20 23:48:52 +00:00
|
|
|
HistoryViewer: func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
|
|
|
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
|
|
|
client, err := clients.ClientForVersion(&mappingVersion)
|
|
|
|
clientset := clientset.FromUnversionedClient(client)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
|
|
|
|
},
|
2015-09-11 03:54:22 +00:00
|
|
|
Validator: func(validate bool, cacheDir string) (validation.Schema, error) {
|
2015-08-18 01:15:34 +00:00
|
|
|
if validate {
|
2015-12-08 20:14:38 +00:00
|
|
|
client, err := clients.ClientForVersion(nil)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-09-11 03:54:22 +00:00
|
|
|
dir := cacheDir
|
|
|
|
if len(dir) > 0 {
|
|
|
|
version, err := client.ServerVersion()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dir = path.Join(cacheDir, version.String())
|
2015-09-10 21:58:09 +00:00
|
|
|
}
|
2015-09-11 03:54:22 +00:00
|
|
|
return &clientSwaggerSchema{
|
|
|
|
c: client,
|
|
|
|
cacheDir: dir,
|
2015-08-27 20:45:29 +00:00
|
|
|
mapper: api.RESTMapper,
|
2015-09-11 03:54:22 +00:00
|
|
|
}, nil
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
return validation.NullSchema{}, nil
|
|
|
|
},
|
2015-12-11 22:15:41 +00:00
|
|
|
SwaggerSchema: func(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) {
|
|
|
|
client, err := clients.ClientForVersion(&version)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-01-05 21:24:44 +00:00
|
|
|
return client.Discovery().SwaggerSchema(version)
|
2015-12-11 22:15:41 +00:00
|
|
|
},
|
2015-06-26 20:49:34 +00:00
|
|
|
DefaultNamespace: func() (string, bool, error) {
|
2015-04-07 18:21:25 +00:00
|
|
|
return clientConfig.Namespace()
|
|
|
|
},
|
2015-11-19 18:14:10 +00:00
|
|
|
Generators: func(cmdName string) map[string]kubectl.Generator {
|
|
|
|
return DefaultGenerators(cmdName)
|
2015-05-05 09:15:01 +00:00
|
|
|
},
|
2015-12-01 16:52:11 +00:00
|
|
|
CanBeExposed: func(kind unversioned.GroupKind) error {
|
2015-10-20 15:50:20 +00:00
|
|
|
switch kind {
|
2016-01-15 13:47:11 +00:00
|
|
|
case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"):
|
2015-10-20 15:50:20 +00:00
|
|
|
// nothing to do here
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("cannot expose a %s", kind)
|
2015-08-26 08:06:40 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
2015-12-01 16:52:11 +00:00
|
|
|
CanBeAutoscaled: func(kind unversioned.GroupKind) error {
|
2015-10-20 15:50:20 +00:00
|
|
|
switch kind {
|
2015-12-01 16:52:11 +00:00
|
|
|
case api.Kind("ReplicationController"), extensions.Kind("Deployment"):
|
2015-10-20 15:50:20 +00:00
|
|
|
// nothing to do here
|
|
|
|
default:
|
2015-12-01 16:52:11 +00:00
|
|
|
return fmt.Errorf("cannot autoscale a %v", kind)
|
2015-10-15 22:15:13 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
2015-10-22 17:34:19 +00:00
|
|
|
AttachablePodForObject: func(object runtime.Object) (*api.Pod, error) {
|
2015-12-08 20:14:38 +00:00
|
|
|
client, err := clients.ClientForVersion(nil)
|
2015-10-22 17:34:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
switch t := object.(type) {
|
|
|
|
case *api.ReplicationController:
|
2015-11-13 01:07:21 +00:00
|
|
|
return GetFirstPod(client, t.Namespace, t.Spec.Selector)
|
|
|
|
case *extensions.Deployment:
|
|
|
|
return GetFirstPod(client, t.Namespace, t.Spec.Selector)
|
|
|
|
case *extensions.Job:
|
|
|
|
return GetFirstPod(client, t.Namespace, t.Spec.Selector.MatchLabels)
|
2015-10-22 17:34:19 +00:00
|
|
|
case *api.Pod:
|
|
|
|
return t, nil
|
|
|
|
default:
|
2015-11-20 12:38:32 +00:00
|
|
|
gvk, err := api.Scheme.ObjectKind(object)
|
2015-10-22 17:34:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-11-20 12:38:32 +00:00
|
|
|
return nil, fmt.Errorf("cannot attach to %v: not implemented", gvk)
|
2015-10-22 17:34:19 +00:00
|
|
|
}
|
|
|
|
},
|
2015-11-11 11:29:52 +00:00
|
|
|
EditorEnvs: func() []string {
|
|
|
|
return []string{"KUBE_EDITOR", "EDITOR"}
|
|
|
|
},
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-13 01:07:21 +00:00
|
|
|
// GetFirstPod returns the first pod of an object from its namespace and selector
|
|
|
|
func GetFirstPod(client *client.Client, namespace string, selector map[string]string) (*api.Pod, error) {
|
|
|
|
var pods *api.PodList
|
|
|
|
for pods == nil || len(pods.Items) == 0 {
|
|
|
|
var err error
|
2015-12-02 11:12:57 +00:00
|
|
|
labelSelector := labels.SelectorFromSet(selector)
|
2015-12-10 09:39:03 +00:00
|
|
|
options := api.ListOptions{LabelSelector: labelSelector}
|
2015-12-02 11:12:57 +00:00
|
|
|
if pods, err = client.Pods(namespace).List(options); err != nil {
|
2015-11-13 01:07:21 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(pods.Items) == 0 {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pod := &pods.Items[0]
|
|
|
|
return pod, nil
|
|
|
|
}
|
|
|
|
|
2015-04-07 18:21:25 +00:00
|
|
|
// BindFlags adds any flags that are common to all kubectl sub commands.
|
|
|
|
func (f *Factory) BindFlags(flags *pflag.FlagSet) {
|
|
|
|
// any flags defined by external projects (not part of pflags)
|
2015-08-17 06:33:44 +00:00
|
|
|
flags.AddGoFlagSet(flag.CommandLine)
|
2015-04-07 18:21:25 +00:00
|
|
|
|
2015-05-16 16:44:42 +00:00
|
|
|
// Merge factory's flags
|
2015-08-17 06:33:44 +00:00
|
|
|
flags.AddFlagSet(f.flags)
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
// Globally persistent flags across all subcommands.
|
|
|
|
// TODO Change flag names to consts to allow safer lookup from subcommands.
|
|
|
|
// TODO Add a verbose flag that turns on glog logging. Probably need a way
|
|
|
|
// to do that automatically for every subcommand.
|
|
|
|
flags.BoolVar(&f.clients.matchVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")
|
2015-05-16 16:44:42 +00:00
|
|
|
|
2015-08-08 21:29:57 +00:00
|
|
|
// Normalize all flags that are coming from other packages or pre-configurations
|
2015-05-16 16:44:42 +00:00
|
|
|
// a.k.a. change all "_" to "-". e.g. glog package
|
|
|
|
flags.SetNormalizeFunc(util.WordSepNormalizeFunc)
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getPorts(spec api.PodSpec) []string {
|
|
|
|
result := []string{}
|
|
|
|
for _, container := range spec.Containers {
|
|
|
|
for _, port := range container.Ports {
|
|
|
|
result = append(result, strconv.Itoa(port.ContainerPort))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2015-07-17 01:02:36 +00:00
|
|
|
// Extracts the ports exposed by a service from the given service spec.
|
|
|
|
func getServicePorts(spec api.ServiceSpec) []string {
|
|
|
|
result := []string{}
|
|
|
|
for _, servicePort := range spec.Ports {
|
|
|
|
result = append(result, strconv.Itoa(servicePort.Port))
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2015-04-07 18:21:25 +00:00
|
|
|
type clientSwaggerSchema struct {
|
2015-09-10 21:58:09 +00:00
|
|
|
c *client.Client
|
|
|
|
cacheDir string
|
2015-08-27 20:45:29 +00:00
|
|
|
mapper meta.RESTMapper
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
|
2015-09-10 21:58:09 +00:00
|
|
|
const schemaFileName = "schema.json"
|
|
|
|
|
|
|
|
type schemaClient interface {
|
|
|
|
Get() *client.Request
|
|
|
|
}
|
|
|
|
|
2015-09-16 23:31:45 +00:00
|
|
|
func recursiveSplit(dir string) []string {
|
|
|
|
parent, file := path.Split(dir)
|
|
|
|
if len(parent) == 0 {
|
|
|
|
return []string{file}
|
|
|
|
}
|
|
|
|
return append(recursiveSplit(parent[:len(parent)-1]), file)
|
|
|
|
}
|
|
|
|
|
|
|
|
func substituteUserHome(dir string) (string, error) {
|
|
|
|
if len(dir) == 0 || dir[0] != '~' {
|
|
|
|
return dir, nil
|
|
|
|
}
|
|
|
|
parts := recursiveSplit(dir)
|
|
|
|
if len(parts[0]) == 1 {
|
|
|
|
parts[0] = os.Getenv("HOME")
|
|
|
|
} else {
|
|
|
|
usr, err := user.Lookup(parts[0][1:])
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
parts[0] = usr.HomeDir
|
|
|
|
}
|
|
|
|
return path.Join(parts...), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeSchemaFile(schemaData []byte, cacheDir, cacheFile, prefix, groupVersion string) error {
|
|
|
|
if err := os.MkdirAll(path.Join(cacheDir, prefix, groupVersion), 0755); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
tmpFile, err := ioutil.TempFile(cacheDir, "schema")
|
|
|
|
if err != nil {
|
|
|
|
// If we can't write, keep going.
|
|
|
|
if os.IsPermission(err) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err := io.Copy(tmpFile, bytes.NewBuffer(schemaData)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := os.Link(tmpFile.Name(), cacheFile); err != nil {
|
|
|
|
// If we can't write due to file existing, or permission problems, keep going.
|
|
|
|
if os.IsExist(err) || os.IsPermission(err) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-09-17 05:15:05 +00:00
|
|
|
func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string) (err error) {
|
2015-09-10 21:58:09 +00:00
|
|
|
var schemaData []byte
|
2015-09-16 23:31:45 +00:00
|
|
|
fullDir, err := substituteUserHome(cacheDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cacheFile := path.Join(fullDir, prefix, groupVersion, schemaFileName)
|
2015-09-10 21:58:09 +00:00
|
|
|
|
|
|
|
if len(cacheDir) != 0 {
|
|
|
|
if schemaData, err = ioutil.ReadFile(cacheFile); err != nil && !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if schemaData == nil {
|
|
|
|
schemaData, err = c.Get().
|
2015-09-17 05:15:05 +00:00
|
|
|
AbsPath("/swaggerapi", prefix, groupVersion).
|
2015-09-10 21:58:09 +00:00
|
|
|
Do().
|
|
|
|
Raw()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(cacheDir) != 0 {
|
2015-09-16 23:31:45 +00:00
|
|
|
if err := writeSchemaFile(schemaData, fullDir, cacheFile, prefix, groupVersion); err != nil {
|
2015-09-10 21:58:09 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return schema.ValidateBytes(data)
|
|
|
|
}
|
|
|
|
|
2015-08-10 20:08:34 +00:00
|
|
|
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
|
2015-12-21 05:37:49 +00:00
|
|
|
gvk, err := json.DefaultMetaFactory.Interpret(data)
|
2015-08-10 20:08:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-19 05:08:34 +00:00
|
|
|
if ok := registered.IsEnabledVersion(gvk.GroupVersion()); !ok {
|
|
|
|
return fmt.Errorf("API version %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), registered.EnabledVersions())
|
2015-08-27 20:45:29 +00:00
|
|
|
}
|
2015-12-08 14:21:04 +00:00
|
|
|
if gvk.Group == extensions.GroupName {
|
2015-10-12 18:06:42 +00:00
|
|
|
if c.c.ExtensionsClient == nil {
|
2015-08-27 20:45:29 +00:00
|
|
|
return errors.New("unable to validate: no experimental client")
|
2015-08-10 20:08:34 +00:00
|
|
|
}
|
2015-11-20 12:38:32 +00:00
|
|
|
return getSchemaAndValidate(c.c.ExtensionsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir)
|
2015-08-10 20:08:34 +00:00
|
|
|
}
|
2015-11-20 12:38:32 +00:00
|
|
|
return getSchemaAndValidate(c.c.RESTClient, data, "api", gvk.GroupVersion().String(), c.cacheDir)
|
2015-08-10 20:08:34 +00:00
|
|
|
}
|
|
|
|
|
2015-04-07 18:21:25 +00:00
|
|
|
// 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.
|
2015-04-10 12:54:22 +00:00
|
|
|
// 1. Merge together the kubeconfig itself. This is done with the following hierarchy rules:
|
|
|
|
// 1. CommandLineLocation - this parsed from the command line, so it must be late bound. If you specify this,
|
|
|
|
// then no other kubeconfig files are merged. This file must exist.
|
|
|
|
// 2. If $KUBECONFIG is set, then it is treated as a list of files that should be merged.
|
2015-06-01 23:58:19 +00:00
|
|
|
// 3. HomeDirectoryLocation
|
2015-04-07 18:21:25 +00:00
|
|
|
// Empty filenames are ignored. Files with non-deserializable content produced errors.
|
|
|
|
// The first file to set a particular value or map key wins and the value or map key is never changed.
|
|
|
|
// This means that the first file to set CurrentContext will have its context preserved. It also means
|
|
|
|
// that if two files specify a "red-user", only values from the first file's red-user are used. Even
|
|
|
|
// non-conflicting entries from the second file's "red-user" are discarded.
|
|
|
|
// 2. Determine the context to use based on the first hit in this chain
|
|
|
|
// 1. command line argument - again, parsed from the command line, so it must be late bound
|
|
|
|
// 2. CurrentContext from the merged kubeconfig file
|
|
|
|
// 3. Empty is allowed at this stage
|
|
|
|
// 3. Determine the cluster info and auth info to use. At this point, we may or may not have a context. They
|
|
|
|
// are built based on the first hit in this chain. (run it twice, once for auth, once for cluster)
|
|
|
|
// 1. command line argument
|
|
|
|
// 2. If context is present, then use the context value
|
|
|
|
// 3. Empty is allowed
|
|
|
|
// 4. Determine the actual cluster info to use. At this point, we may or may not have a cluster info. Build
|
|
|
|
// each piece of the cluster info based on the chain:
|
|
|
|
// 1. command line argument
|
|
|
|
// 2. If cluster info is present and a value for the attribute is present, use it.
|
|
|
|
// 3. If you don't have a server location, bail.
|
|
|
|
// 5. Auth info is build using the same rules as cluster info, EXCEPT that you can only have one authentication
|
|
|
|
// technique per auth info. The following conditions result in an error:
|
|
|
|
// 1. If there are two conflicting techniques specified from the command line, fail.
|
|
|
|
// 2. If the command line does not specify one, and the auth info has conflicting techniques, fail.
|
|
|
|
// 3. If the command line specifies one and the auth info specifies another, honor the command line technique.
|
|
|
|
// 2. Use default values and potentially prompt for auth information
|
2015-06-01 23:58:19 +00:00
|
|
|
//
|
|
|
|
// However, if it appears that we're running in a kubernetes cluster
|
|
|
|
// container environment, then run with the auth info kubernetes mounted for
|
|
|
|
// us. Specifically:
|
|
|
|
// The env vars KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT are
|
|
|
|
// set, and the file /var/run/secrets/kubernetes.io/serviceaccount/token
|
|
|
|
// exists and is not a directory.
|
2015-04-07 18:21:25 +00:00
|
|
|
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
|
|
|
|
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
|
|
|
flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
|
|
|
|
|
|
|
|
overrides := &clientcmd.ConfigOverrides{}
|
|
|
|
flagNames := clientcmd.RecommendedConfigOverrideFlags("")
|
|
|
|
// short flagnames are disabled by default. These are here for compatibility with existing scripts
|
2015-04-13 17:14:23 +00:00
|
|
|
flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
clientcmd.BindOverrideFlags(overrides, flags, flagNames)
|
|
|
|
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
|
|
|
|
|
|
|
|
return clientConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
// PrintObject prints an api object given command line flags to modify the output format
|
|
|
|
func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error {
|
|
|
|
mapper, _ := f.Object()
|
2015-11-20 12:38:32 +00:00
|
|
|
gvk, err := api.Scheme.ObjectKind(obj)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-11-20 12:38:32 +00:00
|
|
|
mapping, err := mapper.RESTMapping(gvk.GroupKind())
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-05-12 11:14:31 +00:00
|
|
|
printer, err := f.PrinterForMapping(cmd, mapping, false)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return printer.PrintObj(obj, out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
|
|
|
|
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
|
2015-05-12 11:14:31 +00:00
|
|
|
func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) {
|
2015-04-07 18:21:25 +00:00
|
|
|
printer, ok, err := PrinterForCommand(cmd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if ok {
|
|
|
|
clientConfig, err := f.ClientConfig()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-12-01 16:52:11 +00:00
|
|
|
version, err := OutputVersion(cmd, clientConfig.GroupVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if version.IsEmpty() {
|
|
|
|
version = mapping.GroupVersionKind.GroupVersion()
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
2015-12-01 16:52:11 +00:00
|
|
|
if version.IsEmpty() {
|
2015-04-07 18:21:25 +00:00
|
|
|
return nil, fmt.Errorf("you must specify an output-version when using this output format")
|
|
|
|
}
|
2015-12-01 16:52:11 +00:00
|
|
|
|
|
|
|
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion())
|
|
|
|
|
2015-04-07 18:21:25 +00:00
|
|
|
} else {
|
2015-08-05 14:21:47 +00:00
|
|
|
// Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper
|
|
|
|
columnLabel, err := cmd.Flags().GetStringSlice("label-columns")
|
|
|
|
if err != nil {
|
|
|
|
columnLabel = []string{}
|
|
|
|
}
|
2015-11-10 11:34:28 +00:00
|
|
|
printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers"), withNamespace, GetWideFlag(cmd), GetFlagBool(cmd, "show-all"), isWatch(cmd), columnLabel)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-08-08 06:04:25 +00:00
|
|
|
printer = maybeWrapSortingPrinter(cmd, printer)
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
2015-12-01 16:52:11 +00:00
|
|
|
|
2015-04-07 18:21:25 +00:00
|
|
|
return printer, nil
|
|
|
|
}
|
|
|
|
|
2015-10-31 00:16:57 +00:00
|
|
|
// One stop shopping for a Builder
|
|
|
|
func (f *Factory) NewBuilder() *resource.Builder {
|
|
|
|
mapper, typer := f.Object()
|
|
|
|
|
2015-12-21 05:37:49 +00:00
|
|
|
return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true))
|
2015-10-31 00:16:57 +00:00
|
|
|
}
|