mirror of https://github.com/k3s-io/k3s
make kubectl factory rings
parent
59ad9a30ca
commit
50f6733800
|
@ -12,6 +12,7 @@ go_library(
|
||||||
srcs = ["fake.go"],
|
srcs = ["fake.go"],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//federation/client/clientset_generated/federation_internalclientset:go_default_library",
|
||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
"//pkg/api/meta:go_default_library",
|
"//pkg/api/meta:go_default_library",
|
||||||
"//pkg/api/testapi:go_default_library",
|
"//pkg/api/testapi:go_default_library",
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/meta"
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
|
@ -233,6 +234,19 @@ func (f *FakeFactory) ClientForMapping(*meta.RESTMapping) (resource.RESTClient,
|
||||||
return f.tf.Client, f.tf.Err
|
return f.tf.Client, f.tf.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeFactory) FederationClientSetForVersion(version *schema.GroupVersion) (fedclientset.Interface, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (f *FakeFactory) FederationClientForVersion(version *schema.GroupVersion) (*restclient.RESTClient, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (f *FakeFactory) ClientSetForVersion(requiredVersion *schema.GroupVersion) (*internalclientset.Clientset, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (f *FakeFactory) ClientConfigForVersion(requiredVersion *schema.GroupVersion) (*restclient.Config, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FakeFactory) UnstructuredClientForMapping(*meta.RESTMapping) (resource.RESTClient, error) {
|
func (f *FakeFactory) UnstructuredClientForMapping(*meta.RESTMapping) (resource.RESTClient, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ go_library(
|
||||||
"cached_discovery.go",
|
"cached_discovery.go",
|
||||||
"clientcache.go",
|
"clientcache.go",
|
||||||
"factory.go",
|
"factory.go",
|
||||||
|
"factory_builder.go",
|
||||||
|
"factory_client_access.go",
|
||||||
|
"factory_object_mapping.go",
|
||||||
"helpers.go",
|
"helpers.go",
|
||||||
"printing.go",
|
"printing.go",
|
||||||
"shortcut_restmapper.go",
|
"shortcut_restmapper.go",
|
||||||
|
|
|
@ -29,12 +29,13 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/runtime/schema"
|
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
|
func NewClientCache(loader clientcmd.ClientConfig, discoveryClientFactory DiscoveryClientFactory) *ClientCache {
|
||||||
return &ClientCache{
|
return &ClientCache{
|
||||||
clientsets: make(map[schema.GroupVersion]*internalclientset.Clientset),
|
clientsets: make(map[schema.GroupVersion]*internalclientset.Clientset),
|
||||||
configs: make(map[schema.GroupVersion]*restclient.Config),
|
configs: make(map[schema.GroupVersion]*restclient.Config),
|
||||||
fedClientSets: make(map[schema.GroupVersion]fedclientset.Interface),
|
fedClientSets: make(map[schema.GroupVersion]fedclientset.Interface),
|
||||||
loader: loader,
|
loader: loader,
|
||||||
|
discoveryClientFactory: discoveryClientFactory,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +51,10 @@ type ClientCache struct {
|
||||||
|
|
||||||
defaultConfigLock sync.Mutex
|
defaultConfigLock sync.Mutex
|
||||||
defaultConfig *restclient.Config
|
defaultConfig *restclient.Config
|
||||||
discoveryClient discovery.DiscoveryInterface
|
// discoveryClientFactory comes as a factory method so that we can defer resolution until after
|
||||||
|
// argument evaluation
|
||||||
|
discoveryClientFactory DiscoveryClientFactory
|
||||||
|
discoveryClient discovery.DiscoveryInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// also looks up the discovery client. We can't do this during init because the flags won't have been set
|
// also looks up the discovery client. We can't do this during init because the flags won't have been set
|
||||||
|
@ -67,7 +71,7 @@ func (c *ClientCache) getDefaultConfig() (restclient.Config, discovery.Discovery
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return restclient.Config{}, nil, err
|
return restclient.Config{}, nil, err
|
||||||
}
|
}
|
||||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
discoveryClient, err := c.discoveryClientFactory.DiscoveryClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return restclient.Config{}, nil, err
|
return restclient.Config{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// this file contains factories with no other dependencies
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ring2Factory struct {
|
||||||
|
clientAccessFactory ClientAccessFactory
|
||||||
|
objectMappingFactory ObjectMappingFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFactory ObjectMappingFactory) BuilderFactory {
|
||||||
|
f := &ring2Factory{
|
||||||
|
clientAccessFactory: clientAccessFactory,
|
||||||
|
objectMappingFactory: objectMappingFactory,
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring2Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error {
|
||||||
|
gvks, _, err := api.Scheme.ObjectKinds(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping, err := mapper.RESTMapping(gvks[0].GroupKind())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printer, err := f.objectMappingFactory.PrinterForMapping(cmd, mapping, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return printer.PrintObj(obj, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring2Factory) NewBuilder() *resource.Builder {
|
||||||
|
mapper, typer := f.objectMappingFactory.Object()
|
||||||
|
|
||||||
|
return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping), f.clientAccessFactory.Decoder(true))
|
||||||
|
}
|
|
@ -0,0 +1,592 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// this file contains factories with no other dependencies
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
|
"k8s.io/kubernetes/pkg/api/service"
|
||||||
|
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
|
"k8s.io/kubernetes/pkg/client/restclient"
|
||||||
|
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||||
|
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||||
|
utilflag "k8s.io/kubernetes/pkg/util/flag"
|
||||||
|
"k8s.io/kubernetes/pkg/util/homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ring0Factory struct {
|
||||||
|
flags *pflag.FlagSet
|
||||||
|
clientConfig clientcmd.ClientConfig
|
||||||
|
discoveryFactory DiscoveryClientFactory
|
||||||
|
clientCache *ClientCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientAccessFactory(optionalClientConfig clientcmd.ClientConfig) ClientAccessFactory {
|
||||||
|
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
||||||
|
|
||||||
|
clientConfig := optionalClientConfig
|
||||||
|
if optionalClientConfig == nil {
|
||||||
|
clientConfig = DefaultClientConfig(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewClientAccessFactoryFromDiscovery(flags, clientConfig, &discoveryFactory{clientConfig: clientConfig})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientAccessFactoryFromDiscovery allows an external caller to substitute a different discoveryFactory
|
||||||
|
// Which allows for the client cache to be built in ring0, but still rely on a custom discovery client
|
||||||
|
func NewClientAccessFactoryFromDiscovery(flags *pflag.FlagSet, clientConfig clientcmd.ClientConfig, discoveryFactory DiscoveryClientFactory) ClientAccessFactory {
|
||||||
|
flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags
|
||||||
|
|
||||||
|
clientCache := NewClientCache(clientConfig, discoveryFactory)
|
||||||
|
|
||||||
|
f := &ring0Factory{
|
||||||
|
flags: flags,
|
||||||
|
clientConfig: clientConfig,
|
||||||
|
discoveryFactory: discoveryFactory,
|
||||||
|
clientCache: clientCache,
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
type discoveryFactory struct {
|
||||||
|
clientConfig clientcmd.ClientConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *discoveryFactory) DiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
|
||||||
|
cfg, err := f.clientConfig.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
discoveryClient, err := discovery.NewDiscoveryClientForConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cacheDir := computeDiscoverCacheDir(filepath.Join(homedir.HomeDir(), ".kube", "cache", "discovery"), cfg.Host)
|
||||||
|
return NewCachedDiscoveryClient(discoveryClient, cacheDir, time.Duration(10*time.Minute)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
|
||||||
|
// 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me.
|
||||||
|
// 1. Merge 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.
|
||||||
|
// 3. HomeDirectoryLocation
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
|
||||||
|
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||||
|
// use the standard defaults for this client command
|
||||||
|
// DEPRECATED: remove and replace with something more accurate
|
||||||
|
loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
|
||||||
|
|
||||||
|
flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
|
||||||
|
|
||||||
|
overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}
|
||||||
|
|
||||||
|
flagNames := clientcmd.RecommendedConfigOverrideFlags("")
|
||||||
|
// short flagnames are disabled by default. These are here for compatibility with existing scripts
|
||||||
|
flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"
|
||||||
|
|
||||||
|
clientcmd.BindOverrideFlags(overrides, flags, flagNames)
|
||||||
|
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
|
||||||
|
|
||||||
|
return clientConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) DiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
|
||||||
|
return f.discoveryFactory.DiscoveryClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) ClientSet() (*internalclientset.Clientset, error) {
|
||||||
|
return f.clientCache.ClientSetForVersion(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) ClientSetForVersion(requiredVersion *schema.GroupVersion) (*internalclientset.Clientset, error) {
|
||||||
|
return f.clientCache.ClientSetForVersion(requiredVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) ClientConfig() (*restclient.Config, error) {
|
||||||
|
return f.clientCache.ClientConfigForVersion(nil)
|
||||||
|
}
|
||||||
|
func (f *ring0Factory) ClientConfigForVersion(requiredVersion *schema.GroupVersion) (*restclient.Config, error) {
|
||||||
|
return f.clientCache.ClientConfigForVersion(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) RESTClient() (*restclient.RESTClient, error) {
|
||||||
|
clientConfig, err := f.clientCache.ClientConfigForVersion(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return restclient.RESTClientFor(clientConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) FederationClientSetForVersion(version *schema.GroupVersion) (fedclientset.Interface, error) {
|
||||||
|
return f.clientCache.FederationClientSetForVersion(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) FederationClientForVersion(version *schema.GroupVersion) (*restclient.RESTClient, error) {
|
||||||
|
return f.clientCache.FederationClientForVersion(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) Decoder(toInternal bool) runtime.Decoder {
|
||||||
|
var decoder runtime.Decoder
|
||||||
|
if toInternal {
|
||||||
|
decoder = api.Codecs.UniversalDecoder()
|
||||||
|
} else {
|
||||||
|
decoder = api.Codecs.UniversalDeserializer()
|
||||||
|
}
|
||||||
|
return thirdpartyresourcedata.NewDecoder(decoder, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) JSONEncoder() runtime.Encoder {
|
||||||
|
return api.Codecs.LegacyCodec(registered.EnabledVersions()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) UpdatePodSpecForObject(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.StatefulSet:
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) MapBasedSelectorForObject(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:
|
||||||
|
return kubectl.MakeLabels(t.Spec.Selector), nil
|
||||||
|
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 {
|
||||||
|
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
|
||||||
|
}
|
||||||
|
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
|
||||||
|
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 {
|
||||||
|
return "", fmt.Errorf("couldn't convert expressions - \"%+v\" to map-based selector format", t.Spec.Selector.MatchExpressions)
|
||||||
|
}
|
||||||
|
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
|
||||||
|
default:
|
||||||
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) PortsForObject(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:
|
||||||
|
return getPorts(t.Spec.Template.Spec), nil
|
||||||
|
case *api.Pod:
|
||||||
|
return getPorts(t.Spec), nil
|
||||||
|
case *api.Service:
|
||||||
|
return getServicePorts(t.Spec), nil
|
||||||
|
case *extensions.Deployment:
|
||||||
|
return getPorts(t.Spec.Template.Spec), nil
|
||||||
|
case *extensions.ReplicaSet:
|
||||||
|
return getPorts(t.Spec.Template.Spec), nil
|
||||||
|
default:
|
||||||
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("cannot extract ports from %v", gvks[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) ProtocolsForObject(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:
|
||||||
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) LabelsForObject(object runtime.Object) (map[string]string, error) {
|
||||||
|
return meta.NewAccessor().Labels(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) FlagSet() *pflag.FlagSet {
|
||||||
|
return f.flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We need to filter out stuff like secrets.
|
||||||
|
func (f *ring0Factory) Command() string {
|
||||||
|
if len(os.Args) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
base := filepath.Base(os.Args[0])
|
||||||
|
args := append([]string{base}, os.Args[1:]...)
|
||||||
|
return strings.Join(args, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) BindFlags(flags *pflag.FlagSet) {
|
||||||
|
// Merge factory's flags
|
||||||
|
flags.AddFlagSet(f.flags)
|
||||||
|
|
||||||
|
// 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.clientCache.matchVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")
|
||||||
|
|
||||||
|
// Normalize all flags that are coming from other packages or pre-configurations
|
||||||
|
// a.k.a. change all "_" to "-". e.g. glog package
|
||||||
|
flags.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) BindExternalFlags(flags *pflag.FlagSet) {
|
||||||
|
// any flags defined by external projects (not part of pflags)
|
||||||
|
flags.AddGoFlagSet(flag.CommandLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions {
|
||||||
|
columnLabel, err := cmd.Flags().GetStringSlice("label-columns")
|
||||||
|
if err != nil {
|
||||||
|
columnLabel = []string{}
|
||||||
|
}
|
||||||
|
opts := &kubectl.PrintOptions{
|
||||||
|
NoHeaders: GetFlagBool(cmd, "no-headers"),
|
||||||
|
WithNamespace: withNamespace,
|
||||||
|
Wide: GetWideFlag(cmd),
|
||||||
|
ShowAll: GetFlagBool(cmd, "show-all"),
|
||||||
|
ShowLabels: GetFlagBool(cmd, "show-labels"),
|
||||||
|
AbsoluteTimestamps: isWatch(cmd),
|
||||||
|
ColumnLabels: columnLabel,
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) DefaultResourceFilterFunc() kubectl.Filters {
|
||||||
|
return kubectl.NewResourceFilter()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource {
|
||||||
|
return []schema.GroupResource{
|
||||||
|
{Resource: "replicationcontroller"},
|
||||||
|
{Resource: "deployment"},
|
||||||
|
{Resource: "daemonset"},
|
||||||
|
{Resource: "job"},
|
||||||
|
{Resource: "replicaset"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
|
||||||
|
return kubectl.NewHumanReadablePrinter(options), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) Pauser(info *resource.Info) (bool, error) {
|
||||||
|
switch obj := info.Object.(type) {
|
||||||
|
case *extensions.Deployment:
|
||||||
|
if obj.Spec.Paused {
|
||||||
|
return true, errors.New("is already paused")
|
||||||
|
}
|
||||||
|
obj.Spec.Paused = true
|
||||||
|
return true, nil
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("pausing is not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) ResolveImage(name string) (string, error) {
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) Resumer(info *resource.Info) (bool, error) {
|
||||||
|
switch obj := info.Object.(type) {
|
||||||
|
case *extensions.Deployment:
|
||||||
|
if !obj.Spec.Paused {
|
||||||
|
return true, errors.New("is not paused")
|
||||||
|
}
|
||||||
|
obj.Spec.Paused = false
|
||||||
|
return true, nil
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("resuming is not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) DefaultNamespace() (string, bool, error) {
|
||||||
|
return f.clientConfig.Namespace()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RunV1GeneratorName = "run/v1"
|
||||||
|
RunPodV1GeneratorName = "run-pod/v1"
|
||||||
|
ServiceV1GeneratorName = "service/v1"
|
||||||
|
ServiceV2GeneratorName = "service/v2"
|
||||||
|
ServiceNodePortGeneratorV1Name = "service-nodeport/v1"
|
||||||
|
ServiceClusterIPGeneratorV1Name = "service-clusterip/v1"
|
||||||
|
ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1"
|
||||||
|
ServiceExternalNameGeneratorV1Name = "service-externalname/v1"
|
||||||
|
ServiceAccountV1GeneratorName = "serviceaccount/v1"
|
||||||
|
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
|
||||||
|
HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1"
|
||||||
|
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
|
||||||
|
DeploymentBasicV1Beta1GeneratorName = "deployment-basic/v1beta1"
|
||||||
|
JobV1Beta1GeneratorName = "job/v1beta1"
|
||||||
|
JobV1GeneratorName = "job/v1"
|
||||||
|
CronJobV2Alpha1GeneratorName = "cronjob/v2alpha1"
|
||||||
|
ScheduledJobV2Alpha1GeneratorName = "scheduledjob/v2alpha1"
|
||||||
|
NamespaceV1GeneratorName = "namespace/v1"
|
||||||
|
ResourceQuotaV1GeneratorName = "resourcequotas/v1"
|
||||||
|
SecretV1GeneratorName = "secret/v1"
|
||||||
|
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
|
||||||
|
SecretForTLSV1GeneratorName = "secret-for-tls/v1"
|
||||||
|
ConfigMapV1GeneratorName = "configmap/v1"
|
||||||
|
ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1"
|
||||||
|
ClusterV1Beta1GeneratorName = "cluster/v1beta1"
|
||||||
|
PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultGenerators returns the set of default generators for use in Factory instances
|
||||||
|
func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
|
||||||
|
var generator map[string]kubectl.Generator
|
||||||
|
switch cmdName {
|
||||||
|
case "expose":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
ServiceV1GeneratorName: kubectl.ServiceGeneratorV1{},
|
||||||
|
ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{},
|
||||||
|
}
|
||||||
|
case "service-clusterip":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
ServiceClusterIPGeneratorV1Name: kubectl.ServiceClusterIPGeneratorV1{},
|
||||||
|
}
|
||||||
|
case "service-nodeport":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
ServiceNodePortGeneratorV1Name: kubectl.ServiceNodePortGeneratorV1{},
|
||||||
|
}
|
||||||
|
case "service-loadbalancer":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
ServiceLoadBalancerGeneratorV1Name: kubectl.ServiceLoadBalancerGeneratorV1{},
|
||||||
|
}
|
||||||
|
case "deployment":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
DeploymentBasicV1Beta1GeneratorName: kubectl.DeploymentBasicGeneratorV1{},
|
||||||
|
}
|
||||||
|
case "run":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
RunV1GeneratorName: kubectl.BasicReplicationController{},
|
||||||
|
RunPodV1GeneratorName: kubectl.BasicPod{},
|
||||||
|
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
|
||||||
|
JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
|
||||||
|
JobV1GeneratorName: kubectl.JobV1{},
|
||||||
|
ScheduledJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{},
|
||||||
|
CronJobV2Alpha1GeneratorName: kubectl.CronJobV2Alpha1{},
|
||||||
|
}
|
||||||
|
case "autoscale":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},
|
||||||
|
HorizontalPodAutoscalerV1GeneratorName: kubectl.HorizontalPodAutoscalerV1{},
|
||||||
|
}
|
||||||
|
case "namespace":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{},
|
||||||
|
}
|
||||||
|
case "quota":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
ResourceQuotaV1GeneratorName: kubectl.ResourceQuotaGeneratorV1{},
|
||||||
|
}
|
||||||
|
case "secret":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
SecretV1GeneratorName: kubectl.SecretGeneratorV1{},
|
||||||
|
}
|
||||||
|
case "secret-for-docker-registry":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
SecretForDockerRegistryV1GeneratorName: kubectl.SecretForDockerRegistryGeneratorV1{},
|
||||||
|
}
|
||||||
|
case "secret-for-tls":
|
||||||
|
generator = map[string]kubectl.Generator{
|
||||||
|
SecretForTLSV1GeneratorName: kubectl.SecretForTLSGeneratorV1{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) Generators(cmdName string) map[string]kubectl.Generator {
|
||||||
|
return DefaultGenerators(cmdName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) CanBeExposed(kind schema.GroupKind) error {
|
||||||
|
switch kind {
|
||||||
|
case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"):
|
||||||
|
// nothing to do here
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("cannot expose a %s", kind)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) CanBeAutoscaled(kind schema.GroupKind) error {
|
||||||
|
switch kind {
|
||||||
|
case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"):
|
||||||
|
// nothing to do here
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("cannot autoscale a %v", kind)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) EditorEnvs() []string {
|
||||||
|
return []string{"KUBE_EDITOR", "EDITOR"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring0Factory) PrintObjectSpecificMessage(obj runtime.Object, out io.Writer) {
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *api.Service:
|
||||||
|
if obj.Spec.Type == api.ServiceTypeNodePort {
|
||||||
|
msg := fmt.Sprintf(
|
||||||
|
`You have exposed your service on an external port on all nodes in your
|
||||||
|
cluster. If you want to expose this service to the external internet, you may
|
||||||
|
need to set up firewall rules for the service port(s) (%s) to serve traffic.
|
||||||
|
|
||||||
|
See http://kubernetes.io/docs/user-guide/services-firewalls for more details.
|
||||||
|
`,
|
||||||
|
makePortsString(obj.Spec.Ports, true))
|
||||||
|
out.Write([]byte(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := obj.Annotations[service.AnnotationLoadBalancerSourceRangesKey]; ok {
|
||||||
|
msg := fmt.Sprintf(
|
||||||
|
`You are using service annotation [service.beta.kubernetes.io/load-balancer-source-ranges].
|
||||||
|
It has been promoted to field [loadBalancerSourceRanges] in service spec. This annotation will be deprecated in the future.
|
||||||
|
Please use the loadBalancerSourceRanges field instead.
|
||||||
|
|
||||||
|
See http://kubernetes.io/docs/user-guide/services-firewalls for more details.
|
||||||
|
`)
|
||||||
|
out.Write([]byte(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// overlyCautiousIllegalFileCharacters matches characters that *might* not be supported. Windows is really restrictive, so this is really restrictive
|
||||||
|
var overlyCautiousIllegalFileCharacters = regexp.MustCompile(`[^(\w/\.)]`)
|
||||||
|
|
||||||
|
// computeDiscoverCacheDir takes the parentDir and the host and comes up with a "usually non-colliding" name.
|
||||||
|
func computeDiscoverCacheDir(parentDir, host string) string {
|
||||||
|
// strip the optional scheme from host if its there:
|
||||||
|
schemelessHost := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1)
|
||||||
|
// now do a simple collapse of non-AZ09 characters. Collisions are possible but unlikely. Even if we do collide the problem is short lived
|
||||||
|
safeHost := overlyCautiousIllegalFileCharacters.ReplaceAllString(schemelessHost, "_")
|
||||||
|
|
||||||
|
return filepath.Join(parentDir, safeHost)
|
||||||
|
}
|
|
@ -0,0 +1,410 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// this file contains factories with no other dependencies
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/emicklei/go-restful/swagger"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/federation/apis/federation"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/meta"
|
||||||
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/api/validation"
|
||||||
|
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/client/restclient"
|
||||||
|
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||||
|
"k8s.io/kubernetes/pkg/client/typed/dynamic"
|
||||||
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
|
"k8s.io/kubernetes/pkg/registry/extensions/thirdpartyresourcedata"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ring1Factory struct {
|
||||||
|
clientAccessFactory ClientAccessFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMappingFactory {
|
||||||
|
f := &ring1Factory{
|
||||||
|
clientAccessFactory: clientAccessFactory,
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) Object() (meta.RESTMapper, runtime.ObjectTyper) {
|
||||||
|
mapper := registered.RESTMapper()
|
||||||
|
discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
|
||||||
|
if err == nil {
|
||||||
|
mapper = meta.FirstHitRESTMapper{
|
||||||
|
MultiRESTMapper: meta.MultiRESTMapper{
|
||||||
|
discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor),
|
||||||
|
registered.RESTMapper(), // hardcoded fall back
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap with shortcuts
|
||||||
|
mapper = NewShortcutExpander(mapper, discoveryClient)
|
||||||
|
|
||||||
|
// wrap with output preferences
|
||||||
|
cfg, err := f.clientAccessFactory.ClientConfigForVersion(nil)
|
||||||
|
checkErrWithPrefix("failed to get client config: ", err)
|
||||||
|
cmdApiVersion := schema.GroupVersion{}
|
||||||
|
if cfg.GroupVersion != nil {
|
||||||
|
cmdApiVersion = *cfg.GroupVersion
|
||||||
|
}
|
||||||
|
mapper = kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []schema.GroupVersion{cmdApiVersion}}
|
||||||
|
return mapper, api.Scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) UnstructuredObject() (meta.RESTMapper, runtime.ObjectTyper, error) {
|
||||||
|
discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
groupResources, err := discovery.GetAPIGroupResources(discoveryClient)
|
||||||
|
if err != nil && !discoveryClient.Fresh() {
|
||||||
|
discoveryClient.Invalidate()
|
||||||
|
groupResources, err = discovery.GetAPIGroupResources(discoveryClient)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.InterfacesForUnstructured)
|
||||||
|
typer := discovery.NewUnstructuredObjectTyper(groupResources)
|
||||||
|
return NewShortcutExpander(mapper, discoveryClient), typer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
||||||
|
cfg, err := f.clientAccessFactory.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := client.SetKubernetesDefaults(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
gvk := mapping.GroupVersionKind
|
||||||
|
switch gvk.Group {
|
||||||
|
case federation.GroupName:
|
||||||
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||||
|
return f.clientAccessFactory.FederationClientForVersion(&mappingVersion)
|
||||||
|
case api.GroupName:
|
||||||
|
cfg.APIPath = "/api"
|
||||||
|
default:
|
||||||
|
cfg.APIPath = "/apis"
|
||||||
|
}
|
||||||
|
gv := gvk.GroupVersion()
|
||||||
|
cfg.GroupVersion = &gv
|
||||||
|
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
||||||
|
cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv)
|
||||||
|
}
|
||||||
|
return restclient.RESTClientFor(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
||||||
|
cfg, err := f.clientAccessFactory.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := restclient.SetKubernetesDefaults(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg.APIPath = "/apis"
|
||||||
|
if mapping.GroupVersionKind.Group == api.GroupName {
|
||||||
|
cfg.APIPath = "/api"
|
||||||
|
}
|
||||||
|
gv := mapping.GroupVersionKind.GroupVersion()
|
||||||
|
cfg.ContentConfig = dynamic.ContentConfig()
|
||||||
|
cfg.GroupVersion = &gv
|
||||||
|
return restclient.RESTClientFor(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, error) {
|
||||||
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||||
|
if mapping.GroupVersionKind.Group == federation.GroupName {
|
||||||
|
fedClientSet, err := f.clientAccessFactory.FederationClientSetForVersion(&mappingVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if mapping.GroupVersionKind.Kind == "Cluster" {
|
||||||
|
return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok {
|
||||||
|
return describer, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) LogsForObject(object, options runtime.Object) (*restclient.Request, error) {
|
||||||
|
clientset, err := f.clientAccessFactory.ClientSetForVersion(nil)
|
||||||
|
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 clientset.Core().Pods(t.Namespace).GetLogs(t.Name, opts), nil
|
||||||
|
|
||||||
|
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)
|
||||||
|
sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }
|
||||||
|
pod, numPods, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 20*time.Second, sortBy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if numPods > 1 {
|
||||||
|
fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientset.Core().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 := metav1.LabelSelectorAsSelector(t.Spec.Selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid label selector: %v", err)
|
||||||
|
}
|
||||||
|
sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }
|
||||||
|
pod, numPods, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 20*time.Second, sortBy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if numPods > 1 {
|
||||||
|
fmt.Fprintf(os.Stderr, "Found %v pods, using pod/%v\n", numPods, pod.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientset.Core().Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("cannot get the logs from %v", gvks[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) Scaler(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
|
||||||
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||||
|
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kubectl.ScalerFor(mapping.GroupVersionKind.GroupKind(), clientset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
|
||||||
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||||
|
clientset, clientsetErr := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
|
||||||
|
reaper, reaperErr := kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), clientset)
|
||||||
|
|
||||||
|
if kubectl.IsNoSuchReaperError(reaperErr) {
|
||||||
|
return nil, reaperErr
|
||||||
|
}
|
||||||
|
if clientsetErr != nil {
|
||||||
|
return nil, clientsetErr
|
||||||
|
}
|
||||||
|
return reaper, reaperErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) HistoryViewer(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error) {
|
||||||
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||||
|
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kubectl.HistoryViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) Rollbacker(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) {
|
||||||
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||||
|
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), clientset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) StatusViewer(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
|
||||||
|
mappingVersion := mapping.GroupVersionKind.GroupVersion()
|
||||||
|
clientset, err := f.clientAccessFactory.ClientSetForVersion(&mappingVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), clientset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) AttachablePodForObject(object runtime.Object) (*api.Pod, error) {
|
||||||
|
clientset, err := f.clientAccessFactory.ClientSetForVersion(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch t := object.(type) {
|
||||||
|
case *api.ReplicationController:
|
||||||
|
selector := labels.SelectorFromSet(t.Spec.Selector)
|
||||||
|
sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
|
||||||
|
pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy)
|
||||||
|
return pod, err
|
||||||
|
case *extensions.Deployment:
|
||||||
|
selector, err := metav1.LabelSelectorAsSelector(t.Spec.Selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid label selector: %v", err)
|
||||||
|
}
|
||||||
|
sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
|
||||||
|
pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy)
|
||||||
|
return pod, err
|
||||||
|
case *batch.Job:
|
||||||
|
selector, err := metav1.LabelSelectorAsSelector(t.Spec.Selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid label selector: %v", err)
|
||||||
|
}
|
||||||
|
sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
|
||||||
|
pod, _, err := GetFirstPod(clientset.Core(), t.Namespace, selector, 1*time.Minute, sortBy)
|
||||||
|
return pod, err
|
||||||
|
case *api.Pod:
|
||||||
|
return t, nil
|
||||||
|
default:
|
||||||
|
gvks, _, err := api.Scheme.ObjectKinds(object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) {
|
||||||
|
printer, generic, err := PrinterForCommand(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we output versioned data for generic printers
|
||||||
|
if generic {
|
||||||
|
clientConfig, err := f.clientAccessFactory.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
version, err := OutputVersion(cmd, clientConfig.GroupVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if version.Empty() && mapping != nil {
|
||||||
|
version = mapping.GroupVersionKind.GroupVersion()
|
||||||
|
}
|
||||||
|
if version.Empty() {
|
||||||
|
return nil, fmt.Errorf("you must specify an output-version when using this output format")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mapping != nil {
|
||||||
|
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion())
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 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{}
|
||||||
|
}
|
||||||
|
printer, err = f.clientAccessFactory.Printer(mapping, kubectl.PrintOptions{
|
||||||
|
NoHeaders: GetFlagBool(cmd, "no-headers"),
|
||||||
|
WithNamespace: withNamespace,
|
||||||
|
Wide: GetWideFlag(cmd),
|
||||||
|
ShowAll: GetFlagBool(cmd, "show-all"),
|
||||||
|
ShowLabels: GetFlagBool(cmd, "show-labels"),
|
||||||
|
AbsoluteTimestamps: isWatch(cmd),
|
||||||
|
ColumnLabels: columnLabel,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
printer = maybeWrapSortingPrinter(cmd, printer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return printer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) Validator(validate bool, cacheDir string) (validation.Schema, error) {
|
||||||
|
if validate {
|
||||||
|
discovery, err := f.clientAccessFactory.DiscoveryClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dir := cacheDir
|
||||||
|
if len(dir) > 0 {
|
||||||
|
version, err := discovery.ServerVersion()
|
||||||
|
if err == nil {
|
||||||
|
dir = path.Join(cacheDir, version.String())
|
||||||
|
} else {
|
||||||
|
dir = "" // disable caching as a fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
swaggerSchema := &clientSwaggerSchema{
|
||||||
|
c: discovery.RESTClient(),
|
||||||
|
cacheDir: dir,
|
||||||
|
}
|
||||||
|
return validation.ConjunctiveSchema{
|
||||||
|
swaggerSchema,
|
||||||
|
validation.NoDoubleKeySchema{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return validation.NullSchema{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ring1Factory) SwaggerSchema(gvk schema.GroupVersionKind) (*swagger.ApiDeclaration, error) {
|
||||||
|
version := gvk.GroupVersion()
|
||||||
|
discovery, err := f.clientAccessFactory.DiscoveryClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return discovery.SwaggerSchema(version)
|
||||||
|
}
|
Loading…
Reference in New Issue