Update ingress godeps

This commit is contained in:
Manuel de Brito Fontes 2016-08-10 14:53:55 -04:00
parent d43021b3f1
commit 28db8fb16d
1068 changed files with 461467 additions and 117300 deletions

View file

@ -76,19 +76,19 @@ func (c *ClientCache) ClientConfigForVersion(version *unversioned.GroupVersion)
preferredGV = &versionCopy
}
client.SetKubernetesDefaults(&config)
negotiatedVersion, err := client.NegotiateVersion(c.defaultClient, &config, preferredGV, registered.EnabledVersions())
if err != nil {
return nil, err
}
config.GroupVersion = negotiatedVersion
client.SetKubernetesDefaults(&config)
if version != nil {
c.configs[*version] = &config
}
// `version` does not necessarily equal `config.Version`. However, we know that we call this method again with
// `config.Version`, we should get the the config we've just built.
// `config.Version`, we should get the config we've just built.
configCopy := config
c.configs[*config.GroupVersion] = &configCopy

View file

@ -53,6 +53,8 @@ import (
"k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/typed/discovery"
"k8s.io/kubernetes/pkg/client/typed/dynamic"
client "k8s.io/kubernetes/pkg/client/unversioned"
clientset "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
@ -83,6 +85,9 @@ type Factory struct {
// Returns interfaces for dealing with arbitrary runtime.Objects. If thirdPartyDiscovery is true, performs API calls
// to discovery dynamic API objects registered by third parties.
Object func(thirdPartyDiscovery bool) (meta.RESTMapper, runtime.ObjectTyper)
// Returns interfaces for dealing with arbitrary
// runtime.Unstructured. This performs API calls to discover types.
UnstructuredObject func() (meta.RESTMapper, runtime.ObjectTyper, error)
// Returns interfaces for decoding objects - if toInternal is set, decoded objects will be converted
// into their internal form (if possible). Eventually the internal form will be removed as an option,
// and only versioned objects will be returned.
@ -96,10 +101,12 @@ type Factory struct {
// Returns a RESTClient for working with the specified RESTMapping or an error. This is intended
// for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer.
ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a RESTClient for working with Unstructured objects.
UnstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a Describer for displaying the specified RESTMapping type or an error.
Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error)
// Returns a Printer for formatting objects of the given type or an error.
Printer func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error)
Printer func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error)
// Returns a Scaler for changing the size of the specified RESTMapping type or an error
Scaler func(mapping *meta.RESTMapping) (kubectl.Scaler, error)
// Returns a Reaper for gracefully shutting down resources.
@ -158,13 +165,19 @@ const (
RunPodV1GeneratorName = "run-pod/v1"
ServiceV1GeneratorName = "service/v1"
ServiceV2GeneratorName = "service/v2"
ServiceNodePortGeneratorV1Name = "service-nodeport/v1"
ServiceClusterIPGeneratorV1Name = "service-clusterip/v1"
ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1"
ServiceAccountV1GeneratorName = "serviceaccount/v1"
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1"
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
DeploymentBasicV1Beta1GeneratorName = "deployment-basic/v1beta1"
JobV1Beta1GeneratorName = "job/v1beta1"
JobV1GeneratorName = "job/v1"
ScheduledJobV2Alpha1GeneratorName = "scheduledjob/v2alpha1"
NamespaceV1GeneratorName = "namespace/v1"
ResourceQuotaV1GeneratorName = "resourcequotas/v1"
SecretV1GeneratorName = "secret/v1"
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
SecretForTLSV1GeneratorName = "secret-for-tls/v1"
@ -178,12 +191,25 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
ServiceV1GeneratorName: kubectl.ServiceGeneratorV1{},
ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{},
}
generators["service-clusterip"] = map[string]kubectl.Generator{
ServiceClusterIPGeneratorV1Name: kubectl.ServiceClusterIPGeneratorV1{},
}
generators["service-nodeport"] = map[string]kubectl.Generator{
ServiceNodePortGeneratorV1Name: kubectl.ServiceNodePortGeneratorV1{},
}
generators["service-loadbalancer"] = map[string]kubectl.Generator{
ServiceLoadBalancerGeneratorV1Name: kubectl.ServiceLoadBalancerGeneratorV1{},
}
generators["deployment"] = map[string]kubectl.Generator{
DeploymentBasicV1Beta1GeneratorName: kubectl.DeploymentBasicGeneratorV1{},
}
generators["run"] = map[string]kubectl.Generator{
RunV1GeneratorName: kubectl.BasicReplicationController{},
RunPodV1GeneratorName: kubectl.BasicPod{},
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
JobV1GeneratorName: kubectl.JobV1{},
RunV1GeneratorName: kubectl.BasicReplicationController{},
RunPodV1GeneratorName: kubectl.BasicPod{},
DeploymentV1Beta1GeneratorName: kubectl.DeploymentV1Beta1{},
JobV1Beta1GeneratorName: kubectl.JobV1Beta1{},
JobV1GeneratorName: kubectl.JobV1{},
ScheduledJobV2Alpha1GeneratorName: kubectl.ScheduledJobV2Alpha1{},
}
generators["autoscale"] = map[string]kubectl.Generator{
HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},
@ -192,6 +218,11 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
generators["namespace"] = map[string]kubectl.Generator{
NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{},
}
generators["quota"] = map[string]kubectl.Generator{
ResourceQuotaV1GeneratorName: kubectl.ResourceQuotaGeneratorV1{},
}
generators["secret"] = map[string]kubectl.Generator{
SecretV1GeneratorName: kubectl.SecretGeneratorV1{},
}
@ -310,29 +341,66 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
multiMapper = append(meta.MultiRESTMapper{thirdPartyMapper}, multiMapper...)
}
priorityMapper.Delegate = multiMapper
// Re-assign to the RESTMapper here because priorityMapper is actually a copy, so if we
// don't re-assign, the above assignement won't actually update mapper.RESTMapper
// Reassign to the RESTMapper here because priorityMapper is actually a copy, so if we
// don't reassign, the above assignement won't actually update mapper.RESTMapper
mapper.RESTMapper = priorityMapper
}
}
outputRESTMapper := kubectl.OutputVersionMapper{RESTMapper: mapper, OutputVersions: []unversioned.GroupVersion{cmdApiVersion}}
priorityRESTMapper := meta.PriorityRESTMapper{
Delegate: outputRESTMapper,
ResourcePriority: []unversioned.GroupVersionResource{
{Group: api.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
{Group: autoscaling.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
{Group: extensions.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
{Group: federation.GroupName, Version: meta.AnyVersion, Resource: meta.AnyResource},
},
KindPriority: []unversioned.GroupVersionKind{
{Group: api.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
{Group: autoscaling.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
{Group: extensions.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
{Group: federation.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
},
}
// TODO: this should come from registered versions
groups := []string{api.GroupName, autoscaling.GroupName, extensions.GroupName, federation.GroupName, batch.GroupName}
// set a preferred version
for _, group := range groups {
gvs := registered.EnabledVersionsForGroup(group)
if len(gvs) == 0 {
continue
}
priorityRESTMapper.ResourcePriority = append(priorityRESTMapper.ResourcePriority, unversioned.GroupVersionResource{Group: group, Version: gvs[0].Version, Resource: meta.AnyResource})
priorityRESTMapper.KindPriority = append(priorityRESTMapper.KindPriority, unversioned.GroupVersionKind{Group: group, Version: gvs[0].Version, Kind: meta.AnyKind})
}
for _, group := range groups {
priorityRESTMapper.ResourcePriority = append(priorityRESTMapper.ResourcePriority, unversioned.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource})
priorityRESTMapper.KindPriority = append(priorityRESTMapper.KindPriority, unversioned.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind})
}
return priorityRESTMapper, api.Scheme
},
UnstructuredObject: func() (meta.RESTMapper, runtime.ObjectTyper, error) {
cfg, err := clients.ClientConfigForVersion(nil)
if err != nil {
return nil, nil, err
}
dc, err := discovery.NewDiscoveryClientForConfig(cfg)
if err != nil {
return nil, nil, err
}
groupResources, err := discovery.GetAPIGroupResources(dc)
if err != nil {
return nil, nil, err
}
// Register unknown APIs as third party for now to make
// validation happy. TODO perhaps make a dynamic schema
// validator to avoid this.
for _, group := range groupResources {
for _, version := range group.Group.Versions {
gv := unversioned.GroupVersion{Group: group.Group.Name, Version: version.Version}
if !registered.IsRegisteredVersion(gv) {
registered.AddThirdPartyAPIGroupVersions(gv)
}
}
}
mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured)
typer := discovery.NewUnstructuredObjectTyper(groupResources)
return kubectl.ShortcutExpander{RESTMapper: mapper}, typer, nil
},
Client: func() (*client.Client, error) {
return clients.ClientForVersion(nil)
},
@ -340,49 +408,46 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return clients.ClientConfigForVersion(nil)
},
ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
gvk := mapping.GroupVersionKind
mappingVersion := mapping.GroupVersionKind.GroupVersion()
c, err := clients.ClientForVersion(&mappingVersion)
cfg, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
switch gvk.Group {
case api.GroupName:
return c.RESTClient, nil
case autoscaling.GroupName:
return c.AutoscalingClient.RESTClient, nil
case batch.GroupName:
return c.BatchClient.RESTClient, nil
case policy.GroupName:
return c.PolicyClient.RESTClient, nil
case apps.GroupName:
return c.AppsClient.RESTClient, nil
case extensions.GroupName:
return c.ExtensionsClient.RESTClient, nil
case api.SchemeGroupVersion.Group:
return c.RESTClient, nil
case extensions.SchemeGroupVersion.Group:
return c.ExtensionsClient.RESTClient, nil
case federation.GroupName:
return clients.FederationClientForVersion(&mappingVersion)
case rbac.GroupName:
return c.RbacClient.RESTClient, nil
case certificates.GroupName:
return c.CertificatesClient.RESTClient, nil
default:
if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
return nil, fmt.Errorf("unknown api group/version: %s", gvk.String())
}
cfg, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
gv := gvk.GroupVersion()
cfg.GroupVersion = &gv
cfg.APIPath = "/apis"
cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv)
return restclient.RESTClientFor(cfg)
if err := client.SetKubernetesDefaults(cfg); err != nil {
return nil, err
}
gvk := mapping.GroupVersionKind
switch gvk.Group {
case federation.GroupName:
mappingVersion := mapping.GroupVersionKind.GroupVersion()
return clients.FederationClientForVersion(&mappingVersion)
case api.GroupName:
cfg.APIPath = "/api"
default:
cfg.APIPath = "/apis"
}
gv := gvk.GroupVersion()
cfg.GroupVersion = &gv
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv)
}
return restclient.RESTClientFor(cfg)
},
UnstructuredClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
cfg, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
if err := restclient.SetKubernetesDefaults(cfg); err != nil {
return nil, err
}
cfg.APIPath = "/apis"
if mapping.GroupVersionKind.Group == api.GroupName {
cfg.APIPath = "/api"
}
gv := mapping.GroupVersionKind.GroupVersion()
cfg.ContentConfig = dynamic.ContentConfig()
cfg.GroupVersion = &gv
return restclient.RESTClientFor(cfg)
},
Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
@ -417,8 +482,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
JSONEncoder: func() runtime.Encoder {
return api.Codecs.LegacyCodec(registered.EnabledVersions()...)
},
Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, showLabels, absoluteTimestamps, columnLabels), nil
Printer: func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
return kubectl.NewHumanReadablePrinter(options), nil
},
MapBasedSelectorForObject: func(object runtime.Object) (string, error) {
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
@ -1017,7 +1082,7 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac
}
err = schema.ValidateBytes(data)
if _, ok := err.(validation.TypeNotFoundError); ok && !firstSeen {
// As a temporay hack, kubectl would re-get the schema if validation
// As a temporary hack, kubectl would re-get the schema if validation
// fails for type not found reason.
// TODO: runtime-config settings needs to make into the file's name
schemaData, err = downloadSchemaAndStore(c, cacheDir, fullDir, cacheFile, prefix, groupVersion)
@ -1059,32 +1124,28 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
if ok := registered.IsEnabledVersion(gvk.GroupVersion()); !ok {
return fmt.Errorf("API version %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), registered.EnabledVersions())
}
if gvk.Group == autoscaling.GroupName {
switch gvk.Group {
case autoscaling.GroupName:
if c.c.AutoscalingClient == nil {
return errors.New("unable to validate: no autoscaling client")
}
return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == policy.GroupName {
case policy.GroupName:
if c.c.PolicyClient == nil {
return errors.New("unable to validate: no policy client")
}
return getSchemaAndValidate(c.c.PolicyClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == apps.GroupName {
case apps.GroupName:
if c.c.AppsClient == nil {
return errors.New("unable to validate: no autoscaling client")
return errors.New("unable to validate: no apps client")
}
return getSchemaAndValidate(c.c.AppsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == batch.GroupName {
case batch.GroupName:
if c.c.BatchClient == nil {
return errors.New("unable to validate: no batch client")
}
return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == rbac.GroupName {
case rbac.GroupName:
if c.c.RbacClient == nil {
return errors.New("unable to validate: no rbac client")
}
@ -1094,19 +1155,18 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
// Don't attempt to validate third party objects
return nil
}
if gvk.Group == extensions.GroupName {
switch gvk.Group {
case extensions.GroupName:
if c.c.ExtensionsClient == nil {
return errors.New("unable to validate: no experimental client")
}
return getSchemaAndValidate(c.c.ExtensionsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == federation.GroupName {
case federation.GroupName:
if c.fedc == nil {
return errors.New("unable to validate: no federation client")
}
return getSchemaAndValidate(c.fedc, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == certificates.GroupName {
case certificates.GroupName:
if c.c.CertificatesClient == nil {
return errors.New("unable to validate: no certificates client")
}
@ -1117,7 +1177,7 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
// 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me.
// 1. Merge together the kubeconfig itself. This is done with the following hierarchy rules:
// 1. Merge the kubeconfig itself. This is done with the following hierarchy rules:
// 1. CommandLineLocation - this parsed from the command line, so it must be late bound. If you specify this,
// then no other kubeconfig files are merged. This file must exist.
// 2. If $KUBECONFIG is set, then it is treated as a list of files that should be merged.
@ -1205,14 +1265,16 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin
if err != nil {
return nil, err
}
if version.IsEmpty() {
if version.Empty() && mapping != nil {
version = mapping.GroupVersionKind.GroupVersion()
}
if version.IsEmpty() {
if version.Empty() {
return nil, fmt.Errorf("you must specify an output-version when using this output format")
}
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion())
if mapping != nil {
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion())
}
} else {
// Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper
@ -1220,7 +1282,15 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin
if err != nil {
columnLabel = []string{}
}
printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers"), withNamespace, GetWideFlag(cmd), GetFlagBool(cmd, "show-all"), GetFlagBool(cmd, "show-labels"), isWatch(cmd), columnLabel)
printer, err = f.Printer(mapping, kubectl.PrintOptions{
NoHeaders: GetFlagBool(cmd, "no-headers"),
WithNamespace: withNamespace,
Wide: GetWideFlag(cmd),
ShowAll: GetFlagBool(cmd, "show-all"),
ShowLabels: GetFlagBool(cmd, "show-labels"),
AbsoluteTimestamps: isWatch(cmd),
ColumnLabels: columnLabel,
})
if err != nil {
return nil, err
}

View file

@ -19,6 +19,7 @@ package util
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
@ -27,7 +28,7 @@ import (
"strings"
"time"
"k8s.io/kubernetes/pkg/api/errors"
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"
@ -38,6 +39,8 @@ import (
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
utilexec "k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/strategicpatch"
"github.com/evanphx/json-patch"
@ -48,6 +51,7 @@ import (
const (
ApplyAnnotationsFlag = "save-config"
DefaultErrorExitCode = 1
)
type debugError interface {
@ -59,10 +63,10 @@ type debugError interface {
// souce 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.(errors.APIStatus); ok {
if statusError, ok := err.(kerrors.APIStatus); ok {
status := statusError.Status()
status.Message = fmt.Sprintf("error when %s %q: %v", verb, source, status.Message)
return &errors.StatusError{ErrStatus: status}
return &kerrors.StatusError{ErrStatus: status}
}
return fmt.Errorf("error when %s %q: %v", verb, source, err)
}
@ -72,9 +76,9 @@ func AddSourceToErr(verb string, source string, err error) error {
var fatalErrHandler = fatal
// BehaviorOnFatal allows you to override the default behavior when a fatal
// error occurs, which is call os.Exit(1). You can pass 'panic' as a function
// error occurs, which is to call os.Exit(code). You can pass 'panic' as a function
// here if you prefer the panic() over os.Exit(1).
func BehaviorOnFatal(f func(string)) {
func BehaviorOnFatal(f func(string, int)) {
fatalErrHandler = f
}
@ -84,19 +88,21 @@ func DefaultBehaviorOnFatal() {
fatalErrHandler = fatal
}
// fatal prints the message and then exits. If V(2) or greater, glog.Fatal
// fatal prints the message if set and then exits. If V(2) or greater, glog.Fatal
// is invoked for extended information.
func fatal(msg string) {
// add newline if needed
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
func fatal(msg string, code int) {
if len(msg) > 0 {
// add newline if needed
if !strings.HasSuffix(msg, "\n") {
msg += "\n"
}
if glog.V(2) {
glog.FatalDepth(2, msg)
if glog.V(2) {
glog.FatalDepth(2, msg)
}
fmt.Fprint(os.Stderr, msg)
}
fmt.Fprint(os.Stderr, msg)
os.Exit(1)
os.Exit(code)
}
// CheckErr prints a user friendly error to STDERR and exits with a non-zero
@ -113,56 +119,65 @@ func checkErrWithPrefix(prefix string, err error) {
checkErr(prefix, err, fatalErrHandler)
}
func checkErr(pref string, err error, handleErr func(string)) {
if err == nil {
// 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)) {
switch {
case err == nil:
return
}
if errors.IsInvalid(err) {
details := err.(*errors.StatusError).Status().Details
prefix := fmt.Sprintf("%sThe %s %q is invalid.\n", pref, details.Kind, details.Name)
errs := statusCausesToAggrError(details.Causes)
handleErr(MultilineError(prefix, errs))
}
if meta.IsNoResourceMatchError(err) {
noMatch := err.(*meta.NoResourceMatchError)
switch {
case len(noMatch.PartialResource.Group) > 0 && len(noMatch.PartialResource.Version) > 0:
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in group %q and version %q", pref, noMatch.PartialResource.Resource, noMatch.PartialResource.Group, noMatch.PartialResource.Version))
case len(noMatch.PartialResource.Group) > 0:
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in group %q", pref, noMatch.PartialResource.Resource, noMatch.PartialResource.Group))
case len(noMatch.PartialResource.Version) > 0:
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in version %q", pref, noMatch.PartialResource.Resource, noMatch.PartialResource.Version))
default:
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q", pref, noMatch.PartialResource.Resource))
case kerrors.IsInvalid(err):
details := err.(*kerrors.StatusError).Status().Details
s := fmt.Sprintf("%sThe %s %q is invalid", prefix, details.Kind, details.Name)
if len(details.Causes) > 0 {
errs := statusCausesToAggrError(details.Causes)
handleErr(MultilineError(s+": ", errs), DefaultErrorExitCode)
} else {
handleErr(s, DefaultErrorExitCode)
}
return
}
// handle multiline errors
if clientcmd.IsConfigurationInvalid(err) {
handleErr(MultilineError(fmt.Sprintf("%sError in configuration: ", pref), err))
}
if agg, ok := err.(utilerrors.Aggregate); ok && len(agg.Errors()) > 0 {
handleErr(MultipleErrors(pref, agg.Errors()))
}
msg, ok := StandardErrorMessage(err)
if !ok {
msg = err.Error()
if !strings.HasPrefix(msg, "error: ") {
msg = fmt.Sprintf("error: %s", msg)
case clientcmd.IsConfigurationInvalid(err):
handleErr(MultilineError(fmt.Sprintf("%sError in configuration: ", prefix), err), DefaultErrorExitCode)
default:
switch err := err.(type) {
case *meta.NoResourceMatchError:
switch {
case len(err.PartialResource.Group) > 0 && len(err.PartialResource.Version) > 0:
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in group %q and version %q", prefix, err.PartialResource.Resource, err.PartialResource.Group, err.PartialResource.Version), DefaultErrorExitCode)
case len(err.PartialResource.Group) > 0:
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in group %q", prefix, err.PartialResource.Resource, err.PartialResource.Group), DefaultErrorExitCode)
case len(err.PartialResource.Version) > 0:
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q in version %q", prefix, err.PartialResource.Resource, err.PartialResource.Version), DefaultErrorExitCode)
default:
handleErr(fmt.Sprintf("%sthe server doesn't have a resource type %q", prefix, err.PartialResource.Resource), DefaultErrorExitCode)
}
case utilerrors.Aggregate:
handleErr(MultipleErrors(prefix, err.Errors()), DefaultErrorExitCode)
case utilexec.ExitError:
// do not print anything, only terminate with given error
handleErr("", err.ExitStatus())
default: // for any other error type
msg, ok := StandardErrorMessage(err)
if !ok {
msg = err.Error()
if !strings.HasPrefix(msg, "error: ") {
msg = fmt.Sprintf("error: %s", msg)
}
}
handleErr(msg, DefaultErrorExitCode)
}
}
handleErr(fmt.Sprintf("%s%s", pref, msg))
}
func statusCausesToAggrError(scs []unversioned.StatusCause) utilerrors.Aggregate {
errs := make([]error, len(scs))
for i, sc := range scs {
errs[i] = fmt.Errorf("%s: %s", sc.Field, sc.Message)
errs := make([]error, 0, len(scs))
errorMsgs := sets.NewString()
for _, sc := range scs {
// check for duplicate error messages and skip them
msg := fmt.Sprintf("%s: %s", sc.Field, sc.Message)
if errorMsgs.Has(msg) {
continue
}
errorMsgs.Insert(msg)
errs = append(errs, errors.New(msg))
}
return utilerrors.NewAggregate(errs)
}
@ -177,7 +192,7 @@ func StandardErrorMessage(err error) (string, bool) {
if debugErr, ok := err.(debugError); ok {
glog.V(4).Infof(debugErr.DebugError())
}
status, isStatus := err.(errors.APIStatus)
status, isStatus := err.(kerrors.APIStatus)
switch {
case isStatus:
switch s := status.Status(); {
@ -186,7 +201,7 @@ func StandardErrorMessage(err error) (string, bool) {
default:
return fmt.Sprintf("Error from server: %s", err.Error()), true
}
case errors.IsUnexpectedObjectError(err):
case kerrors.IsUnexpectedObjectError(err):
return fmt.Sprintf("Server returned an unexpected response: %s", err.Error()), true
}
switch t := err.(type) {
@ -365,7 +380,7 @@ func ReadConfigDataFromReader(reader io.Reader, source string) ([]byte, error) {
}
if len(data) == 0 {
return nil, fmt.Errorf(`Read from %s but no data found`, source)
return nil, fmt.Errorf("Read from %s but no data found", source)
}
return data, nil
@ -437,7 +452,7 @@ func UpdateObject(info *resource.Info, codec runtime.Codec, updateFn func(runtim
// AddCmdRecordFlag adds --record flag to command
func AddRecordFlag(cmd *cobra.Command) {
cmd.Flags().Bool("record", false, "Record current kubectl command in the resource annotation.")
cmd.Flags().Bool("record", false, "Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists.")
}
func GetRecordFlag(cmd *cobra.Command) bool {
@ -491,7 +506,7 @@ func ContainsChangeCause(info *resource.Info) bool {
// ShouldRecord checks if we should record current change cause
func ShouldRecord(cmd *cobra.Command, info *resource.Info) bool {
return GetRecordFlag(cmd) || ContainsChangeCause(info)
return GetRecordFlag(cmd) || (ContainsChangeCause(info) && !cmd.Flags().Changed("record"))
}
// GetThirdPartyGroupVersions returns the thirdparty "group/versions"s and
@ -505,7 +520,7 @@ func GetThirdPartyGroupVersions(discovery discovery.DiscoveryInterface) ([]unver
groupList, err := discovery.ServerGroups()
if err != nil {
// On forbidden or not found, just return empty lists.
if errors.IsForbidden(err) || errors.IsNotFound(err) {
if kerrors.IsForbidden(err) || kerrors.IsNotFound(err) {
return result, gvks, nil
}

View file

@ -47,7 +47,7 @@ func AddOutputFlagsForMutation(cmd *cobra.Command) {
// AddOutputFlags adds output related flags to a command.
func AddOutputFlags(cmd *cobra.Command) {
cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].")
cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See custom columns [http://kubernetes.io/docs/user-guide/kubectl-overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].")
}
// AddNoHeadersFlags adds no-headers flags to a command.

View file

@ -105,7 +105,7 @@ func splitOnWhitespace(line string) []string {
// NewCustomColumnsPrinterFromTemplate creates a custom columns printer from a template stream. The template is expected
// to consist of two lines, whitespace separated. The first line is the header line, the second line is the jsonpath field spec
// For example the template below:
// For example, the template below:
// NAME API_VERSION
// {metadata.name} {apiVersion}
func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader, decoder runtime.Decoder) (*CustomColumnsPrinter, error) {
@ -155,6 +155,10 @@ type CustomColumnsPrinter struct {
NoHeaders bool
}
func (s *CustomColumnsPrinter) FinishPrint(w io.Writer, res string) error {
return nil
}
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
w := tabwriter.NewWriter(out, columnwidth, tabwidth, padding, padding_character, flags)

107
vendor/k8s.io/kubernetes/pkg/kubectl/deployment.go generated vendored Normal file
View file

@ -0,0 +1,107 @@
/*
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"
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime"
)
// DeploymentGeneratorV1 supports stable generation of a deployment
type DeploymentBasicGeneratorV1 struct {
Name string
Images []string
}
// Ensure it supports the generator pattern that uses parameters specified during construction
var _ StructuredGenerator = &DeploymentBasicGeneratorV1{}
func (DeploymentBasicGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"image", true},
}
}
func (s DeploymentBasicGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(s.ParamNames(), params)
if err != nil {
return nil, err
}
name, isString := params["name"].(string)
if !isString {
return nil, fmt.Errorf("expected string, saw %v for 'name'", name)
}
imageStrings, isArray := params["image"].([]string)
if !isArray {
return nil, fmt.Errorf("expected []string, found :%v", imageStrings)
}
delegate := &DeploymentBasicGeneratorV1{Name: name, Images: imageStrings}
return delegate.StructuredGenerate()
}
// StructuredGenerate outputs a deployment object using the configured fields
func (s *DeploymentBasicGeneratorV1) StructuredGenerate() (runtime.Object, error) {
if err := s.validate(); err != nil {
return nil, err
}
podSpec := api.PodSpec{Containers: []api.Container{}}
for _, imageString := range s.Images {
imageSplit := strings.Split(imageString, "/")
name := imageSplit[len(imageSplit)-1]
podSpec.Containers = append(podSpec.Containers, api.Container{Name: name, Image: imageString})
}
// setup default label and selector
labels := map[string]string{}
labels["app"] = s.Name
selector := unversioned.LabelSelector{MatchLabels: labels}
deployment := extensions.Deployment{
ObjectMeta: api.ObjectMeta{
Name: s.Name,
Labels: labels,
},
Spec: extensions.DeploymentSpec{
Replicas: 1,
Selector: &selector,
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: labels,
},
Spec: podSpec,
},
},
}
return &deployment, nil
}
// validate validates required fields are set to support structured generation
func (s *DeploymentBasicGeneratorV1) validate() error {
if len(s.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if len(s.Images) == 0 {
return fmt.Errorf("at least one image must be specified")
}
return nil
}

View file

@ -28,7 +28,6 @@ import (
"strings"
"time"
"github.com/golang/glog"
"k8s.io/kubernetes/federation/apis/federation"
fed_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
"k8s.io/kubernetes/pkg/api"
@ -38,6 +37,7 @@ import (
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/apis/extensions"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
client "k8s.io/kubernetes/pkg/client/unversioned"
@ -48,8 +48,11 @@ import (
"k8s.io/kubernetes/pkg/kubelet/qos"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/types"
utilcertificates "k8s.io/kubernetes/pkg/util/certificates"
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/sets"
"github.com/golang/glog"
)
// Describer generates output for the named resource or an error
@ -101,16 +104,18 @@ func describerMap(c *client.Client) map[unversioned.GroupKind]Describer {
api.Kind("Endpoints"): &EndpointsDescriber{c},
api.Kind("ConfigMap"): &ConfigMapDescriber{c},
extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c},
extensions.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c},
extensions.Kind("NetworkPolicy"): &NetworkPolicyDescriber{c},
autoscaling.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c},
extensions.Kind("DaemonSet"): &DaemonSetDescriber{c},
extensions.Kind("Deployment"): &DeploymentDescriber{adapter.FromUnversionedClient(c)},
extensions.Kind("Job"): &JobDescriber{c},
batch.Kind("Job"): &JobDescriber{c},
apps.Kind("PetSet"): &PetSetDescriber{c},
extensions.Kind("Ingress"): &IngressDescriber{c},
extensions.Kind("ReplicaSet"): &ReplicaSetDescriber{c},
extensions.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c},
extensions.Kind("NetworkPolicy"): &NetworkPolicyDescriber{c},
autoscaling.Kind("HorizontalPodAutoscaler"): &HorizontalPodAutoscalerDescriber{c},
extensions.Kind("DaemonSet"): &DaemonSetDescriber{c},
extensions.Kind("Deployment"): &DeploymentDescriber{adapter.FromUnversionedClient(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},
certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c},
}
return m
@ -541,6 +546,7 @@ func describePod(pod *api.Pod, events *api.EventList) (string, error) {
}
describeVolumes(pod.Spec.Volumes, out, "")
fmt.Fprintf(out, "QoS Class:\t%s\n", qos.GetPodQOS(pod))
printTolerationsInAnnotationMultiline(out, "Tolerations", pod.Annotations)
if events != nil {
DescribeEvents(events, out)
}
@ -598,8 +604,12 @@ func describeVolumes(volumes []api.Volume, out io.Writer, space string) {
printPersistentVolumeClaimVolumeSource(volume.VolumeSource.PersistentVolumeClaim, out)
case volume.VolumeSource.RBD != nil:
printRBDVolumeSource(volume.VolumeSource.RBD, out)
case volume.VolumeSource.Quobyte != nil:
printQuobyteVolumeSource(volume.VolumeSource.Quobyte, out)
case volume.VolumeSource.DownwardAPI != nil:
printDownwardAPIVolumeSource(volume.VolumeSource.DownwardAPI, out)
case volume.VolumeSource.AzureDisk != nil:
printAzureDiskVolumeSource(volume.VolumeSource.AzureDisk, out)
default:
fmt.Fprintf(out, " <unknown>\n")
}
@ -659,6 +669,14 @@ func printNFSVolumeSource(nfs *api.NFSVolumeSource, out io.Writer) {
nfs.Server, nfs.Path, nfs.ReadOnly)
}
func printQuobyteVolumeSource(quobyte *api.QuobyteVolumeSource, out io.Writer) {
fmt.Fprintf(out, " Type:\tQuobyte (a Quobyte mount on the host that shares a pod's lifetime)\n"+
" Registry:\t%v\n"+
" Volume:\t%v\n"+
" ReadOnly:\t%v\n",
quobyte.Registry, quobyte.Volume, quobyte.ReadOnly)
}
func printISCSIVolumeSource(iscsi *api.ISCSIVolumeSource, out io.Writer) {
fmt.Fprintf(out, " Type:\tISCSI (an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod)\n"+
" TargetPortal:\t%v\n"+
@ -710,6 +728,16 @@ func printDownwardAPIVolumeSource(d *api.DownwardAPIVolumeSource, out io.Writer)
}
}
func printAzureDiskVolumeSource(d *api.AzureDiskVolumeSource, out io.Writer) {
fmt.Fprintf(out, " Type:\tAzureDisk (an Azure Data Disk mount on the host and bind mount to the pod)\n"+
" DiskName:\t%v\n"+
" DiskURI:\t%v\n"+
" FSType:\t%v\n"+
" CachingMode:\t%v\n"+
" ReadOnly:\t%v\n",
d.DiskName, d.DataDiskURI, *d.FSType, *d.CachingMode, *d.ReadOnly)
}
type PersistentVolumeDescriber struct {
client.Interface
}
@ -759,6 +787,8 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe
printGlusterfsVolumeSource(pv.Spec.Glusterfs, out)
case pv.Spec.RBD != nil:
printRBDVolumeSource(pv.Spec.RBD, out)
case pv.Spec.Quobyte != nil:
printQuobyteVolumeSource(pv.Spec.Quobyte, out)
}
if events != nil {
@ -931,7 +961,11 @@ func describeContainers(label string, containers []api.Container, containerStatu
if err != nil {
valueFrom = ""
}
fmt.Fprintf(out, " %s:\t%s (%s)\n", e.Name, valueFrom, e.ValueFrom.ResourceFieldRef.Resource)
resource := e.ValueFrom.ResourceFieldRef.Resource
if valueFrom == "0" && (resource == "limits.cpu" || resource == "limits.memory") {
valueFrom = "node allocatable"
}
fmt.Fprintf(out, " %s:\t%s (%s)\n", e.Name, valueFrom, resource)
case e.ValueFrom.SecretKeyRef != nil:
fmt.Fprintf(out, " %s:\t<set to the key '%s' in secret '%s'>\n", e.Name, e.ValueFrom.SecretKeyRef.Key, e.ValueFrom.SecretKeyRef.Name)
case e.ValueFrom.ConfigMapKeyRef != nil:
@ -1019,6 +1053,14 @@ func describeStatus(stateName string, state api.ContainerState, out io.Writer) {
}
}
func printBoolPtr(value *bool) string {
if value != nil {
return printBool(*value)
}
return "<unset>"
}
func printBool(value bool) string {
if value {
return "True"
@ -1148,18 +1190,18 @@ 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 *client.Client
client.Interface
}
func (d *JobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
job, err := d.client.Extensions().Jobs(namespace).Get(name)
job, err := d.Batch().Jobs(namespace).Get(name)
if err != nil {
return "", err
}
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.client.Events(namespace).Search(job)
events, _ = d.Events(namespace).Search(job)
}
return describeJob(job, events)
@ -1194,6 +1236,92 @@ 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 {
clientset.Interface
}
func (d *ScheduledJobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
scheduledJob, err := d.Batch().ScheduledJobs(namespace).Get(name)
if err != nil {
return "", err
}
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Core().Events(namespace).Search(scheduledJob)
}
return describeScheduledJob(scheduledJob, events)
}
func describeScheduledJob(scheduledJob *batch.ScheduledJob, 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)
fmt.Fprintf(out, "Schedule:\t%s\n", scheduledJob.Spec.Schedule)
fmt.Fprintf(out, "Concurrency Policy:\t%s\n", scheduledJob.Spec.ConcurrencyPolicy)
fmt.Fprintf(out, "Suspend:\t%s\n", printBoolPtr(scheduledJob.Spec.Suspend))
if scheduledJob.Spec.StartingDeadlineSeconds != nil {
fmt.Fprintf(out, "Starting Deadline Seconds:\t%ds\n", *scheduledJob.Spec.StartingDeadlineSeconds)
} else {
fmt.Fprintf(out, "Starting Deadline Seconds:\t<unset>\n")
}
describeJobTemplate(scheduledJob.Spec.JobTemplate, out)
printLabelsMultiline(out, "Labels", scheduledJob.Labels)
if scheduledJob.Status.LastScheduleTime != nil {
fmt.Fprintf(out, "Last Schedule Time:\t%s\n", scheduledJob.Status.LastScheduleTime.Time.Format(time.RFC1123Z))
} else {
fmt.Fprintf(out, "Last Schedule Time:\t<unset>\n")
}
printActiveJobs(out, "Active Jobs", scheduledJob.Status.Active)
if events != nil {
DescribeEvents(events, out)
}
return nil
})
}
func describeJobTemplate(jobTemplate batch.JobTemplateSpec, out io.Writer) {
fmt.Fprintf(out, "Image(s):\t%s\n", makeImageList(&jobTemplate.Spec.Template.Spec))
if jobTemplate.Spec.Selector != nil {
selector, _ := unversioned.LabelSelectorAsSelector(jobTemplate.Spec.Selector)
fmt.Fprintf(out, "Selector:\t%s\n", selector)
} else {
fmt.Fprintf(out, "Selector:\t<unset>\n")
}
if jobTemplate.Spec.Parallelism != nil {
fmt.Fprintf(out, "Parallelism:\t%d\n", *jobTemplate.Spec.Parallelism)
} else {
fmt.Fprintf(out, "Parallelism:\t<unset>\n")
}
if jobTemplate.Spec.Completions != nil {
fmt.Fprintf(out, "Completions:\t%d\n", *jobTemplate.Spec.Completions)
} else {
fmt.Fprintf(out, "Completions:\t<unset>\n")
}
if jobTemplate.Spec.ActiveDeadlineSeconds != nil {
fmt.Fprintf(out, "Active Deadline Seconds:\t%ds\n", *jobTemplate.Spec.ActiveDeadlineSeconds)
}
describeVolumes(jobTemplate.Spec.Template.Spec.Volumes, out, "")
}
func printActiveJobs(out io.Writer, title string, jobs []api.ObjectReference) {
fmt.Fprintf(out, "%s:\t", title)
if len(jobs) == 0 {
fmt.Fprintln(out, "<none>")
return
}
for i, job := range jobs {
if i != 0 {
fmt.Fprint(out, ", ")
}
fmt.Fprintf(out, "%s", job.Name)
}
fmt.Fprintln(out, "")
}
// DaemonSetDescriber generates information about a daemon set and the pods it has created.
type DaemonSetDescriber struct {
client.Interface
@ -1446,6 +1574,12 @@ func describeService(service *api.Service, endpoints *api.Endpoints, events *api
fmt.Fprintf(out, "Selector:\t%s\n", labels.FormatLabels(service.Spec.Selector))
fmt.Fprintf(out, "Type:\t%s\n", service.Spec.Type)
fmt.Fprintf(out, "IP:\t%s\n", service.Spec.ClusterIP)
if len(service.Spec.ExternalIPs) > 0 {
fmt.Fprintf(out, "External IPs:\t%v\n", strings.Join(service.Spec.ExternalIPs, ","))
}
if service.Spec.ExternalName != "" {
fmt.Fprintf(out, "External Name:\t%s\n", service.Spec.ExternalName)
}
if len(service.Status.LoadBalancer.Ingress) > 0 {
list := buildIngressString(service.Status.LoadBalancer.Ingress)
fmt.Fprintf(out, "LoadBalancer Ingress:\t%s\n", list)
@ -1787,6 +1921,74 @@ func (p *PetSetDescriber) Describe(namespace, name string, describerSettings Des
})
}
type CertificateSigningRequestDescriber struct {
client *client.Client
}
func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
csr, err := p.client.Certificates().CertificateSigningRequests().Get(name)
if err != nil {
return "", err
}
cr, err := utilcertificates.ParseCertificateRequestObject(csr)
if err != nil {
return "", fmt.Errorf("Error parsing CSR: %v", err)
}
status, err := extractCSRStatus(csr)
if err != nil {
return "", err
}
printListHelper := func(out io.Writer, prefix, name string, values []string) {
if len(values) == 0 {
return
}
fmt.Fprintf(out, prefix+name+":\t")
fmt.Fprintf(out, strings.Join(values, "\n"+prefix+"\t"))
fmt.Fprintf(out, "\n")
}
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", csr.Name)
fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(csr.Labels))
fmt.Fprintf(out, "Annotations:\t%s\n", labels.FormatLabels(csr.Annotations))
fmt.Fprintf(out, "CreationTimestamp:\t%s\n", csr.CreationTimestamp.Time.Format(time.RFC1123Z))
fmt.Fprintf(out, "Requesting User:\t%s\n", csr.Spec.Username)
fmt.Fprintf(out, "Status:\t%s\n", status)
fmt.Fprintf(out, "Subject:\n")
fmt.Fprintf(out, "\tCommon Name:\t%s\n", cr.Subject.CommonName)
fmt.Fprintf(out, "\tSerial Number:\t%s\n", cr.Subject.SerialNumber)
printListHelper(out, "\t", "Organization", cr.Subject.Organization)
printListHelper(out, "\t", "Organizational Unit", cr.Subject.OrganizationalUnit)
printListHelper(out, "\t", "Country", cr.Subject.Country)
printListHelper(out, "\t", "Locality", cr.Subject.Locality)
printListHelper(out, "\t", "Province", cr.Subject.Province)
printListHelper(out, "\t", "StreetAddress", cr.Subject.StreetAddress)
printListHelper(out, "\t", "PostalCode", cr.Subject.PostalCode)
if len(cr.DNSNames)+len(cr.EmailAddresses)+len(cr.IPAddresses) > 0 {
fmt.Fprintf(out, "Subject Alternative Names:\n")
printListHelper(out, "\t", "DNS Names", cr.DNSNames)
printListHelper(out, "\t", "Email Addresses", cr.EmailAddresses)
var ipaddrs []string
for _, ipaddr := range cr.IPAddresses {
ipaddrs = append(ipaddrs, ipaddr.String())
}
printListHelper(out, "\t", "IP Addresses", ipaddrs)
}
if describerSettings.ShowEvents {
events, _ := p.client.Events(namespace).Search(csr)
if events != nil {
DescribeEvents(events, out)
}
}
return nil
})
}
// HorizontalPodAutoscalerDescriber generates information about a horizontal pod autoscaler.
type HorizontalPodAutoscalerDescriber struct {
client *client.Client
@ -2366,14 +2568,64 @@ func printTaintsMultilineWithIndent(out io.Writer, initialIndent, title, innerIn
}
sort.Strings(keys)
effects := []api.TaintEffect{api.TaintEffectNoSchedule, api.TaintEffectPreferNoSchedule}
for i, key := range keys {
for _, taint := range taints {
if taint.Key == key {
for _, effect := range effects {
for _, taint := range taints {
if taint.Key == key && taint.Effect == effect {
if i != 0 {
fmt.Fprint(out, initialIndent)
fmt.Fprint(out, innerIndent)
}
fmt.Fprintf(out, "%s=%s:%s\n", taint.Key, taint.Value, taint.Effect)
i++
}
}
}
}
}
// printTolerationsMultiline prints multiple tolerations with a proper alignment.
func printTolerationsInAnnotationMultiline(out io.Writer, title string, annotations map[string]string) {
tolerations, err := api.GetTolerationsFromPodAnnotations(annotations)
if err != nil {
tolerations = []api.Toleration{}
}
printTolerationsMultilineWithIndent(out, "", title, "\t", tolerations)
}
// printTolerationsMultilineWithIndent prints multiple tolerations with a user-defined alignment.
func printTolerationsMultilineWithIndent(out io.Writer, initialIndent, title, innerIndent string, tolerations []api.Toleration) {
fmt.Fprintf(out, "%s%s:%s", initialIndent, title, innerIndent)
if tolerations == nil || len(tolerations) == 0 {
fmt.Fprintln(out, "<none>")
return
}
// to print tolerations in the sorted order
keys := make([]string, 0, len(tolerations))
for _, toleration := range tolerations {
keys = append(keys, toleration.Key)
}
sort.Strings(keys)
for i, key := range keys {
for _, toleration := range tolerations {
if toleration.Key == key {
if i != 0 {
fmt.Fprint(out, initialIndent)
fmt.Fprint(out, innerIndent)
}
fmt.Fprintf(out, "%s=%s:%s\n", taint.Key, taint.Value, taint.Effect)
fmt.Fprintf(out, "%s=%s", toleration.Key, toleration.Value)
if len(toleration.Operator) != 0 {
fmt.Fprintf(out, ":%s", toleration.Operator)
}
if len(toleration.Effect) != 0 {
fmt.Fprintf(out, ":%s", toleration.Effect)
}
fmt.Fprintf(out, "\n")
i++
}
}

View file

@ -17,6 +17,7 @@ limitations under the License.
package kubectl
import (
"bytes"
"fmt"
"io"
@ -34,9 +35,9 @@ const (
ChangeCauseAnnotation = "kubernetes.io/change-cause"
)
// HistoryViewer provides an interface for resources that can be rolled back.
// HistoryViewer provides an interface for resources have historical information.
type HistoryViewer interface {
History(namespace, name string) (HistoryInfo, error)
ViewHistory(namespace, name string, revision int64) (string, error)
}
func HistoryViewerFor(kind unversioned.GroupKind, c clientset.Interface) (HistoryViewer, error) {
@ -47,68 +48,68 @@ func HistoryViewerFor(kind unversioned.GroupKind, c clientset.Interface) (Histor
return nil, fmt.Errorf("no history viewer has been implemented for %q", kind)
}
// HistoryInfo stores the mapping from revision to podTemplate;
// note that change-cause annotation should be copied to podTemplate
type HistoryInfo struct {
RevisionToTemplate map[int64]*api.PodTemplateSpec
}
type DeploymentHistoryViewer struct {
c clientset.Interface
}
// History returns a revision-to-replicaset map as the revision history of a deployment
func (h *DeploymentHistoryViewer) History(namespace, name string) (HistoryInfo, error) {
historyInfo := HistoryInfo{
RevisionToTemplate: make(map[int64]*api.PodTemplateSpec),
}
// ViewHistory returns a revision-to-replicaset map as the revision history of a deployment
func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
deployment, err := h.c.Extensions().Deployments(namespace).Get(name)
if err != nil {
return historyInfo, fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
return "", fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
}
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, h.c)
if err != nil {
return historyInfo, fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", name, err)
return "", fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", name, err)
}
allRSs := allOldRSs
if newRS != nil {
allRSs = append(allRSs, newRS)
}
historyInfo := make(map[int64]*api.PodTemplateSpec)
for _, rs := range allRSs {
v, err := deploymentutil.Revision(rs)
if err != nil {
continue
}
historyInfo.RevisionToTemplate[v] = &rs.Spec.Template
historyInfo[v] = &rs.Spec.Template
changeCause := getChangeCause(rs)
if historyInfo.RevisionToTemplate[v].Annotations == nil {
historyInfo.RevisionToTemplate[v].Annotations = make(map[string]string)
if historyInfo[v].Annotations == nil {
historyInfo[v].Annotations = make(map[string]string)
}
if len(changeCause) > 0 {
historyInfo.RevisionToTemplate[v].Annotations[ChangeCauseAnnotation] = changeCause
historyInfo[v].Annotations[ChangeCauseAnnotation] = changeCause
}
}
return historyInfo, nil
}
// PrintRolloutHistory prints a formatted table of the input revision history of the deployment
func PrintRolloutHistory(historyInfo HistoryInfo, resource, name string) (string, error) {
if len(historyInfo.RevisionToTemplate) == 0 {
return fmt.Sprintf("No rollout history found in %s %q", resource, name), nil
if len(historyInfo) == 0 {
return "No rollout history found.", nil
}
if revision > 0 {
// Print details of a specific revision
template, ok := historyInfo[revision]
if !ok {
return "", fmt.Errorf("unable to find the specified revision")
}
buf := bytes.NewBuffer([]byte{})
DescribePodTemplate(template, buf)
return buf.String(), nil
}
// Sort the revisionToChangeCause map by revision
revisions := make([]int64, 0, len(historyInfo.RevisionToTemplate))
for r := range historyInfo.RevisionToTemplate {
revisions := make([]int64, 0, len(historyInfo))
for r := range historyInfo {
revisions = append(revisions, r)
}
sliceutil.SortInts64(revisions)
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "%s %q:\n", resource, name)
fmt.Fprintf(out, "REVISION\tCHANGE-CAUSE\n")
for _, r := range revisions {
// Find the change-cause of revision r
changeCause := historyInfo.RevisionToTemplate[r].Annotations[ChangeCauseAnnotation]
changeCause := historyInfo[r].Annotations[ChangeCauseAnnotation]
if len(changeCause) == 0 {
changeCause = "<none>"
}

View file

@ -30,12 +30,6 @@ import (
const (
kubectlAnnotationPrefix = "kubectl.kubernetes.io/"
// TODO: auto-generate this
PossibleResourceTypes = `Possible resource types include (case insensitive): pods (aka 'po'), services (aka 'svc'), deployments,
replicasets (aka 'rs'), replicationcontrollers (aka 'rc'), nodes (aka 'no'), events (aka 'ev'), limitranges (aka 'limits'),
persistentvolumes (aka 'pv'), persistentvolumeclaims (aka 'pvc'), resourcequotas (aka 'quota'), namespaces (aka 'ns'),
serviceaccounts (aka 'sa'), ingresses (aka 'ing'), horizontalpodautoscalers (aka 'hpa'), daemonsets (aka 'ds'), configmaps,
componentstatuses (aka 'cs), endpoints (aka 'ep'), petsets (alpha feature, may be unstable) and secrets.`
)
type NamespaceInfo struct {
@ -127,14 +121,18 @@ func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource)
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)
}
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)
}
@ -144,7 +142,10 @@ 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.
"cm": "configmaps",
"cs": "componentstatuses",
"csr": "certificatesigningrequests",
"deploy": "deployments",
"ds": "daemonsets",
"ep": "endpoints",
"ev": "events",

View file

@ -35,7 +35,7 @@ import (
const (
DefaultHostAcceptRE = "^localhost$,^127\\.0\\.0\\.1$,^\\[::1\\]$"
DefaultPathAcceptRE = "^/.*"
DefaultPathRejectRE = "^/api/.*/exec,^/api/.*/run,^/api/.*/attach"
DefaultPathRejectRE = "^/api/.*/pods/.*/exec,^/api/.*/pods/.*/attach"
DefaultMethodRejectRE = "POST,PUT,PATCH"
)
@ -63,7 +63,7 @@ type FilterServer struct {
delegate http.Handler
}
// Splits a comma separated list of regexps into a array of Regexp objects.
// Splits a comma separated list of regexps into an array of Regexp objects.
func MakeRegexpArray(str string) ([]*regexp.Regexp, error) {
parts := strings.Split(str, ",")
result := make([]*regexp.Regexp, len(parts))

125
vendor/k8s.io/kubernetes/pkg/kubectl/quota.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"
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
)
// ResourceQuotaGeneratorV1 supports stable generation of a resource quota
type ResourceQuotaGeneratorV1 struct {
// The name of a quota object.
Name string
// The hard resource limit string before parsing.
Hard string
// The scopes of a quota object before parsing.
Scopes string
}
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
func (g ResourceQuotaGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"hard", true},
{"scopes", false},
}
}
// Ensure it supports the generator pattern that uses parameter injection
var _ Generator = &ResourceQuotaGeneratorV1{}
// Ensure it supports the generator pattern that uses parameters specified during construction
var _ StructuredGenerator = &ResourceQuotaGeneratorV1{}
func (g ResourceQuotaGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(g.ParamNames(), genericParams)
if err != nil {
return nil, err
}
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
}
delegate := &ResourceQuotaGeneratorV1{}
delegate.Name = params["name"]
delegate.Hard = params["hard"]
delegate.Scopes = params["scopes"]
return delegate.StructuredGenerate()
}
// StructuredGenerate outputs a ResourceQuota object using the configured fields
func (g *ResourceQuotaGeneratorV1) StructuredGenerate() (runtime.Object, error) {
if err := g.validate(); err != nil {
return nil, err
}
resourceList, err := populateResourceList(g.Hard)
if err != nil {
return nil, err
}
scopes, err := parseScopes(g.Scopes)
if err != nil {
return nil, err
}
resourceQuota := &api.ResourceQuota{}
resourceQuota.Name = g.Name
resourceQuota.Spec.Hard = resourceList
resourceQuota.Spec.Scopes = scopes
return resourceQuota, nil
}
// validate validates required fields are set to support structured generation
func (r *ResourceQuotaGeneratorV1) validate() error {
if len(r.Name) == 0 {
return fmt.Errorf("name must be specified")
}
return nil
}
func parseScopes(spec string) ([]api.ResourceQuotaScope, error) {
// empty input gets a nil response to preserve generator test expected behaviors
if spec == "" {
return nil, nil
}
scopes := strings.Split(spec, ",")
result := make([]api.ResourceQuotaScope, 0, len(scopes))
for _, scope := range scopes {
// intentionally do not verify the scope against the valid scope list. This is done by the apiserver anyway.
if scope == "" {
return nil, fmt.Errorf("invalid resource quota scope \"\"")
}
result = append(result, api.ResourceQuotaScope(scope))
}
return result, nil
}

View file

@ -212,6 +212,10 @@ func (b *Builder) ResourceNames(resource string, names ...string) *Builder {
b.resourceTuples = append(b.resourceTuples, tuple)
continue
}
if len(resource) == 0 {
b.errs = append(b.errs, fmt.Errorf("the argument %q must be RESOURCE/NAME", name))
continue
}
// Use the given default type to create a resource tuple
b.resourceTuples = append(b.resourceTuples, resourceTuple{Resource: resource, Name: name})
@ -333,7 +337,7 @@ func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string
}
case len(args) == 0:
default:
b.errs = append(b.errs, fmt.Errorf("when passing arguments, must be resource or resource and name"))
b.errs = append(b.errs, fmt.Errorf("arguments must consist of a resource or a resource and name"))
}
return b
}
@ -362,7 +366,12 @@ func hasCombinedTypeArgs(args []string) (bool, error) {
case hasSlash > 0 && hasSlash == len(args):
return true, nil
case hasSlash > 0 && hasSlash != len(args):
return true, fmt.Errorf("when passing arguments in resource/name form, all arguments must include the resource")
baseCmd := "cmd"
if len(os.Args) > 0 {
baseCmdSlice := strings.Split(os.Args[0], "/")
baseCmd = baseCmdSlice[len(baseCmdSlice)-1]
}
return true, fmt.Errorf("there is no need to specify a resource type as a separate argument when passing arguments in resource/name form (e.g. '%s get resource/<resource_name>' instead of '%s get resource resource/<resource_name>'", baseCmd, baseCmd)
default:
return false, nil
}
@ -449,7 +458,7 @@ func (b *Builder) mappingFor(resourceArg string) (*meta.RESTMapping, error) {
if fullySpecifiedGVR != nil {
gvk, _ = b.mapper.KindFor(*fullySpecifiedGVR)
}
if gvk.IsEmpty() {
if gvk.Empty() {
var err error
gvk, err = b.mapper.KindFor(groupResource.WithVersion(""))
if err != nil {

View file

@ -22,7 +22,6 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
"k8s.io/kubernetes/pkg/runtime"
)
@ -54,20 +53,8 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
if err != nil {
return nil, fmt.Errorf("unable to decode %q: %v", source, err)
}
var obj runtime.Object
var versioned runtime.Object
if isThirdParty, gvkOut, err := thirdpartyresourcedata.IsThirdPartyObject(data, gvk); err != nil {
return nil, err
} else if isThirdParty {
obj, err = runtime.Decode(thirdpartyresourcedata.NewDecoder(nil, gvkOut.Kind), data)
versioned = obj
gvk = gvkOut
} else {
obj, versioned = versions.Last(), versions.First()
}
if err != nil {
return nil, fmt.Errorf("unable to decode %q: %v [%v]", source, err, gvk)
}
obj, versioned := versions.Last(), versions.First()
mapping, err := m.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return nil, fmt.Errorf("unable to recognize %q: %v", source, err)

View file

@ -250,7 +250,7 @@ func AsVersionedObjects(infos []*Info, version unversioned.GroupVersion, encoder
// objects that are not part of api.Scheme must be converted to JSON
// TODO: convert to map[string]interface{}, attach to runtime.Unknown?
if !version.IsEmpty() {
if !version.Empty() {
if _, _, err := api.Scheme.ObjectKinds(info.Object); runtime.IsNotRegisteredError(err) {
// TODO: ideally this would encode to version, but we don't expose multiple codecs here.
data, err := runtime.Encode(encoder, info.Object)
@ -274,13 +274,13 @@ func AsVersionedObjects(infos []*Info, version unversioned.GroupVersion, encoder
// tryConvert attempts to convert the given object to the provided versions in order. This function assumes
// the object is in internal version.
func tryConvert(convertor 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.IsEmpty() {
if version.Empty() {
return object, nil
}
obj, err := convertor.ConvertToVersion(object, version)
obj, err := converter.ConvertToVersion(object, version)
if err != nil {
last = err
continue

View file

@ -207,10 +207,6 @@ func ValidateSchema(data []byte, schema validation.Schema) error {
if schema == nil {
return nil
}
data, err := yaml.ToJSON(data)
if err != nil {
return fmt.Errorf("error converting to YAML: %v", err)
}
if err := schema.ValidateBytes(data); err != nil {
return fmt.Errorf("error validating data: %v; %s", err, stopValidateMessage)
}
@ -389,7 +385,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
// If we have a GroupVersionKind on the list, prioritize that when asking for info on the objects contained in the list
var preferredGVKs []unversioned.GroupVersionKind
if info.Mapping != nil && !info.Mapping.GroupVersionKind.IsEmpty() {
if info.Mapping != nil && !info.Mapping.GroupVersionKind.Empty() {
preferredGVKs = append(preferredGVKs, info.Mapping.GroupVersionKind)
}
@ -431,7 +427,7 @@ func FileVisitorForSTDIN(mapper *Mapper, schema validation.Schema) Visitor {
}
// ExpandPathsToFileVisitors will return a slice of FileVisitors that will handle files from the provided path.
// After FileVisitors open the files, they will pass a io.Reader to a StreamVisitor to do the reading. (stdin
// After FileVisitors open the files, they will pass an io.Reader to a StreamVisitor to do the reading. (stdin
// is also taken care of). Paths argument also accepts a single file, and will return a single visitor
func ExpandPathsToFileVisitors(mapper *Mapper, paths string, recursive bool, extensions []string, schema validation.Schema) ([]Visitor, error) {
var visitors []Visitor
@ -622,13 +618,7 @@ func RetrieveLatest(info *Info, err error) error {
if info.Namespaced() && len(info.Namespace) == 0 {
return fmt.Errorf("no namespace set on resource %s %q", info.Mapping.Resource, info.Name)
}
obj, err := NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export)
if err != nil {
return err
}
info.Object = obj
info.ResourceVersion, _ = info.Mapping.MetadataAccessor.ResourceVersion(obj)
return nil
return info.Get()
}
// RetrieveLazy updates the object if it has not been loaded yet.

View file

@ -39,6 +39,7 @@ import (
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/labels"
@ -127,6 +128,7 @@ func GetPrinter(format, formatArgument string, noHeaders bool) (ResourcePrinter,
if err != nil {
return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
}
defer file.Close()
if printer, err = NewCustomColumnsPrinterFromTemplate(file, api.Codecs.UniversalDecoder()); err != nil {
return nil, false, err
}
@ -145,6 +147,9 @@ type ResourcePrinter interface {
// Print receives a runtime object, formats it and prints it to a writer.
PrintObj(runtime.Object, io.Writer) error
HandledResources() []string
//Can be used to print out warning/clarifications if needed
//after all objects were printed
FinishPrint(io.Writer, string) error
}
// ResourcePrinterFunc is a function that can print objects
@ -160,42 +165,41 @@ func (fn ResourcePrinterFunc) HandledResources() []string {
return []string{}
}
func (fn ResourcePrinterFunc) FinishPrint(io.Writer, string) error {
return nil
}
// VersionedPrinter takes runtime objects and ensures they are converted to a given API version
// prior to being passed to a nested printer.
type VersionedPrinter struct {
printer ResourcePrinter
convertor runtime.ObjectConvertor
converter runtime.ObjectConvertor
versions []unversioned.GroupVersion
}
// NewVersionedPrinter wraps a printer to convert objects to a known API version prior to printing.
func NewVersionedPrinter(printer ResourcePrinter, convertor runtime.ObjectConvertor, versions ...unversioned.GroupVersion) ResourcePrinter {
func NewVersionedPrinter(printer ResourcePrinter, converter runtime.ObjectConvertor, versions ...unversioned.GroupVersion) ResourcePrinter {
return &VersionedPrinter{
printer: printer,
convertor: convertor,
converter: converter,
versions: versions,
}
}
func (p *VersionedPrinter) FinishPrint(w io.Writer, res string) error {
return nil
}
// PrintObj implements ResourcePrinter
func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
if len(p.versions) == 0 {
return fmt.Errorf("no version specified, object cannot be converted")
}
for _, version := range p.versions {
if version.IsEmpty() {
continue
}
converted, err := p.convertor.ConvertToVersion(obj, version)
if runtime.IsNotRegisteredError(err) {
continue
}
if err != nil {
return err
}
return p.printer.PrintObj(converted, w)
converted, err := p.converter.ConvertToVersion(obj, unversioned.GroupVersions(p.versions))
if err != nil {
return err
}
return fmt.Errorf("the object cannot be converted to any of the versions: %v", p.versions)
return p.printer.PrintObj(converted, w)
}
// TODO: implement HandledResources()
@ -209,6 +213,10 @@ type NamePrinter struct {
Typer runtime.ObjectTyper
}
func (p *NamePrinter) FinishPrint(w io.Writer, res string) error {
return nil
}
// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object
// and print "resource/name" pair. If the object is a List, print all items in it.
func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
@ -257,6 +265,10 @@ func (p *NamePrinter) HandledResources() []string {
type JSONPrinter struct {
}
func (p *JSONPrinter) FinishPrint(w io.Writer, res string) error {
return nil
}
// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer.
func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
switch obj := obj.(type) {
@ -265,14 +277,12 @@ func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
return err
}
data, err := json.Marshal(obj)
data, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return err
}
dst := bytes.Buffer{}
err = json.Indent(&dst, data, "", " ")
dst.WriteByte('\n')
_, err = w.Write(dst.Bytes())
data = append(data, '\n')
_, err = w.Write(data)
return err
}
@ -286,7 +296,11 @@ func (p *JSONPrinter) HandledResources() []string {
// to the given version first.
type YAMLPrinter struct {
version string
convertor runtime.ObjectConvertor
converter runtime.ObjectConvertor
}
func (p *YAMLPrinter) FinishPrint(w io.Writer, res string) error {
return nil
}
// PrintObj prints the data as YAML.
@ -317,6 +331,7 @@ func (p *YAMLPrinter) HandledResources() []string {
type handlerEntry struct {
columns []string
printFunc reflect.Value
args []reflect.Value
}
type PrintOptions struct {
@ -327,7 +342,7 @@ type PrintOptions struct {
ShowAll bool
ShowLabels bool
AbsoluteTimestamps bool
KindName string
Kind string
ColumnLabels []string
}
@ -336,31 +351,45 @@ type PrintOptions struct {
// will only be printed if the object type changes. This makes it useful for printing items
// received from watches.
type HumanReadablePrinter struct {
handlerMap map[reflect.Type]*handlerEntry
Options PrintOptions
lastType reflect.Type
handlerMap map[reflect.Type]*handlerEntry
options PrintOptions
lastType reflect.Type
hiddenObjNum int
}
// NewHumanReadablePrinter creates a HumanReadablePrinter.
func NewHumanReadablePrinter(noHeaders, withNamespace bool, wide bool, showAll bool, showLabels bool, absoluteTimestamps bool, columnLabels []string) *HumanReadablePrinter {
func NewHumanReadablePrinter(options PrintOptions) *HumanReadablePrinter {
printer := &HumanReadablePrinter{
handlerMap: make(map[reflect.Type]*handlerEntry),
Options: PrintOptions{
NoHeaders: noHeaders,
WithNamespace: withNamespace,
WithKind: false,
KindName: "",
Wide: wide,
ShowAll: showAll,
ShowLabels: showLabels,
AbsoluteTimestamps: absoluteTimestamps,
ColumnLabels: columnLabels,
},
options: options,
}
printer.addDefaultHandlers()
return printer
}
// formatResourceName receives a resource kind, name, and boolean specifying
// whether or not to update the current name to "kind/name"
func formatResourceName(kind, name string, withKind bool) string {
if !withKind || kind == "" {
return name
}
return kind + "/" + name
}
// GetResourceKind returns the type currently set for a resource
func (h *HumanReadablePrinter) GetResourceKind() string {
return h.options.Kind
}
// EnsurePrintWithKind sets HumanReadablePrinter options "WithKind" to true
// and "Kind" to the string arg it receives, pre-pending this string
// to the "NAME" column in an output of resources.
func (h *HumanReadablePrinter) EnsurePrintWithKind(kind string) {
h.options.WithKind = true
h.options.Kind = kind
}
// Handler adds a print handler with a given set of columns to HumanReadablePrinter instance.
// See validatePrintHandlerFunc for required method signature.
func (h *HumanReadablePrinter) Handler(columns []string, printFunc interface{}) error {
@ -369,6 +398,7 @@ func (h *HumanReadablePrinter) Handler(columns []string, printFunc interface{})
glog.Errorf("Unable to add print handler: %v", err)
return err
}
objType := printFuncValue.Type().In(0)
h.handlerMap[objType] = &handlerEntry{
columns: columns,
@ -416,13 +446,22 @@ 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
}
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", "AGE"}
var replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
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"}
@ -443,6 +482,7 @@ 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"}
// TODO: consider having 'KIND' for third party resource data
var thirdPartyResourceDataColumns = []string{"NAME", "LABELS", "DATA"}
@ -451,13 +491,44 @@ var withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print
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", "VERSION", "AGE"}
var clusterColumns = []string{"NAME", "STATUS", "AGE"}
var networkPolicyColumns = []string{"NAME", "POD-SELECTOR", "AGE"}
var 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
}
return nil
}
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
}
}
return nil
}
// addDefaultHandlers adds print handlers for default Kubernetes types.
func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(podColumns, printPod)
h.Handler(podColumns, printPodList)
h.Handler(podColumns, h.printPodList)
h.Handler(podColumns, h.printPod)
h.Handler(podTemplateColumns, printPodTemplate)
h.Handler(podTemplateColumns, printPodTemplateList)
h.Handler(replicationControllerColumns, printReplicationController)
@ -468,6 +539,8 @@ 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(serviceColumns, printService)
h.Handler(serviceColumns, printServiceList)
h.Handler(ingressColumns, printIngress)
@ -520,6 +593,10 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(clusterRoleColumns, printClusterRoleList)
h.Handler(clusterRoleBindingColumns, printClusterRoleBinding)
h.Handler(clusterRoleBindingColumns, printClusterRoleBindingList)
h.Handler(certificateSigningRequestColumns, printCertificateSigningRequest)
h.Handler(certificateSigningRequestColumns, printCertificateSigningRequestList)
h.Handler(storageClassColumns, printStorageClass)
h.Handler(storageClassColumns, printStorageClassList)
}
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
@ -596,28 +673,15 @@ func translateTimestamp(timestamp unversioned.Time) string {
return shortHumanDuration(time.Now().Sub(timestamp.Time))
}
func printPod(pod *api.Pod, w io.Writer, options PrintOptions) error {
return printPodBase(pod, w, options)
}
func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error {
name := pod.Name
name := formatResourceName(options.Kind, pod.Name, options.WithKind)
namespace := pod.Namespace
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
restarts := 0
totalContainers := len(pod.Spec.Containers)
readyContainers := 0
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)) {
return nil
}
if pod.Status.Reason != "" {
reason = pod.Status.Reason
}
@ -715,23 +779,10 @@ func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error {
return nil
}
func printPodList(podList *api.PodList, w io.Writer, options PrintOptions) error {
for _, pod := range podList.Items {
if err := printPodBase(&pod, w, options); err != nil {
return err
}
}
return nil
}
func printPodTemplate(pod *api.PodTemplate, w io.Writer, options PrintOptions) error {
name := pod.Name
namespace := pod.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, pod.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := pod.Namespace
containers := pod.Template.Spec.Containers
@ -770,14 +821,10 @@ func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, options Pri
// TODO(AdoHe): try to put wide output in a single method
func printReplicationController(controller *api.ReplicationController, w io.Writer, options PrintOptions) error {
name := controller.Name
name := formatResourceName(options.Kind, controller.Name, options.WithKind)
namespace := controller.Namespace
containers := controller.Spec.Template.Spec.Containers
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -787,10 +834,12 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ
desiredReplicas := controller.Spec.Replicas
currentReplicas := controller.Status.Replicas
if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%s",
readyReplicas := controller.Status.ReadyReplicas
if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%d\t%s",
name,
desiredReplicas,
currentReplicas,
readyReplicas,
translateTimestamp(controller.CreationTimestamp),
); err != nil {
return err
@ -824,14 +873,10 @@ func printReplicationControllerList(list *api.ReplicationControllerList, w io.Wr
}
func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options PrintOptions) error {
name := rs.Name
name := formatResourceName(options.Kind, rs.Name, options.WithKind)
namespace := rs.Namespace
containers := rs.Spec.Template.Spec.Containers
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -841,10 +886,12 @@ func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options PrintOption
desiredReplicas := rs.Spec.Replicas
currentReplicas := rs.Status.Replicas
if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%s",
readyReplicas := rs.Status.ReadyReplicas
if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%d\t%s",
name,
desiredReplicas,
currentReplicas,
readyReplicas,
translateTimestamp(rs.CreationTimestamp),
); err != nil {
return err
@ -877,11 +924,8 @@ func printReplicaSetList(list *extensions.ReplicaSetList, w io.Writer, options P
}
func printCluster(c *federation.Cluster, w io.Writer, options PrintOptions) error {
name := c.Name
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
name := formatResourceName(options.Kind, c.Name, options.WithKind)
var statuses []string
for _, condition := range c.Status.Conditions {
if condition.Status == api.ConditionTrue {
@ -913,14 +957,10 @@ func printClusterList(list *federation.ClusterList, w io.Writer, options PrintOp
}
func printJob(job *batch.Job, w io.Writer, options PrintOptions) error {
name := job.Name
name := formatResourceName(options.Kind, job.Name, options.WithKind)
namespace := job.Namespace
containers := job.Spec.Template.Spec.Containers
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -979,6 +1019,42 @@ 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
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
return err
}
}
lastScheduleTime := "<none>"
if scheduledJob.Status.LastScheduleTime != nil {
lastScheduleTime = scheduledJob.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),
lastScheduleTime,
); err != nil {
return err
}
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 {
return err
}
}
return nil
}
// loadBalancerStatusStringer behaves mostly like a string interface and converts the given status to a string.
// `wide` indicates whether the returned value is meant for --o=wide output. If not, it's clipped to 16 bytes.
func loadBalancerStatusStringer(s api.LoadBalancerStatus, wide bool) string {
@ -1020,6 +1096,8 @@ func getServiceExternalIP(svc *api.Service, wide bool) string {
return lbIps
}
return "<pending>"
case api.ServiceTypeExternalName:
return svc.Spec.ExternalName
}
return "<unknown>"
}
@ -1034,16 +1112,12 @@ func makePortString(ports []api.ServicePort) string {
}
func printService(svc *api.Service, w io.Writer, options PrintOptions) error {
name := svc.Name
name := formatResourceName(options.Kind, svc.Name, options.WithKind)
namespace := svc.Namespace
internalIP := svc.Spec.ClusterIP
externalIP := getServiceExternalIP(svc, options.Wide)
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1118,13 +1192,9 @@ func formatPorts(tls []extensions.IngressTLS) string {
}
func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions) error {
name := ingress.Name
namespace := ingress.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, ingress.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := ingress.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1161,14 +1231,10 @@ func printIngressList(ingressList *extensions.IngressList, w io.Writer, options
}
func printPetSet(ps *apps.PetSet, w io.Writer, options PrintOptions) error {
name := ps.Name
name := formatResourceName(options.Kind, ps.Name, options.WithKind)
namespace := ps.Namespace
containers := ps.Spec.Template.Spec.Containers
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1213,13 +1279,9 @@ func printPetSetList(petSetList *apps.PetSetList, w io.Writer, options PrintOpti
}
func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options PrintOptions) error {
name := ds.Name
namespace := ds.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, ds.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := ds.Namespace
containers := ds.Spec.Template.Spec.Containers
@ -1273,13 +1335,9 @@ func printDaemonSetList(list *extensions.DaemonSetList, w io.Writer, options Pri
}
func printEndpoints(endpoints *api.Endpoints, w io.Writer, options PrintOptions) error {
name := endpoints.Name
namespace := endpoints.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, endpoints.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := endpoints.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1306,12 +1364,8 @@ func printEndpointsList(list *api.EndpointsList, w io.Writer, options PrintOptio
}
func printNamespace(item *api.Namespace, w io.Writer, options PrintOptions) error {
name := item.Name
kind := options.KindName
name := formatResourceName(options.Kind, item.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
return fmt.Errorf("namespace is not namespaced")
}
@ -1336,13 +1390,9 @@ func printNamespaceList(list *api.NamespaceList, w io.Writer, options PrintOptio
}
func printSecret(item *api.Secret, w io.Writer, options PrintOptions) error {
name := item.Name
namespace := item.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, item.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := item.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1370,13 +1420,9 @@ func printSecretList(list *api.SecretList, w io.Writer, options PrintOptions) er
}
func printServiceAccount(item *api.ServiceAccount, w io.Writer, options PrintOptions) error {
name := item.Name
namespace := item.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, item.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := item.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1404,12 +1450,8 @@ func printServiceAccountList(list *api.ServiceAccountList, w io.Writer, options
}
func printNode(node *api.Node, w io.Writer, options PrintOptions) error {
name := node.Name
kind := options.KindName
name := formatResourceName(options.Kind, node.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
return fmt.Errorf("node is not namespaced")
}
@ -1457,12 +1499,8 @@ func printNodeList(list *api.NodeList, w io.Writer, options PrintOptions) error
}
func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, options PrintOptions) error {
name := pv.Name
kind := options.KindName
name := formatResourceName(options.Kind, pv.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
return fmt.Errorf("persistentVolume is not namespaced")
}
@ -1506,13 +1544,9 @@ func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer, opti
}
func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, options PrintOptions) error {
name := pvc.Name
namespace := pvc.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, pvc.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := pvc.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1550,13 +1584,9 @@ func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Wr
}
func printEvent(event *api.Event, w io.Writer, options PrintOptions) error {
name := event.InvolvedObject.Name
namespace := event.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, event.InvolvedObject.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := event.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
return err
@ -1622,12 +1652,8 @@ func printLimitRangeList(list *api.LimitRangeList, w io.Writer, options PrintOpt
// printObjectMeta prints the object metadata of a given resource.
func printObjectMeta(meta api.ObjectMeta, w io.Writer, options PrintOptions, namespaced bool) error {
name := meta.Name
kind := options.KindName
name := formatResourceName(options.Kind, meta.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
if namespaced && options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", meta.Namespace); err != nil {
return err
@ -1718,13 +1744,70 @@ func printClusterRoleBindingList(list *rbac.ClusterRoleBindingList, w io.Writer,
return nil
}
func printComponentStatus(item *api.ComponentStatus, w io.Writer, options PrintOptions) error {
name := item.Name
kind := options.KindName
func printCertificateSigningRequest(csr *certificates.CertificateSigningRequest, w io.Writer, options PrintOptions) error {
name := formatResourceName(options.Kind, csr.Name, options.WithKind)
meta := csr.ObjectMeta
if options.WithKind {
name = kind + "/" + name
status, err := extractCSRStatus(csr)
if err != nil {
return err
}
if _, err := fmt.Fprintf(
w, "%s\t%s\t%s\t%s",
name,
translateTimestamp(meta.CreationTimestamp),
csr.Spec.Username,
status,
); err != nil {
return err
}
if _, err := fmt.Fprint(w, AppendLabels(meta.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err = fmt.Fprint(w, AppendAllLabels(options.ShowLabels, meta.Labels))
return err
}
func extractCSRStatus(csr *certificates.CertificateSigningRequest) (string, error) {
var approved, denied bool
for _, c := range csr.Status.Conditions {
switch c.Type {
case certificates.CertificateApproved:
approved = true
case certificates.CertificateDenied:
denied = true
default:
return "", fmt.Errorf("unknown csr conditon %q", c)
}
}
var status string
// must be in order of presidence
if denied {
status += "Denied"
} else if approved {
status += "Approved"
} else {
status += "Pending"
}
if len(csr.Status.Certificate) > 0 {
status += ",Issued"
}
return status, nil
}
func printCertificateSigningRequestList(list *certificates.CertificateSigningRequestList, w io.Writer, options PrintOptions) error {
for i := range list.Items {
if err := printCertificateSigningRequest(&list.Items[i], w, options); err != nil {
return err
}
}
return nil
}
func printComponentStatus(item *api.ComponentStatus, w io.Writer, options PrintOptions) error {
name := formatResourceName(options.Kind, item.Name, options.WithKind)
if options.WithNamespace {
return fmt.Errorf("componentStatus is not namespaced")
}
@ -1765,12 +1848,7 @@ func printComponentStatusList(list *api.ComponentStatusList, w io.Writer, option
}
func printThirdPartyResource(rsrc *extensions.ThirdPartyResource, w io.Writer, options PrintOptions) error {
name := rsrc.Name
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
name := formatResourceName(options.Kind, rsrc.Name, options.WithKind)
versions := make([]string, len(rsrc.Versions))
for ix := range rsrc.Versions {
@ -1802,12 +1880,7 @@ func truncate(str string, maxLen int) string {
}
func printThirdPartyResourceData(rsrc *extensions.ThirdPartyResourceData, w io.Writer, options PrintOptions) error {
name := rsrc.Name
kind := options.KindName
if options.WithKind {
name = kind + "/" + name
}
name := formatResourceName(options.Kind, rsrc.Name, options.WithKind)
l := labels.FormatLabels(rsrc.Labels)
truncateCols := 50
@ -1831,12 +1904,8 @@ func printThirdPartyResourceDataList(list *extensions.ThirdPartyResourceDataList
}
func printDeployment(deployment *extensions.Deployment, w io.Writer, options PrintOptions) error {
name := deployment.Name
kind := options.KindName
name := formatResourceName(options.Kind, deployment.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", deployment.Namespace); err != nil {
return err
@ -1869,12 +1938,8 @@ func printDeploymentList(list *extensions.DeploymentList, w io.Writer, options P
func printHorizontalPodAutoscaler(hpa *autoscaling.HorizontalPodAutoscaler, w io.Writer, options PrintOptions) error {
namespace := hpa.Namespace
name := hpa.Name
kind := options.KindName
name := formatResourceName(options.Kind, hpa.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
reference := fmt.Sprintf("%s/%s",
hpa.Spec.ScaleTargetRef.Kind,
hpa.Spec.ScaleTargetRef.Name)
@ -1926,13 +1991,9 @@ func printHorizontalPodAutoscalerList(list *autoscaling.HorizontalPodAutoscalerL
}
func printConfigMap(configMap *api.ConfigMap, w io.Writer, options PrintOptions) error {
name := configMap.Name
namespace := configMap.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, configMap.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := configMap.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -1959,12 +2020,8 @@ func printConfigMapList(list *api.ConfigMapList, w io.Writer, options PrintOptio
}
func printPodSecurityPolicy(item *extensions.PodSecurityPolicy, w io.Writer, options PrintOptions) error {
name := item.Name
kind := options.KindName
name := formatResourceName(options.Kind, item.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
_, err := fmt.Fprintf(w, "%s\t%t\t%v\t%s\t%s\t%s\t%s\t%t\t%v\n", name, item.Spec.Privileged,
item.Spec.AllowedCapabilities, item.Spec.SELinux.Rule,
item.Spec.RunAsUser.Rule, item.Spec.FSGroup.Rule, item.Spec.SupplementalGroups.Rule, item.Spec.ReadOnlyRootFilesystem, item.Spec.Volumes)
@ -1982,13 +2039,9 @@ func printPodSecurityPolicyList(list *extensions.PodSecurityPolicyList, w io.Wri
}
func printNetworkPolicy(networkPolicy *extensions.NetworkPolicy, w io.Writer, options PrintOptions) error {
name := networkPolicy.Name
namespace := networkPolicy.Namespace
kind := options.KindName
name := formatResourceName(options.Kind, networkPolicy.Name, options.WithKind)
if options.WithKind {
name = kind + "/" + name
}
namespace := networkPolicy.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -2014,6 +2067,32 @@ func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, opt
return nil
}
func printStorageClass(sc *extensions.StorageClass, w io.Writer, options PrintOptions) error {
name := sc.Name
provtype := sc.Provisioner
if _, err := fmt.Fprintf(w, "%s\t%s\t", name, provtype); err != nil {
return err
}
if _, err := fmt.Fprint(w, AppendLabels(sc.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, sc.Labels)); err != nil {
return err
}
return nil
}
func printStorageClassList(scList *extensions.StorageClassList, w io.Writer, options PrintOptions) error {
for _, sc := range scList.Items {
if err := printStorageClass(&sc, w, options); err != nil {
return err
}
}
return nil
}
func AppendLabels(itemLabels map[string]string, columnLabels []string) string {
var buffer bytes.Buffer
@ -2135,18 +2214,18 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
}
t := reflect.TypeOf(obj)
if handler := h.handlerMap[t]; handler != nil {
if !h.Options.NoHeaders && t != h.lastType {
headers := append(handler.columns, formatWideHeaders(h.Options.Wide, t)...)
headers = append(headers, formatLabelHeaders(h.Options.ColumnLabels)...)
if !h.options.NoHeaders && t != h.lastType {
headers := append(handler.columns, formatWideHeaders(h.options.Wide, t)...)
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(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...)
if h.options.WithNamespace {
headers = append(withNamespacePrefixColumns, headers...)
}
h.printHeader(headers, w)
h.lastType = t
}
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.Options)}
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.options)}
resultValue := handler.printFunc.Call(args)[0]
if resultValue.IsNil() {
return nil
@ -2175,6 +2254,10 @@ func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
}, nil
}
func (p *TemplatePrinter) FinishPrint(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)
@ -2322,6 +2405,10 @@ func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
return &JSONPathPrinter{tmpl, j}, nil
}
func (j *JSONPathPrinter) FinishPrint(w io.Writer, res string) error {
return nil
}
// PrintObj formats the obj with the JSONPath Template.
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
var queryObj interface{} = obj

View file

@ -214,7 +214,7 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
if desired > 0 && maxUnavailable == 0 && maxSurge == 0 {
return fmt.Errorf("one of maxSurge or maxUnavailable must be specified")
}
// The minumum pods which must remain available througout the update
// The minimum pods which must remain available throughout the update
// calculated for internal convenience.
minAvailable := int32(integer.IntMax(0, int(desired-maxUnavailable)))
// If the desired new scale is 0, then the max unavailable is necessarily
@ -513,14 +513,14 @@ 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)
return r.c.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); err != nil {
if err := r.c.ReplicationControllers(r.ns).Delete(oldRc.Name, nil); err != nil {
return err
}
fmt.Fprintf(config.Out, "Renaming %s to %s\n", newRc.Name, oldRc.Name)
fmt.Fprintf(config.Out, "Renaming %s to %s\n", oldRc.Name, newRc.Name)
return Rename(r.c, newRc, oldRc.Name)
case PreserveRollingUpdateCleanupPolicy:
return nil
@ -533,13 +533,28 @@ func Rename(c client.ReplicationControllersNamespacer, rc *api.ReplicationContro
oldName := rc.Name
rc.Name = newName
rc.ResourceVersion = ""
_, err := c.ReplicationControllers(rc.Namespace).Create(rc)
// First delete the oldName RC and orphan its pods.
trueVar := true
err := c.ReplicationControllers(rc.Namespace).Delete(oldName, &api.DeleteOptions{OrphanDependents: &trueVar})
if err != nil && !errors.IsNotFound(err) {
return err
}
err = wait.Poll(5*time.Second, 60*time.Second, func() (bool, error) {
_, err := c.ReplicationControllers(rc.Namespace).Get(oldName)
if err == nil {
return false, nil
} else if errors.IsNotFound(err) {
return true, nil
} else {
return false, err
}
})
if err != nil {
return err
}
err = c.ReplicationControllers(rc.Namespace).Delete(oldName)
if err != nil && !errors.IsNotFound(err) {
// Then create the same RC with the new name.
_, err = c.ReplicationControllers(rc.Namespace).Create(rc)
if err != nil {
return err
}
return nil

View file

@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/batch"
batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation"
@ -41,6 +42,7 @@ func (DeploymentV1Beta1) ParamNames() []GeneratorParam {
{"name", true},
{"replicas", true},
{"image", true},
{"image-pull-policy", false},
{"port", false},
{"hostport", false},
{"stdin", false},
@ -89,7 +91,8 @@ func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime
return nil, err
}
if err = updatePodContainers(params, args, envs, podSpec); err != nil {
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
@ -216,6 +219,7 @@ func (JobV1Beta1) ParamNames() []GeneratorParam {
{"default-name", false},
{"name", true},
{"image", true},
{"image-pull-policy", false},
{"port", false},
{"hostport", false},
{"stdin", false},
@ -261,7 +265,8 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object
return nil, err
}
if err = updatePodContainers(params, args, envs, podSpec); err != nil {
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
@ -311,6 +316,7 @@ func (JobV1) ParamNames() []GeneratorParam {
{"default-name", false},
{"name", true},
{"image", true},
{"image-pull-policy", false},
{"port", false},
{"hostport", false},
{"stdin", false},
@ -356,7 +362,8 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err
return nil, err
}
if err = updateV1PodContainers(params, args, envs, podSpec); err != nil {
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updateV1PodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
@ -394,6 +401,106 @@ func (JobV1) Generate(genericParams map[string]interface{}) (runtime.Object, err
return &job, nil
}
type ScheduledJobV2Alpha1 struct{}
func (ScheduledJobV2Alpha1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"labels", false},
{"default-name", false},
{"name", true},
{"image", true},
{"image-pull-policy", false},
{"port", false},
{"hostport", false},
{"stdin", false},
{"leave-stdin-open", false},
{"tty", false},
{"command", false},
{"args", false},
{"env", false},
{"requests", false},
{"limits", false},
{"restart", false},
{"schedule", true},
}
}
func (ScheduledJobV2Alpha1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
args, err := getArgs(genericParams)
if err != nil {
return nil, err
}
envs, err := getV1Envs(genericParams)
if err != nil {
return nil, err
}
params, err := getParams(genericParams)
if err != nil {
return nil, err
}
name, err := getName(params)
if err != nil {
return nil, err
}
labels, err := getLabels(params, true, name)
if err != nil {
return nil, err
}
podSpec, err := makeV1PodSpec(params, name)
if err != nil {
return nil, err
}
imagePullPolicy := v1.PullPolicy(params["image-pull-policy"])
if err = updateV1PodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
leaveStdinOpen, err := GetBool(params, "leave-stdin-open", false)
if err != nil {
return nil, err
}
podSpec.Containers[0].StdinOnce = !leaveStdinOpen && podSpec.Containers[0].Stdin
if err := updateV1PodPorts(params, podSpec); err != nil {
return nil, err
}
restartPolicy := v1.RestartPolicy(params["restart"])
if len(restartPolicy) == 0 {
restartPolicy = v1.RestartPolicyNever
}
podSpec.RestartPolicy = restartPolicy
scheduledJob := batchv2alpha1.ScheduledJob{
ObjectMeta: v1.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: batchv2alpha1.ScheduledJobSpec{
Schedule: params["schedule"],
ConcurrencyPolicy: batchv2alpha1.AllowConcurrent,
JobTemplate: batchv2alpha1.JobTemplateSpec{
Spec: batchv2alpha1.JobSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{
Labels: labels,
},
Spec: *podSpec,
},
},
},
},
}
return &scheduledJob, nil
}
type BasicReplicationController struct{}
func (BasicReplicationController) ParamNames() []GeneratorParam {
@ -403,6 +510,7 @@ func (BasicReplicationController) ParamNames() []GeneratorParam {
{"name", true},
{"replicas", true},
{"image", true},
{"image-pull-policy", false},
{"port", false},
{"hostport", false},
{"stdin", false},
@ -591,7 +699,8 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{})
return nil, err
}
if err = updatePodContainers(params, args, envs, podSpec); err != nil {
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, podSpec); err != nil {
return nil, err
}
@ -618,7 +727,7 @@ func (BasicReplicationController) Generate(genericParams map[string]interface{})
return &controller, nil
}
func updatePodContainers(params map[string]string, args []string, envs []api.EnvVar, podSpec *api.PodSpec) error {
func updatePodContainers(params map[string]string, args []string, envs []api.EnvVar, imagePullPolicy api.PullPolicy, podSpec *api.PodSpec) error {
if len(args) > 0 {
command, err := GetBool(params, "command", false)
if err != nil {
@ -634,10 +743,15 @@ func updatePodContainers(params map[string]string, args []string, envs []api.Env
if len(envs) > 0 {
podSpec.Containers[0].Env = envs
}
if len(imagePullPolicy) > 0 {
// imagePullPolicy should be valid here since we have verified it before.
podSpec.Containers[0].ImagePullPolicy = imagePullPolicy
}
return nil
}
func updateV1PodContainers(params map[string]string, args []string, envs []v1.EnvVar, podSpec *v1.PodSpec) error {
func updateV1PodContainers(params map[string]string, args []string, envs []v1.EnvVar, imagePullPolicy v1.PullPolicy, podSpec *v1.PodSpec) error {
if len(args) > 0 {
command, err := GetBool(params, "command", false)
if err != nil {
@ -653,6 +767,11 @@ func updateV1PodContainers(params map[string]string, args []string, envs []v1.En
if len(envs) > 0 {
podSpec.Containers[0].Env = envs
}
if len(imagePullPolicy) > 0 {
// imagePullPolicy should be valid here since we have verified it before.
podSpec.Containers[0].ImagePullPolicy = imagePullPolicy
}
return nil
}
@ -732,6 +851,7 @@ func (BasicPod) ParamNames() []GeneratorParam {
{"default-name", false},
{"name", true},
{"image", true},
{"image-pull-policy", false},
{"port", false},
{"hostport", false},
{"stdin", false},
@ -795,6 +915,8 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
if len(restartPolicy) == 0 {
restartPolicy = api.RestartPolicyAlways
}
// TODO: Figure out why we set ImagePullPolicy here, whether we can make it
// consistent with the other places imagePullPolicy is set using flag.
pod := api.Pod{
ObjectMeta: api.ObjectMeta{
Name: name,
@ -816,7 +938,8 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
RestartPolicy: restartPolicy,
},
}
if err = updatePodContainers(params, args, envs, &pod.Spec); err != nil {
imagePullPolicy := api.PullPolicy(params["image-pull-policy"])
if err = updatePodContainers(params, args, envs, imagePullPolicy, &pod.Spec); err != nil {
return nil, err
}
@ -835,7 +958,7 @@ func parseEnvs(envArray []string) ([]api.EnvVar, error) {
}
name := env[:pos]
value := env[pos+1:]
if len(name) == 0 || len(value) == 0 {
if len(name) == 0 {
return nil, fmt.Errorf("invalid env: %v", env)
}
if len(validation.IsCIdentifier(name)) != 0 {
@ -856,7 +979,7 @@ func parseV1Envs(envArray []string) ([]v1.EnvVar, error) {
}
name := env[:pos]
value := env[pos+1:]
if len(name) == 0 || len(validation.IsCIdentifier(name)) != 0 || len(value) == 0 {
if len(name) == 0 || len(validation.IsCIdentifier(name)) != 0 {
return nil, fmt.Errorf("invalid env: %v", env)
}
envVar := v1.EnvVar{Name: name, Value: value}

View file

@ -24,10 +24,13 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
"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/fields"
"k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/pkg/watch"
)
// Scaler provides an interface for resources that can be scaled.
@ -36,9 +39,9 @@ type Scaler interface {
// retries in the event of resource version mismatch (if retry is not nil),
// and optionally waits until the status of the resource matches newSize (if wait is not nil)
Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, wait *RetryParams) error
// ScaleSimple does a simple one-shot attempt at scaling - not useful on it's own, but
// ScaleSimple does a simple one-shot attempt at scaling - not useful on its own, but
// a necessary building block for Scale
ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error
ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (updatedResourceVersion string, err error)
}
func ScalerFor(kind unversioned.GroupKind, c client.Interface) (Scaler, error) {
@ -49,6 +52,8 @@ func ScalerFor(kind unversioned.GroupKind, c client.Interface) (Scaler, error) {
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 extensions.Kind("Deployment"):
return &DeploymentScaler{c.Extensions()}, nil
}
@ -108,9 +113,12 @@ func NewRetryParams(interval, timeout time.Duration) *RetryParams {
}
// ScaleCondition is a closure around Scale that facilitates retries via util.wait
func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name string, count uint) wait.ConditionFunc {
func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name string, count uint, updatedResourceVersion *string) wait.ConditionFunc {
return func() (bool, error) {
err := r.ScaleSimple(namespace, name, precondition, count)
rv, err := r.ScaleSimple(namespace, name, precondition, count)
if updatedResourceVersion != nil {
*updatedResourceVersion = rv
}
switch e, _ := err.(ScaleError); err.(type) {
case nil:
return true, nil
@ -124,6 +132,17 @@ 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 {
if precondition.Size != -1 && int(ps.Spec.Replicas) != precondition.Size {
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(int(ps.Spec.Replicas))}
}
if len(precondition.ResourceVersion) != 0 && ps.ResourceVersion != precondition.ResourceVersion {
return PreconditionError{"resource version", precondition.ResourceVersion, ps.ResourceVersion}
}
return nil
}
// ValidateReplicationController ensures that the preconditions match. Returns nil if they are valid, an error otherwise
func (precondition *ScalePrecondition) ValidateReplicationController(controller *api.ReplicationController) error {
if precondition.Size != -1 && int(controller.Spec.Replicas) != precondition.Size {
@ -139,24 +158,27 @@ type ReplicationControllerScaler struct {
c client.Interface
}
func (scaler *ReplicationControllerScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
// resourceVersion of the replication controller if the update is successful.
func (scaler *ReplicationControllerScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
controller, err := scaler.c.ReplicationControllers(namespace).Get(name)
if err != nil {
return ScaleError{ScaleGetFailure, "Unknown", err}
return "", ScaleError{ScaleGetFailure, "Unknown", err}
}
if preconditions != nil {
if err := preconditions.ValidateReplicationController(controller); err != nil {
return err
return "", err
}
}
controller.Spec.Replicas = int32(newSize)
if _, err := scaler.c.ReplicationControllers(namespace).Update(controller); err != nil {
updatedRC, err := scaler.c.ReplicationControllers(namespace).Update(controller)
if err != nil {
if errors.IsConflict(err) {
return ScaleError{ScaleUpdateConflictFailure, controller.ResourceVersion, err}
return "", ScaleError{ScaleUpdateConflictFailure, controller.ResourceVersion, err}
}
return ScaleError{ScaleUpdateFailure, controller.ResourceVersion, err}
return "", ScaleError{ScaleUpdateFailure, controller.ResourceVersion, err}
}
return nil
return updatedRC.ObjectMeta.ResourceVersion, nil
}
// Scale updates a ReplicationController to a new size, with optional precondition check (if preconditions is not nil),
@ -170,20 +192,51 @@ func (scaler *ReplicationControllerScaler) Scale(namespace, name string, newSize
// Make it try only once, immediately
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
}
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize)
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
var updatedResourceVersion string
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, &updatedResourceVersion)
if err := wait.PollImmediate(retry.Interval, retry.Timeout, cond); err != nil {
return err
}
if waitForReplicas != nil {
rc, err := scaler.c.ReplicationControllers(namespace).Get(name)
checkRC := func(rc *api.ReplicationController) bool {
if uint(rc.Spec.Replicas) != newSize {
// the size is changed by other party. Don't need to wait for the new change to complete.
return true
}
return rc.Status.ObservedGeneration >= rc.Generation && rc.Status.Replicas == rc.Spec.Replicas
}
// If number of replicas doesn't change, then the update may not event
// be sent to underlying databse (we don't send no-op changes).
// In such case, <updatedResourceVersion> will have value of the most
// recent update (which may be far in the past) so we may get "too old
// RV" error from watch or potentially no ReplicationController events
// will be deliver, since it may already be in the expected state.
// To protect from these two, we first issue Get() to ensure that we
// are not already in the expected state.
currentRC, err := scaler.c.ReplicationControllers(namespace).Get(name)
if err != nil {
return err
}
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.ControllerHasDesiredReplicas(scaler.c, rc))
if err == wait.ErrWaitTimeout {
return fmt.Errorf("timed out waiting for %q to be synced", name)
if !checkRC(currentRC) {
watchOptions := api.ListOptions{
FieldSelector: fields.OneTermEqualSelector("metadata.name", name),
ResourceVersion: updatedResourceVersion,
}
watcher, err := scaler.c.ReplicationControllers(namespace).Watch(watchOptions)
if err != nil {
return err
}
_, err = watch.Until(waitForReplicas.Timeout, watcher, func(event watch.Event) (bool, error) {
if event.Type != watch.Added && event.Type != watch.Modified {
return false, nil
}
return checkRC(event.Object.(*api.ReplicationController)), nil
})
if err == wait.ErrWaitTimeout {
return fmt.Errorf("timed out waiting for %q to be synced", name)
}
return err
}
return err
}
return nil
}
@ -203,24 +256,27 @@ type ReplicaSetScaler struct {
c client.ExtensionsInterface
}
func (scaler *ReplicaSetScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
// resourceVersion of the replicaset if the update is successful.
func (scaler *ReplicaSetScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
rs, err := scaler.c.ReplicaSets(namespace).Get(name)
if err != nil {
return ScaleError{ScaleGetFailure, "Unknown", err}
return "", ScaleError{ScaleGetFailure, "Unknown", err}
}
if preconditions != nil {
if err := preconditions.ValidateReplicaSet(rs); err != nil {
return err
return "", err
}
}
rs.Spec.Replicas = int32(newSize)
if _, err := scaler.c.ReplicaSets(namespace).Update(rs); err != nil {
updatedRS, err := scaler.c.ReplicaSets(namespace).Update(rs)
if err != nil {
if errors.IsConflict(err) {
return ScaleError{ScaleUpdateConflictFailure, rs.ResourceVersion, err}
return "", ScaleError{ScaleUpdateConflictFailure, rs.ResourceVersion, err}
}
return ScaleError{ScaleUpdateFailure, rs.ResourceVersion, err}
return "", ScaleError{ScaleUpdateFailure, rs.ResourceVersion, err}
}
return nil
return updatedRS.ObjectMeta.ResourceVersion, nil
}
// Scale updates a ReplicaSet to a new size, with optional precondition check (if preconditions is
@ -234,7 +290,7 @@ func (scaler *ReplicaSetScaler) Scale(namespace, name string, newSize uint, prec
// Make it try only once, immediately
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
}
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize)
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
return err
}
@ -244,6 +300,7 @@ func (scaler *ReplicaSetScaler) Scale(namespace, name string, newSize uint, prec
return err
}
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.ReplicaSetHasDesiredReplicas(scaler.c, rs))
if err == wait.ErrWaitTimeout {
return fmt.Errorf("timed out waiting for %q to be synced", name)
}
@ -266,30 +323,85 @@ func (precondition *ScalePrecondition) ValidateJob(job *batch.Job) error {
return nil
}
type PetSetScaler struct {
c client.AppsInterface
}
// 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)
if err != nil {
return "", ScaleError{ScaleGetFailure, "Unknown", err}
}
if preconditions != nil {
if err := preconditions.ValidatePetSet(ps); err != nil {
return "", err
}
}
ps.Spec.Replicas = int(newSize)
updatedPetSet, err := scaler.c.PetSets(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
}
func (scaler *PetSetScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
if preconditions == nil {
preconditions = &ScalePrecondition{-1, ""}
}
if retry == nil {
// Make it try only once, immediately
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
}
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
return err
}
if waitForReplicas != nil {
job, err := scaler.c.PetSets(namespace).Get(name)
if err != nil {
return err
}
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.PetSetHasDesiredPets(scaler.c, job))
if err == wait.ErrWaitTimeout {
return fmt.Errorf("timed out waiting for %q to be synced", name)
}
return err
}
return nil
}
type JobScaler struct {
c client.BatchInterface
}
// ScaleSimple is responsible for updating job's parallelism.
func (scaler *JobScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
// ScaleSimple is responsible for updating job's parallelism. It returns the
// resourceVersion of the job if the update is successful.
func (scaler *JobScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
job, err := scaler.c.Jobs(namespace).Get(name)
if err != nil {
return ScaleError{ScaleGetFailure, "Unknown", err}
return "", ScaleError{ScaleGetFailure, "Unknown", err}
}
if preconditions != nil {
if err := preconditions.ValidateJob(job); err != nil {
return err
return "", err
}
}
parallelism := int32(newSize)
job.Spec.Parallelism = &parallelism
if _, err := scaler.c.Jobs(namespace).Update(job); err != nil {
udpatedJob, err := scaler.c.Jobs(namespace).Update(job)
if err != nil {
if errors.IsConflict(err) {
return ScaleError{ScaleUpdateConflictFailure, job.ResourceVersion, err}
return "", ScaleError{ScaleUpdateConflictFailure, job.ResourceVersion, err}
}
return ScaleError{ScaleUpdateFailure, job.ResourceVersion, err}
return "", ScaleError{ScaleUpdateFailure, job.ResourceVersion, err}
}
return nil
return udpatedJob.ObjectMeta.ResourceVersion, nil
}
// Scale updates a Job to a new size, with optional precondition check (if preconditions is not nil),
@ -303,7 +415,7 @@ func (scaler *JobScaler) Scale(namespace, name string, newSize uint, preconditio
// Make it try only once, immediately
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
}
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize)
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
return err
}
@ -336,28 +448,31 @@ type DeploymentScaler struct {
c client.ExtensionsInterface
}
// ScaleSimple is responsible for updating a deployment's desired replicas count.
func (scaler *DeploymentScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) error {
// ScaleSimple is responsible for updating a deployment's desired replicas
// count. It returns the resourceVersion of the deployment if the update is
// successful.
func (scaler *DeploymentScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
deployment, err := scaler.c.Deployments(namespace).Get(name)
if err != nil {
return ScaleError{ScaleGetFailure, "Unknown", err}
return "", ScaleError{ScaleGetFailure, "Unknown", err}
}
if preconditions != nil {
if err := preconditions.ValidateDeployment(deployment); err != nil {
return err
return "", err
}
}
// TODO(madhusudancs): Fix this when Scale group issues are resolved (see issue #18528).
// For now I'm falling back to regular Deployment update operation.
deployment.Spec.Replicas = int32(newSize)
if _, err := scaler.c.Deployments(namespace).Update(deployment); err != nil {
updatedDeployment, err := scaler.c.Deployments(namespace).Update(deployment)
if err != nil {
if errors.IsConflict(err) {
return ScaleError{ScaleUpdateConflictFailure, deployment.ResourceVersion, err}
return "", ScaleError{ScaleUpdateConflictFailure, deployment.ResourceVersion, err}
}
return ScaleError{ScaleUpdateFailure, deployment.ResourceVersion, err}
return "", ScaleError{ScaleUpdateFailure, deployment.ResourceVersion, err}
}
return nil
return updatedDeployment.ObjectMeta.ResourceVersion, nil
}
// Scale updates a deployment to a new size, with optional precondition check (if preconditions is not nil),
@ -370,7 +485,7 @@ func (scaler *DeploymentScaler) Scale(namespace, name string, newSize uint, prec
// Make it try only once, immediately
retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond}
}
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize)
cond := ScaleCondition(scaler, preconditions, namespace, name, newSize, nil)
if err := wait.Poll(retry.Interval, retry.Timeout, cond); err != nil {
return err
}

View file

@ -72,6 +72,7 @@ func paramNames() []GeneratorParam {
{"target-port", false},
{"port-name", false},
{"session-affinity", false},
{"cluster-ip", false},
}
}
@ -180,11 +181,11 @@ func generate(genericParams map[string]interface{}) (runtime.Object, error) {
Ports: ports,
},
}
targetPortString, found := params["target-port"]
if !found {
targetPortString, found = params["container-port"]
targetPortString := params["target-port"]
if len(targetPortString) == 0 {
targetPortString = params["container-port"]
}
if found && len(targetPortString) > 0 {
if len(targetPortString) > 0 {
var targetPort intstr.IntOrString
if portNum, err := strconv.Atoi(targetPortString); err != nil {
targetPort = intstr.FromString(targetPortString)
@ -225,5 +226,12 @@ func generate(genericParams map[string]interface{}) (runtime.Object, error) {
return nil, fmt.Errorf("unknown session affinity: %s", params["session-affinity"])
}
}
if len(params["cluster-ip"]) != 0 {
if params["cluster-ip"] == "None" {
service.Spec.ClusterIP = api.ClusterIPNone
} else {
service.Spec.ClusterIP = params["cluster-ip"]
}
}
return &service, nil
}

207
vendor/k8s.io/kubernetes/pkg/kubectl/service_basic.go generated vendored Normal file
View file

@ -0,0 +1,207 @@
/*
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"
"strconv"
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/intstr"
)
type ServiceCommonGeneratorV1 struct {
Name string
TCP []string
Type api.ServiceType
ClusterIP string
}
type ServiceClusterIPGeneratorV1 struct {
ServiceCommonGeneratorV1
}
type ServiceNodePortGeneratorV1 struct {
ServiceCommonGeneratorV1
}
type ServiceLoadBalancerGeneratorV1 struct {
ServiceCommonGeneratorV1
}
func (ServiceClusterIPGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"tcp", true},
{"clusterip", false},
}
}
func (ServiceNodePortGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"tcp", true},
}
}
func (ServiceLoadBalancerGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"tcp", true},
}
}
func parsePorts(portString string) (int32, intstr.IntOrString, error) {
portStringSlice := strings.Split(portString, ":")
port, err := strconv.Atoi(portStringSlice[0])
if err != nil {
return 0, intstr.FromInt(0), err
}
if len(portStringSlice) == 1 {
return int32(port), intstr.FromInt(int(port)), nil
}
var targetPort intstr.IntOrString
if portNum, err := strconv.Atoi(portStringSlice[1]); err != nil {
targetPort = intstr.FromString(portStringSlice[1])
} else {
targetPort = intstr.FromInt(portNum)
}
return int32(port), targetPort, nil
}
func (s ServiceCommonGeneratorV1) GenerateCommon(params map[string]interface{}) error {
name, isString := params["name"].(string)
if !isString {
return fmt.Errorf("expected string, saw %v for 'name'", name)
}
tcpStrings, isArray := params["tcp"].([]string)
if !isArray {
return fmt.Errorf("expected []string, found :%v", tcpStrings)
}
clusterip, isString := params["clusterip"].(string)
if !isString {
return fmt.Errorf("expected string, saw %v for 'clusterip'", clusterip)
}
s.Name = name
s.TCP = tcpStrings
s.ClusterIP = clusterip
return nil
}
func (s ServiceLoadBalancerGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(s.ParamNames(), params)
if err != nil {
return nil, err
}
delegate := &ServiceCommonGeneratorV1{Type: api.ServiceTypeLoadBalancer, ClusterIP: ""}
err = delegate.GenerateCommon(params)
if err != nil {
return nil, err
}
return delegate.StructuredGenerate()
}
func (s ServiceNodePortGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(s.ParamNames(), params)
if err != nil {
return nil, err
}
delegate := &ServiceCommonGeneratorV1{Type: api.ServiceTypeNodePort, ClusterIP: ""}
err = delegate.GenerateCommon(params)
if err != nil {
return nil, err
}
return delegate.StructuredGenerate()
}
func (s ServiceClusterIPGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(s.ParamNames(), params)
if err != nil {
return nil, err
}
delegate := &ServiceCommonGeneratorV1{Type: api.ServiceTypeClusterIP, ClusterIP: ""}
err = delegate.GenerateCommon(params)
if err != nil {
return nil, err
}
return delegate.StructuredGenerate()
}
// validate validates required fields are set to support structured generation
func (s ServiceCommonGeneratorV1) validate() error {
if len(s.Name) == 0 {
return fmt.Errorf("name must be specified")
}
if len(s.Type) == 0 {
return fmt.Errorf("type must be specified")
}
if s.ClusterIP == api.ClusterIPNone && s.Type != api.ServiceTypeClusterIP {
return fmt.Errorf("ClusterIP=None can only be used with ClusterIP service type")
}
if s.ClusterIP == api.ClusterIPNone && len(s.TCP) > 0 {
return fmt.Errorf("can not map ports with clusterip=None")
}
if s.ClusterIP != api.ClusterIPNone && len(s.TCP) == 0 {
return fmt.Errorf("at least one tcp port specifier must be provided")
}
return nil
}
func (s ServiceCommonGeneratorV1) StructuredGenerate() (runtime.Object, error) {
err := s.validate()
if err != nil {
return nil, err
}
ports := []api.ServicePort{}
for _, tcpString := range s.TCP {
port, targetPort, err := parsePorts(tcpString)
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"),
})
}
// setup default label and selector
labels := map[string]string{}
labels["app"] = s.Name
selector := map[string]string{}
selector["app"] = s.Name
service := api.Service{
ObjectMeta: api.ObjectMeta{
Name: s.Name,
Labels: labels,
},
Spec: api.ServiceSpec{
Type: api.ServiceType(s.Type),
Selector: selector,
Ports: ports,
},
}
if len(s.ClusterIP) > 0 {
service.Spec.ClusterIP = s.ClusterIP
}
return &service, nil
}

View file

@ -40,6 +40,10 @@ type SortingPrinter struct {
Decoder runtime.Decoder
}
func (s *SortingPrinter) FinishPrint(w io.Writer, res string) error {
return nil
}
func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
if !meta.IsListType(obj) {
return s.Delegate.PrintObj(obj, out)

View file

@ -25,6 +25,7 @@ import (
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"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"
@ -32,6 +33,7 @@ import (
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/uuid"
"k8s.io/kubernetes/pkg/util/wait"
)
@ -81,6 +83,9 @@ func ReaperFor(kind unversioned.GroupKind, c client.Interface) (Reaper, error) {
case extensions.Kind("Job"), batch.Kind("Job"):
return &JobReaper{c, Interval, Timeout}, nil
case apps.Kind("PetSet"):
return &PetSetReaper{c, Interval, Timeout}, nil
case extensions.Kind("Deployment"):
return &DeploymentReaper{c, Interval, Timeout}, nil
@ -118,6 +123,10 @@ type PodReaper struct {
type ServiceReaper struct {
client.Interface
}
type PetSetReaper struct {
client.Interface
pollInterval, timeout time.Duration
}
type objInterface interface {
Delete(name string) error
@ -201,7 +210,9 @@ func (reaper *ReplicationControllerReaper) Stop(namespace, name string, timeout
return err
}
}
return rc.Delete(name)
falseVar := false
deleteOptions := &api.DeleteOptions{OrphanDependents: &falseVar}
return rc.Delete(name, deleteOptions)
}
// TODO(madhusudancs): Implement it when controllerRef is implemented - https://github.com/kubernetes/kubernetes/issues/2210
@ -273,10 +284,9 @@ func (reaper *ReplicaSetReaper) Stop(namespace, name string, timeout time.Durati
}
}
if err := rsc.Delete(name, nil); err != nil {
return err
}
return nil
falseVar := false
deleteOptions := &api.DeleteOptions{OrphanDependents: &falseVar}
return rsc.Delete(name, deleteOptions)
}
func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
@ -290,7 +300,7 @@ func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duratio
// daemon pods. Once it's done deleting the daemon pods, it's safe to delete
// the DaemonSet.
ds.Spec.Template.Spec.NodeSelector = map[string]string{
string(util.NewUUID()): string(util.NewUUID()),
string(uuid.NewUUID()): string(uuid.NewUUID()),
}
// force update to avoid version conflict
ds.ResourceVersion = ""
@ -305,6 +315,7 @@ func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duratio
if err != nil {
return false, nil
}
return updatedDS.Status.CurrentNumberScheduled+updatedDS.Status.NumberMisscheduled == 0, nil
}); err != nil {
return err
@ -313,6 +324,53 @@ func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duratio
return reaper.Extensions().DaemonSets(namespace).Delete(name)
}
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)
if err != nil {
return err
}
if timeout == 0 {
numPets := ps.Spec.Replicas
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 {
return err
}
// TODO: This shouldn't be needed, see corresponding TODO in PetSetHasDesiredPets.
// PetSet should track generation number.
pods := reaper.Pods(namespace)
selector, _ := unversioned.LabelSelectorAsSelector(ps.Spec.Selector)
options := api.ListOptions{LabelSelector: selector}
podList, err := pods.List(options)
if err != nil {
return err
}
errList := []error{}
for _, pod := range podList.Items {
if err := pods.Delete(pod.Name, gracePeriod); err != nil {
if !errors.IsNotFound(err) {
errList = append(errList, err)
}
}
}
if len(errList) > 0 {
return utilerrors.NewAggregate(errList)
}
// TODO: Cleanup volumes? We don't want to accidentally delete volumes from
// stop, so just leave this up to the the petset.
return petsets.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)
@ -397,9 +455,10 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati
for _, rc := range rsList.Items {
if err := rsReaper.Stop(rc.Namespace, rc.Name, timeout, gracePeriod); err != nil {
scaleGetErr, ok := err.(*ScaleError)
if !errors.IsNotFound(err) || ok && !errors.IsNotFound(scaleGetErr.ActualError) {
errList = append(errList, err)
if errors.IsNotFound(err) || (ok && errors.IsNotFound(scaleGetErr.ActualError)) {
continue
}
errList = append(errList, err)
}
}
if len(errList) > 0 {
@ -407,7 +466,7 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati
}
// Delete deployment at the end.
// Note: We delete deployment at the end so that if removing RSs fails, we atleast have the deployment to retry.
// Note: We delete deployment at the end so that if removing RSs fails, we at least have the deployment to retry.
return deployments.Delete(name, nil)
}
@ -424,7 +483,11 @@ func (reaper *DeploymentReaper) updateDeploymentWithRetries(namespace, name stri
if deployment, err = deployments.Update(deployment); err == nil {
return true, nil
}
return false, nil
// Retry only on update conflict.
if errors.IsConflict(err) {
return false, nil
}
return false, err
})
return deployment, err
}