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"
|
2016-02-16 17:17:47 +00:00
|
|
|
"path/filepath"
|
2016-03-29 17:01:54 +00:00
|
|
|
"sort"
|
2015-04-07 18:21:25 +00:00
|
|
|
"strconv"
|
2016-01-22 18:33:23 +00:00
|
|
|
"strings"
|
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"
|
|
|
|
|
2016-04-26 14:33:47 +00:00
|
|
|
"k8s.io/kubernetes/federation/apis/federation"
|
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-03-10 01:27:19 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apimachinery"
|
2016-01-13 22:40:56 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
2016-04-15 22:30:15 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/apps"
|
2016-02-09 12:53:14 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
2016-02-17 23:07:38 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/batch"
|
2015-11-13 01:07:21 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
2016-02-15 16:49:01 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/metrics"
|
2016-05-07 00:03:43 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/policy"
|
2016-05-25 21:25:56 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/rbac"
|
2016-02-12 18:58:43 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/restclient"
|
2015-08-13 19:01:50 +00:00
|
|
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
2016-03-01 22:15:55 +00:00
|
|
|
clientset "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
|
2015-08-13 19:01:50 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
2016-03-29 17:01:54 +00:00
|
|
|
"k8s.io/kubernetes/pkg/controller"
|
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"
|
2016-03-10 01:27:19 +00:00
|
|
|
"k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
|
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"
|
2016-03-11 05:33:16 +00:00
|
|
|
utilflag "k8s.io/kubernetes/pkg/util/flag"
|
2016-03-29 17:01:54 +00:00
|
|
|
"k8s.io/kubernetes/pkg/watch"
|
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
|
|
|
|
2016-03-10 01:27:19 +00:00
|
|
|
// Returns interfaces for dealing with arbitrary runtime.Objects. If thirdPartyDiscovery is true, performs API calls
|
|
|
|
// to discovery dynamic API objects registered by third parties.
|
|
|
|
Object func(thirdPartyDiscovery bool) (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.
|
2016-02-12 18:58:43 +00:00
|
|
|
ClientConfig func() (*restclient.Config, error)
|
2015-04-07 18:21:25 +00:00
|
|
|
// 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.
|
2016-02-04 07:08:44 +00:00
|
|
|
Printer func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels 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)
|
2016-01-19 22:50:03 +00:00
|
|
|
// Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error
|
|
|
|
Rollbacker func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error)
|
2016-01-21 19:07:23 +00:00
|
|
|
// Returns a StatusViewer for printing rollout status.
|
|
|
|
StatusViewer func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error)
|
2016-02-06 02:43:02 +00:00
|
|
|
// MapBasedSelectorForObject returns the map-based selector associated with the provided object. If a
|
|
|
|
// new set-based selector is provided, an error is returned if the selector cannot be converted to a
|
|
|
|
// map-based selector
|
|
|
|
MapBasedSelectorForObject func(object runtime.Object) (string, error)
|
2015-05-07 15:30:28 +00:00
|
|
|
// PortsForObject returns the ports associated with the provided object
|
|
|
|
PortsForObject func(object runtime.Object) ([]string, error)
|
2016-05-12 04:07:07 +00:00
|
|
|
// ProtocolsForObject returns the <port, protocol> mapping associated with the provided object
|
|
|
|
ProtocolsForObject func(object runtime.Object) (map[string]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
|
2016-02-12 18:58:43 +00:00
|
|
|
LogsForObject func(object, options runtime.Object) (*restclient.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)
|
2016-02-07 02:19:42 +00:00
|
|
|
// SwaggerSchema returns the schema declaration for the provided group version kind.
|
|
|
|
SwaggerSchema func(unversioned.GroupVersionKind) (*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
|
2016-05-11 07:14:23 +00:00
|
|
|
// overridden.
|
2015-06-26 20:49:34 +00:00
|
|
|
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)
|
2016-05-11 00:26:39 +00:00
|
|
|
// 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 func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, 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"
|
2016-02-17 18:15:32 +00:00
|
|
|
ServiceAccountV1GeneratorName = "serviceaccount/v1"
|
2015-10-21 14:41:42 +00:00
|
|
|
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
|
|
|
|
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
|
|
|
|
JobV1Beta1GeneratorName = "job/v1beta1"
|
2016-03-09 23:06:49 +00:00
|
|
|
JobV1GeneratorName = "job/v1"
|
2015-10-21 14:41:42 +00:00
|
|
|
NamespaceV1GeneratorName = "namespace/v1"
|
|
|
|
SecretV1GeneratorName = "secret/v1"
|
|
|
|
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
|
2016-04-24 19:46:58 +00:00
|
|
|
SecretForTLSV1GeneratorName = "secret-for-tls/v1"
|
2016-02-19 02:24:21 +00:00
|
|
|
ConfigMapV1GeneratorName = "configmap/v1"
|
2015-10-21 14:41:42 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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{},
|
2016-03-09 23:06:49 +00:00
|
|
|
JobV1GeneratorName: kubectl.JobV1{},
|
2015-11-19 18:14:10 +00:00
|
|
|
}
|
|
|
|
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{},
|
|
|
|
}
|
2016-04-24 19:46:58 +00:00
|
|
|
generators["secret-for-tls"] = map[string]kubectl.Generator{
|
|
|
|
SecretForTLSV1GeneratorName: kubectl.SecretForTLSGeneratorV1{},
|
|
|
|
}
|
|
|
|
|
2015-11-19 18:14:10 +00:00
|
|
|
return generators[cmdName]
|
2015-10-21 14:41:42 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 01:27:19 +00:00
|
|
|
func getGroupVersionKinds(gvks []unversioned.GroupVersionKind, group string) []unversioned.GroupVersionKind {
|
|
|
|
result := []unversioned.GroupVersionKind{}
|
|
|
|
for ix := range gvks {
|
|
|
|
if gvks[ix].Group == group {
|
|
|
|
result = append(result, gvks[ix])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeInterfacesFor(versionList []unversioned.GroupVersion) func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
|
|
|
|
accessor := meta.NewAccessor()
|
|
|
|
return func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
|
|
|
|
for ix := range versionList {
|
|
|
|
if versionList[ix].String() == version.String() {
|
|
|
|
return &meta.VersionInterfaces{
|
|
|
|
ObjectConvertor: thirdpartyresourcedata.NewThirdPartyObjectConverter(api.Scheme),
|
|
|
|
MetadataAccessor: accessor,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, versionList)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2016-03-11 16:41:18 +00:00
|
|
|
mapper := kubectl.ShortcutExpander{RESTMapper: registered.RESTMapper()}
|
2015-04-07 18:21:25 +00:00
|
|
|
|
|
|
|
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
2016-03-11 05:33:16 +00:00
|
|
|
flags.SetNormalizeFunc(utilflag.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
|
|
|
|
2016-03-10 01:27:19 +00:00
|
|
|
// If discoverDynamicAPIs is true, make API calls to the discovery service to find APIs that
|
|
|
|
// have been dynamically added to the apiserver
|
|
|
|
Object: func(discoverDynamicAPIs bool) (meta.RESTMapper, runtime.ObjectTyper) {
|
2015-04-07 18:21:25 +00:00
|
|
|
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
|
|
|
}
|
2016-03-10 01:27:19 +00:00
|
|
|
if discoverDynamicAPIs {
|
|
|
|
client, err := clients.ClientForVersion(&unversioned.GroupVersion{Version: "v1"})
|
|
|
|
CheckErr(err)
|
2015-04-07 18:21:25 +00:00
|
|
|
|
2016-03-10 01:27:19 +00:00
|
|
|
versions, gvks, err := GetThirdPartyGroupVersions(client.Discovery())
|
|
|
|
CheckErr(err)
|
|
|
|
if len(versions) > 0 {
|
|
|
|
priorityMapper, ok := mapper.RESTMapper.(meta.PriorityRESTMapper)
|
|
|
|
if !ok {
|
|
|
|
CheckErr(fmt.Errorf("expected PriorityMapper, saw: %v", mapper.RESTMapper))
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
multiMapper, ok := priorityMapper.Delegate.(meta.MultiRESTMapper)
|
|
|
|
if !ok {
|
|
|
|
CheckErr(fmt.Errorf("unexpected type: %v", mapper.RESTMapper))
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
groupsMap := map[string][]unversioned.GroupVersion{}
|
|
|
|
for _, version := range versions {
|
|
|
|
groupsMap[version.Group] = append(groupsMap[version.Group], version)
|
|
|
|
}
|
|
|
|
for group, versionList := range groupsMap {
|
|
|
|
preferredExternalVersion := versionList[0]
|
|
|
|
|
|
|
|
thirdPartyMapper, err := kubectl.NewThirdPartyResourceMapper(versionList, getGroupVersionKinds(gvks, group))
|
|
|
|
CheckErr(err)
|
|
|
|
accessor := meta.NewAccessor()
|
|
|
|
groupMeta := apimachinery.GroupMeta{
|
|
|
|
GroupVersion: preferredExternalVersion,
|
|
|
|
GroupVersions: versionList,
|
|
|
|
RESTMapper: thirdPartyMapper,
|
|
|
|
SelfLinker: runtime.SelfLinker(accessor),
|
|
|
|
InterfacesFor: makeInterfacesFor(versionList),
|
|
|
|
}
|
2016-02-15 16:49:01 +00:00
|
|
|
|
2016-03-10 01:27:19 +00:00
|
|
|
CheckErr(registered.RegisterGroup(groupMeta))
|
|
|
|
registered.AddThirdPartyAPIGroupVersions(versionList...)
|
|
|
|
multiMapper = append(meta.MultiRESTMapper{thirdPartyMapper}, multiMapper...)
|
|
|
|
}
|
|
|
|
priorityMapper.Delegate = multiMapper
|
|
|
|
// Re-assign to the RESTMapper here because priorityMapper is actually a copy, so if we
|
|
|
|
// don't re-assign, the above assignement won't actually update mapper.RESTMapper
|
|
|
|
mapper.RESTMapper = priorityMapper
|
|
|
|
}
|
|
|
|
}
|
|
|
|
outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}
|
2016-02-15 16:49:01 +00:00
|
|
|
priorityRESTMapper := meta.PriorityRESTMapper{
|
|
|
|
Delegate: outputRESTMapper,
|
|
|
|
ResourcePriority: []unversioned.GroupVersionResource{
|
|
|
|
{Group: api.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
|
|
|
|
{Group: extensions.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
|
|
|
|
{Group: metrics.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
|
2016-04-26 14:33:47 +00:00
|
|
|
{Group: federation.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
|
2016-02-15 16:49:01 +00:00
|
|
|
},
|
|
|
|
KindPriority: []unversioned.GroupVersionKind{
|
|
|
|
{Group: api.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
|
|
|
|
{Group: extensions.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
|
|
|
|
{Group: metrics.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
|
2016-04-26 14:33:47 +00:00
|
|
|
{Group: federation.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
|
2016-02-15 16:49:01 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
return priorityRESTMapper, 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
|
|
|
},
|
2016-02-12 18:58:43 +00:00
|
|
|
ClientConfig: func() (*restclient.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) {
|
2016-03-10 01:27:19 +00:00
|
|
|
gvk := mapping.GroupVersionKind
|
2015-12-08 20:14:38 +00:00
|
|
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
2016-03-10 01:27:19 +00:00
|
|
|
c, err := clients.ClientForVersion(&mappingVersion)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-03-10 01:27:19 +00:00
|
|
|
switch gvk.Group {
|
2015-12-08 14:21:04 +00:00
|
|
|
case api.GroupName:
|
2016-03-10 01:27:19 +00:00
|
|
|
return c.RESTClient, nil
|
2016-02-09 12:53:14 +00:00
|
|
|
case autoscaling.GroupName:
|
2016-03-10 01:27:19 +00:00
|
|
|
return c.AutoscalingClient.RESTClient, nil
|
2016-02-17 23:07:38 +00:00
|
|
|
case batch.GroupName:
|
2016-03-10 01:27:19 +00:00
|
|
|
return c.BatchClient.RESTClient, nil
|
2016-05-07 00:03:43 +00:00
|
|
|
case policy.GroupName:
|
|
|
|
return c.PolicyClient.RESTClient, nil
|
2016-04-15 22:30:15 +00:00
|
|
|
case apps.GroupName:
|
|
|
|
return c.AppsClient.RESTClient, nil
|
2015-12-08 14:21:04 +00:00
|
|
|
case extensions.GroupName:
|
2016-03-10 01:27:19 +00:00
|
|
|
return c.ExtensionsClient.RESTClient, nil
|
|
|
|
case api.SchemeGroupVersion.Group:
|
|
|
|
return c.RESTClient, nil
|
|
|
|
case extensions.SchemeGroupVersion.Group:
|
|
|
|
return c.ExtensionsClient.RESTClient, nil
|
2016-04-26 14:33:47 +00:00
|
|
|
case federation.GroupName:
|
|
|
|
return clients.FederationClientForVersion(&mappingVersion)
|
2016-05-25 21:25:56 +00:00
|
|
|
case rbac.GroupName:
|
|
|
|
return c.RbacClient.RESTClient, nil
|
2016-03-10 01:27:19 +00:00
|
|
|
default:
|
|
|
|
if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
|
|
|
return nil, fmt.Errorf("unknown api group/version: %s", gvk.String())
|
|
|
|
}
|
|
|
|
cfg, err := clientConfig.ClientConfig()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
gv := gvk.GroupVersion()
|
|
|
|
cfg.GroupVersion = &gv
|
|
|
|
cfg.APIPath = "/apis"
|
|
|
|
cfg.Codec = thirdpartyresourcedata.NewCodec(c.ExtensionsClient.RESTClient.Codec(), gvk.Kind)
|
|
|
|
return restclient.RESTClientFor(cfg)
|
2015-08-10 20:08:34 +00:00
|
|
|
}
|
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()
|
2016-05-05 17:30:50 +00:00
|
|
|
if mapping.GroupVersionKind.Group == federation.GroupName {
|
|
|
|
fedClientSet, err := clients.FederationClientSetForVersion(&mappingVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if mapping.GroupVersionKind.Kind == "Cluster" {
|
|
|
|
return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil
|
|
|
|
}
|
|
|
|
}
|
2015-12-08 20:14:38 +00:00
|
|
|
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()...)
|
|
|
|
},
|
2016-02-04 07:08:44 +00:00
|
|
|
Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
|
|
|
|
return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, showLabels, absoluteTimestamps, columnLabels), nil
|
2015-04-07 18:21:25 +00:00
|
|
|
},
|
2016-02-06 02:43:02 +00:00
|
|
|
MapBasedSelectorForObject: func(object runtime.Object) (string, error) {
|
|
|
|
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
|
|
|
|
switch t := object.(type) {
|
|
|
|
case *api.ReplicationController:
|
2016-01-15 13:47:11 +00:00
|
|
|
return kubectl.MakeLabels(t.Spec.Selector), nil
|
2016-02-06 02:43:02 +00:00
|
|
|
case *api.Pod:
|
|
|
|
if len(t.Labels) == 0 {
|
|
|
|
return "", fmt.Errorf("the pod has no labels and cannot be exposed")
|
|
|
|
}
|
|
|
|
return kubectl.MakeLabels(t.Labels), nil
|
|
|
|
case *api.Service:
|
|
|
|
if t.Spec.Selector == nil {
|
|
|
|
return "", fmt.Errorf("the service has no pod selector set")
|
|
|
|
}
|
|
|
|
return kubectl.MakeLabels(t.Spec.Selector), nil
|
|
|
|
case *extensions.Deployment:
|
|
|
|
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
|
|
|
|
// operator, DoubleEquals operator and In operator with only one element in the set.
|
|
|
|
if len(t.Spec.Selector.MatchExpressions) > 0 {
|
2016-03-23 23:45:24 +00:00
|
|
|
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
|
2016-02-06 02:43:02 +00:00
|
|
|
}
|
|
|
|
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
|
2016-02-09 21:18:45 +00:00
|
|
|
case *extensions.ReplicaSet:
|
|
|
|
// TODO(madhusudancs): Make this smarter by admitting MatchExpressions with Equals
|
|
|
|
// operator, DoubleEquals operator and In operator with only one element in the set.
|
|
|
|
if len(t.Spec.Selector.MatchExpressions) > 0 {
|
2016-03-23 23:45:24 +00:00
|
|
|
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
|
2016-02-09 21:18:45 +00:00
|
|
|
}
|
|
|
|
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
|
2015-04-07 18:21:25 +00:00
|
|
|
default:
|
2016-05-21 04:15:31 +00:00
|
|
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
2015-05-07 15:30:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2016-05-21 04:15:31 +00:00
|
|
|
return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0])
|
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
|
2016-02-09 21:18:45 +00:00
|
|
|
case *extensions.ReplicaSet:
|
|
|
|
return getPorts(t.Spec.Template.Spec), nil
|
2015-05-07 15:30:28 +00:00
|
|
|
default:
|
2016-05-21 04:15:31 +00:00
|
|
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-05-21 04:15:31 +00:00
|
|
|
return nil, fmt.Errorf("cannot extract ports from %v", gvks[0])
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
},
|
2016-05-12 04:07:07 +00:00
|
|
|
ProtocolsForObject: func(object runtime.Object) (map[string]string, error) {
|
|
|
|
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
|
|
|
|
switch t := object.(type) {
|
|
|
|
case *api.ReplicationController:
|
|
|
|
return getProtocols(t.Spec.Template.Spec), nil
|
|
|
|
case *api.Pod:
|
|
|
|
return getProtocols(t.Spec), nil
|
|
|
|
case *api.Service:
|
|
|
|
return getServiceProtocols(t.Spec), nil
|
|
|
|
case *extensions.Deployment:
|
|
|
|
return getProtocols(t.Spec.Template.Spec), nil
|
|
|
|
case *extensions.ReplicaSet:
|
|
|
|
return getProtocols(t.Spec.Template.Spec), nil
|
|
|
|
default:
|
2016-05-21 04:15:31 +00:00
|
|
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
2016-05-12 04:07:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-05-21 04:15:31 +00:00
|
|
|
return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0])
|
2016-05-12 04:07:07 +00:00
|
|
|
}
|
|
|
|
},
|
2015-05-13 08:52:25 +00:00
|
|
|
LabelsForObject: func(object runtime.Object) (map[string]string, error) {
|
|
|
|
return meta.NewAccessor().Labels(object)
|
|
|
|
},
|
2016-02-12 18:58:43 +00:00
|
|
|
LogsForObject: func(object, options runtime.Object) (*restclient.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
|
2016-02-16 20:21:35 +00:00
|
|
|
|
|
|
|
case *api.ReplicationController:
|
|
|
|
opts, ok := options.(*api.PodLogOptions)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("provided options object is not a PodLogOptions")
|
|
|
|
}
|
|
|
|
selector := labels.SelectorFromSet(t.Spec.Selector)
|
2016-03-29 17:01:54 +00:00
|
|
|
sortBy := func(pods []*api.Pod) sort.Interface { return controller.ActivePods(pods) }
|
|
|
|
pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy)
|
2016-02-16 20:21:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if numPods > 1 {
|
|
|
|
fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
|
|
|
|
|
|
|
|
case *extensions.ReplicaSet:
|
|
|
|
opts, ok := options.(*api.PodLogOptions)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("provided options object is not a PodLogOptions")
|
|
|
|
}
|
|
|
|
selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid label selector: %v", err)
|
|
|
|
}
|
2016-03-29 17:01:54 +00:00
|
|
|
sortBy := func(pods []*api.Pod) sort.Interface { return controller.ActivePods(pods) }
|
|
|
|
pod, numPods, err := GetFirstPod(c, t.Namespace, selector, 20*time.Second, sortBy)
|
2016-02-16 20:21:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if numPods > 1 {
|
|
|
|
fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
|
|
|
|
|
2015-10-18 14:20:28 +00:00
|
|
|
default:
|
2016-05-21 04:15:31 +00:00
|
|
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
2015-10-18 14:20:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-05-21 04:15:31 +00:00
|
|
|
return nil, fmt.Errorf("cannot get the logs from %v", gvks[0])
|
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:
|
2016-05-21 04:15:31 +00:00
|
|
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
2016-01-25 11:34:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2016-05-21 04:15:31 +00:00
|
|
|
return false, fmt.Errorf("cannot pause %v", gvks[0])
|
2016-01-25 11:34:48 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
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:
|
2016-05-21 04:15:31 +00:00
|
|
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
2016-01-25 11:34:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
2016-05-21 04:15:31 +00:00
|
|
|
return false, fmt.Errorf("cannot resume %v", gvks[0])
|
2016-01-25 11:34:48 +00:00
|
|
|
}
|
|
|
|
},
|
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)
|
|
|
|
},
|
2016-01-19 22:50:03 +00:00
|
|
|
Rollbacker: func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
|
|
|
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
|
|
|
client, err := clients.ClientForVersion(&mappingVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), client)
|
|
|
|
},
|
2016-01-21 19:07:23 +00:00
|
|
|
StatusViewer: func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
|
|
|
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
|
|
|
client, err := clients.ClientForVersion(&mappingVersion)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), client)
|
|
|
|
},
|
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
|
|
|
}
|
2016-04-26 14:33:47 +00:00
|
|
|
fedClient, err := clients.FederationClientForVersion(nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-09-11 03:54:22 +00:00
|
|
|
return &clientSwaggerSchema{
|
|
|
|
c: client,
|
2016-04-26 14:33:47 +00:00
|
|
|
fedc: fedClient,
|
2015-09-11 03:54:22 +00:00
|
|
|
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
|
|
|
|
},
|
2016-02-07 02:19:42 +00:00
|
|
|
SwaggerSchema: func(gvk unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error) {
|
|
|
|
version := gvk.GroupVersion()
|
2015-12-11 22:15:41 +00:00
|
|
|
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-02-09 21:18:45 +00:00
|
|
|
case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"):
|
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 {
|
2016-03-10 18:45:23 +00:00
|
|
|
case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"):
|
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:
|
2016-01-20 00:40:18 +00:00
|
|
|
selector := labels.SelectorFromSet(t.Spec.Selector)
|
2016-03-29 17:01:54 +00:00
|
|
|
sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
|
|
|
|
pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy)
|
2016-02-16 20:21:35 +00:00
|
|
|
return pod, err
|
2015-11-13 01:07:21 +00:00
|
|
|
case *extensions.Deployment:
|
2016-02-06 02:43:02 +00:00
|
|
|
selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
|
2016-01-20 00:40:18 +00:00
|
|
|
if err != nil {
|
2016-02-10 17:43:30 +00:00
|
|
|
return nil, fmt.Errorf("invalid label selector: %v", err)
|
2016-01-20 00:40:18 +00:00
|
|
|
}
|
2016-03-29 17:01:54 +00:00
|
|
|
sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
|
|
|
|
pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy)
|
2016-02-16 20:21:35 +00:00
|
|
|
return pod, err
|
2016-04-18 15:44:19 +00:00
|
|
|
case *batch.Job:
|
2016-02-06 02:43:02 +00:00
|
|
|
selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
|
2016-01-20 00:40:18 +00:00
|
|
|
if err != nil {
|
2016-02-10 17:43:30 +00:00
|
|
|
return nil, fmt.Errorf("invalid label selector: %v", err)
|
2016-01-20 00:40:18 +00:00
|
|
|
}
|
2016-03-29 17:01:54 +00:00
|
|
|
sortBy := func(pods []*api.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
|
|
|
|
pod, _, err := GetFirstPod(client, t.Namespace, selector, 1*time.Minute, sortBy)
|
2016-02-16 20:21:35 +00:00
|
|
|
return pod, err
|
2015-10-22 17:34:19 +00:00
|
|
|
case *api.Pod:
|
|
|
|
return t, nil
|
|
|
|
default:
|
2016-05-21 04:15:31 +00:00
|
|
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
2015-10-22 17:34:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-05-21 04:15:31 +00:00
|
|
|
return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0])
|
2015-10-22 17:34:19 +00:00
|
|
|
}
|
|
|
|
},
|
2016-05-11 00:26:39 +00:00
|
|
|
// UpdatePodSpecForObject update the pod specification for the provided object
|
|
|
|
UpdatePodSpecForObject: func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
|
|
|
|
// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
|
|
|
|
switch t := obj.(type) {
|
|
|
|
case *api.Pod:
|
|
|
|
return true, fn(&t.Spec)
|
|
|
|
case *api.ReplicationController:
|
|
|
|
if t.Spec.Template == nil {
|
|
|
|
t.Spec.Template = &api.PodTemplateSpec{}
|
|
|
|
}
|
|
|
|
return true, fn(&t.Spec.Template.Spec)
|
|
|
|
case *extensions.Deployment:
|
|
|
|
return true, fn(&t.Spec.Template.Spec)
|
|
|
|
case *extensions.DaemonSet:
|
|
|
|
return true, fn(&t.Spec.Template.Spec)
|
|
|
|
case *extensions.ReplicaSet:
|
|
|
|
return true, fn(&t.Spec.Template.Spec)
|
|
|
|
case *apps.PetSet:
|
|
|
|
return true, fn(&t.Spec.Template.Spec)
|
|
|
|
case *batch.Job:
|
|
|
|
return true, fn(&t.Spec.Template.Spec)
|
|
|
|
default:
|
|
|
|
return false, fmt.Errorf("the object is not a pod or does not have a pod template")
|
|
|
|
}
|
|
|
|
},
|
2015-11-11 11:29:52 +00:00
|
|
|
EditorEnvs: func() []string {
|
|
|
|
return []string{"KUBE_EDITOR", "EDITOR"}
|
|
|
|
},
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-29 17:01:54 +00:00
|
|
|
// GetFirstPod returns a pod matching the namespace and label selector
|
|
|
|
// and the number of all pods that match the label selector.
|
|
|
|
func GetFirstPod(client client.Interface, namespace string, selector labels.Selector, timeout time.Duration, sortBy func([]*api.Pod) sort.Interface) (*api.Pod, int, error) {
|
|
|
|
options := api.ListOptions{LabelSelector: selector}
|
|
|
|
|
|
|
|
podList, err := client.Pods(namespace).List(options)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
pods := []*api.Pod{}
|
|
|
|
for i := range podList.Items {
|
|
|
|
pod := podList.Items[i]
|
|
|
|
pods = append(pods, &pod)
|
|
|
|
}
|
|
|
|
if len(pods) > 0 {
|
|
|
|
sort.Sort(sortBy(pods))
|
|
|
|
return pods[0], len(podList.Items), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Watch until we observe a pod
|
|
|
|
options.ResourceVersion = podList.ResourceVersion
|
|
|
|
w, err := client.Pods(namespace).Watch(options)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
defer w.Stop()
|
|
|
|
|
|
|
|
condition := func(event watch.Event) (bool, error) {
|
|
|
|
return event.Type == watch.Added || event.Type == watch.Modified, nil
|
|
|
|
}
|
|
|
|
event, err := watch.Until(timeout, w, condition)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
pod, ok := event.Object.(*api.Pod)
|
|
|
|
if !ok {
|
|
|
|
return nil, 0, fmt.Errorf("%#v is not a pod event", event)
|
2015-11-13 01:07:21 +00:00
|
|
|
}
|
2016-03-29 17:01:54 +00:00
|
|
|
return pod, 1, nil
|
2015-11-13 01:07:21 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 17:17:47 +00:00
|
|
|
// Command will stringify and return all environment arguments ie. a command run by a client
|
|
|
|
// using the factory.
|
|
|
|
// TODO: We need to filter out stuff like secrets.
|
|
|
|
func (f *Factory) Command() string {
|
|
|
|
if len(os.Args) == 0 {
|
|
|
|
return ""
|
2016-01-22 18:33:23 +00:00
|
|
|
}
|
2016-02-16 17:17:47 +00:00
|
|
|
base := filepath.Base(os.Args[0])
|
|
|
|
args := append([]string{base}, os.Args[1:]...)
|
2016-01-22 18:33:23 +00:00
|
|
|
return strings.Join(args, " ")
|
|
|
|
}
|
|
|
|
|
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) {
|
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
|
2016-03-11 05:33:16 +00:00
|
|
|
flags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
|
2016-02-19 22:40:14 +00:00
|
|
|
// BindCommonFlags adds any flags defined by external projects (not part of pflags)
|
|
|
|
func (f *Factory) BindExternalFlags(flags *pflag.FlagSet) {
|
|
|
|
// any flags defined by external projects (not part of pflags)
|
|
|
|
flags.AddGoFlagSet(flag.CommandLine)
|
|
|
|
}
|
|
|
|
|
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 {
|
2016-04-27 04:35:14 +00:00
|
|
|
result = append(result, strconv.Itoa(int(port.ContainerPort)))
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2016-05-12 04:07:07 +00:00
|
|
|
func getProtocols(spec api.PodSpec) map[string]string {
|
|
|
|
result := make(map[string]string)
|
|
|
|
for _, container := range spec.Containers {
|
|
|
|
for _, port := range container.Ports {
|
|
|
|
result[strconv.Itoa(int(port.ContainerPort))] = string(port.Protocol)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 {
|
2016-04-27 04:35:14 +00:00
|
|
|
result = append(result, strconv.Itoa(int(servicePort.Port)))
|
2015-07-17 01:02:36 +00:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2016-05-12 04:07:07 +00:00
|
|
|
// Extracts the protocols exposed by a service from the given service spec.
|
|
|
|
func getServiceProtocols(spec api.ServiceSpec) map[string]string {
|
|
|
|
result := make(map[string]string)
|
|
|
|
for _, servicePort := range spec.Ports {
|
|
|
|
result[strconv.Itoa(int(servicePort.Port))] = string(servicePort.Protocol)
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2015-04-07 18:21:25 +00:00
|
|
|
type clientSwaggerSchema struct {
|
2015-09-10 21:58:09 +00:00
|
|
|
c *client.Client
|
2016-04-26 14:33:47 +00:00
|
|
|
fedc *restclient.RESTClient
|
2015-09-10 21:58:09 +00:00
|
|
|
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 {
|
2016-02-12 18:58:43 +00:00
|
|
|
Get() *restclient.Request
|
2015-09-10 21:58:09 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-05-04 22:52:19 +00:00
|
|
|
func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string, delegate validation.Schema) (err error) {
|
2015-09-10 21:58:09 +00:00
|
|
|
var schemaData []byte
|
2016-03-04 23:59:40 +00:00
|
|
|
var firstSeen bool
|
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 {
|
2016-03-04 23:59:40 +00:00
|
|
|
firstSeen = true
|
|
|
|
schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion)
|
2015-09-10 21:58:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
2016-05-04 22:52:19 +00:00
|
|
|
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData, delegate)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-03-04 23:59:40 +00:00
|
|
|
err = schema.ValidateBytes(data)
|
|
|
|
if _, ok := err.(validation.TypeNotFoundError); ok && !firstSeen {
|
|
|
|
// As a temporay hack, kubectl would re-get the schema if validation
|
|
|
|
// fails for type not found reason.
|
|
|
|
// TODO: runtime-config settings needs to make into the file's name
|
|
|
|
schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-05-04 22:52:19 +00:00
|
|
|
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData, delegate)
|
2016-03-04 23:59:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return schema.ValidateBytes(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Download swagger schema from apiserver and store it to file.
|
|
|
|
func downloadSchemaAndStore(c schemaClient, cacheDir, fullDir, cacheFile, prefix, groupVersion string) (schemaData []byte, err error) {
|
|
|
|
schemaData, err = c.Get().
|
|
|
|
AbsPath("/swaggerapi", prefix, groupVersion).
|
|
|
|
Do().
|
|
|
|
Raw()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(cacheDir) != 0 {
|
|
|
|
if err = writeSchemaFile(schemaData, fullDir, cacheFile, prefix, groupVersion); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
2015-04-07 18:21:25 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2016-02-09 12:53:14 +00:00
|
|
|
if gvk.Group == autoscaling.GroupName {
|
|
|
|
if c.c.AutoscalingClient == nil {
|
|
|
|
return errors.New("unable to validate: no autoscaling client")
|
|
|
|
}
|
2016-05-04 22:52:19 +00:00
|
|
|
return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
2016-05-07 00:03:43 +00:00
|
|
|
}
|
|
|
|
if gvk.Group == policy.GroupName {
|
|
|
|
if c.c.PolicyClient == nil {
|
|
|
|
return errors.New("unable to validate: no policy client")
|
|
|
|
}
|
|
|
|
return getSchemaAndValidate(c.c.PolicyClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
2016-02-09 12:53:14 +00:00
|
|
|
}
|
2016-04-15 22:30:15 +00:00
|
|
|
if gvk.Group == apps.GroupName {
|
|
|
|
if c.c.AppsClient == nil {
|
|
|
|
return errors.New("unable to validate: no autoscaling client")
|
|
|
|
}
|
2016-05-04 22:52:19 +00:00
|
|
|
return getSchemaAndValidate(c.c.AppsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
2016-04-15 22:30:15 +00:00
|
|
|
}
|
|
|
|
|
2016-02-17 23:07:38 +00:00
|
|
|
if gvk.Group == batch.GroupName {
|
|
|
|
if c.c.BatchClient == nil {
|
|
|
|
return errors.New("unable to validate: no batch client")
|
|
|
|
}
|
2016-05-04 22:52:19 +00:00
|
|
|
return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
2016-02-17 23:07:38 +00:00
|
|
|
}
|
2016-05-25 21:25:56 +00:00
|
|
|
if gvk.Group == rbac.GroupName {
|
|
|
|
if c.c.RbacClient == nil {
|
|
|
|
return errors.New("unable to validate: no rbac client")
|
|
|
|
}
|
|
|
|
return getSchemaAndValidate(c.c.RbacClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
|
|
|
}
|
2016-03-10 01:27:19 +00:00
|
|
|
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
|
|
|
// Don't attempt to validate third party objects
|
|
|
|
return nil
|
|
|
|
}
|
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
|
|
|
}
|
2016-05-04 22:52:19 +00:00
|
|
|
return getSchemaAndValidate(c.c.ExtensionsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
2015-08-10 20:08:34 +00:00
|
|
|
}
|
2016-04-26 14:33:47 +00:00
|
|
|
if gvk.Group == federation.GroupName {
|
|
|
|
if c.fedc == nil {
|
|
|
|
return errors.New("unable to validate: no federation client")
|
|
|
|
}
|
|
|
|
return getSchemaAndValidate(c.fedc, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
|
|
|
}
|
2016-05-04 22:52:19 +00:00
|
|
|
return getSchemaAndValidate(c.c.RESTClient, data, "api", gvk.GroupVersion().String(), c.cacheDir, c)
|
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
|
2016-03-10 01:27:19 +00:00
|
|
|
func (f *Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error {
|
2016-05-21 04:15:31 +00:00
|
|
|
gvks, _, err := api.Scheme.ObjectKinds(obj)
|
2015-04-07 18:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-05-21 04:15:31 +00:00
|
|
|
mapping, err := mapper.RESTMapping(gvks[0].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{}
|
|
|
|
}
|
2016-02-04 07:08:44 +00:00
|
|
|
printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers"), withNamespace, GetWideFlag(cmd), GetFlagBool(cmd, "show-all"), GetFlagBool(cmd, "show-labels"), 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
|
2016-03-10 01:27:19 +00:00
|
|
|
func (f *Factory) NewBuilder(thirdPartyDiscovery bool) *resource.Builder {
|
|
|
|
mapper, typer := f.Object(thirdPartyDiscovery)
|
2015-10-31 00:16:57 +00:00
|
|
|
|
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
|
|
|
}
|