Update godeps

This commit is contained in:
Manuel de Brito Fontes 2016-11-10 19:57:28 -03:00
parent 1c8773fc98
commit 1bc383f9c5
1723 changed files with 287976 additions and 411028 deletions

179
vendor/k8s.io/kubernetes/pkg/kubectl/BUILD generated vendored Normal file
View file

@ -0,0 +1,179 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"apply.go",
"autoscale.go",
"bash_comp_utils.go",
"cluster.go",
"configmap.go",
"custom_column_printer.go",
"deployment.go",
"describe.go",
"doc.go",
"explain.go",
"generate.go",
"history.go",
"interfaces.go",
"kubectl.go",
"namespace.go",
"proxy_server.go",
"quota.go",
"resource_filter.go",
"resource_printer.go",
"rollback.go",
"rolling_updater.go",
"rollout_status.go",
"run.go",
"scale.go",
"secret.go",
"secret_for_docker_registry.go",
"secret_for_tls.go",
"service.go",
"service_basic.go",
"serviceaccount.go",
"sorted_resource_name_list.go",
"sorting_printer.go",
"stop.go",
"version.go",
],
tags = ["automanaged"],
deps = [
"//federation/apis/federation:go_default_library",
"//federation/apis/federation/v1beta1:go_default_library",
"//federation/client/clientset_generated/federation_internalclientset:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/annotations:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/events:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/api/util:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/batch/v1:go_default_library",
"//pkg/apis/batch/v2alpha1:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/apis/storage/util:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/apps/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/retry:go_default_library",
"//pkg/client/unversioned:go_default_library",
"//pkg/controller/deployment/util:go_default_library",
"//pkg/credentialprovider:go_default_library",
"//pkg/fieldpath:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubelet/qos:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/types:go_default_library",
"//pkg/util:go_default_library",
"//pkg/util/cert:go_default_library",
"//pkg/util/errors:go_default_library",
"//pkg/util/integer:go_default_library",
"//pkg/util/intstr:go_default_library",
"//pkg/util/jsonpath:go_default_library",
"//pkg/util/node:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/util/slice:go_default_library",
"//pkg/util/uuid:go_default_library",
"//pkg/util/validation:go_default_library",
"//pkg/util/wait:go_default_library",
"//pkg/version:go_default_library",
"//pkg/watch:go_default_library",
"//vendor:github.com/emicklei/go-restful/swagger",
"//vendor:github.com/ghodss/yaml",
"//vendor:github.com/golang/glog",
"//vendor:github.com/spf13/cobra",
"//vendor:github.com/spf13/pflag",
],
)
go_test(
name = "go_default_test",
srcs = [
"cluster_test.go",
"configmap_test.go",
"custom_column_printer_test.go",
"deployment_test.go",
"describe_test.go",
"generate_test.go",
"kubectl_test.go",
"namespace_test.go",
"proxy_server_test.go",
"quota_test.go",
"resource_printer_test.go",
"rolling_updater_test.go",
"rollout_status_test.go",
"run_test.go",
"scale_test.go",
"secret_for_docker_registry_test.go",
"secret_for_tls_test.go",
"secret_test.go",
"service_basic_test.go",
"service_test.go",
"serviceaccount_test.go",
"sorted_resource_name_list_test.go",
"sorting_printer_test.go",
"stop_test.go",
],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//federation/apis/federation:go_default_library",
"//federation/apis/federation/v1beta1:go_default_library",
"//federation/client/clientset_generated/federation_internalclientset/fake:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/restclient/fake:go_default_library",
"//pkg/client/testing/core:go_default_library",
"//pkg/controller/deployment/util:go_default_library",
"//pkg/kubectl/testing:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/serializer/yaml:go_default_library",
"//pkg/util/diff:go_default_library",
"//pkg/util/intstr:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/util/testing:go_default_library",
"//pkg/watch:go_default_library",
"//vendor:github.com/ghodss/yaml",
"//vendor:github.com/spf13/cobra",
],
)

View file

@ -1,7 +1,4 @@
assignees:
- brendandburns
- deads2k
- janetkuo
- jlowdermilk
- pwittrock
- smarterclayton
approvers:
- sig-cli-maintainers
reviewers:
- sig-cli

View file

@ -25,10 +25,6 @@ import (
"k8s.io/kubernetes/pkg/runtime"
)
type debugError interface {
DebugError() (msg string, args []interface{})
}
// GetOriginalConfiguration retrieves the original configuration of the object
// from the annotation, or nil if no annotation was found.
func GetOriginalConfiguration(mapping *meta.RESTMapping, obj runtime.Object) ([]byte, error) {
@ -75,7 +71,7 @@ func SetOriginalConfiguration(info *resource.Info, original []byte) error {
}
// GetModifiedConfiguration retrieves the modified configuration of the object.
// If annotate is true, it embeds the result as an anotation in the modified
// If annotate is true, it embeds the result as an annotation in the modified
// configuration. If an object was read from the command input, it will use that
// version of the object. Otherwise, it will use the version from the server.
func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.Encoder) ([]byte, error) {

125
vendor/k8s.io/kubernetes/pkg/kubectl/cluster.go generated vendored Normal file
View file

@ -0,0 +1,125 @@
/*
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.
*/
package kubectl
import (
"fmt"
federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
)
// ClusterGeneratorV1Beta1 supports stable generation of a
// federation/cluster resource.
type ClusterGeneratorV1Beta1 struct {
// Name of the cluster context (required)
Name string
// ClientCIDR is the CIDR range in which the Kubernetes APIServer
// is available for the client (optional)
ClientCIDR string
// ServerAddress is the APIServer address of the Kubernetes cluster
// that is being registered (required)
ServerAddress string
// SecretName is the name of the secret that stores the credentials
// for the Kubernetes cluster that is being registered (optional)
SecretName string
}
// Ensure it supports the generator pattern that uses parameter
// injection.
var _ Generator = &ClusterGeneratorV1Beta1{}
// Ensure it supports the generator pattern that uses parameters
// specified during construction.
var _ StructuredGenerator = &ClusterGeneratorV1Beta1{}
// Generate returns a cluster resource using the specified parameters.
func (s ClusterGeneratorV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(s.ParamNames(), genericParams)
if err != nil {
return nil, err
}
clustergen := &ClusterGeneratorV1Beta1{}
params := map[string]string{}
for key, value := range genericParams {
strVal, isString := value.(string)
if !isString {
return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key)
}
params[key] = strVal
}
clustergen.Name = params["name"]
clustergen.ClientCIDR = params["client-cidr"]
clustergen.ServerAddress = params["server-address"]
clustergen.SecretName = params["secret"]
return clustergen.StructuredGenerate()
}
// ParamNames returns the set of supported input parameters when using
// the parameter injection generator pattern.
func (s ClusterGeneratorV1Beta1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"client-cidr", false},
{"server-address", true},
{"secret", false},
}
}
// StructuredGenerate outputs a federation cluster resource object
// using the configured fields.
func (s ClusterGeneratorV1Beta1) StructuredGenerate() (runtime.Object, error) {
if err := s.validate(); err != nil {
return nil, err
}
if s.ClientCIDR == "" {
s.ClientCIDR = "0.0.0.0/0"
}
if s.SecretName == "" {
s.SecretName = s.Name
}
cluster := &federationapi.Cluster{
ObjectMeta: v1.ObjectMeta{
Name: s.Name,
},
Spec: federationapi.ClusterSpec{
ServerAddressByClientCIDRs: []federationapi.ServerAddressByClientCIDR{
{
ClientCIDR: s.ClientCIDR,
ServerAddress: s.ServerAddress,
},
},
SecretRef: &v1.LocalObjectReference{
Name: s.SecretName,
},
},
}
return cluster, nil
}
// validate validates required fields are set to support structured
// generation.
func (s ClusterGeneratorV1Beta1) validate() error {
if len(s.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if len(s.ServerAddress) == 0 {
return fmt.Errorf("server address must be specified")
}
return nil
}

111
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util/BUILD generated vendored Normal file
View file

@ -0,0 +1,111 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"cached_discovery.go",
"clientcache.go",
"factory.go",
"helpers.go",
"printing.go",
"shortcut_restmapper.go",
],
tags = ["automanaged"],
deps = [
"//federation/apis/federation:go_default_library",
"//federation/client/clientset_generated/federation_internalclientset:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/service:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/typed/discovery:go_default_library",
"//pkg/client/typed/dynamic:go_default_library",
"//pkg/client/unversioned:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/registry/extensions/thirdpartyresourcedata:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/serializer/json:go_default_library",
"//pkg/util/errors:go_default_library",
"//pkg/util/exec:go_default_library",
"//pkg/util/flag:go_default_library",
"//pkg/util/homedir:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/util/strategicpatch:go_default_library",
"//pkg/version:go_default_library",
"//pkg/watch:go_default_library",
"//vendor:github.com/emicklei/go-restful/swagger",
"//vendor:github.com/evanphx/json-patch",
"//vendor:github.com/golang/glog",
"//vendor:github.com/spf13/cobra",
"//vendor:github.com/spf13/pflag",
],
)
go_test(
name = "go_default_test",
srcs = [
"cached_discovery_test.go",
"factory_test.go",
"helpers_test.go",
"shortcut_restmapper_test.go",
],
data = [
"//api/swagger-spec",
],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/client/restclient/fake:go_default_library",
"//pkg/client/testing/core:go_default_library",
"//pkg/client/typed/discovery:go_default_library",
"//pkg/client/unversioned/clientcmd:go_default_library",
"//pkg/client/unversioned/clientcmd/api:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/exec:go_default_library",
"//pkg/util/flag:go_default_library",
"//pkg/util/validation/field:go_default_library",
"//pkg/version:go_default_library",
"//pkg/watch:go_default_library",
"//vendor:github.com/emicklei/go-restful/swagger",
"//vendor:github.com/stretchr/testify/assert",
],
)

View file

@ -0,0 +1,252 @@
/*
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.
*/
package util
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"sync"
"time"
"github.com/emicklei/go-restful/swagger"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/typed/discovery"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/version"
)
// CachedDiscoveryClient implements the functions that discovery server-supported API groups,
// versions and resources.
type CachedDiscoveryClient struct {
delegate discovery.DiscoveryInterface
// cacheDirectory is the directory where discovery docs are held. It must be unique per host:port combination to work well.
cacheDirectory string
// ttl is how long the cache should be considered valid
ttl time.Duration
// mutex protects the variables below
mutex sync.Mutex
// ourFiles are all filenames of cache files created by this process
ourFiles map[string]struct{}
// invalidated is true if all cache files should be ignored that are not ours (e.g. after Invalidate() was called)
invalidated bool
// fresh is true if all used cache files were ours
fresh bool
}
var _ discovery.CachedDiscoveryInterface = &CachedDiscoveryClient{}
// ServerResourcesForGroupVersion returns the supported resources for a group and version.
func (d *CachedDiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (*unversioned.APIResourceList, error) {
filename := filepath.Join(d.cacheDirectory, groupVersion, "serverresources.json")
cachedBytes, err := d.getCachedFile(filename)
// don't fail on errors, we either don't have a file or won't be able to run the cached check. Either way we can fallback.
if err == nil {
cachedResources := &unversioned.APIResourceList{}
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), cachedBytes, cachedResources); err == nil {
glog.V(6).Infof("returning cached discovery info from %v", filename)
return cachedResources, nil
}
}
liveResources, err := d.delegate.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
return liveResources, err
}
if err := d.writeCachedFile(filename, liveResources); err != nil {
glog.V(3).Infof("failed to write cache to %v due to %v", filename, err)
}
return liveResources, nil
}
// ServerResources returns the supported resources for all groups and versions.
func (d *CachedDiscoveryClient) ServerResources() (map[string]*unversioned.APIResourceList, error) {
apiGroups, err := d.ServerGroups()
if err != nil {
return nil, err
}
groupVersions := unversioned.ExtractGroupVersions(apiGroups)
result := map[string]*unversioned.APIResourceList{}
for _, groupVersion := range groupVersions {
resources, err := d.ServerResourcesForGroupVersion(groupVersion)
if err != nil {
return nil, err
}
result[groupVersion] = resources
}
return result, nil
}
func (d *CachedDiscoveryClient) ServerGroups() (*unversioned.APIGroupList, error) {
filename := filepath.Join(d.cacheDirectory, "servergroups.json")
cachedBytes, err := d.getCachedFile(filename)
// don't fail on errors, we either don't have a file or won't be able to run the cached check. Either way we can fallback.
if err == nil {
cachedGroups := &unversioned.APIGroupList{}
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), cachedBytes, cachedGroups); err == nil {
glog.V(6).Infof("returning cached discovery info from %v", filename)
return cachedGroups, nil
}
}
liveGroups, err := d.delegate.ServerGroups()
if err != nil {
return liveGroups, err
}
if err := d.writeCachedFile(filename, liveGroups); err != nil {
glog.V(3).Infof("failed to write cache to %v due to %v", filename, err)
}
return liveGroups, nil
}
func (d *CachedDiscoveryClient) getCachedFile(filename string) ([]byte, error) {
// after invalidation ignore cache files not created by this process
d.mutex.Lock()
_, ourFile := d.ourFiles[filename]
if d.invalidated && !ourFile {
d.mutex.Unlock()
return nil, errors.New("cache invalidated")
}
d.mutex.Unlock()
file, err := os.Open(filename)
if err != nil {
return nil, err
}
fileInfo, err := file.Stat()
if err != nil {
return nil, err
}
if time.Now().After(fileInfo.ModTime().Add(d.ttl)) {
return nil, errors.New("cache expired")
}
// the cache is present and its valid. Try to read and use it.
cachedBytes, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
d.mutex.Lock()
defer d.mutex.Unlock()
d.fresh = d.fresh && ourFile
return cachedBytes, nil
}
func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Object) error {
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return err
}
bytes, err := runtime.Encode(api.Codecs.LegacyCodec(), obj)
if err != nil {
return err
}
f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+".")
if err != nil {
return err
}
defer os.Remove(f.Name())
_, err = f.Write(bytes)
if err != nil {
return err
}
err = f.Chmod(0755)
if err != nil {
return err
}
name := f.Name()
err = f.Close()
if err != nil {
return err
}
// atomic rename
d.mutex.Lock()
defer d.mutex.Unlock()
err = os.Rename(name, filename)
if err == nil {
d.ourFiles[filename] = struct{}{}
}
return err
}
func (d *CachedDiscoveryClient) RESTClient() restclient.Interface {
return d.delegate.RESTClient()
}
func (d *CachedDiscoveryClient) ServerPreferredResources() ([]unversioned.GroupVersionResource, error) {
return d.delegate.ServerPreferredResources()
}
func (d *CachedDiscoveryClient) ServerPreferredNamespacedResources() ([]unversioned.GroupVersionResource, error) {
return d.delegate.ServerPreferredNamespacedResources()
}
func (d *CachedDiscoveryClient) ServerVersion() (*version.Info, error) {
return d.delegate.ServerVersion()
}
func (d *CachedDiscoveryClient) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) {
return d.delegate.SwaggerSchema(version)
}
func (d *CachedDiscoveryClient) Fresh() bool {
d.mutex.Lock()
defer d.mutex.Unlock()
return d.fresh
}
func (d *CachedDiscoveryClient) Invalidate() {
d.mutex.Lock()
defer d.mutex.Unlock()
d.ourFiles = map[string]struct{}{}
d.fresh = true
d.invalidated = true
}
// NewCachedDiscoveryClient creates a new DiscoveryClient. cacheDirectory is the directory where discovery docs are held. It must be unique per host:port combination to work well.
func NewCachedDiscoveryClient(delegate discovery.DiscoveryInterface, cacheDirectory string, ttl time.Duration) *CachedDiscoveryClient {
return &CachedDiscoveryClient{
delegate: delegate,
cacheDirectory: cacheDirectory,
ttl: ttl,
ourFiles: map[string]struct{}{},
fresh: true,
}
}

View file

@ -17,17 +17,21 @@ limitations under the License.
package util
import (
"sync"
fed_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/restclient"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/typed/discovery"
oldclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)
func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
return &ClientCache{
clients: make(map[unversioned.GroupVersion]*client.Client),
clientsets: make(map[unversioned.GroupVersion]*internalclientset.Clientset),
configs: make(map[unversioned.GroupVersion]*restclient.Config),
fedClientSets: make(map[unversioned.GroupVersion]fed_clientset.Interface),
loader: loader,
@ -38,53 +42,78 @@ func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
// is invoked only once
type ClientCache struct {
loader clientcmd.ClientConfig
clients map[unversioned.GroupVersion]*client.Client
clientsets map[unversioned.GroupVersion]*internalclientset.Clientset
fedClientSets map[unversioned.GroupVersion]fed_clientset.Interface
configs map[unversioned.GroupVersion]*restclient.Config
defaultConfig *restclient.Config
defaultClient *client.Client
matchVersion bool
matchVersion bool
defaultConfigLock sync.Mutex
defaultConfig *restclient.Config
discoveryClient discovery.DiscoveryInterface
}
// also looks up the discovery client. We can't do this during init because the flags won't have been set
// because this is constructed pre-command execution before the command tree is even set up
func (c *ClientCache) getDefaultConfig() (restclient.Config, discovery.DiscoveryInterface, error) {
c.defaultConfigLock.Lock()
defer c.defaultConfigLock.Unlock()
if c.defaultConfig != nil && c.discoveryClient != nil {
return *c.defaultConfig, c.discoveryClient, nil
}
config, err := c.loader.ClientConfig()
if err != nil {
return restclient.Config{}, nil, err
}
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil {
return restclient.Config{}, nil, err
}
if c.matchVersion {
if err := discovery.MatchesServerVersion(discoveryClient); err != nil {
return restclient.Config{}, nil, err
}
}
c.defaultConfig = config
c.discoveryClient = discoveryClient
return *c.defaultConfig, c.discoveryClient, nil
}
// ClientConfigForVersion returns the correct config for a server
func (c *ClientCache) ClientConfigForVersion(version *unversioned.GroupVersion) (*restclient.Config, error) {
if c.defaultConfig == nil {
config, err := c.loader.ClientConfig()
if err != nil {
return nil, err
}
c.defaultConfig = config
if c.matchVersion {
if err := client.MatchesServerVersion(c.defaultClient, config); err != nil {
return nil, err
}
}
func (c *ClientCache) ClientConfigForVersion(requiredVersion *unversioned.GroupVersion) (*restclient.Config, error) {
// TODO: have a better config copy method
config, discoveryClient, err := c.getDefaultConfig()
if err != nil {
return nil, err
}
if version != nil {
if config, ok := c.configs[*version]; ok {
if requiredVersion == nil && config.GroupVersion != nil {
// if someone has set the values via flags, our config will have the groupVersion set
// that means it is required.
requiredVersion = config.GroupVersion
}
// required version may still be nil, since config.GroupVersion may have been nil. Do the check
// before looking up from the cache
if requiredVersion != nil {
if config, ok := c.configs[*requiredVersion]; ok {
return config, nil
}
}
// TODO: have a better config copy method
config := *c.defaultConfig
// TODO these fall out when we finish the refactor
var preferredGV *unversioned.GroupVersion
if version != nil {
versionCopy := *version
preferredGV = &versionCopy
}
client.SetKubernetesDefaults(&config)
negotiatedVersion, err := client.NegotiateVersion(c.defaultClient, &config, preferredGV, registered.EnabledVersions())
negotiatedVersion, err := discovery.NegotiateVersion(discoveryClient, requiredVersion, registered.EnabledVersions())
if err != nil {
return nil, err
}
config.GroupVersion = negotiatedVersion
if version != nil {
c.configs[*version] = &config
// TODO this isn't what we want. Each clientset should be setting defaults as it sees fit.
oldclient.SetKubernetesDefaults(&config)
if requiredVersion != nil {
c.configs[*requiredVersion] = &config
}
// `version` does not necessarily equal `config.Version`. However, we know that we call this method again with
@ -95,38 +124,38 @@ func (c *ClientCache) ClientConfigForVersion(version *unversioned.GroupVersion)
return &config, nil
}
// ClientForVersion initializes or reuses a client for the specified version, or returns an
// ClientSetForVersion initializes or reuses a clientset for the specified version, or returns an
// error if that is not possible
func (c *ClientCache) ClientForVersion(version *unversioned.GroupVersion) (*client.Client, error) {
if version != nil {
if client, ok := c.clients[*version]; ok {
return client, nil
func (c *ClientCache) ClientSetForVersion(requiredVersion *unversioned.GroupVersion) (*internalclientset.Clientset, error) {
if requiredVersion != nil {
if clientset, ok := c.clientsets[*requiredVersion]; ok {
return clientset, nil
}
}
config, err := c.ClientConfigForVersion(version)
config, err := c.ClientConfigForVersion(requiredVersion)
if err != nil {
return nil, err
}
kubeclient, err := client.New(config)
clientset, err := internalclientset.NewForConfig(config)
if err != nil {
return nil, err
}
c.clients[*config.GroupVersion] = kubeclient
c.clientsets[*config.GroupVersion] = clientset
// `version` does not necessarily equal `config.Version`. However, we know that if we call this method again with
// `version`, we should get a client based on the same config we just found. There's no guarantee that a client
// is copiable, so create a new client and save it in the cache.
if version != nil {
if requiredVersion != nil {
configCopy := *config
kubeclient, err := client.New(&configCopy)
clientset, err := internalclientset.NewForConfig(&configCopy)
if err != nil {
return nil, err
}
c.clients[*version] = kubeclient
c.clientsets[*requiredVersion] = clientset
}
return kubeclient, nil
return clientset, nil
}
func (c *ClientCache) FederationClientSetForVersion(version *unversioned.GroupVersion) (fed_clientset.Interface, error) {
@ -164,5 +193,5 @@ func (c *ClientCache) FederationClientForVersion(version *unversioned.GroupVersi
if err != nil {
return nil, err
}
return fedClientSet.(*fed_clientset.Clientset).FederationClient.RESTClient, nil
return fedClientSet.Federation().RESTClient().(*restclient.RESTClient), nil
}

File diff suppressed because it is too large Load diff

View file

@ -28,12 +28,12 @@ import (
"strings"
"time"
"k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/extensions"
"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"
@ -43,10 +43,9 @@ import (
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/strategicpatch"
"github.com/evanphx/json-patch"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
const (
@ -60,7 +59,7 @@ type debugError interface {
// AddSourceToErr adds handleResourcePrefix and source string to error message.
// verb is the string like "creating", "deleting" etc.
// souce is the filename or URL to the template file(*.json or *.yaml), or stdin to use to handle the resource.
// source is the filename or URL to the template file(*.json or *.yaml), or stdin to use to handle the resource.
func AddSourceToErr(verb string, source string, err error) error {
if source != "" {
if statusError, ok := err.(kerrors.APIStatus); ok {
@ -88,23 +87,26 @@ func DefaultBehaviorOnFatal() {
fatalErrHandler = fatal
}
// fatal prints the message if set and then exits. If V(2) or greater, glog.Fatal
// is invoked for extended information.
// fatal prints the message (if provided) and then exits. If V(2) or greater,
// glog.Fatal is invoked for extended information.
func fatal(msg string, code int) {
if glog.V(2) {
glog.FatalDepth(2, msg)
}
if len(msg) > 0 {
// add newline if needed
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
if glog.V(2) {
glog.FatalDepth(2, msg)
}
fmt.Fprint(os.Stderr, msg)
}
os.Exit(code)
}
// ErrExit may be passed to CheckError to instruct it to output nothing but exit with
// status code 1.
var ErrExit = fmt.Errorf("exit")
// CheckErr prints a user friendly error to STDERR and exits with a non-zero
// exit code. Unrecognized errors will be printed with an "error: " prefix.
//
@ -122,9 +124,17 @@ func checkErrWithPrefix(prefix string, err error) {
// checkErr formats a given error as a string and calls the passed handleErr
// func with that string and an kubectl exit code.
func checkErr(prefix string, err error, handleErr func(string, int)) {
// unwrap aggregates of 1
if agg, ok := err.(utilerrors.Aggregate); ok && len(agg.Errors()) == 1 {
err = agg.Errors()[0]
}
switch {
case err == nil:
return
case err == ErrExit:
handleErr("", DefaultErrorExitCode)
return
case kerrors.IsInvalid(err):
details := err.(*kerrors.StatusError).Status().Details
s := fmt.Sprintf("%sThe %s %q is invalid", prefix, details.Kind, details.Name)
@ -196,8 +206,10 @@ func StandardErrorMessage(err error) (string, bool) {
switch {
case isStatus:
switch s := status.Status(); {
case s.Reason == "Unauthorized":
case s.Reason == unversioned.StatusReasonUnauthorized:
return fmt.Sprintf("error: You must be logged in to the server (%s)", s.Message), true
case len(s.Reason) > 0:
return fmt.Sprintf("Error from server (%s): %s", s.Reason, err.Error()), true
default:
return fmt.Sprintf("Error from server: %s", err.Error()), true
}
@ -242,6 +254,26 @@ func MultilineError(prefix string, err error) string {
return fmt.Sprintf("%s%s\n", prefix, err)
}
// PrintErrorWithCauses prints an error's kind, name, and each of the error's causes in a new line.
// The returned string will end with a newline.
// Returns true if a case exists to handle the error type, or false otherwise.
func PrintErrorWithCauses(err error, errOut io.Writer) bool {
switch t := err.(type) {
case *kerrors.StatusError:
errorDetails := t.Status().Details
if errorDetails != nil {
fmt.Fprintf(errOut, "error: %s %q is invalid\n\n", errorDetails.Kind, errorDetails.Name)
for _, cause := range errorDetails.Causes {
fmt.Fprintf(errOut, "* %s: %s\n", cause.Field, cause.Message)
}
return true
}
}
fmt.Fprintf(errOut, "error: %v\n", err)
return false
}
// MultipleErrors returns a newline delimited string containing
// the prefix and referenced errors in standard form.
func MultipleErrors(prefix string, errs []error) string {
@ -266,6 +298,13 @@ func UsageError(cmd *cobra.Command, format string, args ...interface{}) error {
return fmt.Errorf("%s\nSee '%s -h' for help and examples.", msg, cmd.CommandPath())
}
func IsFilenameEmpty(filenames []string) bool {
if len(filenames) == 0 {
return true
}
return false
}
// Whether this cmd need watching objects.
func isWatch(cmd *cobra.Command) bool {
if w, err := cmd.Flags().GetBool("watch"); w && err == nil {
@ -279,25 +318,26 @@ func isWatch(cmd *cobra.Command) bool {
return false
}
func getFlag(cmd *cobra.Command, flag string) *pflag.Flag {
f := cmd.Flags().Lookup(flag)
if f == nil {
glog.Fatalf("flag accessed but not defined for command %s: %s", cmd.Name(), flag)
}
return f
}
func GetFlagString(cmd *cobra.Command, flag string) string {
s, err := cmd.Flags().GetString(flag)
if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err)
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return s
}
// GetFlagStringList can be used to accept multiple argument with flag repetition (e.g. -f arg1 -f arg2 ...)
// GetFlagStringSlice can be used to accept multiple argument with flag repetition (e.g. -f arg1,arg2 -f arg3 ...)
func GetFlagStringSlice(cmd *cobra.Command, flag string) []string {
s, err := cmd.Flags().GetStringSlice(flag)
if err != nil {
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return s
}
// GetFlagStringArray can be used to accept multiple argument with flag repetition (e.g. -f arg1 -f arg2 ...)
func GetFlagStringArray(cmd *cobra.Command, flag string) []string {
s, err := cmd.Flags().GetStringArray(flag)
if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
@ -316,7 +356,7 @@ func GetWideFlag(cmd *cobra.Command) bool {
func GetFlagBool(cmd *cobra.Command, flag string) bool {
b, err := cmd.Flags().GetBool(flag)
if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err)
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return b
}
@ -325,7 +365,7 @@ func GetFlagBool(cmd *cobra.Command, flag string) bool {
func GetFlagInt(cmd *cobra.Command, flag string) int {
i, err := cmd.Flags().GetInt(flag)
if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err)
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return i
}
@ -334,7 +374,7 @@ func GetFlagInt(cmd *cobra.Command, flag string) int {
func GetFlagInt64(cmd *cobra.Command, flag string) int64 {
i, err := cmd.Flags().GetInt64(flag)
if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err)
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return i
}
@ -342,7 +382,7 @@ func GetFlagInt64(cmd *cobra.Command, flag string) int64 {
func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration {
d, err := cmd.Flags().GetDuration(flag)
if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err)
glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
}
return d
}
@ -353,8 +393,9 @@ func AddValidateFlags(cmd *cobra.Command) {
cmd.MarkFlagFilename("schema-cache-dir")
}
func AddRecursiveFlag(cmd *cobra.Command, value *bool) {
cmd.Flags().BoolVarP(value, "recursive", "R", *value, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, "Filename, directory, or URL to files "+usage)
cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
}
// AddDryRunFlag adds dry-run flag to a command. Usually used by mutations.
@ -509,58 +550,9 @@ func ShouldRecord(cmd *cobra.Command, info *resource.Info) bool {
return GetRecordFlag(cmd) || (ContainsChangeCause(info) && !cmd.Flags().Changed("record"))
}
// GetThirdPartyGroupVersions returns the thirdparty "group/versions"s and
// resources supported by the server. A user may delete a thirdparty resource
// when this function is running, so this function may return a "NotFound" error
// due to the race.
func GetThirdPartyGroupVersions(discovery discovery.DiscoveryInterface) ([]unversioned.GroupVersion, []unversioned.GroupVersionKind, error) {
result := []unversioned.GroupVersion{}
gvks := []unversioned.GroupVersionKind{}
groupList, err := discovery.ServerGroups()
if err != nil {
// On forbidden or not found, just return empty lists.
if kerrors.IsForbidden(err) || kerrors.IsNotFound(err) {
return result, gvks, nil
}
return nil, nil, err
}
for ix := range groupList.Groups {
group := &groupList.Groups[ix]
for jx := range group.Versions {
gv, err2 := unversioned.ParseGroupVersion(group.Versions[jx].GroupVersion)
if err2 != nil {
return nil, nil, err
}
// Skip GroupVersionKinds that have been statically registered.
if registered.IsRegisteredVersion(gv) {
continue
}
result = append(result, gv)
resourceList, err := discovery.ServerResourcesForGroupVersion(group.Versions[jx].GroupVersion)
if err != nil {
return nil, nil, err
}
for kx := range resourceList.APIResources {
gvks = append(gvks, gv.WithKind(resourceList.APIResources[kx].Kind))
}
}
}
return result, gvks, nil
}
func GetIncludeThirdPartyAPIs(cmd *cobra.Command) bool {
if cmd.Flags().Lookup("include-extended-apis") == nil {
return false
}
return GetFlagBool(cmd, "include-extended-apis")
}
func AddInclude3rdPartyFlags(cmd *cobra.Command) {
cmd.Flags().Bool("include-extended-apis", true, "If true, include definitions of new APIs via calls to the API server. [default true]")
cmd.Flags().MarkDeprecated("include-extended-apis", "No longer required.")
}
// GetResourcesAndPairs retrieves resources and "KEY=VALUE or KEY-" pair args from given args
@ -595,7 +587,7 @@ func ParsePairs(pairArgs []string, pairType string, supportRemove bool) (newPair
for _, pairArg := range pairArgs {
if strings.Index(pairArg, "=") != -1 {
parts := strings.SplitN(pairArg, "=", 2)
if len(parts) != 2 || len(parts[1]) == 0 {
if len(parts) != 2 {
if invalidBuf.Len() > 0 {
invalidBuf.WriteString(", ")
}
@ -631,3 +623,105 @@ func MaybeConvertObject(obj runtime.Object, gv unversioned.GroupVersion, convert
return converter.ConvertToVersion(obj, gv)
}
}
// MustPrintWithKinds determines if printer is dealing
// with multiple resource kinds, in which case it will
// return true, indicating resource kind will be
// included as part of printer output
func MustPrintWithKinds(objs []runtime.Object, infos []*resource.Info, sorter *kubectl.RuntimeSort, printAll bool) bool {
var lastMap *meta.RESTMapping
if len(infos) == 1 && printAll {
return true
}
for ix := range objs {
var mapping *meta.RESTMapping
if sorter != nil {
mapping = infos[sorter.OriginalPosition(ix)].Mapping
} else {
mapping = infos[ix].Mapping
}
// display "kind" only if we have mixed resources
if lastMap != nil && mapping.Resource != lastMap.Resource {
return true
}
lastMap = mapping
}
return false
}
// FilterResourceList receives a list of runtime objects.
// If any objects are filtered, that number is returned along with a modified list.
func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *kubectl.PrintOptions) (int, []runtime.Object, error) {
items, err := meta.ExtractList(obj)
if err != nil {
return 0, []runtime.Object{obj}, utilerrors.NewAggregate([]error{err})
}
if errs := runtime.DecodeList(items, api.Codecs.UniversalDecoder(), runtime.UnstructuredJSONScheme); len(errs) > 0 {
return 0, []runtime.Object{obj}, utilerrors.NewAggregate(errs)
}
filterCount := 0
list := make([]runtime.Object, 0, len(items))
for _, obj := range items {
if isFiltered, err := filterFuncs.Filter(obj, filterOpts); !isFiltered {
if err != nil {
glog.V(2).Infof("Unable to filter resource: %v", err)
continue
}
list = append(list, obj)
} else if isFiltered {
filterCount++
}
}
return filterCount, list, nil
}
func PrintFilterCount(hiddenObjNum int, resource string, options *kubectl.PrintOptions) {
if !options.NoHeaders && !options.ShowAll && hiddenObjNum > 0 {
glog.V(2).Infof(" info: %d completed object(s) was(were) not shown in %s list. Pass --show-all to see all objects.\n\n", hiddenObjNum, resource)
}
}
// ObjectListToVersionedObject receives a list of api objects and a group version
// and squashes the list's items into a single versioned runtime.Object.
func ObjectListToVersionedObject(objects []runtime.Object, version unversioned.GroupVersion) (runtime.Object, error) {
objectList := &api.List{Items: objects}
converted, err := resource.TryConvert(api.Scheme, objectList, version, registered.GroupOrDie(api.GroupName).GroupVersion)
if err != nil {
return nil, err
}
return converted, nil
}
// IsSiblingCommandExists receives a pointer to a cobra command and a target string.
// Returns true if the target string is found in the list of sibling commands.
func IsSiblingCommandExists(cmd *cobra.Command, targetCmdName string) bool {
for _, c := range cmd.Parent().Commands() {
if c.Name() == targetCmdName {
return true
}
}
return false
}
// DefaultSubCommandRun prints a command's help string to the specified output if no
// arguments (sub-commands) are provided, or a usage error otherwise.
func DefaultSubCommandRun(out io.Writer) func(c *cobra.Command, args []string) {
return func(c *cobra.Command, args []string) {
c.SetOutput(out)
RequireNoArguments(c, args)
c.Help()
}
}
// RequireNoArguments exits with a usage error if extra arguments are provided.
func RequireNoArguments(c *cobra.Command, args []string) {
if len(args) > 0 {
CheckErr(UsageError(c, fmt.Sprintf(`unknown command %q`, strings.Join(args, " "))))
}
}

View file

@ -56,8 +56,12 @@ func AddNoHeadersFlags(cmd *cobra.Command) {
}
// PrintSuccess prints message after finishing mutating operations
func PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource string, name string, operation string) {
func PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource string, name string, dryRun bool, operation string) {
resource, _ = mapper.ResourceSingularizer(resource)
dryRunMsg := ""
if dryRun {
dryRunMsg = " (dry run)"
}
if shortOutput {
// -o name: prints resource/name
if len(resource) > 0 {
@ -68,9 +72,9 @@ func PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resou
} else {
// understandable output by default
if len(resource) > 0 {
fmt.Fprintf(out, "%s \"%s\" %s\n", resource, name, operation)
fmt.Fprintf(out, "%s \"%s\" %s%s\n", resource, name, operation, dryRunMsg)
} else {
fmt.Fprintf(out, "\"%s\" %s\n", name, operation)
fmt.Fprintf(out, "\"%s\" %s%s\n", name, operation, dryRunMsg)
}
}
}

View file

@ -0,0 +1,147 @@
/*
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.
*/
package util
import (
"strings"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/typed/discovery"
"k8s.io/kubernetes/pkg/kubectl"
)
// ShortcutExpander is a RESTMapper that can be used for Kubernetes resources. It expands the resource first, then invokes the wrapped
type ShortcutExpander struct {
RESTMapper meta.RESTMapper
All []unversioned.GroupResource
discoveryClient discovery.DiscoveryInterface
}
var _ meta.RESTMapper = &ShortcutExpander{}
func NewShortcutExpander(delegate meta.RESTMapper, client discovery.DiscoveryInterface) ShortcutExpander {
return ShortcutExpander{All: userResources, RESTMapper: delegate, discoveryClient: client}
}
func (e ShortcutExpander) getAll() []unversioned.GroupResource {
if e.discoveryClient == nil {
return e.All
}
// Check if we have access to server resources
apiResources, err := e.discoveryClient.ServerResources()
if err != nil {
return e.All
}
availableResources := []unversioned.GroupVersionResource{}
for groupVersionString, resourceList := range apiResources {
currVersion, err := unversioned.ParseGroupVersion(groupVersionString)
if err != nil {
return e.All
}
for _, resource := range resourceList.APIResources {
availableResources = append(availableResources, currVersion.WithResource(resource.Name))
}
}
availableAll := []unversioned.GroupResource{}
for _, requestedResource := range e.All {
for _, availableResource := range availableResources {
if requestedResource.Group == availableResource.Group &&
requestedResource.Resource == availableResource.Resource {
availableAll = append(availableAll, requestedResource)
break
}
}
}
return availableAll
}
func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
return e.RESTMapper.KindFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
return e.RESTMapper.ResourceFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) {
return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
}
func (e ShortcutExpander) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
return e.RESTMapper.RESTMapping(gk, versions...)
}
func (e ShortcutExpander) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) {
return e.RESTMapper.RESTMappings(gk)
}
// userResources are the resource names that apply to the primary, user facing resources used by
// client tools. They are in deletion-first order - dependent resources should be last.
var userResources = []unversioned.GroupResource{
{Group: "", Resource: "pods"},
{Group: "", Resource: "replicationcontrollers"},
{Group: "", Resource: "services"},
{Group: "apps", Resource: "statefulsets"},
{Group: "autoscaling", Resource: "horizontalpodautoscalers"},
{Group: "extensions", Resource: "jobs"},
{Group: "extensions", Resource: "deployments"},
{Group: "extensions", Resource: "replicasets"},
}
// AliasesForResource returns whether a resource has an alias or not
func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
if strings.ToLower(resource) == "all" {
var resources []unversioned.GroupResource
if resources = e.getAll(); len(resources) == 0 {
resources = userResources
}
aliases := []string{}
for _, r := range resources {
aliases = append(aliases, r.Resource)
}
return aliases, true
}
expanded := expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource
return []string{expanded}, (expanded != resource)
}
// expandResourceShortcut will return the expanded version of resource
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. Otherwise, will return resource unmodified.
func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource {
if expanded, ok := kubectl.ShortForms[resource.Resource]; ok {
resource.Resource = expanded
return resource
}
return resource
}

View file

@ -155,7 +155,7 @@ type CustomColumnsPrinter struct {
NoHeaders bool
}
func (s *CustomColumnsPrinter) FinishPrint(w io.Writer, res string) error {
func (s *CustomColumnsPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
@ -206,9 +206,18 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso
}
}
}
for ix := range parsers {
parser := parsers[ix]
values, err := parser.FindResults(reflect.ValueOf(obj).Elem().Interface())
var values [][]reflect.Value
var err error
if unstructured, ok := obj.(*runtime.Unstructured); ok {
values, err = parser.FindResults(unstructured.Object)
} else {
values, err = parser.FindResults(reflect.ValueOf(obj).Elem().Interface())
}
if err != nil {
return err
}

View file

@ -32,6 +32,7 @@ import (
fed_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/events"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/apps"
@ -39,16 +40,18 @@ import (
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/storage"
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
client "k8s.io/kubernetes/pkg/client/unversioned"
adapter "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/pkg/fieldpath"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/kubelet/qos"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/types"
utilcertificates "k8s.io/kubernetes/pkg/util/certificates"
certutil "k8s.io/kubernetes/pkg/util/cert"
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/sets"
@ -88,7 +91,7 @@ func (e ErrNoDescriber) Error() string {
return fmt.Sprintf("no describer has been defined for %v", e.Types)
}
func describerMap(c *client.Client) map[unversioned.GroupKind]Describer {
func describerMap(c clientset.Interface) map[unversioned.GroupKind]Describer {
m := map[unversioned.GroupKind]Describer{
api.Kind("Pod"): &PodDescriber{c},
api.Kind("ReplicationController"): &ReplicationControllerDescriber{c},
@ -109,13 +112,14 @@ func describerMap(c *client.Client) map[unversioned.GroupKind]Describer {
extensions.Kind("NetworkPolicy"): &NetworkPolicyDescriber{c},
autoscaling.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c},
extensions.Kind("DaemonSet"): &DaemonSetDescriber{c},
extensions.Kind("Deployment"): &DeploymentDescriber{adapter.FromUnversionedClient(c)},
extensions.Kind("Deployment"): &DeploymentDescriber{c},
extensions.Kind("Job"): &JobDescriber{c},
extensions.Kind("Ingress"): &IngressDescriber{c},
batch.Kind("Job"): &JobDescriber{c},
batch.Kind("ScheduledJob"): &ScheduledJobDescriber{adapter.FromUnversionedClient(c)},
apps.Kind("PetSet"): &PetSetDescriber{c},
batch.Kind("CronJob"): &CronJobDescriber{c},
apps.Kind("StatefulSet"): &StatefulSetDescriber{c},
certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c},
storage.Kind("StorageClass"): &StorageClassDescriber{c},
}
return m
@ -134,7 +138,7 @@ func DescribableResources() []string {
// Describer returns the default describe functions for each of the standard
// Kubernetes types.
func DescriberFor(kind unversioned.GroupKind, c *client.Client) (Describer, bool) {
func DescriberFor(kind unversioned.GroupKind, c clientset.Interface) (Describer, bool) {
f, ok := describerMap(c)[kind]
return f, ok
}
@ -162,15 +166,15 @@ func init() {
// NamespaceDescriber generates information about a namespace
type NamespaceDescriber struct {
client.Interface
clientset.Interface
}
func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
ns, err := d.Namespaces().Get(name)
ns, err := d.Core().Namespaces().Get(name)
if err != nil {
return "", err
}
resourceQuotaList, err := d.ResourceQuotas(name).List(api.ListOptions{})
resourceQuotaList, err := d.Core().ResourceQuotas(name).List(api.ListOptions{})
if err != nil {
if errors.IsNotFound(err) {
// Server does not support resource quotas.
@ -180,7 +184,7 @@ func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings
return "", err
}
}
limitRangeList, err := d.LimitRanges(name).List(api.ListOptions{})
limitRangeList, err := d.Core().LimitRanges(name).List(api.ListOptions{})
if err != nil {
if errors.IsNotFound(err) {
// Server does not support limit ranges.
@ -210,6 +214,71 @@ func describeNamespace(namespace *api.Namespace, resourceQuotaList *api.Resource
})
}
func describeLimitRangeSpec(spec api.LimitRangeSpec, prefix string, w io.Writer) {
for i := range spec.Limits {
item := spec.Limits[i]
maxResources := item.Max
minResources := item.Min
defaultLimitResources := item.Default
defaultRequestResources := item.DefaultRequest
ratio := item.MaxLimitRequestRatio
set := map[api.ResourceName]bool{}
for k := range maxResources {
set[k] = true
}
for k := range minResources {
set[k] = true
}
for k := range defaultLimitResources {
set[k] = true
}
for k := range defaultRequestResources {
set[k] = true
}
for k := range ratio {
set[k] = true
}
for k := range set {
// if no value is set, we output -
maxValue := "-"
minValue := "-"
defaultLimitValue := "-"
defaultRequestValue := "-"
ratioValue := "-"
maxQuantity, maxQuantityFound := maxResources[k]
if maxQuantityFound {
maxValue = maxQuantity.String()
}
minQuantity, minQuantityFound := minResources[k]
if minQuantityFound {
minValue = minQuantity.String()
}
defaultLimitQuantity, defaultLimitQuantityFound := defaultLimitResources[k]
if defaultLimitQuantityFound {
defaultLimitValue = defaultLimitQuantity.String()
}
defaultRequestQuantity, defaultRequestQuantityFound := defaultRequestResources[k]
if defaultRequestQuantityFound {
defaultRequestValue = defaultRequestQuantity.String()
}
ratioQuantity, ratioQuantityFound := ratio[k]
if ratioQuantityFound {
ratioValue = ratioQuantity.String()
}
msg := "%s%s\t%v\t%v\t%v\t%v\t%v\t%v\n"
fmt.Fprintf(w, msg, prefix, item.Type, k, minValue, maxValue, defaultRequestValue, defaultLimitValue, ratioValue)
}
}
}
// DescribeLimitRanges merges a set of limit range items into a single tabular description
func DescribeLimitRanges(limitRanges *api.LimitRangeList, w io.Writer) {
if len(limitRanges.Items) == 0 {
@ -219,68 +288,7 @@ func DescribeLimitRanges(limitRanges *api.LimitRangeList, w io.Writer) {
fmt.Fprintf(w, "Resource Limits\n Type\tResource\tMin\tMax\tDefault Request\tDefault Limit\tMax Limit/Request Ratio\n")
fmt.Fprintf(w, " ----\t--------\t---\t---\t---------------\t-------------\t-----------------------\n")
for _, limitRange := range limitRanges.Items {
for i := range limitRange.Spec.Limits {
item := limitRange.Spec.Limits[i]
maxResources := item.Max
minResources := item.Min
defaultLimitResources := item.Default
defaultRequestResources := item.DefaultRequest
ratio := item.MaxLimitRequestRatio
set := map[api.ResourceName]bool{}
for k := range maxResources {
set[k] = true
}
for k := range minResources {
set[k] = true
}
for k := range defaultLimitResources {
set[k] = true
}
for k := range defaultRequestResources {
set[k] = true
}
for k := range ratio {
set[k] = true
}
for k := range set {
// if no value is set, we output -
maxValue := "-"
minValue := "-"
defaultLimitValue := "-"
defaultRequestValue := "-"
ratioValue := "-"
maxQuantity, maxQuantityFound := maxResources[k]
if maxQuantityFound {
maxValue = maxQuantity.String()
}
minQuantity, minQuantityFound := minResources[k]
if minQuantityFound {
minValue = minQuantity.String()
}
defaultLimitQuantity, defaultLimitQuantityFound := defaultLimitResources[k]
if defaultLimitQuantityFound {
defaultLimitValue = defaultLimitQuantity.String()
}
defaultRequestQuantity, defaultRequestQuantityFound := defaultRequestResources[k]
if defaultRequestQuantityFound {
defaultRequestValue = defaultRequestQuantity.String()
}
ratioQuantity, ratioQuantityFound := ratio[k]
if ratioQuantityFound {
ratioValue = ratioQuantity.String()
}
msg := " %s\t%v\t%v\t%v\t%v\t%v\t%v\n"
fmt.Fprintf(w, msg, item.Type, k, minValue, maxValue, defaultRequestValue, defaultLimitValue, ratioValue)
}
}
describeLimitRangeSpec(limitRange.Spec, " ", w)
}
}
@ -329,11 +337,11 @@ func DescribeResourceQuotas(quotas *api.ResourceQuotaList, w io.Writer) {
// LimitRangeDescriber generates information about a limit range
type LimitRangeDescriber struct {
client.Interface
clientset.Interface
}
func (d *LimitRangeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
lr := d.LimitRanges(namespace)
lr := d.Core().LimitRanges(namespace)
limitRange, err := lr.Get(name)
if err != nil {
@ -348,79 +356,18 @@ func describeLimitRange(limitRange *api.LimitRange) (string, error) {
fmt.Fprintf(out, "Namespace:\t%s\n", limitRange.Namespace)
fmt.Fprintf(out, "Type\tResource\tMin\tMax\tDefault Request\tDefault Limit\tMax Limit/Request Ratio\n")
fmt.Fprintf(out, "----\t--------\t---\t---\t---------------\t-------------\t-----------------------\n")
for i := range limitRange.Spec.Limits {
item := limitRange.Spec.Limits[i]
maxResources := item.Max
minResources := item.Min
defaultLimitResources := item.Default
defaultRequestResources := item.DefaultRequest
ratio := item.MaxLimitRequestRatio
set := map[api.ResourceName]bool{}
for k := range maxResources {
set[k] = true
}
for k := range minResources {
set[k] = true
}
for k := range defaultLimitResources {
set[k] = true
}
for k := range defaultRequestResources {
set[k] = true
}
for k := range ratio {
set[k] = true
}
for k := range set {
// if no value is set, we output -
maxValue := "-"
minValue := "-"
defaultLimitValue := "-"
defaultRequestValue := "-"
ratioValue := "-"
maxQuantity, maxQuantityFound := maxResources[k]
if maxQuantityFound {
maxValue = maxQuantity.String()
}
minQuantity, minQuantityFound := minResources[k]
if minQuantityFound {
minValue = minQuantity.String()
}
defaultLimitQuantity, defaultLimitQuantityFound := defaultLimitResources[k]
if defaultLimitQuantityFound {
defaultLimitValue = defaultLimitQuantity.String()
}
defaultRequestQuantity, defaultRequestQuantityFound := defaultRequestResources[k]
if defaultRequestQuantityFound {
defaultRequestValue = defaultRequestQuantity.String()
}
ratioQuantity, ratioQuantityFound := ratio[k]
if ratioQuantityFound {
ratioValue = ratioQuantity.String()
}
msg := "%v\t%v\t%v\t%v\t%v\t%v\t%v\n"
fmt.Fprintf(out, msg, item.Type, k, minValue, maxValue, defaultRequestValue, defaultLimitValue, ratioValue)
}
}
describeLimitRangeSpec(limitRange.Spec, "", out)
return nil
})
}
// ResourceQuotaDescriber generates information about a resource quota
type ResourceQuotaDescriber struct {
client.Interface
clientset.Interface
}
func (d *ResourceQuotaDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
rq := d.ResourceQuotas(namespace)
rq := d.Core().ResourceQuotas(namespace)
resourceQuota, err := rq.Get(name)
if err != nil {
@ -485,14 +432,14 @@ func describeQuota(resourceQuota *api.ResourceQuota) (string, error) {
// PodDescriber generates information about a pod and the replication controllers that
// create it.
type PodDescriber struct {
client.Interface
clientset.Interface
}
func (d *PodDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
pod, err := d.Pods(namespace).Get(name)
pod, err := d.Core().Pods(namespace).Get(name)
if err != nil {
if describerSettings.ShowEvents {
eventsInterface := d.Events(namespace)
eventsInterface := d.Core().Events(namespace)
selector := eventsInterface.GetFieldSelector(&name, &namespace, nil, nil)
options := api.ListOptions{FieldSelector: selector}
events, err2 := eventsInterface.List(options)
@ -513,7 +460,7 @@ func (d *PodDescriber) Describe(namespace, name string, describerSettings Descri
glog.Errorf("Unable to construct reference to '%#v': %v", pod, err)
} else {
ref.Kind = ""
events, _ = d.Events(namespace).Search(ref)
events, _ = d.Core().Events(namespace).Search(ref)
}
}
@ -621,6 +568,12 @@ func describeVolumes(volumes []api.Volume, out io.Writer, space string) {
printDownwardAPIVolumeSource(volume.VolumeSource.DownwardAPI, out)
case volume.VolumeSource.AzureDisk != nil:
printAzureDiskVolumeSource(volume.VolumeSource.AzureDisk, out)
case volume.VolumeSource.VsphereVolume != nil:
printVsphereVolumeSource(volume.VolumeSource.VsphereVolume, out)
case volume.VolumeSource.Cinder != nil:
printCinderVolumeSource(volume.VolumeSource.Cinder, out)
case volume.VolumeSource.PhotonPersistentDisk != nil:
printPhotonPersistentDiskVolumeSource(volume.VolumeSource.PhotonPersistentDisk, out)
default:
fmt.Fprintf(out, " <unknown>\n")
}
@ -749,12 +702,34 @@ func printAzureDiskVolumeSource(d *api.AzureDiskVolumeSource, out io.Writer) {
d.DiskName, d.DataDiskURI, *d.FSType, *d.CachingMode, *d.ReadOnly)
}
func printVsphereVolumeSource(vsphere *api.VsphereVirtualDiskVolumeSource, out io.Writer) {
fmt.Fprintf(out, " Type:\tvSphereVolume (a Persistent Disk resource in vSphere)\n"+
" VolumePath:\t%v\n"+
" FSType:\t%v\n",
vsphere.VolumePath, vsphere.FSType)
}
func printPhotonPersistentDiskVolumeSource(photon *api.PhotonPersistentDiskVolumeSource, out io.Writer) {
fmt.Fprintf(out, " Type:\tPhotonPersistentDisk (a Persistent Disk resource in photon platform)\n"+
" PdID:\t%v\n"+
" FSType:\t%v\n",
photon.PdID, photon.FSType)
}
func printCinderVolumeSource(cinder *api.CinderVolumeSource, out io.Writer) {
fmt.Fprintf(out, " Type:\tCinder (a Persistent Disk resource in OpenStack)\n"+
" VolumeID:\t%v\n"+
" FSType:\t%v\n"+
" ReadOnly:\t%v\n",
cinder.VolumeID, cinder.FSType, cinder.ReadOnly)
}
type PersistentVolumeDescriber struct {
client.Interface
clientset.Interface
}
func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := d.PersistentVolumes()
c := d.Core().PersistentVolumes()
pv, err := c.Get(name)
if err != nil {
@ -765,12 +740,13 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Events(namespace).Search(pv)
events, _ = d.Core().Events(namespace).Search(pv)
}
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", pv.Name)
printLabelsMultiline(out, "Labels", pv.Labels)
fmt.Fprintf(out, "StorageClass:\t%s\n", storageutil.GetStorageClassAnnotation(pv.ObjectMeta))
fmt.Fprintf(out, "Status:\t%s\n", pv.Status.Phase)
if pv.Spec.ClaimRef != nil {
fmt.Fprintf(out, "Claim:\t%s\n", pv.Spec.ClaimRef.Namespace+"/"+pv.Spec.ClaimRef.Name)
@ -800,6 +776,14 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe
printRBDVolumeSource(pv.Spec.RBD, out)
case pv.Spec.Quobyte != nil:
printQuobyteVolumeSource(pv.Spec.Quobyte, out)
case pv.Spec.VsphereVolume != nil:
printVsphereVolumeSource(pv.Spec.VsphereVolume, out)
case pv.Spec.Cinder != nil:
printCinderVolumeSource(pv.Spec.Cinder, out)
case pv.Spec.AzureDisk != nil:
printAzureDiskVolumeSource(pv.Spec.AzureDisk, out)
case pv.Spec.PhotonPersistentDisk != nil:
printPhotonPersistentDiskVolumeSource(pv.Spec.PhotonPersistentDisk, out)
}
if events != nil {
@ -811,11 +795,11 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe
}
type PersistentVolumeClaimDescriber struct {
client.Interface
clientset.Interface
}
func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := d.PersistentVolumeClaims(namespace)
c := d.Core().PersistentVolumeClaims(namespace)
pvc, err := c.Get(name)
if err != nil {
@ -831,11 +815,12 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, descri
capacity = storage.String()
}
events, _ := d.Events(namespace).Search(pvc)
events, _ := d.Core().Events(namespace).Search(pvc)
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", pvc.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", pvc.Namespace)
fmt.Fprintf(out, "StorageClass:\t%s\n", storageutil.GetStorageClassAnnotation(pvc.ObjectMeta))
fmt.Fprintf(out, "Status:\t%v\n", pvc.Status.Phase)
fmt.Fprintf(out, "Volume:\t%s\n", pvc.Spec.VolumeName)
printLabelsMultiline(out, "Labels", pvc.Labels)
@ -1083,12 +1068,12 @@ func printBool(value bool) string {
// ReplicationControllerDescriber generates information about a replication controller
// and the pods it has created.
type ReplicationControllerDescriber struct {
client.Interface
clientset.Interface
}
func (d *ReplicationControllerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
rc := d.ReplicationControllers(namespace)
pc := d.Pods(namespace)
rc := d.Core().ReplicationControllers(namespace)
pc := d.Core().Pods(namespace)
controller, err := rc.Get(name)
if err != nil {
@ -1102,7 +1087,7 @@ func (d *ReplicationControllerDescriber) Describe(namespace, name string, descri
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Events(namespace).Search(controller)
events, _ = d.Core().Events(namespace).Search(controller)
}
return describeReplicationController(controller, events, running, waiting, succeeded, failed)
@ -1152,12 +1137,12 @@ func DescribePodTemplate(template *api.PodTemplateSpec, out io.Writer) {
// ReplicaSetDescriber generates information about a ReplicaSet and the pods it has created.
type ReplicaSetDescriber struct {
client.Interface
clientset.Interface
}
func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
rsc := d.Extensions().ReplicaSets(namespace)
pc := d.Pods(namespace)
pc := d.Core().Pods(namespace)
rs, err := rsc.Get(name)
if err != nil {
@ -1169,20 +1154,17 @@ func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings
return "", err
}
running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector)
if err != nil {
return "", err
}
running, waiting, succeeded, failed, getPodErr := getPodStatusForController(pc, selector)
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Events(namespace).Search(rs)
events, _ = d.Core().Events(namespace).Search(rs)
}
return describeReplicaSet(rs, events, running, waiting, succeeded, failed)
return describeReplicaSet(rs, events, running, waiting, succeeded, failed, getPodErr)
}
func describeReplicaSet(rs *extensions.ReplicaSet, events *api.EventList, running, waiting, succeeded, failed int) (string, error) {
func describeReplicaSet(rs *extensions.ReplicaSet, events *api.EventList, running, waiting, succeeded, failed int, getPodErr error) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", rs.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", rs.Namespace)
@ -1190,7 +1172,12 @@ func describeReplicaSet(rs *extensions.ReplicaSet, events *api.EventList, runnin
fmt.Fprintf(out, "Selector:\t%s\n", unversioned.FormatLabelSelector(rs.Spec.Selector))
printLabelsMultiline(out, "Labels", rs.Labels)
fmt.Fprintf(out, "Replicas:\t%d current / %d desired\n", rs.Status.Replicas, rs.Spec.Replicas)
fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
fmt.Fprintf(out, "Pods Status:\t")
if getPodErr != nil {
fmt.Fprintf(out, "error in fetching pods: %s\n", getPodErr)
} else {
fmt.Fprintf(out, "%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
}
describeVolumes(rs.Spec.Template.Spec.Volumes, out, "")
if events != nil {
DescribeEvents(events, out)
@ -1201,7 +1188,7 @@ func describeReplicaSet(rs *extensions.ReplicaSet, events *api.EventList, runnin
// JobDescriber generates information about a job and the pods it has created.
type JobDescriber struct {
client.Interface
clientset.Interface
}
func (d *JobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
@ -1212,7 +1199,7 @@ func (d *JobDescriber) Describe(namespace, name string, describerSettings Descri
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Events(namespace).Search(job)
events, _ = d.Core().Events(namespace).Search(job)
}
return describeJob(job, events)
@ -1247,13 +1234,13 @@ func describeJob(job *batch.Job, events *api.EventList) (string, error) {
})
}
// ScheduledJobDescriber generates information about a scheduled job and the jobs it has created.
type ScheduledJobDescriber struct {
// CronJobDescriber generates information about a scheduled job and the jobs it has created.
type CronJobDescriber struct {
clientset.Interface
}
func (d *ScheduledJobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
scheduledJob, err := d.Batch().ScheduledJobs(namespace).Get(name)
func (d *CronJobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
scheduledJob, err := d.Batch().CronJobs(namespace).Get(name)
if err != nil {
return "", err
}
@ -1263,10 +1250,10 @@ func (d *ScheduledJobDescriber) Describe(namespace, name string, describerSettin
events, _ = d.Core().Events(namespace).Search(scheduledJob)
}
return describeScheduledJob(scheduledJob, events)
return describeCronJob(scheduledJob, events)
}
func describeScheduledJob(scheduledJob *batch.ScheduledJob, events *api.EventList) (string, error) {
func describeCronJob(scheduledJob *batch.CronJob, events *api.EventList) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", scheduledJob.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", scheduledJob.Namespace)
@ -1335,12 +1322,12 @@ func printActiveJobs(out io.Writer, title string, jobs []api.ObjectReference) {
// DaemonSetDescriber generates information about a daemon set and the pods it has created.
type DaemonSetDescriber struct {
client.Interface
clientset.Interface
}
func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
dc := d.Extensions().DaemonSets(namespace)
pc := d.Pods(namespace)
pc := d.Core().Pods(namespace)
daemon, err := dc.Get(name)
if err != nil {
@ -1358,7 +1345,7 @@ func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Events(namespace).Search(daemon)
events, _ = d.Core().Events(namespace).Search(daemon)
}
return describeDaemonSet(daemon, events, running, waiting, succeeded, failed)
@ -1389,11 +1376,11 @@ func describeDaemonSet(daemon *extensions.DaemonSet, events *api.EventList, runn
// SecretDescriber generates information about a secret
type SecretDescriber struct {
client.Interface
clientset.Interface
}
func (d *SecretDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := d.Secrets(namespace)
c := d.Core().Secrets(namespace)
secret, err := c.Get(name)
if err != nil {
@ -1427,11 +1414,11 @@ func describeSecret(secret *api.Secret) (string, error) {
}
type IngressDescriber struct {
client.Interface
clientset.Interface
}
func (i *IngressDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := i.Extensions().Ingress(namespace)
c := i.Extensions().Ingresses(namespace)
ing, err := c.Get(name)
if err != nil {
return "", err
@ -1440,8 +1427,8 @@ func (i *IngressDescriber) Describe(namespace, name string, describerSettings De
}
func (i *IngressDescriber) describeBackend(ns string, backend *extensions.IngressBackend) string {
endpoints, _ := i.Endpoints(ns).Get(backend.ServiceName)
service, _ := i.Services(ns).Get(backend.ServiceName)
endpoints, _ := i.Core().Endpoints(ns).Get(backend.ServiceName)
service, _ := i.Core().Services(ns).Get(backend.ServiceName)
spName := ""
for i := range service.Spec.Ports {
sp := &service.Spec.Ports[i]
@ -1502,7 +1489,7 @@ func (i *IngressDescriber) describeIngress(ing *extensions.Ingress, describerSet
describeIngressAnnotations(out, ing.Annotations)
if describerSettings.ShowEvents {
events, _ := i.Events(ing.Namespace).Search(ing)
events, _ := i.Core().Events(ing.Namespace).Search(ing)
if events != nil {
DescribeEvents(events, out)
}
@ -1539,21 +1526,21 @@ func describeIngressAnnotations(out io.Writer, annotations map[string]string) {
// ServiceDescriber generates information about a service.
type ServiceDescriber struct {
client.Interface
clientset.Interface
}
func (d *ServiceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := d.Services(namespace)
c := d.Core().Services(namespace)
service, err := c.Get(name)
if err != nil {
return "", err
}
endpoints, _ := d.Endpoints(namespace).Get(name)
endpoints, _ := d.Core().Endpoints(namespace).Get(name)
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Events(namespace).Search(service)
events, _ = d.Core().Events(namespace).Search(service)
}
return describeService(service, endpoints, events)
}
@ -1618,11 +1605,11 @@ func describeService(service *api.Service, endpoints *api.Endpoints, events *api
// EndpointsDescriber generates information about an Endpoint.
type EndpointsDescriber struct {
client.Interface
clientset.Interface
}
func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := d.Endpoints(namespace)
c := d.Core().Endpoints(namespace)
ep, err := c.Get(name)
if err != nil {
@ -1631,7 +1618,7 @@ func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Events(namespace).Search(ep)
events, _ = d.Core().Events(namespace).Search(ep)
}
return describeEndpoints(ep, events)
@ -1691,11 +1678,11 @@ func describeEndpoints(ep *api.Endpoints, events *api.EventList) (string, error)
// ServiceAccountDescriber generates information about a service.
type ServiceAccountDescriber struct {
client.Interface
clientset.Interface
}
func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := d.ServiceAccounts(namespace)
c := d.Core().ServiceAccounts(namespace)
serviceAccount, err := c.Get(name)
if err != nil {
@ -1706,7 +1693,7 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
tokenSelector := fields.SelectorFromSet(map[string]string{api.SecretTypeField: string(api.SecretTypeServiceAccountToken)})
options := api.ListOptions{FieldSelector: tokenSelector}
secrets, err := d.Secrets(namespace).List(options)
secrets, err := d.Core().Secrets(namespace).List(options)
if err == nil {
for _, s := range secrets.Items {
name, _ := s.Annotations[api.ServiceAccountNameKey]
@ -1772,11 +1759,11 @@ func describeServiceAccount(serviceAccount *api.ServiceAccount, tokens []api.Sec
// NodeDescriber generates information about a node.
type NodeDescriber struct {
client.Interface
clientset.Interface
}
func (d *NodeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
mc := d.Nodes()
mc := d.Core().Nodes()
node, err := mc.Get(name)
if err != nil {
return "", err
@ -1789,7 +1776,7 @@ func (d *NodeDescriber) Describe(namespace, name string, describerSettings Descr
// in a policy aware setting, users may have access to a node, but not all pods
// in that case, we note that the user does not have access to the pods
canViewPods := true
nodeNonTerminatedPodsList, err := d.Pods(namespace).List(api.ListOptions{FieldSelector: fieldSelector})
nodeNonTerminatedPodsList, err := d.Core().Pods(namespace).List(api.ListOptions{FieldSelector: fieldSelector})
if err != nil {
if !errors.IsForbidden(err) {
return "", err
@ -1804,7 +1791,7 @@ func (d *NodeDescriber) Describe(namespace, name string, describerSettings Descr
} else {
// TODO: We haven't decided the namespace for Node object yet.
ref.UID = types.UID(ref.Name)
events, _ = d.Events("").Search(ref)
events, _ = d.Core().Events("").Search(ref)
}
}
@ -1890,16 +1877,16 @@ func describeNode(node *api.Node, nodeNonTerminatedPodsList *api.PodList, events
})
}
type PetSetDescriber struct {
client *client.Client
type StatefulSetDescriber struct {
client clientset.Interface
}
func (p *PetSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
ps, err := p.client.Apps().PetSets(namespace).Get(name)
func (p *StatefulSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
ps, err := p.client.Apps().StatefulSets(namespace).Get(name)
if err != nil {
return "", err
}
pc := p.client.Pods(namespace)
pc := p.client.Core().Pods(namespace)
selector, err := unversioned.LabelSelectorAsSelector(ps.Spec.Selector)
if err != nil {
@ -1923,7 +1910,7 @@ func (p *PetSetDescriber) Describe(namespace, name string, describerSettings Des
fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed)
describeVolumes(ps.Spec.Template.Spec.Volumes, out, "")
if describerSettings.ShowEvents {
events, _ := p.client.Events(namespace).Search(ps)
events, _ := p.client.Core().Events(namespace).Search(ps)
if events != nil {
DescribeEvents(events, out)
}
@ -1933,7 +1920,7 @@ func (p *PetSetDescriber) Describe(namespace, name string, describerSettings Des
}
type CertificateSigningRequestDescriber struct {
client *client.Client
client clientset.Interface
}
func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
@ -1942,7 +1929,7 @@ func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, de
return "", err
}
cr, err := utilcertificates.ParseCertificateRequestObject(csr)
cr, err := certutil.ParseCSR(csr)
if err != nil {
return "", fmt.Errorf("Error parsing CSR: %v", err)
}
@ -1991,7 +1978,7 @@ func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, de
}
if describerSettings.ShowEvents {
events, _ := p.client.Events(namespace).Search(csr)
events, _ := p.client.Core().Events(namespace).Search(csr)
if events != nil {
DescribeEvents(events, out)
}
@ -2002,7 +1989,7 @@ func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, de
// HorizontalPodAutoscalerDescriber generates information about a horizontal pod autoscaler.
type HorizontalPodAutoscalerDescriber struct {
client *client.Client
client clientset.Interface
}
func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
@ -2038,7 +2025,7 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, desc
// TODO: switch to scale subresource once the required code is submitted.
if strings.ToLower(hpa.Spec.ScaleTargetRef.Kind) == "replicationcontroller" {
fmt.Fprintf(out, "ReplicationController pods:\t")
rc, err := d.client.ReplicationControllers(hpa.Namespace).Get(hpa.Spec.ScaleTargetRef.Name)
rc, err := d.client.Core().ReplicationControllers(hpa.Namespace).Get(hpa.Spec.ScaleTargetRef.Name)
if err == nil {
fmt.Fprintf(out, "%d current / %d desired\n", rc.Status.Replicas, rc.Spec.Replicas)
} else {
@ -2047,7 +2034,7 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, desc
}
if describerSettings.ShowEvents {
events, _ := d.client.Events(namespace).Search(hpa)
events, _ := d.client.Core().Events(namespace).Search(hpa)
if events != nil {
DescribeEvents(events, out)
}
@ -2140,11 +2127,11 @@ func getPodsTotalRequestsAndLimits(podList *api.PodList) (reqs map[api.ResourceN
func DescribeEvents(el *api.EventList, w io.Writer) {
if len(el.Items) == 0 {
fmt.Fprint(w, "No events.")
fmt.Fprint(w, "No events.\n")
return
}
sort.Sort(SortableEvents(el.Items))
fmt.Fprint(w, "Events:\n FirstSeen\tLastSeen\tCount\tFrom\tSubobjectPath\tType\tReason\tMessage\n")
sort.Sort(events.SortableEvents(el.Items))
fmt.Fprint(w, "Events:\n FirstSeen\tLastSeen\tCount\tFrom\tSubObjectPath\tType\tReason\tMessage\n")
fmt.Fprint(w, " ---------\t--------\t-----\t----\t-------------\t--------\t------\t-------\n")
for _, e := range el.Items {
fmt.Fprintf(w, " %s\t%s\t%d\t%v\t%v\t%v\t%v\t%v\n",
@ -2186,6 +2173,13 @@ func (dd *DeploymentDescriber) Describe(namespace, name string, describerSetting
ru := d.Spec.Strategy.RollingUpdate
fmt.Fprintf(out, "RollingUpdateStrategy:\t%s max unavailable, %s max surge\n", ru.MaxUnavailable.String(), ru.MaxSurge.String())
}
if len(d.Status.Conditions) > 0 {
fmt.Fprint(out, "Conditions:\n Type\tStatus\tReason\n")
fmt.Fprint(out, " ----\t------\t------\n")
for _, c := range d.Status.Conditions {
fmt.Fprintf(out, " %v \t%v\t%v\n", c.Type, c.Status, c.Reason)
}
}
oldRSs, _, newRS, err := deploymentutil.GetAllReplicaSets(d, dd)
if err == nil {
fmt.Fprintf(out, "OldReplicaSets:\t%s\n", printReplicaSetsByLabels(oldRSs))
@ -2214,7 +2208,7 @@ func (dd *DeploymentDescriber) Describe(namespace, name string, describerSetting
// of getting all DS's and searching through them manually).
// TODO: write an interface for controllers and fuse getReplicationControllersForLabels
// and getDaemonSetsForLabels.
func getDaemonSetsForLabels(c client.DaemonSetInterface, labelsToMatch labels.Labels) ([]extensions.DaemonSet, error) {
func getDaemonSetsForLabels(c extensionsclient.DaemonSetInterface, labelsToMatch labels.Labels) ([]extensions.DaemonSet, error) {
// Get all daemon sets
// TODO: this needs a namespace scope as argument
dss, err := c.List(api.ListOptions{})
@ -2265,7 +2259,7 @@ func printReplicaSetsByLabels(matchingRSs []*extensions.ReplicaSet) string {
return list
}
func getPodStatusForController(c client.PodInterface, selector labels.Selector) (running, waiting, succeeded, failed int, err error) {
func getPodStatusForController(c coreclient.PodInterface, selector labels.Selector) (running, waiting, succeeded, failed int, err error) {
options := api.ListOptions{LabelSelector: selector}
rcPods, err := c.List(options)
if err != nil {
@ -2288,11 +2282,11 @@ func getPodStatusForController(c client.PodInterface, selector labels.Selector)
// ConfigMapDescriber generates information about a ConfigMap
type ConfigMapDescriber struct {
client.Interface
clientset.Interface
}
func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := d.ConfigMaps(namespace)
c := d.Core().ConfigMaps(namespace)
configMap, err := c.Get(name)
if err != nil {
@ -2360,7 +2354,7 @@ func describeCluster(cluster *federation.Cluster) (string, error) {
// NetworkPolicyDescriber generates information about a NetworkPolicy
type NetworkPolicyDescriber struct {
client.Interface
clientset.Interface
}
func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
@ -2385,6 +2379,34 @@ func describeNetworkPolicy(networkPolicy *extensions.NetworkPolicy) (string, err
})
}
type StorageClassDescriber struct {
clientset.Interface
}
func (s *StorageClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
sc, err := s.Storage().StorageClasses().Get(name)
if err != nil {
return "", err
}
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", sc.Name)
fmt.Fprintf(out, "IsDefaultClass:\t%s\n", storageutil.IsDefaultAnnotationText(sc.ObjectMeta))
fmt.Fprintf(out, "Annotations:\t%s\n", labels.FormatLabels(sc.Annotations))
fmt.Fprintf(out, "Provisioner:\t%s\n", sc.Provisioner)
fmt.Fprintf(out, "Parameters:\t%s\n", labels.FormatLabels(sc.Parameters))
if describerSettings.ShowEvents {
events, err := s.Core().Events(namespace).Search(sc)
if err != nil {
return err
}
if events != nil {
DescribeEvents(events, out)
}
}
return nil
})
}
// newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types.
func newErrNoDescriber(types ...reflect.Type) error {
names := make([]string, 0, len(types))

View file

@ -48,28 +48,6 @@ func makeImageList(spec *api.PodSpec) string {
return strings.Join(listOfImages(spec), ",")
}
func NewThirdPartyResourceMapper(gvs []unversioned.GroupVersion, gvks []unversioned.GroupVersionKind) (meta.RESTMapper, error) {
mapper := meta.NewDefaultRESTMapper(gvs, func(gv unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
for ix := range gvs {
if gvs[ix].Group == gv.Group && gvs[ix].Version == gv.Version {
return &meta.VersionInterfaces{
ObjectConvertor: api.Scheme,
MetadataAccessor: meta.NewAccessor(),
}, nil
}
}
groupVersions := make([]string, 0, len(gvs))
for ix := range gvs {
groupVersions = append(groupVersions, gvs[ix].String())
}
return nil, fmt.Errorf("unsupported storage version: %s (valid: %s)", gv.String(), strings.Join(groupVersions, ", "))
})
for ix := range gvks {
mapper.Add(gvks[ix], meta.RESTScopeNamespace)
}
return mapper, nil
}
// OutputVersionMapper is a RESTMapper that will prefer mappings that
// correspond to a preferred output version (if feasible)
type OutputVersionMapper struct {
@ -97,48 +75,8 @@ func (m OutputVersionMapper) RESTMapping(gk unversioned.GroupKind, versions ...s
return m.RESTMapper.RESTMapping(gk, versions...)
}
// ShortcutExpander is a RESTMapper that can be used for Kubernetes
// resources. It expands the resource first, then invokes the wrapped RESTMapper
type ShortcutExpander struct {
RESTMapper meta.RESTMapper
}
var _ meta.RESTMapper = &ShortcutExpander{}
func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
return e.RESTMapper.KindFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
return e.RESTMapper.ResourceFor(expandResourceShortcut(resource))
}
func (e ShortcutExpander) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
return e.RESTMapper.RESTMapping(gk, versions...)
}
func (e ShortcutExpander) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) {
return e.RESTMapper.RESTMappings(gk)
}
func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) {
return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
}
func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
return e.RESTMapper.AliasesForResource(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
}
// shortForms is the list of short names to their expanded names
var shortForms = map[string]string{
// ShortForms is the list of short names to their expanded names
var ShortForms = map[string]string{
// Please keep this alphabetized
// If you add an entry here, please also take a look at pkg/kubectl/cmd/cmd.go
// and add an entry to valid_resources when appropriate.
@ -165,30 +103,20 @@ var shortForms = map[string]string{
"svc": "services",
}
// Look-up for resource short forms by value
// ResourceShortFormFor looks up for a short form of resource names.
func ResourceShortFormFor(resource string) (string, bool) {
var alias string
exists := false
for k, val := range shortForms {
for k, val := range ShortForms {
if val == resource {
alias = k
exists = true
break
}
}
return alias, exists
}
// expandResourceShortcut will return the expanded version of resource
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. Otherwise, will return resource unmodified.
func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource {
if expanded, ok := shortForms[resource.Resource]; ok {
// don't change the group or version that's already been specified
resource.Resource = expanded
}
return resource
}
// ResourceAliases returns the resource shortcuts and plural forms for the given resources.
func ResourceAliases(rs []string) []string {
as := make([]string, 0, len(rs))
@ -210,7 +138,7 @@ func ResourceAliases(rs []string) []string {
plurals[plural] = struct{}{}
}
for sf, r := range shortForms {
for sf, r := range ShortForms {
if _, found := plurals[r]; found {
as = append(as, sf)
}

80
vendor/k8s.io/kubernetes/pkg/kubectl/resource/BUILD generated vendored Normal file
View file

@ -0,0 +1,80 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"builder.go",
"doc.go",
"helper.go",
"interfaces.go",
"mapper.go",
"result.go",
"selector.go",
"visitor.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/restclient:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/util/errors:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/util/yaml:go_default_library",
"//pkg/watch:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = [
"builder_test.go",
"helper_test.go",
"visitor_test.go",
],
data = [
"//examples:config",
"//test/fixtures",
],
library = "go_default_library",
tags = [
"automanaged",
"skip",
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/api/unversioned:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/client/restclient/fake:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/serializer/streaming:go_default_library",
"//pkg/util/errors:go_default_library",
"//pkg/util/testing:go_default_library",
"//pkg/watch:go_default_library",
"//pkg/watch/versioned:go_default_library",
"//vendor:github.com/ghodss/yaml",
"//vendor:github.com/stretchr/testify/assert",
],
)

View file

@ -55,8 +55,9 @@ type Builder struct {
resources []string
namespace string
names []string
namespace string
allNamespace bool
names []string
resourceTuples []resourceTuple
@ -93,6 +94,11 @@ func IsUsageError(err error) bool {
return err == missingResourceError
}
type FilenameOptions struct {
Filenames []string
Recursive bool
}
type resourceTuple struct {
Resource string
Name string
@ -117,7 +123,9 @@ func (b *Builder) Schema(schema validation.Schema) *Builder {
// will cause an error.
// If ContinueOnError() is set prior to this method, objects on the path that are not
// recognized will be ignored (but logged at V(2)).
func (b *Builder) FilenameParam(enforceNamespace, recursive bool, paths ...string) *Builder {
func (b *Builder) FilenameParam(enforceNamespace bool, filenameOptions *FilenameOptions) *Builder {
recursive := filenameOptions.Recursive
paths := filenameOptions.Filenames
for _, s := range paths {
switch {
case s == "-":
@ -284,11 +292,12 @@ func (b *Builder) DefaultNamespace() *Builder {
}
// AllNamespaces instructs the builder to use NamespaceAll as a namespace to request resources
// acroll all namespace. This overrides the namespace set by NamespaceParam().
// across all of the namespace. This overrides the namespace set by NamespaceParam().
func (b *Builder) AllNamespaces(allNamespace bool) *Builder {
if allNamespace {
b.namespace = api.NamespaceAll
}
b.allNamespace = allNamespace
return b
}
@ -336,7 +345,7 @@ func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string
}
if len(args) > 0 {
// Try replacing aliases only in types
args[0] = b.replaceAliases(args[0])
args[0] = b.ReplaceAliases(args[0])
}
switch {
case len(args) > 2:
@ -357,9 +366,9 @@ func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string
return b
}
// replaceAliases accepts an argument and tries to expand any existing
// ReplaceAliases accepts an argument and tries to expand any existing
// aliases found in it
func (b *Builder) replaceAliases(input string) string {
func (b *Builder) ReplaceAliases(input string) string {
replaced := []string{}
for _, arg := range strings.Split(input, ",") {
if aliases, ok := b.mapper.AliasesForResource(arg); ok {
@ -531,191 +540,213 @@ func (b *Builder) visitorResult() *Result {
b.selector = labels.Everything()
}
// visit items specified by paths
if len(b.paths) != 0 {
return b.visitByPaths()
}
// visit selectors
if b.selector != nil {
if len(b.names) != 0 {
return &Result{err: fmt.Errorf("name cannot be provided when a selector is specified")}
}
if len(b.resourceTuples) != 0 {
return &Result{err: fmt.Errorf("selectors and the all flag cannot be used when passing resource/name arguments")}
}
if len(b.resources) == 0 {
return &Result{err: fmt.Errorf("at least one resource must be specified to use a selector")}
}
// empty selector has different error message for paths being provided
if len(b.paths) != 0 {
if b.selector.Empty() {
return &Result{err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
} else {
return &Result{err: fmt.Errorf("a selector may not be specified when path, URL, or stdin is provided as input")}
}
}
mappings, err := b.resourceMappings()
if err != nil {
return &Result{err: err}
}
visitors := []Visitor{}
for _, mapping := range mappings {
client, err := b.mapper.ClientForMapping(mapping)
if err != nil {
return &Result{err: err}
}
selectorNamespace := b.namespace
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
}
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, b.selector, b.export))
}
if b.continueOnError {
return &Result{visitor: EagerVisitorList(visitors), sources: visitors}
}
return &Result{visitor: VisitorList(visitors), sources: visitors}
return b.visitBySelector()
}
// visit items specified by resource and name
if len(b.resourceTuples) != 0 {
// if b.singular is false, this could be by default, so double-check length
// of resourceTuples to determine if in fact it is singular or not
isSingular := b.singular
if !isSingular {
isSingular = len(b.resourceTuples) == 1
}
if len(b.paths) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
}
if len(b.resources) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("you may not specify individual resources and bulk resources in the same call")}
}
// retrieve one client for each resource
mappings, err := b.resourceTupleMappings()
if err != nil {
return &Result{singular: isSingular, err: err}
}
clients := make(map[string]RESTClient)
for _, mapping := range mappings {
s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource)
if _, ok := clients[s]; ok {
continue
}
client, err := b.mapper.ClientForMapping(mapping)
if err != nil {
return &Result{err: err}
}
clients[s] = client
}
items := []Visitor{}
for _, tuple := range b.resourceTuples {
mapping, ok := mappings[tuple.Resource]
if !ok {
return &Result{singular: isSingular, err: fmt.Errorf("resource %q is not recognized: %v", tuple.Resource, mappings)}
}
s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource)
client, ok := clients[s]
if !ok {
return &Result{singular: isSingular, err: fmt.Errorf("could not find a client for resource %q", tuple.Resource)}
}
selectorNamespace := b.namespace
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
} else {
if len(b.namespace) == 0 {
return &Result{singular: isSingular, err: fmt.Errorf("namespace may not be empty when retrieving a resource by name")}
}
}
info := NewInfo(client, mapping, selectorNamespace, tuple.Name, b.export)
items = append(items, info)
}
var visitors Visitor
if b.continueOnError {
visitors = EagerVisitorList(items)
} else {
visitors = VisitorList(items)
}
return &Result{singular: isSingular, visitor: visitors, sources: items}
return b.visitByResource()
}
// visit items specified by name
if len(b.names) != 0 {
isSingular := len(b.names) == 1
return b.visitByName()
}
if len(b.paths) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
}
if len(b.resources) == 0 {
return &Result{singular: isSingular, err: fmt.Errorf("you must provide a resource and a resource name together")}
}
if len(b.resources) > 1 {
return &Result{singular: isSingular, err: fmt.Errorf("you must specify only one resource")}
}
if len(b.resources) != 0 {
return &Result{err: fmt.Errorf("resource(s) were provided, but no name, label selector, or --all flag specified")}
}
return &Result{err: missingResourceError}
}
mappings, err := b.resourceMappings()
if err != nil {
return &Result{singular: isSingular, err: err}
}
mapping := mappings[0]
func (b *Builder) visitBySelector() *Result {
if len(b.names) != 0 {
return &Result{err: fmt.Errorf("name cannot be provided when a selector is specified")}
}
if len(b.resourceTuples) != 0 {
return &Result{err: fmt.Errorf("selectors and the all flag cannot be used when passing resource/name arguments")}
}
if len(b.resources) == 0 {
return &Result{err: fmt.Errorf("at least one resource must be specified to use a selector")}
}
mappings, err := b.resourceMappings()
if err != nil {
return &Result{err: err}
}
visitors := []Visitor{}
for _, mapping := range mappings {
client, err := b.mapper.ClientForMapping(mapping)
if err != nil {
return &Result{err: err}
}
selectorNamespace := b.namespace
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
}
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, b.selector, b.export))
}
if b.continueOnError {
return &Result{visitor: EagerVisitorList(visitors), sources: visitors}
}
return &Result{visitor: VisitorList(visitors), sources: visitors}
}
func (b *Builder) visitByResource() *Result {
// if b.singular is false, this could be by default, so double-check length
// of resourceTuples to determine if in fact it is singular or not
isSingular := b.singular
if !isSingular {
isSingular = len(b.resourceTuples) == 1
}
if len(b.resources) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("you may not specify individual resources and bulk resources in the same call")}
}
// retrieve one client for each resource
mappings, err := b.resourceTupleMappings()
if err != nil {
return &Result{singular: isSingular, err: err}
}
clients := make(map[string]RESTClient)
for _, mapping := range mappings {
s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource)
if _, ok := clients[s]; ok {
continue
}
client, err := b.mapper.ClientForMapping(mapping)
if err != nil {
return &Result{err: err}
}
clients[s] = client
}
items := []Visitor{}
for _, tuple := range b.resourceTuples {
mapping, ok := mappings[tuple.Resource]
if !ok {
return &Result{singular: isSingular, err: fmt.Errorf("resource %q is not recognized: %v", tuple.Resource, mappings)}
}
s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource)
client, ok := clients[s]
if !ok {
return &Result{singular: isSingular, err: fmt.Errorf("could not find a client for resource %q", tuple.Resource)}
}
selectorNamespace := b.namespace
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
} else {
if len(b.namespace) == 0 {
return &Result{singular: isSingular, err: fmt.Errorf("namespace may not be empty when retrieving a resource by name")}
errMsg := "namespace may not be empty when retrieving a resource by name"
if b.allNamespace {
errMsg = "a resource cannot be retrieved by name across all namespaces"
}
return &Result{singular: isSingular, err: fmt.Errorf(errMsg)}
}
}
visitors := []Visitor{}
for _, name := range b.names {
info := NewInfo(client, mapping, selectorNamespace, name, b.export)
visitors = append(visitors, info)
}
return &Result{singular: isSingular, visitor: VisitorList(visitors), sources: visitors}
info := NewInfo(client, mapping, selectorNamespace, tuple.Name, b.export)
items = append(items, info)
}
// visit items specified by paths
var visitors Visitor
if b.continueOnError {
visitors = EagerVisitorList(items)
} else {
visitors = VisitorList(items)
}
return &Result{singular: isSingular, visitor: visitors, sources: items}
}
func (b *Builder) visitByName() *Result {
isSingular := len(b.names) == 1
if len(b.paths) != 0 {
singular := !b.dir && !b.stream && len(b.paths) == 1
if len(b.resources) != 0 {
return &Result{singular: singular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
}
var visitors Visitor
if b.continueOnError {
visitors = EagerVisitorList(b.paths)
} else {
visitors = VisitorList(b.paths)
}
// only items from disk can be refetched
if b.latest {
// must flatten lists prior to fetching
if b.flatten {
visitors = NewFlattenListVisitor(visitors, b.mapper)
}
// must set namespace prior to fetching
if b.defaultNamespace {
visitors = NewDecoratedVisitor(visitors, SetNamespace(b.namespace))
}
visitors = NewDecoratedVisitor(visitors, RetrieveLatest)
}
return &Result{singular: singular, visitor: visitors, sources: b.paths}
return &Result{singular: isSingular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
}
if len(b.resources) == 0 {
return &Result{singular: isSingular, err: fmt.Errorf("you must provide a resource and a resource name together")}
}
if len(b.resources) > 1 {
return &Result{singular: isSingular, err: fmt.Errorf("you must specify only one resource")}
}
mappings, err := b.resourceMappings()
if err != nil {
return &Result{singular: isSingular, err: err}
}
mapping := mappings[0]
client, err := b.mapper.ClientForMapping(mapping)
if err != nil {
return &Result{err: err}
}
selectorNamespace := b.namespace
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
} else {
if len(b.namespace) == 0 {
errMsg := "namespace may not be empty when retrieving a resource by name"
if b.allNamespace {
errMsg = "a resource cannot be retrieved by name across all namespaces"
}
return &Result{singular: isSingular, err: fmt.Errorf(errMsg)}
}
}
visitors := []Visitor{}
for _, name := range b.names {
info := NewInfo(client, mapping, selectorNamespace, name, b.export)
visitors = append(visitors, info)
}
return &Result{singular: isSingular, visitor: VisitorList(visitors), sources: visitors}
}
func (b *Builder) visitByPaths() *Result {
singular := !b.dir && !b.stream && len(b.paths) == 1
if len(b.resources) != 0 {
return &Result{err: fmt.Errorf("resource(s) were provided, but no name, label selector, or --all flag specified")}
return &Result{singular: singular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
}
return &Result{err: missingResourceError}
if len(b.names) != 0 {
return &Result{err: fmt.Errorf("name cannot be provided when a path is specified")}
}
if len(b.resourceTuples) != 0 {
return &Result{err: fmt.Errorf("resource/name arguments cannot be provided when a path is specified")}
}
var visitors Visitor
if b.continueOnError {
visitors = EagerVisitorList(b.paths)
} else {
visitors = VisitorList(b.paths)
}
// only items from disk can be refetched
if b.latest {
// must flatten lists prior to fetching
if b.flatten {
visitors = NewFlattenListVisitor(visitors, b.mapper)
}
// must set namespace prior to fetching
if b.defaultNamespace {
visitors = NewDecoratedVisitor(visitors, SetNamespace(b.namespace))
}
visitors = NewDecoratedVisitor(visitors, RetrieveLatest)
}
if b.selector != nil {
visitors = NewFilteredVisitor(visitors, FilterBySelector(b.selector))
}
return &Result{singular: singular, visitor: visitors, sources: b.paths}
}
// Do returns a Result object with a Visitor for the resources identified by the Builder.

View file

@ -222,7 +222,7 @@ func AsVersionedObject(infos []*Info, forceList bool, version unversioned.GroupV
object = objects[0]
} else {
object = &api.List{Items: objects}
converted, err := tryConvert(api.Scheme, object, version, registered.GroupOrDie(api.GroupName).GroupVersion)
converted, err := TryConvert(api.Scheme, object, version, registered.GroupOrDie(api.GroupName).GroupVersion)
if err != nil {
return nil, err
}
@ -263,7 +263,7 @@ func AsVersionedObjects(infos []*Info, version unversioned.GroupVersion, encoder
}
}
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion())
converted, err := TryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil {
return nil, err
}
@ -272,9 +272,9 @@ func AsVersionedObjects(infos []*Info, version unversioned.GroupVersion, encoder
return objects, nil
}
// tryConvert attempts to convert the given object to the provided versions in order. This function assumes
// TryConvert attempts to convert the given object to the provided versions in order. This function assumes
// the object is in internal version.
func tryConvert(converter runtime.ObjectConvertor, object runtime.Object, versions ...unversioned.GroupVersion) (runtime.Object, error) {
func TryConvert(converter runtime.ObjectConvertor, object runtime.Object, versions ...unversioned.GroupVersion) (runtime.Object, error) {
var last error
for _, version := range versions {
if version.Empty() {

View file

@ -26,9 +26,12 @@ import (
"path/filepath"
"time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/yaml"
@ -110,6 +113,12 @@ func (i *Info) Visit(fn VisitorFunc) error {
func (i *Info) Get() (err error) {
obj, err := NewHelper(i.Client, i.Mapping).Get(i.Namespace, i.Name, i.Export)
if err != nil {
if errors.IsNotFound(err) && len(i.Namespace) > 0 && i.Namespace != api.NamespaceDefault && i.Namespace != api.NamespaceAll {
err2 := i.Client.Get().AbsPath("api", "v1", "namespaces", i.Namespace).Do().Error()
if err2 != nil && errors.IsNotFound(err2) {
return err2
}
}
return err
}
i.Object = obj
@ -631,3 +640,51 @@ func RetrieveLazy(info *Info, err error) error {
}
return nil
}
type FilterFunc func(info *Info, err error) (bool, error)
type FilteredVisitor struct {
visitor Visitor
filters []FilterFunc
}
func NewFilteredVisitor(v Visitor, fn ...FilterFunc) Visitor {
if len(fn) == 0 {
return v
}
return FilteredVisitor{v, fn}
}
func (v FilteredVisitor) Visit(fn VisitorFunc) error {
return v.visitor.Visit(func(info *Info, err error) error {
if err != nil {
return err
}
for _, filter := range v.filters {
ok, err := filter(info, nil)
if err != nil {
return err
}
if !ok {
return nil
}
}
return fn(info, nil)
})
}
func FilterBySelector(s labels.Selector) FilterFunc {
return func(info *Info, err error) (bool, error) {
if err != nil {
return false, err
}
a, err := meta.Accessor(info.Object)
if err != nil {
return false, err
}
if !s.Matches(labels.Set(a.GetLabels())) {
return false, nil
}
return true, nil
}
}

View file

@ -0,0 +1,67 @@
/*
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.
*/
package kubectl
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
)
// FilterFunc is a function that knows how to filter a specific resource kind.
// It receives a generic runtime.Object which must be type-checked by the function.
// Returns a boolean value true if a resource is filtered, or false otherwise.
type FilterFunc func(runtime.Object, PrintOptions) bool
// Filters is a collection of filter funcs
type Filters []FilterFunc
func NewResourceFilter() Filters {
return []FilterFunc{
filterPods,
}
}
// filterPods returns true if a pod should be skipped.
// defaults to true for terminated pods
func filterPods(obj runtime.Object, options PrintOptions) bool {
switch p := obj.(type) {
case *v1.Pod:
reason := string(p.Status.Phase)
if p.Status.Reason != "" {
reason = p.Status.Reason
}
return !options.ShowAll && (reason == string(v1.PodSucceeded) || reason == string(v1.PodFailed))
case *api.Pod:
reason := string(p.Status.Phase)
if p.Status.Reason != "" {
reason = p.Status.Reason
}
return !options.ShowAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed))
}
return false
}
// Filter loops through a collection of FilterFuncs until it finds one that can filter the given resource
func (f Filters) Filter(obj runtime.Object, opts *PrintOptions) (bool, error) {
for _, filter := range f {
if ok := filter(obj, *opts); ok {
return true, nil
}
}
return false, nil
}

View file

@ -30,10 +30,9 @@ import (
"text/template"
"time"
"github.com/ghodss/yaml"
"github.com/golang/glog"
"k8s.io/kubernetes/federation/apis/federation"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/events"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/apps"
@ -43,11 +42,16 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/storage"
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/jsonpath"
"k8s.io/kubernetes/pkg/util/node"
"k8s.io/kubernetes/pkg/util/sets"
"github.com/ghodss/yaml"
"github.com/golang/glog"
)
const (
@ -150,7 +154,7 @@ type ResourcePrinter interface {
HandledResources() []string
//Can be used to print out warning/clarifications if needed
//after all objects were printed
FinishPrint(io.Writer, string) error
AfterPrint(io.Writer, string) error
}
// ResourcePrinterFunc is a function that can print objects
@ -166,7 +170,7 @@ func (fn ResourcePrinterFunc) HandledResources() []string {
return []string{}
}
func (fn ResourcePrinterFunc) FinishPrint(io.Writer, string) error {
func (fn ResourcePrinterFunc) AfterPrint(io.Writer, string) error {
return nil
}
@ -187,7 +191,7 @@ func NewVersionedPrinter(printer ResourcePrinter, converter runtime.ObjectConver
}
}
func (p *VersionedPrinter) FinishPrint(w io.Writer, res string) error {
func (p *VersionedPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
@ -214,7 +218,7 @@ type NamePrinter struct {
Typer runtime.ObjectTyper
}
func (p *NamePrinter) FinishPrint(w io.Writer, res string) error {
func (p *NamePrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
@ -237,8 +241,6 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
return nil
}
// TODO: this is wrong, runtime.Unknown and runtime.Unstructured are not handled properly here.
name := "<unknown>"
if acc, err := meta.Accessor(obj); err == nil {
if n := acc.GetName(); len(n) > 0 {
@ -246,12 +248,22 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
}
}
if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil {
// TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper
_, resource := meta.KindToResource(gvks[0])
fmt.Fprintf(w, "%s/%s\n", resource.Resource, name)
if kind := obj.GetObjectKind().GroupVersionKind(); len(kind.Kind) == 0 {
// this is the old code. It's unnecessary on decoded external objects, but on internal objects
// you may have to do it. Tests are definitely calling it with internals and I'm not sure who else
// is
if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil {
// TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper
_, resource := meta.KindToResource(gvks[0])
fmt.Fprintf(w, "%s/%s\n", resource.Resource, name)
} else {
fmt.Fprintf(w, "<unknown>/%s\n", name)
}
} else {
fmt.Fprintf(w, "<unknown>/%s\n", name)
// TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper
_, resource := meta.KindToResource(kind)
fmt.Fprintf(w, "%s/%s\n", resource.Resource, name)
}
return nil
@ -266,7 +278,7 @@ func (p *NamePrinter) HandledResources() []string {
type JSONPrinter struct {
}
func (p *JSONPrinter) FinishPrint(w io.Writer, res string) error {
func (p *JSONPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
@ -274,7 +286,13 @@ func (p *JSONPrinter) FinishPrint(w io.Writer, res string) error {
func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
switch obj := obj.(type) {
case *runtime.Unknown:
_, err := w.Write(obj.Raw)
var buf bytes.Buffer
err := json.Indent(&buf, obj.Raw, "", " ")
if err != nil {
return err
}
buf.WriteRune('\n')
_, err = buf.WriteTo(w)
return err
}
@ -300,7 +318,7 @@ type YAMLPrinter struct {
converter runtime.ObjectConvertor
}
func (p *YAMLPrinter) FinishPrint(w io.Writer, res string) error {
func (p *YAMLPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
@ -447,62 +465,55 @@ func (h *HumanReadablePrinter) HandledResources() []string {
return keys
}
func (h *HumanReadablePrinter) FinishPrint(output io.Writer, res string) error {
if !h.options.NoHeaders && !h.options.ShowAll && h.hiddenObjNum > 0 {
_, err := fmt.Fprintf(output, " info: %d completed object(s) was(were) not shown in %s list. Pass --show-all to see all objects.\n\n", h.hiddenObjNum, res)
return err
}
func (h *HumanReadablePrinter) AfterPrint(output io.Writer, res string) error {
return nil
}
// NOTE: When adding a new resource type here, please update the list
// pkg/kubectl/cmd/get.go to reflect the new resource type.
var podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"}
var podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"}
var replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
var replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
var jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"}
var scheduledJobColumns = []string{"NAME", "SCHEDULE", "SUSPEND", "ACTIVE", "LAST-SCHEDULE"}
var serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "AGE"}
var ingressColumns = []string{"NAME", "HOSTS", "ADDRESS", "PORTS", "AGE"}
var petSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
var endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"}
var nodeColumns = []string{"NAME", "STATUS", "AGE"}
var daemonSetColumns = []string{"NAME", "DESIRED", "CURRENT", "NODE-SELECTOR", "AGE"}
var eventColumns = []string{"LASTSEEN", "FIRSTSEEN", "COUNT", "NAME", "KIND", "SUBOBJECT", "TYPE", "REASON", "SOURCE", "MESSAGE"}
var limitRangeColumns = []string{"NAME", "AGE"}
var resourceQuotaColumns = []string{"NAME", "AGE"}
var namespaceColumns = []string{"NAME", "STATUS", "AGE"}
var secretColumns = []string{"NAME", "TYPE", "DATA", "AGE"}
var serviceAccountColumns = []string{"NAME", "SECRETS", "AGE"}
var persistentVolumeColumns = []string{"NAME", "CAPACITY", "ACCESSMODES", "RECLAIMPOLICY", "STATUS", "CLAIM", "REASON", "AGE"}
var persistentVolumeClaimColumns = []string{"NAME", "STATUS", "VOLUME", "CAPACITY", "ACCESSMODES", "AGE"}
var componentStatusColumns = []string{"NAME", "STATUS", "MESSAGE", "ERROR"}
var thirdPartyResourceColumns = []string{"NAME", "DESCRIPTION", "VERSION(S)"}
var roleColumns = []string{"NAME", "AGE"}
var roleBindingColumns = []string{"NAME", "AGE"}
var clusterRoleColumns = []string{"NAME", "AGE"}
var clusterRoleBindingColumns = []string{"NAME", "AGE"}
var storageClassColumns = []string{"NAME", "TYPE"}
var (
podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"}
podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"}
replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"}
cronJobColumns = []string{"NAME", "SCHEDULE", "SUSPEND", "ACTIVE", "LAST-SCHEDULE"}
serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "AGE"}
ingressColumns = []string{"NAME", "HOSTS", "ADDRESS", "PORTS", "AGE"}
statefulSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"}
nodeColumns = []string{"NAME", "STATUS", "AGE"}
daemonSetColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "NODE-SELECTOR", "AGE"}
eventColumns = []string{"LASTSEEN", "FIRSTSEEN", "COUNT", "NAME", "KIND", "SUBOBJECT", "TYPE", "REASON", "SOURCE", "MESSAGE"}
limitRangeColumns = []string{"NAME", "AGE"}
resourceQuotaColumns = []string{"NAME", "AGE"}
namespaceColumns = []string{"NAME", "STATUS", "AGE"}
secretColumns = []string{"NAME", "TYPE", "DATA", "AGE"}
serviceAccountColumns = []string{"NAME", "SECRETS", "AGE"}
persistentVolumeColumns = []string{"NAME", "CAPACITY", "ACCESSMODES", "RECLAIMPOLICY", "STATUS", "CLAIM", "REASON", "AGE"}
persistentVolumeClaimColumns = []string{"NAME", "STATUS", "VOLUME", "CAPACITY", "ACCESSMODES", "AGE"}
componentStatusColumns = []string{"NAME", "STATUS", "MESSAGE", "ERROR"}
thirdPartyResourceColumns = []string{"NAME", "DESCRIPTION", "VERSION(S)"}
roleColumns = []string{"NAME", "AGE"}
roleBindingColumns = []string{"NAME", "AGE"}
clusterRoleColumns = []string{"NAME", "AGE"}
clusterRoleBindingColumns = []string{"NAME", "AGE"}
storageClassColumns = []string{"NAME", "TYPE"}
statusColumns = []string{"STATUS", "REASON", "MESSAGE"}
// TODO: consider having 'KIND' for third party resource data
var thirdPartyResourceDataColumns = []string{"NAME", "LABELS", "DATA"}
var horizontalPodAutoscalerColumns = []string{"NAME", "REFERENCE", "TARGET", "CURRENT", "MINPODS", "MAXPODS", "AGE"}
var withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print cluster name too.
var deploymentColumns = []string{"NAME", "DESIRED", "CURRENT", "UP-TO-DATE", "AVAILABLE", "AGE"}
var configMapColumns = []string{"NAME", "DATA", "AGE"}
var podSecurityPolicyColumns = []string{"NAME", "PRIV", "CAPS", "VOLUMEPLUGINS", "SELINUX", "RUNASUSER"}
var clusterColumns = []string{"NAME", "STATUS", "AGE"}
var networkPolicyColumns = []string{"NAME", "POD-SELECTOR", "AGE"}
var certificateSigningRequestColumns = []string{"NAME", "AGE", "REQUESTOR", "CONDITION"}
// TODO: consider having 'KIND' for third party resource data
thirdPartyResourceDataColumns = []string{"NAME", "LABELS", "DATA"}
horizontalPodAutoscalerColumns = []string{"NAME", "REFERENCE", "TARGET", "CURRENT", "MINPODS", "MAXPODS", "AGE"}
withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print cluster name too.
deploymentColumns = []string{"NAME", "DESIRED", "CURRENT", "UP-TO-DATE", "AVAILABLE", "AGE"}
configMapColumns = []string{"NAME", "DATA", "AGE"}
podSecurityPolicyColumns = []string{"NAME", "PRIV", "CAPS", "VOLUMEPLUGINS", "SELINUX", "RUNASUSER"}
clusterColumns = []string{"NAME", "STATUS", "AGE"}
networkPolicyColumns = []string{"NAME", "POD-SELECTOR", "AGE"}
certificateSigningRequestColumns = []string{"NAME", "AGE", "REQUESTOR", "CONDITION"}
)
func (h *HumanReadablePrinter) printPod(pod *api.Pod, w io.Writer, options PrintOptions) error {
reason := string(pod.Status.Phase)
// if not printing all pods, skip terminated pods (default)
if !options.ShowAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed)) {
h.hiddenObjNum++
return nil
}
if err := printPodBase(pod, w, options); err != nil {
return err
}
@ -512,13 +523,6 @@ func (h *HumanReadablePrinter) printPod(pod *api.Pod, w io.Writer, options Print
func (h *HumanReadablePrinter) printPodList(podList *api.PodList, w io.Writer, options PrintOptions) error {
for _, pod := range podList.Items {
reason := string(pod.Status.Phase)
// if not printing all pods, skip terminated pods (default)
if !options.ShowAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed)) {
h.hiddenObjNum++
continue
}
if err := printPodBase(&pod, w, options); err != nil {
return err
}
@ -540,14 +544,14 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(daemonSetColumns, printDaemonSetList)
h.Handler(jobColumns, printJob)
h.Handler(jobColumns, printJobList)
h.Handler(scheduledJobColumns, printScheduledJob)
h.Handler(scheduledJobColumns, printScheduledJobList)
h.Handler(cronJobColumns, printCronJob)
h.Handler(cronJobColumns, printCronJobList)
h.Handler(serviceColumns, printService)
h.Handler(serviceColumns, printServiceList)
h.Handler(ingressColumns, printIngress)
h.Handler(ingressColumns, printIngressList)
h.Handler(petSetColumns, printPetSet)
h.Handler(petSetColumns, printPetSetList)
h.Handler(statefulSetColumns, printStatefulSet)
h.Handler(statefulSetColumns, printStatefulSetList)
h.Handler(endpointColumns, printEndpoints)
h.Handler(endpointColumns, printEndpointsList)
h.Handler(nodeColumns, printNode)
@ -598,6 +602,7 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(certificateSigningRequestColumns, printCertificateSigningRequestList)
h.Handler(storageClassColumns, printStorageClass)
h.Handler(storageClassColumns, printStorageClassList)
h.Handler(statusColumns, printStatus)
}
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
@ -736,7 +741,10 @@ func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error {
}
}
}
if pod.DeletionTimestamp != nil {
if pod.DeletionTimestamp != nil && pod.Status.Reason == node.NodeUnreachablePodReason {
reason = "Unknown"
} else if pod.DeletionTimestamp != nil {
reason = "Terminating"
}
@ -1020,9 +1028,9 @@ func printJobList(list *batch.JobList, w io.Writer, options PrintOptions) error
return nil
}
func printScheduledJob(scheduledJob *batch.ScheduledJob, w io.Writer, options PrintOptions) error {
name := scheduledJob.Name
namespace := scheduledJob.Namespace
func printCronJob(cronJob *batch.CronJob, w io.Writer, options PrintOptions) error {
name := cronJob.Name
namespace := cronJob.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1031,14 +1039,14 @@ func printScheduledJob(scheduledJob *batch.ScheduledJob, w io.Writer, options Pr
}
lastScheduleTime := "<none>"
if scheduledJob.Status.LastScheduleTime != nil {
lastScheduleTime = scheduledJob.Status.LastScheduleTime.Time.Format(time.RFC1123Z)
if cronJob.Status.LastScheduleTime != nil {
lastScheduleTime = cronJob.Status.LastScheduleTime.Time.Format(time.RFC1123Z)
}
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%s\n",
name,
scheduledJob.Spec.Schedule,
printBoolPtr(scheduledJob.Spec.Suspend),
len(scheduledJob.Status.Active),
cronJob.Spec.Schedule,
printBoolPtr(cronJob.Spec.Suspend),
len(cronJob.Status.Active),
lastScheduleTime,
); err != nil {
return err
@ -1047,9 +1055,9 @@ func printScheduledJob(scheduledJob *batch.ScheduledJob, w io.Writer, options Pr
return nil
}
func printScheduledJobList(list *batch.ScheduledJobList, w io.Writer, options PrintOptions) error {
for _, scheduledJob := range list.Items {
if err := printScheduledJob(&scheduledJob, w, options); err != nil {
func printCronJobList(list *batch.CronJobList, w io.Writer, options PrintOptions) error {
for _, cronJob := range list.Items {
if err := printCronJob(&cronJob, w, options); err != nil {
return err
}
}
@ -1108,6 +1116,9 @@ func makePortString(ports []api.ServicePort) string {
for ix := range ports {
port := &ports[ix]
pieces[ix] = fmt.Sprintf("%d/%s", port.Port, port.Protocol)
if port.NodePort > 0 {
pieces[ix] = fmt.Sprintf("%d:%d/%s", port.Port, port.NodePort, port.Protocol)
}
}
return strings.Join(pieces, ",")
}
@ -1231,7 +1242,7 @@ func printIngressList(ingressList *extensions.IngressList, w io.Writer, options
return nil
}
func printPetSet(ps *apps.PetSet, w io.Writer, options PrintOptions) error {
func printStatefulSet(ps *apps.StatefulSet, w io.Writer, options PrintOptions) error {
name := formatResourceName(options.Kind, ps.Name, options.WithKind)
namespace := ps.Namespace
@ -1270,9 +1281,9 @@ func printPetSet(ps *apps.PetSet, w io.Writer, options PrintOptions) error {
return nil
}
func printPetSetList(petSetList *apps.PetSetList, w io.Writer, options PrintOptions) error {
for _, ps := range petSetList.Items {
if err := printPetSet(&ps, w, options); err != nil {
func printStatefulSetList(statefulSetList *apps.StatefulSetList, w io.Writer, options PrintOptions) error {
for _, ps := range statefulSetList.Items {
if err := printStatefulSet(&ps, w, options); err != nil {
return err
}
}
@ -1294,15 +1305,17 @@ func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options PrintOptions)
desiredScheduled := ds.Status.DesiredNumberScheduled
currentScheduled := ds.Status.CurrentNumberScheduled
numberReady := ds.Status.NumberReady
selector, err := unversioned.LabelSelectorAsSelector(ds.Spec.Selector)
if err != nil {
// this shouldn't happen if LabelSelector passed validation
return err
}
if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%s\t%s",
if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%d\t%s\t%s",
name,
desiredScheduled,
currentScheduled,
numberReady,
labels.FormatLabels(ds.Spec.Template.Spec.NodeSelector),
translateTimestamp(ds.CreationTimestamp),
); err != nil {
@ -1482,6 +1495,12 @@ func printNode(node *api.Node, w io.Writer, options PrintOptions) error {
if _, err := fmt.Fprintf(w, "%s\t%s\t%s", name, strings.Join(status, ","), translateTimestamp(node.CreationTimestamp)); err != nil {
return err
}
if options.Wide {
if _, err := fmt.Fprintf(w, "\t%s", getNodeExternalIP(node)); err != nil {
return err
}
}
// Display caller specify column labels first.
if _, err := fmt.Fprint(w, AppendLabels(node.Labels, options.ColumnLabels)); err != nil {
return err
@ -1490,6 +1509,17 @@ func printNode(node *api.Node, w io.Writer, options PrintOptions) error {
return err
}
// Returns first external ip of the node or "<none>" if none is found.
func getNodeExternalIP(node *api.Node) string {
for _, address := range node.Status.Addresses {
if address.Type == api.NodeExternalIP {
return address.Address
}
}
return "<none>"
}
func printNodeList(list *api.NodeList, w io.Writer, options PrintOptions) error {
for _, node := range list.Items {
if err := printNode(&node, w, options); err != nil {
@ -1629,7 +1659,7 @@ func printEvent(event *api.Event, w io.Writer, options PrintOptions) error {
// Sorts and prints the EventList in a human-friendly format.
func printEventList(list *api.EventList, w io.Writer, options PrintOptions) error {
sort.Sort(SortableEvents(list.Items))
sort.Sort(events.SortableEvents(list.Items))
for i := range list.Items {
if err := printEvent(&list.Items[i], w, options); err != nil {
return err
@ -2071,6 +2101,10 @@ func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, opt
func printStorageClass(sc *storage.StorageClass, w io.Writer, options PrintOptions) error {
name := sc.Name
if storageutil.IsDefaultAnnotation(sc.ObjectMeta) {
name += " (default)"
}
provtype := sc.Provisioner
if _, err := fmt.Fprintf(w, "%s\t%s\t", name, provtype); err != nil {
@ -2095,6 +2129,14 @@ func printStorageClassList(scList *storage.StorageClassList, w io.Writer, option
return nil
}
func printStatus(status *unversioned.Status, w io.Writer, options PrintOptions) error {
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\n", status.Status, status.Reason, status.Message); err != nil {
return err
}
return nil
}
func AppendLabels(itemLabels map[string]string, columnLabels []string) string {
var buffer bytes.Buffer
@ -2187,6 +2229,9 @@ func formatWideHeaders(wide bool, t reflect.Type) []string {
if t.String() == "*extensions.ReplicaSet" || t.String() == "*extensions.ReplicaSetList" {
return []string{"CONTAINER(S)", "IMAGE(S)", "SELECTOR"}
}
if t.String() == "*api.Node" || t.String() == "*api.NodeList" {
return []string{"EXTERNAL-IP"}
}
}
return nil
}
@ -2214,6 +2259,18 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
w = GetNewTabWriter(output)
defer w.Flush()
}
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand before
// trying to print, since the printers are keyed by type. This is extremely expensive.
switch obj.(type) {
case *runtime.Unstructured, *runtime.Unknown:
if objBytes, err := runtime.Encode(api.Codecs.LegacyCodec(), obj); err == nil {
if decodedObj, err := runtime.Decode(api.Codecs.UniversalDecoder(), objBytes); err == nil {
obj = decodedObj
}
}
}
t := reflect.TypeOf(obj)
if handler := h.handlerMap[t]; handler != nil {
if !h.options.NoHeaders && t != h.lastType {
@ -2234,9 +2291,78 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
}
return resultValue.Interface().(error)
}
// we don't recognize this type, but we can still attempt to print some reasonable information about.
unstructured, ok := obj.(*runtime.Unstructured)
if !ok {
return fmt.Errorf("error: unknown type %#v", obj)
}
if _, err := meta.Accessor(obj); err == nil {
if !h.options.NoHeaders && t != h.lastType {
headers := []string{"NAME", "KIND"}
headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...)
// LABELS is always the last column.
headers = append(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...)
if h.options.WithNamespace {
headers = append(withNamespacePrefixColumns, headers...)
}
h.printHeader(headers, w)
h.lastType = t
}
// if the error isn't nil, report the "I don't recognize this" error
if err := printUnstructured(unstructured, w, h.options); err != nil {
return err
}
return nil
}
// we failed all reasonable printing efforts, report failure
return fmt.Errorf("error: unknown type %#v", obj)
}
func printUnstructured(unstructured *runtime.Unstructured, w io.Writer, options PrintOptions) error {
metadata, err := meta.Accessor(unstructured)
if err != nil {
return err
}
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", metadata.GetNamespace()); err != nil {
return err
}
}
kind := "<missing>"
if objKind, ok := unstructured.Object["kind"]; ok {
if str, ok := objKind.(string); ok {
kind = str
}
}
if objAPIVersion, ok := unstructured.Object["apiVersion"]; ok {
if str, ok := objAPIVersion.(string); ok {
version, err := unversioned.ParseGroupVersion(str)
if err != nil {
return err
}
kind = kind + "." + version.Version + "." + version.Group
}
}
name := formatResourceName(options.Kind, metadata.GetName(), options.WithKind)
if _, err := fmt.Fprintf(w, "%s\t%s", name, kind); err != nil {
return err
}
if _, err := fmt.Fprint(w, AppendLabels(metadata.GetLabels(), options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, metadata.GetLabels())); err != nil {
return err
}
return nil
}
// TemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template.
type TemplatePrinter struct {
rawTemplate string
@ -2256,16 +2382,24 @@ func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
}, nil
}
func (p *TemplatePrinter) FinishPrint(w io.Writer, res string) error {
func (p *TemplatePrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
// PrintObj formats the obj with the Go Template.
func (p *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
data, err := json.Marshal(obj)
var data []byte
var err error
if unstructured, ok := obj.(*runtime.Unstructured); ok {
data, err = json.Marshal(unstructured.Object)
} else {
data, err = json.Marshal(obj)
}
if err != nil {
return err
}
out := map[string]interface{}{}
if err := json.Unmarshal(data, &out); err != nil {
return err
@ -2407,7 +2541,7 @@ func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
return &JSONPathPrinter{tmpl, j}, nil
}
func (j *JSONPathPrinter) FinishPrint(w io.Writer, res string) error {
func (j *JSONPathPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
@ -2425,6 +2559,20 @@ func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
}
}
if unknown, ok := obj.(*runtime.Unknown); ok {
data, err := json.Marshal(unknown)
if err != nil {
return err
}
queryObj = map[string]interface{}{}
if err := json.Unmarshal(data, &queryObj); err != nil {
return err
}
}
if unstructured, ok := obj.(*runtime.Unstructured); ok {
queryObj = unstructured.Object
}
if err := j.JSONPath.Execute(w, queryObj); err != nil {
fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err)
fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", j.rawTemplate)

View file

@ -17,6 +17,7 @@ limitations under the License.
package kubectl
import (
"bytes"
"fmt"
"os"
"os/signal"
@ -25,18 +26,19 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/pkg/runtime"
sliceutil "k8s.io/kubernetes/pkg/util/slice"
"k8s.io/kubernetes/pkg/watch"
)
// Rollbacker provides an interface for resources that can be rolled back.
type Rollbacker interface {
Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64) (string, error)
Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error)
}
func RollbackerFor(kind unversioned.GroupKind, c client.Interface) (Rollbacker, error) {
func RollbackerFor(kind unversioned.GroupKind, c clientset.Interface) (Rollbacker, error) {
switch kind {
case extensions.Kind("Deployment"):
return &DeploymentRollbacker{c}, nil
@ -45,14 +47,17 @@ func RollbackerFor(kind unversioned.GroupKind, c client.Interface) (Rollbacker,
}
type DeploymentRollbacker struct {
c client.Interface
c clientset.Interface
}
func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64) (string, error) {
func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations map[string]string, toRevision int64, dryRun bool) (string, error) {
d, ok := obj.(*extensions.Deployment)
if !ok {
return "", fmt.Errorf("passed object is not a Deployment: %#v", obj)
}
if dryRun {
return simpleDryRun(d, r.c, toRevision)
}
if d.Spec.Paused {
return "", fmt.Errorf("you cannot rollback a paused deployment; resume it first with 'kubectl rollout resume deployment/%s' and try again", d.Name)
}
@ -66,7 +71,7 @@ func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations m
result := ""
// Get current events
events, err := r.c.Events(d.Namespace).List(api.ListOptions{})
events, err := r.c.Core().Events(d.Namespace).List(api.ListOptions{})
if err != nil {
return result, err
}
@ -75,7 +80,7 @@ func (r *DeploymentRollbacker) Rollback(obj runtime.Object, updatedAnnotations m
return result, err
}
// Watch for the changes of events
watch, err := r.c.Events(d.Namespace).Watch(api.ListOptions{Watch: true, ResourceVersion: events.ResourceVersion})
watch, err := r.c.Core().Events(d.Namespace).Watch(api.ListOptions{Watch: true, ResourceVersion: events.ResourceVersion})
if err != nil {
return result, err
}
@ -123,3 +128,50 @@ func isRollbackEvent(e *api.Event) (bool, string) {
}
return false, ""
}
func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRevision int64) (string, error) {
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, c)
if err != nil {
return "", fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", deployment.Name, err)
}
allRSs := allOldRSs
if newRS != nil {
allRSs = append(allRSs, newRS)
}
revisionToSpec := make(map[int64]*api.PodTemplateSpec)
for _, rs := range allRSs {
v, err := deploymentutil.Revision(rs)
if err != nil {
continue
}
revisionToSpec[v] = &rs.Spec.Template
}
if len(revisionToSpec) == 0 {
return "No rollout history found.", nil
}
if toRevision > 0 {
template, ok := revisionToSpec[toRevision]
if !ok {
return "", fmt.Errorf("unable to find specified revision")
}
buf := bytes.NewBuffer([]byte{})
DescribePodTemplate(template, buf)
return buf.String(), nil
}
// Sort the revisionToSpec map by revision
revisions := make([]int64, 0, len(revisionToSpec))
for r := range revisionToSpec {
revisions = append(revisions, r)
}
sliceutil.SortInts64(revisions)
template, _ := revisionToSpec[revisions[len(revisions)-1]]
buf := bytes.NewBuffer([]byte{})
buf.WriteString("\n")
DescribePodTemplate(template, buf)
return buf.String(), nil
}

View file

@ -27,6 +27,8 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/client/retry"
client "k8s.io/kubernetes/pkg/client/unversioned"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/pkg/labels"
@ -109,8 +111,8 @@ const (
// RollingUpdater provides methods for updating replicated pods in a predictable,
// fault-tolerant way.
type RollingUpdater struct {
// Client interface for creating and updating controllers
c client.Interface
rcClient coreclient.ReplicationControllersGetter
podClient coreclient.PodsGetter
// Namespace for resources
ns string
// scaleAndWait scales a controller and returns its updated state.
@ -127,10 +129,11 @@ type RollingUpdater struct {
}
// NewRollingUpdater creates a RollingUpdater from a client.
func NewRollingUpdater(namespace string, client client.Interface) *RollingUpdater {
func NewRollingUpdater(namespace string, rcClient coreclient.ReplicationControllersGetter, podClient coreclient.PodsGetter) *RollingUpdater {
updater := &RollingUpdater{
c: client,
ns: namespace,
rcClient: rcClient,
podClient: podClient,
ns: namespace,
}
// Inject real implementations.
updater.scaleAndWait = updater.scaleAndWaitWithScaler
@ -189,7 +192,7 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
// annotation if it doesn't yet exist.
_, hasOriginalAnnotation := oldRc.Annotations[originalReplicasAnnotation]
if !hasOriginalAnnotation {
existing, err := r.c.ReplicationControllers(oldRc.Namespace).Get(oldRc.Name)
existing, err := r.rcClient.ReplicationControllers(oldRc.Namespace).Get(oldRc.Name)
if err != nil {
return err
}
@ -200,7 +203,7 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
}
rc.Annotations[originalReplicasAnnotation] = originReplicas
}
if oldRc, err = updateRcWithRetries(r.c, existing.Namespace, existing, applyUpdate); err != nil {
if oldRc, err = updateRcWithRetries(r.rcClient, existing.Namespace, existing, applyUpdate); err != nil {
return err
}
}
@ -390,14 +393,11 @@ func (r *RollingUpdater) scaleDown(newRc, oldRc *api.ReplicationController, desi
// scalerScaleAndWait scales a controller using a Scaler and a real client.
func (r *RollingUpdater) scaleAndWaitWithScaler(rc *api.ReplicationController, retry *RetryParams, wait *RetryParams) (*api.ReplicationController, error) {
scaler, err := ScalerFor(api.Kind("ReplicationController"), r.c)
if err != nil {
return nil, fmt.Errorf("Couldn't make scaler: %s", err)
}
scaler := &ReplicationControllerScaler{r.rcClient}
if err := scaler.Scale(rc.Namespace, rc.Name, uint(rc.Spec.Replicas), &ScalePrecondition{-1, ""}, retry, wait); err != nil {
return nil, err
}
return r.c.ReplicationControllers(rc.Namespace).Get(rc.Name)
return r.rcClient.ReplicationControllers(rc.Namespace).Get(rc.Name)
}
// readyPods returns the old and new ready counts for their pods.
@ -415,7 +415,7 @@ func (r *RollingUpdater) readyPods(oldRc, newRc *api.ReplicationController, minR
controller := controllers[i]
selector := labels.Set(controller.Spec.Selector).AsSelector()
options := api.ListOptions{LabelSelector: selector}
pods, err := r.c.Pods(controller.Namespace).List(options)
pods, err := r.podClient.Pods(controller.Namespace).List(options)
if err != nil {
return 0, 0, err
}
@ -460,7 +460,7 @@ func (r *RollingUpdater) getOrCreateTargetControllerWithClient(controller *api.R
controller.Annotations[desiredReplicasAnnotation] = fmt.Sprintf("%d", controller.Spec.Replicas)
controller.Annotations[sourceIdAnnotation] = sourceId
controller.Spec.Replicas = 0
newRc, err := r.c.ReplicationControllers(r.ns).Create(controller)
newRc, err := r.rcClient.ReplicationControllers(r.ns).Create(controller)
return newRc, false, err
}
// Validate and use the existing controller.
@ -480,7 +480,7 @@ func (r *RollingUpdater) existingController(controller *api.ReplicationControlle
return nil, errors.NewNotFound(api.Resource("replicationcontrollers"), controller.Name)
}
// controller name is required to get rc back
return r.c.ReplicationControllers(controller.Namespace).Get(controller.Name)
return r.rcClient.ReplicationControllers(controller.Namespace).Get(controller.Name)
}
// cleanupWithClients performs cleanup tasks after the rolling update. Update
@ -489,7 +489,7 @@ func (r *RollingUpdater) existingController(controller *api.ReplicationControlle
func (r *RollingUpdater) cleanupWithClients(oldRc, newRc *api.ReplicationController, config *RollingUpdaterConfig) error {
// Clean up annotations
var err error
newRc, err = r.c.ReplicationControllers(r.ns).Get(newRc.Name)
newRc, err = r.rcClient.ReplicationControllers(r.ns).Get(newRc.Name)
if err != nil {
return err
}
@ -497,14 +497,14 @@ func (r *RollingUpdater) cleanupWithClients(oldRc, newRc *api.ReplicationControl
delete(rc.Annotations, sourceIdAnnotation)
delete(rc.Annotations, desiredReplicasAnnotation)
}
if newRc, err = updateRcWithRetries(r.c, r.ns, newRc, applyUpdate); err != nil {
if newRc, err = updateRcWithRetries(r.rcClient, r.ns, newRc, applyUpdate); err != nil {
return err
}
if err = wait.Poll(config.Interval, config.Timeout, client.ControllerHasDesiredReplicas(r.c, newRc)); err != nil {
if err = wait.Poll(config.Interval, config.Timeout, client.ControllerHasDesiredReplicas(r.rcClient, newRc)); err != nil {
return err
}
newRc, err = r.c.ReplicationControllers(r.ns).Get(newRc.Name)
newRc, err = r.rcClient.ReplicationControllers(r.ns).Get(newRc.Name)
if err != nil {
return err
}
@ -513,15 +513,15 @@ func (r *RollingUpdater) cleanupWithClients(oldRc, newRc *api.ReplicationControl
case DeleteRollingUpdateCleanupPolicy:
// delete old rc
fmt.Fprintf(config.Out, "Update succeeded. Deleting %s\n", oldRc.Name)
return r.c.ReplicationControllers(r.ns).Delete(oldRc.Name, nil)
return r.rcClient.ReplicationControllers(r.ns).Delete(oldRc.Name, nil)
case RenameRollingUpdateCleanupPolicy:
// delete old rc
fmt.Fprintf(config.Out, "Update succeeded. Deleting old controller: %s\n", oldRc.Name)
if err := r.c.ReplicationControllers(r.ns).Delete(oldRc.Name, nil); err != nil {
if err := r.rcClient.ReplicationControllers(r.ns).Delete(oldRc.Name, nil); err != nil {
return err
}
fmt.Fprintf(config.Out, "Renaming %s to %s\n", oldRc.Name, newRc.Name)
return Rename(r.c, newRc, oldRc.Name)
return Rename(r.rcClient, newRc, oldRc.Name)
case PreserveRollingUpdateCleanupPolicy:
return nil
default:
@ -529,7 +529,7 @@ func (r *RollingUpdater) cleanupWithClients(oldRc, newRc *api.ReplicationControl
}
}
func Rename(c client.ReplicationControllersNamespacer, rc *api.ReplicationController, newName string) error {
func Rename(c coreclient.ReplicationControllersGetter, rc *api.ReplicationController, newName string) error {
oldName := rc.Name
rc.Name = newName
rc.ResourceVersion = ""
@ -560,7 +560,7 @@ func Rename(c client.ReplicationControllersNamespacer, rc *api.ReplicationContro
return nil
}
func LoadExistingNextReplicationController(c client.ReplicationControllersNamespacer, namespace, newName string) (*api.ReplicationController, error) {
func LoadExistingNextReplicationController(c coreclient.ReplicationControllersGetter, namespace, newName string) (*api.ReplicationController, error) {
if len(newName) == 0 {
return nil, nil
}
@ -580,10 +580,10 @@ type NewControllerConfig struct {
PullPolicy api.PullPolicy
}
func CreateNewControllerFromCurrentController(c client.Interface, codec runtime.Codec, cfg *NewControllerConfig) (*api.ReplicationController, error) {
func CreateNewControllerFromCurrentController(rcClient coreclient.ReplicationControllersGetter, codec runtime.Codec, cfg *NewControllerConfig) (*api.ReplicationController, error) {
containerIndex := 0
// load the old RC into the "new" RC
newRc, err := c.ReplicationControllers(cfg.Namespace).Get(cfg.OldName)
newRc, err := rcClient.ReplicationControllers(cfg.Namespace).Get(cfg.OldName)
if err != nil {
return nil, err
}
@ -669,21 +669,21 @@ func SetNextControllerAnnotation(rc *api.ReplicationController, name string) {
rc.Annotations[nextControllerAnnotation] = name
}
func UpdateExistingReplicationController(c client.Interface, oldRc *api.ReplicationController, namespace, newName, deploymentKey, deploymentValue string, out io.Writer) (*api.ReplicationController, error) {
func UpdateExistingReplicationController(rcClient coreclient.ReplicationControllersGetter, podClient coreclient.PodsGetter, oldRc *api.ReplicationController, namespace, newName, deploymentKey, deploymentValue string, out io.Writer) (*api.ReplicationController, error) {
if _, found := oldRc.Spec.Selector[deploymentKey]; !found {
SetNextControllerAnnotation(oldRc, newName)
return AddDeploymentKeyToReplicationController(oldRc, c, deploymentKey, deploymentValue, namespace, out)
return AddDeploymentKeyToReplicationController(oldRc, rcClient, podClient, deploymentKey, deploymentValue, namespace, out)
} else {
// If we didn't need to update the controller for the deployment key, we still need to write
// the "next" controller.
applyUpdate := func(rc *api.ReplicationController) {
SetNextControllerAnnotation(rc, newName)
}
return updateRcWithRetries(c, namespace, oldRc, applyUpdate)
return updateRcWithRetries(rcClient, namespace, oldRc, applyUpdate)
}
}
func AddDeploymentKeyToReplicationController(oldRc *api.ReplicationController, client client.Interface, deploymentKey, deploymentValue, namespace string, out io.Writer) (*api.ReplicationController, error) {
func AddDeploymentKeyToReplicationController(oldRc *api.ReplicationController, rcClient coreclient.ReplicationControllersGetter, podClient coreclient.PodsGetter, deploymentKey, deploymentValue, namespace string, out io.Writer) (*api.ReplicationController, error) {
var err error
// First, update the template label. This ensures that any newly created pods will have the new label
applyUpdate := func(rc *api.ReplicationController) {
@ -692,7 +692,7 @@ func AddDeploymentKeyToReplicationController(oldRc *api.ReplicationController, c
}
rc.Spec.Template.Labels[deploymentKey] = deploymentValue
}
if oldRc, err = updateRcWithRetries(client, namespace, oldRc, applyUpdate); err != nil {
if oldRc, err = updateRcWithRetries(rcClient, namespace, oldRc, applyUpdate); err != nil {
return nil, err
}
@ -700,7 +700,7 @@ func AddDeploymentKeyToReplicationController(oldRc *api.ReplicationController, c
// TODO: extract the code from the label command and re-use it here.
selector := labels.SelectorFromSet(oldRc.Spec.Selector)
options := api.ListOptions{LabelSelector: selector}
podList, err := client.Pods(namespace).List(options)
podList, err := podClient.Pods(namespace).List(options)
if err != nil {
return nil, err
}
@ -715,7 +715,7 @@ func AddDeploymentKeyToReplicationController(oldRc *api.ReplicationController, c
p.Labels[deploymentKey] = deploymentValue
}
}
if pod, err = updatePodWithRetries(client, namespace, pod, applyUpdate); err != nil {
if pod, err = updatePodWithRetries(podClient, namespace, pod, applyUpdate); err != nil {
return nil, err
}
}
@ -732,7 +732,7 @@ func AddDeploymentKeyToReplicationController(oldRc *api.ReplicationController, c
rc.Spec.Selector[deploymentKey] = deploymentValue
}
// Update the selector of the rc so it manages all the pods we updated above
if oldRc, err = updateRcWithRetries(client, namespace, oldRc, applyUpdate); err != nil {
if oldRc, err = updateRcWithRetries(rcClient, namespace, oldRc, applyUpdate); err != nil {
return nil, err
}
@ -741,11 +741,11 @@ func AddDeploymentKeyToReplicationController(oldRc *api.ReplicationController, c
// we've finished re-adopting existing pods to the rc.
selector = labels.SelectorFromSet(selectorCopy)
options = api.ListOptions{LabelSelector: selector}
podList, err = client.Pods(namespace).List(options)
podList, err = podClient.Pods(namespace).List(options)
for ix := range podList.Items {
pod := &podList.Items[ix]
if value, found := pod.Labels[deploymentKey]; !found || value != deploymentValue {
if err := client.Pods(namespace).Delete(pod.Name, nil); err != nil {
if err := podClient.Pods(namespace).Delete(pod.Name, nil); err != nil {
return nil, err
}
}
@ -760,24 +760,24 @@ type updateRcFunc func(controller *api.ReplicationController)
// 1. Get latest resource
// 2. applyUpdate
// 3. Update the resource
func updateRcWithRetries(c client.Interface, namespace string, rc *api.ReplicationController, applyUpdate updateRcFunc) (*api.ReplicationController, error) {
func updateRcWithRetries(rcClient coreclient.ReplicationControllersGetter, namespace string, rc *api.ReplicationController, applyUpdate updateRcFunc) (*api.ReplicationController, error) {
// Deep copy the rc in case we failed on Get during retry loop
obj, err := api.Scheme.Copy(rc)
if err != nil {
return nil, fmt.Errorf("failed to deep copy rc before updating it: %v", err)
}
oldRc := obj.(*api.ReplicationController)
err = client.RetryOnConflict(client.DefaultBackoff, func() (e error) {
err = retry.RetryOnConflict(retry.DefaultBackoff, func() (e error) {
// Apply the update, then attempt to push it to the apiserver.
applyUpdate(rc)
if rc, e = c.ReplicationControllers(namespace).Update(rc); e == nil {
if rc, e = rcClient.ReplicationControllers(namespace).Update(rc); e == nil {
// rc contains the latest controller post update
return
}
updateErr := e
// Update the controller with the latest resource version, if the update failed we
// can't trust rc so use oldRc.Name.
if rc, e = c.ReplicationControllers(namespace).Get(oldRc.Name); e != nil {
if rc, e = rcClient.ReplicationControllers(namespace).Get(oldRc.Name); e != nil {
// The Get failed: Value in rc cannot be trusted.
rc = oldRc
}
@ -795,21 +795,21 @@ type updatePodFunc func(controller *api.Pod)
// 1. Get latest resource
// 2. applyUpdate
// 3. Update the resource
func updatePodWithRetries(c client.Interface, namespace string, pod *api.Pod, applyUpdate updatePodFunc) (*api.Pod, error) {
func updatePodWithRetries(podClient coreclient.PodsGetter, namespace string, pod *api.Pod, applyUpdate updatePodFunc) (*api.Pod, error) {
// Deep copy the pod in case we failed on Get during retry loop
obj, err := api.Scheme.Copy(pod)
if err != nil {
return nil, fmt.Errorf("failed to deep copy pod before updating it: %v", err)
}
oldPod := obj.(*api.Pod)
err = client.RetryOnConflict(client.DefaultBackoff, func() (e error) {
err = retry.RetryOnConflict(retry.DefaultBackoff, func() (e error) {
// Apply the update, then attempt to push it to the apiserver.
applyUpdate(pod)
if pod, e = c.Pods(namespace).Update(pod); e == nil {
if pod, e = podClient.Pods(namespace).Update(pod); e == nil {
return
}
updateErr := e
if pod, e = c.Pods(namespace).Get(oldPod.Name); e != nil {
if pod, e = podClient.Pods(namespace).Get(oldPod.Name); e != nil {
pod = oldPod
}
// Only return the error from update
@ -820,7 +820,7 @@ func updatePodWithRetries(c client.Interface, namespace string, pod *api.Pod, ap
return pod, err
}
func FindSourceController(r client.ReplicationControllersNamespacer, namespace, name string) (*api.ReplicationController, error) {
func FindSourceController(r coreclient.ReplicationControllersGetter, namespace, name string) (*api.ReplicationController, error) {
list, err := r.ReplicationControllers(namespace).List(api.ListOptions{})
if err != nil {
return nil, err

View file

@ -21,15 +21,17 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
"k8s.io/kubernetes/pkg/controller/deployment/util"
)
// StatusViewer provides an interface for resources that provides rollout status.
// StatusViewer provides an interface for resources that have rollout status.
type StatusViewer interface {
Status(namespace, name string) (string, bool, error)
Status(namespace, name string, revision int64) (string, bool, error)
}
func StatusViewerFor(kind unversioned.GroupKind, c client.Interface) (StatusViewer, error) {
func StatusViewerFor(kind unversioned.GroupKind, c internalclientset.Interface) (StatusViewer, error) {
switch kind {
case extensions.Kind("Deployment"):
return &DeploymentStatusViewer{c.Extensions()}, nil
@ -38,20 +40,39 @@ func StatusViewerFor(kind unversioned.GroupKind, c client.Interface) (StatusView
}
type DeploymentStatusViewer struct {
c client.ExtensionsInterface
c extensionsclient.DeploymentsGetter
}
// Status returns a message describing deployment status, and a bool value indicating if the status is considered done
func (s *DeploymentStatusViewer) Status(namespace, name string) (string, bool, error) {
func (s *DeploymentStatusViewer) Status(namespace, name string, revision int64) (string, bool, error) {
deployment, err := s.c.Deployments(namespace).Get(name)
if err != nil {
return "", false, err
}
if deployment.Generation <= deployment.Status.ObservedGeneration {
if deployment.Status.UpdatedReplicas == deployment.Spec.Replicas {
return fmt.Sprintf("deployment %s successfully rolled out\n", name), true, nil
if revision > 0 {
deploymentRev, err := util.Revision(deployment)
if err != nil {
return "", false, fmt.Errorf("cannot get the revision of deployment %q: %v", deployment.Name, err)
}
return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...\n", deployment.Status.UpdatedReplicas, deployment.Spec.Replicas), false, nil
if revision != deploymentRev {
return "", false, fmt.Errorf("desired revision (%d) is different from the running revision (%d)", revision, deploymentRev)
}
}
if deployment.Generation <= deployment.Status.ObservedGeneration {
cond := util.GetDeploymentCondition(deployment.Status, extensions.DeploymentProgressing)
if cond != nil && cond.Reason == util.TimedOutReason {
return "", false, fmt.Errorf("deployment %q exceeded its progress deadline", name)
}
if deployment.Status.UpdatedReplicas < deployment.Spec.Replicas {
return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...\n", deployment.Status.UpdatedReplicas, deployment.Spec.Replicas), false, nil
}
if deployment.Status.Replicas > deployment.Status.UpdatedReplicas {
return fmt.Sprintf("Waiting for rollout to finish: %d old replicas are pending termination...\n", deployment.Status.Replicas-deployment.Status.UpdatedReplicas), false, nil
}
if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas {
return fmt.Sprintf("Waiting for rollout to finish: %d of %d updated replicas are available...\n", deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas), false, nil
}
return fmt.Sprintf("deployment %q successfully rolled out\n", name), true, nil
}
return fmt.Sprintf("Waiting for deployment spec update to be observed...\n"), false, nil
}

View file

@ -401,9 +401,9 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err
return &job, nil
}
type ScheduledJobV2Alpha1 struct{}
type CronJobV2Alpha1 struct{}
func (ScheduledJobV2Alpha1) ParamNames() []GeneratorParam {
func (CronJobV2Alpha1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"labels", false},
{"default-name", false},
@ -425,7 +425,7 @@ func (ScheduledJobV2Alpha1) ParamNames() []GeneratorParam {
}
}
func (ScheduledJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
func (CronJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
args, err := getArgs(genericParams)
if err != nil {
return nil, err
@ -477,12 +477,12 @@ func (ScheduledJobV2Alpha1) Generate(genericParams map[string]interface{}) (runt
}
podSpec.RestartPolicy = restartPolicy
scheduledJob := batchv2alpha1.ScheduledJob{
cronJob := batchv2alpha1.CronJob{
ObjectMeta: v1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: batchv2alpha1.ScheduledJobSpec{
Spec: batchv2alpha1.CronJobSpec{
Schedule: params["schedule"],
ConcurrencyPolicy: batchv2alpha1.AllowConcurrent,
JobTemplate: batchv2alpha1.JobTemplateSpec{
@ -498,7 +498,7 @@ func (ScheduledJobV2Alpha1) Generate(genericParams map[string]interface{}) (runt
},
}
return &scheduledJob, nil
return &cronJob, nil
}
type BasicReplicationController struct{}
@ -796,7 +796,7 @@ func updatePodPorts(params map[string]string, podSpec *api.PodSpec) (err error)
}
// Don't include the port if it was not specified.
if port > 0 {
if len(params["port"]) > 0 {
podSpec.Containers[0].Ports = []api.ContainerPort{
{
ContainerPort: int32(port),
@ -830,7 +830,7 @@ func updateV1PodPorts(params map[string]string, podSpec *v1.PodSpec) (err error)
}
// Don't include the port if it was not specified.
if port > 0 {
if len(params["port"]) > 0 {
podSpec.Containers[0].Ports = []v1.ContainerPort{
{
ContainerPort: int32(port),

View file

@ -27,6 +27,11 @@ import (
"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"
appsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion"
batchclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/util/wait"
@ -44,16 +49,16 @@ type Scaler interface {
ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (updatedResourceVersion string, err error)
}
func ScalerFor(kind unversioned.GroupKind, c client.Interface) (Scaler, error) {
func ScalerFor(kind unversioned.GroupKind, c internalclientset.Interface) (Scaler, error) {
switch kind {
case api.Kind("ReplicationController"):
return &ReplicationControllerScaler{c}, nil
return &ReplicationControllerScaler{c.Core()}, nil
case extensions.Kind("ReplicaSet"):
return &ReplicaSetScaler{c.Extensions()}, nil
case extensions.Kind("Job"), batch.Kind("Job"):
return &JobScaler{c.Batch()}, nil // Either kind of job can be scaled with Batch interface.
case apps.Kind("PetSet"):
return &PetSetScaler{c.Apps()}, nil
case apps.Kind("StatefulSet"):
return &StatefulSetScaler{c.Apps()}, nil
case extensions.Kind("Deployment"):
return &DeploymentScaler{c.Extensions()}, nil
}
@ -132,8 +137,8 @@ func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name s
}
}
// ValidatePetSet ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
func (precondition *ScalePrecondition) ValidatePetSet(ps *apps.PetSet) error {
// ValidateStatefulSet ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
func (precondition *ScalePrecondition) ValidateStatefulSet(ps *apps.StatefulSet) error {
if precondition.Size != -1 && int(ps.Spec.Replicas) != precondition.Size {
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(int(ps.Spec.Replicas))}
}
@ -155,7 +160,7 @@ func (precondition *ScalePrecondition) ValidateReplicationController(controller
}
type ReplicationControllerScaler struct {
c client.Interface
c coreclient.ReplicationControllersGetter
}
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
@ -253,7 +258,7 @@ func (precondition *ScalePrecondition) ValidateReplicaSet(replicaSet *extensions
}
type ReplicaSetScaler struct {
c client.ExtensionsInterface
c extensionsclient.ReplicaSetsGetter
}
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
@ -323,34 +328,34 @@ func (precondition *ScalePrecondition) ValidateJob(job *batch.Job) error {
return nil
}
type PetSetScaler struct {
c client.AppsInterface
type StatefulSetScaler struct {
c appsclient.StatefulSetsGetter
}
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
// resourceVersion of the petset if the update is successful.
func (scaler *PetSetScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
ps, err := scaler.c.PetSets(namespace).Get(name)
// resourceVersion of the statefulset if the update is successful.
func (scaler *StatefulSetScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
ps, err := scaler.c.StatefulSets(namespace).Get(name)
if err != nil {
return "", ScaleError{ScaleGetFailure, "Unknown", err}
}
if preconditions != nil {
if err := preconditions.ValidatePetSet(ps); err != nil {
if err := preconditions.ValidateStatefulSet(ps); err != nil {
return "", err
}
}
ps.Spec.Replicas = int(newSize)
updatedPetSet, err := scaler.c.PetSets(namespace).Update(ps)
ps.Spec.Replicas = int32(newSize)
updatedStatefulSet, err := scaler.c.StatefulSets(namespace).Update(ps)
if err != nil {
if errors.IsConflict(err) {
return "", ScaleError{ScaleUpdateConflictFailure, ps.ResourceVersion, err}
}
return "", ScaleError{ScaleUpdateFailure, ps.ResourceVersion, err}
}
return updatedPetSet.ResourceVersion, nil
return updatedStatefulSet.ResourceVersion, nil
}
func (scaler *PetSetScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
func (scaler *StatefulSetScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
if preconditions == nil {
preconditions = &ScalePrecondition{-1, ""}
}
@ -363,11 +368,11 @@ func (scaler *PetSetScaler) Scale(namespace, name string, newSize uint, precondi
return err
}
if waitForReplicas != nil {
job, err := scaler.c.PetSets(namespace).Get(name)
job, err := scaler.c.StatefulSets(namespace).Get(name)
if err != nil {
return err
}
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.PetSetHasDesiredPets(scaler.c, job))
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.StatefulSetHasDesiredPets(scaler.c, job))
if err == wait.ErrWaitTimeout {
return fmt.Errorf("timed out waiting for %q to be synced", name)
}
@ -377,7 +382,7 @@ func (scaler *PetSetScaler) Scale(namespace, name string, newSize uint, precondi
}
type JobScaler struct {
c client.BatchInterface
c batchclient.JobsGetter
}
// ScaleSimple is responsible for updating job's parallelism. It returns the
@ -445,7 +450,7 @@ func (precondition *ScalePrecondition) ValidateDeployment(deployment *extensions
}
type DeploymentScaler struct {
c client.ExtensionsInterface
c extensionsclient.DeploymentsGetter
}
// ScaleSimple is responsible for updating a deployment's desired replicas

View file

@ -31,6 +31,7 @@ type ServiceCommonGeneratorV1 struct {
TCP []string
Type api.ServiceType
ClusterIP string
NodePort int
}
type ServiceClusterIPGeneratorV1 struct {
@ -56,6 +57,7 @@ func (ServiceNodePortGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"tcp", true},
{"nodeport", true},
}
}
func (ServiceLoadBalancerGeneratorV1) ParamNames() []GeneratorParam {
@ -174,12 +176,14 @@ func (s ServiceCommonGeneratorV1) StructuredGenerate() (runtime.Object, error) {
if err != nil {
return nil, err
}
portName := strings.Replace(tcpString, ":", "-", -1)
ports = append(ports, api.ServicePort{
Name: portName,
Port: port,
TargetPort: targetPort,
Protocol: api.Protocol("TCP"),
NodePort: int32(s.NodePort),
})
}

View file

@ -1,36 +0,0 @@
/*
Copyright 2014 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.
*/
package kubectl
import (
"k8s.io/kubernetes/pkg/api"
)
// SortableEvents implements sort.Interface for []api.Event based on the Timestamp field
type SortableEvents []api.Event
func (list SortableEvents) Len() int {
return len(list)
}
func (list SortableEvents) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableEvents) Less(i, j int) bool {
return list[i].LastTimestamp.Time.Before(list[j].LastTimestamp.Time)
}

View file

@ -40,7 +40,7 @@ type SortingPrinter struct {
Decoder runtime.Decoder
}
func (s *SortingPrinter) FinishPrint(w io.Writer, res string) error {
func (s *SortingPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
@ -109,7 +109,13 @@ func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput stri
}
}
values, err := parser.FindResults(reflect.ValueOf(objs[0]).Elem().Interface())
var values [][]reflect.Value
if unstructured, ok := objs[0].(*runtime.Unstructured); ok {
values, err = parser.FindResults(unstructured.Object)
} else {
values, err = parser.FindResults(reflect.ValueOf(objs[0]).Elem().Interface())
}
if err != nil {
return nil, err
}
@ -182,6 +188,66 @@ func isLess(i, j reflect.Value) (bool, error) {
}
}
return true, nil
case reflect.Interface:
switch itype := i.Interface().(type) {
case uint8:
if jtype, ok := j.Interface().(uint8); ok {
return itype < jtype, nil
}
case uint16:
if jtype, ok := j.Interface().(uint16); ok {
return itype < jtype, nil
}
case uint32:
if jtype, ok := j.Interface().(uint32); ok {
return itype < jtype, nil
}
case uint64:
if jtype, ok := j.Interface().(uint64); ok {
return itype < jtype, nil
}
case int8:
if jtype, ok := j.Interface().(int8); ok {
return itype < jtype, nil
}
case int16:
if jtype, ok := j.Interface().(int16); ok {
return itype < jtype, nil
}
case int32:
if jtype, ok := j.Interface().(int32); ok {
return itype < jtype, nil
}
case int64:
if jtype, ok := j.Interface().(int64); ok {
return itype < jtype, nil
}
case uint:
if jtype, ok := j.Interface().(uint); ok {
return itype < jtype, nil
}
case int:
if jtype, ok := j.Interface().(int); ok {
return itype < jtype, nil
}
case float32:
if jtype, ok := j.Interface().(float32); ok {
return itype < jtype, nil
}
case float64:
if jtype, ok := j.Interface().(float64); ok {
return itype < jtype, nil
}
case string:
if jtype, ok := j.Interface().(string); ok {
return itype < jtype, nil
}
default:
return false, fmt.Errorf("unsortable type: %T", itype)
}
return false, fmt.Errorf("unsortable interface: %v", i.Kind())
default:
return false, fmt.Errorf("unsortable type: %v", i.Kind())
}
@ -194,11 +260,24 @@ func (r *RuntimeSort) Less(i, j int) bool {
parser := jsonpath.New("sorting")
parser.Parse(r.field)
iValues, err := parser.FindResults(reflect.ValueOf(iObj).Elem().Interface())
var iValues [][]reflect.Value
var jValues [][]reflect.Value
var err error
if unstructured, ok := iObj.(*runtime.Unstructured); ok {
iValues, err = parser.FindResults(unstructured.Object)
} else {
iValues, err = parser.FindResults(reflect.ValueOf(iObj).Elem().Interface())
}
if err != nil {
glog.Fatalf("Failed to get i values for %#v using %s (%#v)", iObj, r.field, err)
}
jValues, err := parser.FindResults(reflect.ValueOf(jObj).Elem().Interface())
if unstructured, ok := jObj.(*runtime.Unstructured); ok {
jValues, err = parser.FindResults(unstructured.Object)
} else {
jValues, err = parser.FindResults(reflect.ValueOf(jObj).Elem().Interface())
}
if err != nil {
glog.Fatalf("Failed to get j values for %#v using %s (%v)", jObj, r.field, err)
}

View file

@ -28,7 +28,11 @@ import (
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
appsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion"
batchclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util"
@ -63,68 +67,71 @@ func IsNoSuchReaperError(err error) bool {
return ok
}
func ReaperFor(kind unversioned.GroupKind, c client.Interface) (Reaper, error) {
func ReaperFor(kind unversioned.GroupKind, c internalclientset.Interface) (Reaper, error) {
switch kind {
case api.Kind("ReplicationController"):
return &ReplicationControllerReaper{c, Interval, Timeout}, nil
return &ReplicationControllerReaper{c.Core(), Interval, Timeout}, nil
case extensions.Kind("ReplicaSet"):
return &ReplicaSetReaper{c, Interval, Timeout}, nil
return &ReplicaSetReaper{c.Extensions(), Interval, Timeout}, nil
case extensions.Kind("DaemonSet"):
return &DaemonSetReaper{c, Interval, Timeout}, nil
return &DaemonSetReaper{c.Extensions(), Interval, Timeout}, nil
case api.Kind("Pod"):
return &PodReaper{c}, nil
return &PodReaper{c.Core()}, nil
case api.Kind("Service"):
return &ServiceReaper{c}, nil
return &ServiceReaper{c.Core()}, nil
case extensions.Kind("Job"), batch.Kind("Job"):
return &JobReaper{c, Interval, Timeout}, nil
return &JobReaper{c.Batch(), c.Core(), Interval, Timeout}, nil
case apps.Kind("PetSet"):
return &PetSetReaper{c, Interval, Timeout}, nil
case apps.Kind("StatefulSet"):
return &StatefulSetReaper{c.Apps(), c.Core(), Interval, Timeout}, nil
case extensions.Kind("Deployment"):
return &DeploymentReaper{c, Interval, Timeout}, nil
return &DeploymentReaper{c.Extensions(), c.Extensions(), Interval, Timeout}, nil
}
return nil, &NoSuchReaperError{kind}
}
func ReaperForReplicationController(c client.Interface, timeout time.Duration) (Reaper, error) {
return &ReplicationControllerReaper{c, Interval, timeout}, nil
func ReaperForReplicationController(rcClient coreclient.ReplicationControllersGetter, timeout time.Duration) (Reaper, error) {
return &ReplicationControllerReaper{rcClient, Interval, timeout}, nil
}
type ReplicationControllerReaper struct {
client.Interface
client coreclient.ReplicationControllersGetter
pollInterval, timeout time.Duration
}
type ReplicaSetReaper struct {
client.Interface
client extensionsclient.ReplicaSetsGetter
pollInterval, timeout time.Duration
}
type DaemonSetReaper struct {
client.Interface
client extensionsclient.DaemonSetsGetter
pollInterval, timeout time.Duration
}
type JobReaper struct {
client.Interface
client batchclient.JobsGetter
podClient coreclient.PodsGetter
pollInterval, timeout time.Duration
}
type DeploymentReaper struct {
client.Interface
dClient extensionsclient.DeploymentsGetter
rsClient extensionsclient.ReplicaSetsGetter
pollInterval, timeout time.Duration
}
type PodReaper struct {
client.Interface
client coreclient.PodsGetter
}
type ServiceReaper struct {
client.Interface
client coreclient.ServicesGetter
}
type PetSetReaper struct {
client.Interface
type StatefulSetReaper struct {
client appsclient.StatefulSetsGetter
podClient coreclient.PodsGetter
pollInterval, timeout time.Duration
}
@ -134,8 +141,8 @@ type objInterface interface {
}
// getOverlappingControllers finds rcs that this controller overlaps, as well as rcs overlapping this controller.
func getOverlappingControllers(c client.ReplicationControllerInterface, rc *api.ReplicationController) ([]api.ReplicationController, error) {
rcs, err := c.List(api.ListOptions{})
func getOverlappingControllers(rcClient coreclient.ReplicationControllerInterface, rc *api.ReplicationController) ([]api.ReplicationController, error) {
rcs, err := rcClient.List(api.ListOptions{})
if err != nil {
return nil, fmt.Errorf("error getting replication controllers: %v", err)
}
@ -151,11 +158,8 @@ func getOverlappingControllers(c client.ReplicationControllerInterface, rc *api.
}
func (reaper *ReplicationControllerReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
rc := reaper.ReplicationControllers(namespace)
scaler, err := ScalerFor(api.Kind("ReplicationController"), *reaper)
if err != nil {
return err
}
rc := reaper.client.ReplicationControllers(namespace)
scaler := &ReplicationControllerScaler{reaper.client}
ctrl, err := rc.Get(name)
if err != nil {
return err
@ -217,17 +221,14 @@ func (reaper *ReplicationControllerReaper) Stop(namespace, name string, timeout
// TODO(madhusudancs): Implement it when controllerRef is implemented - https://github.com/kubernetes/kubernetes/issues/2210
// getOverlappingReplicaSets finds ReplicaSets that this ReplicaSet overlaps, as well as ReplicaSets overlapping this ReplicaSet.
func getOverlappingReplicaSets(c client.ReplicaSetInterface, rs *extensions.ReplicaSet) ([]extensions.ReplicaSet, []extensions.ReplicaSet, error) {
func getOverlappingReplicaSets(c extensionsclient.ReplicaSetInterface, rs *extensions.ReplicaSet) ([]extensions.ReplicaSet, []extensions.ReplicaSet, error) {
var overlappingRSs, exactMatchRSs []extensions.ReplicaSet
return overlappingRSs, exactMatchRSs, nil
}
func (reaper *ReplicaSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
rsc := reaper.Extensions().ReplicaSets(namespace)
scaler, err := ScalerFor(extensions.Kind("ReplicaSet"), *reaper)
if err != nil {
return err
}
rsc := reaper.client.ReplicaSets(namespace)
scaler := &ReplicaSetScaler{reaper.client}
rs, err := rsc.Get(name)
if err != nil {
return err
@ -290,7 +291,7 @@ func (reaper *ReplicaSetReaper) Stop(namespace, name string, timeout time.Durati
}
func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
ds, err := reaper.Extensions().DaemonSets(namespace).Get(name)
ds, err := reaper.client.DaemonSets(namespace).Get(name)
if err != nil {
return err
}
@ -305,13 +306,13 @@ func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duratio
// force update to avoid version conflict
ds.ResourceVersion = ""
if ds, err = reaper.Extensions().DaemonSets(namespace).Update(ds); err != nil {
if ds, err = reaper.client.DaemonSets(namespace).Update(ds); err != nil {
return err
}
// Wait for the daemon set controller to kill all the daemon pods.
if err := wait.Poll(reaper.pollInterval, reaper.timeout, func() (bool, error) {
updatedDS, err := reaper.Extensions().DaemonSets(namespace).Get(name)
updatedDS, err := reaper.client.DaemonSets(namespace).Get(name)
if err != nil {
return false, nil
}
@ -321,16 +322,13 @@ func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duratio
return err
}
return reaper.Extensions().DaemonSets(namespace).Delete(name)
return reaper.client.DaemonSets(namespace).Delete(name, nil)
}
func (reaper *PetSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
petsets := reaper.Apps().PetSets(namespace)
scaler, err := ScalerFor(apps.Kind("PetSet"), *reaper)
if err != nil {
return err
}
ps, err := petsets.Get(name)
func (reaper *StatefulSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
statefulsets := reaper.client.StatefulSets(namespace)
scaler := &StatefulSetScaler{reaper.client}
ps, err := statefulsets.Get(name)
if err != nil {
return err
}
@ -339,14 +337,14 @@ func (reaper *PetSetReaper) Stop(namespace, name string, timeout time.Duration,
timeout = Timeout + time.Duration(10*numPets)*time.Second
}
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
waitForPetSet := NewRetryParams(reaper.pollInterval, reaper.timeout)
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForPetSet); err != nil {
waitForStatefulSet := NewRetryParams(reaper.pollInterval, reaper.timeout)
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForStatefulSet); err != nil {
return err
}
// TODO: This shouldn't be needed, see corresponding TODO in PetSetHasDesiredPets.
// PetSet should track generation number.
pods := reaper.Pods(namespace)
// TODO: This shouldn't be needed, see corresponding TODO in StatefulSetHasDesiredPets.
// StatefulSet should track generation number.
pods := reaper.podClient.Pods(namespace)
selector, _ := unversioned.LabelSelectorAsSelector(ps.Spec.Selector)
options := api.ListOptions{LabelSelector: selector}
podList, err := pods.List(options)
@ -367,17 +365,14 @@ func (reaper *PetSetReaper) Stop(namespace, name string, timeout time.Duration,
}
// TODO: Cleanup volumes? We don't want to accidentally delete volumes from
// stop, so just leave this up to the petset.
return petsets.Delete(name, nil)
// stop, so just leave this up to the statefulset.
return statefulsets.Delete(name, nil)
}
func (reaper *JobReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
jobs := reaper.Batch().Jobs(namespace)
pods := reaper.Pods(namespace)
scaler, err := ScalerFor(batch.Kind("Job"), *reaper)
if err != nil {
return err
}
jobs := reaper.client.Jobs(namespace)
pods := reaper.podClient.Pods(namespace)
scaler := &JobScaler{reaper.client}
job, err := jobs.Get(name)
if err != nil {
return err
@ -418,9 +413,9 @@ func (reaper *JobReaper) Stop(namespace, name string, timeout time.Duration, gra
}
func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
deployments := reaper.Extensions().Deployments(namespace)
replicaSets := reaper.Extensions().ReplicaSets(namespace)
rsReaper, _ := ReaperFor(extensions.Kind("ReplicaSet"), reaper)
deployments := reaper.dClient.Deployments(namespace)
replicaSets := reaper.rsClient.ReplicaSets(namespace)
rsReaper := &ReplicaSetReaper{reaper.rsClient, reaper.pollInterval, reaper.timeout}
deployment, err := reaper.updateDeploymentWithRetries(namespace, name, func(d *extensions.Deployment) {
// set deployment's history and scale to 0
@ -440,6 +435,11 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati
return err
}
// Do not cascade deletion for overlapping deployments.
if len(deployment.Annotations[deploymentutil.OverlapAnnotation]) > 0 {
return deployments.Delete(name, nil)
}
// Stop all replica sets.
selector, err := unversioned.LabelSelectorAsSelector(deployment.Spec.Selector)
if err != nil {
@ -473,7 +473,7 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati
type updateDeploymentFunc func(d *extensions.Deployment)
func (reaper *DeploymentReaper) updateDeploymentWithRetries(namespace, name string, applyUpdate updateDeploymentFunc) (deployment *extensions.Deployment, err error) {
deployments := reaper.Extensions().Deployments(namespace)
deployments := reaper.dClient.Deployments(namespace)
err = wait.Poll(10*time.Millisecond, 1*time.Minute, func() (bool, error) {
if deployment, err = deployments.Get(name); err != nil {
return false, err
@ -493,7 +493,7 @@ func (reaper *DeploymentReaper) updateDeploymentWithRetries(namespace, name stri
}
func (reaper *PodReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
pods := reaper.Pods(namespace)
pods := reaper.client.Pods(namespace)
_, err := pods.Get(name)
if err != nil {
return err
@ -502,10 +502,10 @@ func (reaper *PodReaper) Stop(namespace, name string, timeout time.Duration, gra
}
func (reaper *ServiceReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
services := reaper.Services(namespace)
services := reaper.client.Services(namespace)
_, err := services.Get(name)
if err != nil {
return err
}
return services.Delete(name)
return services.Delete(name, nil)
}

View file

@ -19,22 +19,10 @@ package kubectl
import (
"fmt"
"io"
"os"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/version"
)
func GetServerVersion(w io.Writer, kubeClient client.Interface) {
serverVersion, err := kubeClient.Discovery().ServerVersion()
if err != nil {
fmt.Printf("Couldn't read server version from server: %v\n", err)
os.Exit(1)
}
fmt.Fprintf(w, "Server Version: %#v\n", *serverVersion)
}
func GetClientVersion(w io.Writer) {
fmt.Fprintf(w, "Client Version: %#v\n", version.Get())
}

View file

@ -1,45 +0,0 @@
/*
Copyright 2014 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.
*/
package kubectl
import (
"os"
"os/signal"
"k8s.io/kubernetes/pkg/watch"
)
// WatchLoop loops, passing events in w to fn.
// If user sends interrupt signal, shut down cleanly. Otherwise, never return.
func WatchLoop(w watch.Interface, fn func(watch.Event) error) {
signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt)
defer signal.Stop(signals)
for {
select {
case event, ok := <-w.ResultChan():
if !ok {
return
}
if err := fn(event); err != nil {
w.Stop()
}
case <-signals:
w.Stop()
}
}
}