Update godeps

This commit is contained in:
Prashanth Balasubramanian 2016-06-21 11:58:43 -07:00
parent 423433bc5f
commit 701c5a0e30
482 changed files with 86915 additions and 19741 deletions

View file

@ -4,4 +4,5 @@ assignees:
- deads2k
- janetkuo
- jlowdermilk
- pwittrock
- smarterclayton

View file

@ -19,6 +19,7 @@ package kubectl
import (
"encoding/json"
"k8s.io/kubernetes/pkg/api/annotations"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
@ -28,23 +29,19 @@ type debugError interface {
DebugError() (msg string, args []interface{})
}
// LastAppliedConfigAnnotation is the annotation used to store the previous
// configuration of a resource for use in a three way diff by UpdateApplyAnnotation.
const LastAppliedConfigAnnotation = kubectlAnnotationPrefix + "last-applied-configuration"
// GetOriginalConfiguration retrieves the original configuration of the object
// from the annotation, or nil if no annotation was found.
func GetOriginalConfiguration(info *resource.Info) ([]byte, error) {
annotations, err := info.Mapping.MetadataAccessor.Annotations(info.Object)
func GetOriginalConfiguration(mapping *meta.RESTMapping, obj runtime.Object) ([]byte, error) {
annots, err := mapping.MetadataAccessor.Annotations(obj)
if err != nil {
return nil, err
}
if annotations == nil {
if annots == nil {
return nil, nil
}
original, ok := annotations[LastAppliedConfigAnnotation]
original, ok := annots[annotations.LastAppliedConfigAnnotation]
if !ok {
return nil, nil
}
@ -60,17 +57,17 @@ func SetOriginalConfiguration(info *resource.Info, original []byte) error {
}
accessor := info.Mapping.MetadataAccessor
annotations, err := accessor.Annotations(info.Object)
annots, err := accessor.Annotations(info.Object)
if err != nil {
return err
}
if annotations == nil {
annotations = map[string]string{}
if annots == nil {
annots = map[string]string{}
}
annotations[LastAppliedConfigAnnotation] = string(original)
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annotations); err != nil {
annots[annotations.LastAppliedConfigAnnotation] = string(original)
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
return err
}
@ -93,14 +90,14 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
}
// Get the current annotations from the object.
annotations := accessor.GetAnnotations()
if annotations == nil {
annotations = map[string]string{}
annots := accessor.GetAnnotations()
if annots == nil {
annots = map[string]string{}
}
original := annotations[LastAppliedConfigAnnotation]
delete(annotations, LastAppliedConfigAnnotation)
accessor.SetAnnotations(annotations)
original := annots[annotations.LastAppliedConfigAnnotation]
delete(annots, annotations.LastAppliedConfigAnnotation)
accessor.SetAnnotations(annots)
// TODO: this needs to be abstracted - there should be no assumption that versioned object
// can be marshalled to JSON.
modified, err = json.Marshal(info.VersionedObject)
@ -109,8 +106,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
}
if annotate {
annotations[LastAppliedConfigAnnotation] = string(modified)
accessor.SetAnnotations(annotations)
annots[annotations.LastAppliedConfigAnnotation] = string(modified)
accessor.SetAnnotations(annots)
// TODO: this needs to be abstracted - there should be no assumption that versioned object
// can be marshalled to JSON.
modified, err = json.Marshal(info.VersionedObject)
@ -120,24 +117,24 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
}
// Restore the object to its original condition.
annotations[LastAppliedConfigAnnotation] = original
accessor.SetAnnotations(annotations)
annots[annotations.LastAppliedConfigAnnotation] = original
accessor.SetAnnotations(annots)
} else {
// Otherwise, use the server side version of the object.
accessor := info.Mapping.MetadataAccessor
// Get the current annotations from the object.
annotations, err := accessor.Annotations(info.Object)
annots, err := accessor.Annotations(info.Object)
if err != nil {
return nil, err
}
if annotations == nil {
annotations = map[string]string{}
if annots == nil {
annots = map[string]string{}
}
original := annotations[LastAppliedConfigAnnotation]
delete(annotations, LastAppliedConfigAnnotation)
if err := accessor.SetAnnotations(info.Object, annotations); err != nil {
original := annots[annotations.LastAppliedConfigAnnotation]
delete(annots, annotations.LastAppliedConfigAnnotation)
if err := accessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err
}
@ -147,8 +144,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
}
if annotate {
annotations[LastAppliedConfigAnnotation] = string(modified)
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annotations); err != nil {
annots[annotations.LastAppliedConfigAnnotation] = string(modified)
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err
}
@ -159,8 +156,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
}
// Restore the object to its original condition.
annotations[LastAppliedConfigAnnotation] = original
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annotations); err != nil {
annots[annotations.LastAppliedConfigAnnotation] = original
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err
}
}
@ -171,7 +168,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
// UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied
// configuration annotation is already present. Otherwise, it does nothing.
func UpdateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error {
if original, err := GetOriginalConfiguration(info); err != nil || len(original) <= 0 {
if original, err := GetOriginalConfiguration(info.Mapping, info.Object); err != nil || len(original) <= 0 {
return err
}
return CreateApplyAnnotation(info, codec)

View file

@ -21,14 +21,10 @@ import (
"strconv"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/runtime"
)
const (
scaleSubResource = "scale"
)
type HorizontalPodAutoscalerV1Beta1 struct{}
func (HorizontalPodAutoscalerV1Beta1) ParamNames() []GeneratorParam {
@ -46,6 +42,29 @@ func (HorizontalPodAutoscalerV1Beta1) ParamNames() []GeneratorParam {
}
func (HorizontalPodAutoscalerV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
return generateHPA(genericParams)
}
type HorizontalPodAutoscalerV1 struct{}
func (HorizontalPodAutoscalerV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"default-name", true},
{"name", false},
{"scaleRef-kind", false},
{"scaleRef-name", false},
{"scaleRef-apiVersion", false},
{"min", false},
{"max", true},
{"cpu-percent", false},
}
}
func (HorizontalPodAutoscalerV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
return generateHPA(genericParams)
}
func generateHPA(genericParams map[string]interface{}) (runtime.Object, error) {
params := map[string]string{}
for key, value := range genericParams {
strVal, isString := value.(string)
@ -86,16 +105,15 @@ func (HorizontalPodAutoscalerV1Beta1) Generate(genericParams map[string]interfac
}
}
scaler := extensions.HorizontalPodAutoscaler{
scaler := autoscaling.HorizontalPodAutoscaler{
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: extensions.HorizontalPodAutoscalerSpec{
ScaleRef: extensions.SubresourceReference{
Kind: params["scaleRef-kind"],
Name: params["scaleRef-name"],
APIVersion: params["scaleRef-apiVersion"],
Subresource: scaleSubResource,
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{
Kind: params["scaleRef-kind"],
Name: params["scaleRef-name"],
APIVersion: params["scaleRef-apiVersion"],
},
MaxReplicas: int32(max),
},
@ -105,7 +123,8 @@ func (HorizontalPodAutoscalerV1Beta1) Generate(genericParams map[string]interfac
scaler.Spec.MinReplicas = &v
}
if cpu >= 0 {
scaler.Spec.CPUUtilization = &extensions.CPUTargetUtilization{TargetPercentage: int32(cpu)}
c := int32(cpu)
scaler.Spec.TargetCPUUtilizationPercentage = &c
}
return &scaler, nil
}

View file

@ -28,7 +28,7 @@ import (
func AddJsonFilenameFlag(cmd *cobra.Command, value *[]string, usage string) {
cmd.Flags().StringSliceVarP(value, "filename", "f", *value, usage)
annotations := []string{}
annotations := make([]string, 0, len(resource.FileExtensions))
for _, ext := range resource.FileExtensions {
annotations = append(annotations, strings.TrimLeft(ext, "."))
}

View file

@ -17,6 +17,7 @@ limitations under the License.
package util
import (
fed_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/client/restclient"
@ -26,9 +27,10 @@ import (
func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
return &ClientCache{
clients: make(map[unversioned.GroupVersion]*client.Client),
configs: make(map[unversioned.GroupVersion]*restclient.Config),
loader: loader,
clients: make(map[unversioned.GroupVersion]*client.Client),
configs: make(map[unversioned.GroupVersion]*restclient.Config),
fedClientSets: make(map[unversioned.GroupVersion]fed_clientset.Interface),
loader: loader,
}
}
@ -37,6 +39,7 @@ func NewClientCache(loader clientcmd.ClientConfig) *ClientCache {
type ClientCache struct {
loader clientcmd.ClientConfig
clients map[unversioned.GroupVersion]*client.Client
fedClientSets map[unversioned.GroupVersion]fed_clientset.Interface
configs map[unversioned.GroupVersion]*restclient.Config
defaultConfig *restclient.Config
defaultClient *client.Client
@ -125,3 +128,41 @@ func (c *ClientCache) ClientForVersion(version *unversioned.GroupVersion) (*clie
return kubeclient, nil
}
func (c *ClientCache) FederationClientSetForVersion(version *unversioned.GroupVersion) (fed_clientset.Interface, error) {
if version != nil {
if clientSet, found := c.fedClientSets[*version]; found {
return clientSet, nil
}
}
config, err := c.ClientConfigForVersion(version)
if err != nil {
return nil, err
}
// TODO: support multi versions of client with clientset
clientSet, err := fed_clientset.NewForConfig(config)
if err != nil {
return nil, err
}
c.fedClientSets[*config.GroupVersion] = clientSet
if version != nil {
configCopy := *config
clientSet, err := fed_clientset.NewForConfig(&configCopy)
if err != nil {
return nil, err
}
c.fedClientSets[*version] = clientSet
}
return clientSet, nil
}
func (c *ClientCache) FederationClientForVersion(version *unversioned.GroupVersion) (*restclient.RESTClient, error) {
fedClientSet, err := c.FederationClientSetForVersion(version)
if err != nil {
return nil, err
}
return fedClientSet.(*fed_clientset.Clientset).FederationClient.RESTClient, nil
}

View file

@ -36,8 +36,11 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/kubernetes/federation/apis/federation"
"k8s.io/kubernetes/pkg/api"
apierrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/service"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apimachinery"
@ -46,7 +49,8 @@ import (
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/metrics"
"k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/client/restclient"
client "k8s.io/kubernetes/pkg/client/unversioned"
clientset "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
@ -103,12 +107,16 @@ type Factory struct {
HistoryViewer func(mapping *meta.RESTMapping) (kubectl.HistoryViewer, error)
// Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error
Rollbacker func(mapping *meta.RESTMapping) (kubectl.Rollbacker, error)
// Returns a StatusViewer for printing rollout status.
StatusViewer func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error)
// MapBasedSelectorForObject returns the map-based selector associated with the provided object. If a
// new set-based selector is provided, an error is returned if the selector cannot be converted to a
// map-based selector
MapBasedSelectorForObject func(object runtime.Object) (string, error)
// PortsForObject returns the ports associated with the provided object
PortsForObject func(object runtime.Object) ([]string, error)
// ProtocolsForObject returns the <port, protocol> mapping associated with the provided object
ProtocolsForObject func(object runtime.Object) (map[string]string, error)
// LabelsForObject returns the labels associated with the provided object
LabelsForObject func(object runtime.Object) (map[string]string, error)
// LogsForObject returns a request for the logs associated with the provided object
@ -123,7 +131,7 @@ type Factory struct {
SwaggerSchema func(unversioned.GroupVersionKind) (*swagger.ApiDeclaration, error)
// Returns the default namespace to use in cases where no
// other namespace is specified and whether the namespace was
// overriden.
// overridden.
DefaultNamespace func() (string, bool, error)
// Generators returns the generators for the provided command
Generators func(cmdName string) map[string]kubectl.Generator
@ -133,10 +141,15 @@ type Factory struct {
CanBeAutoscaled func(kind unversioned.GroupKind) error
// AttachablePodForObject returns the pod to which to attach given an object.
AttachablePodForObject func(object runtime.Object) (*api.Pod, error)
// UpdatePodSpecForObject will call the provided function on the pod spec this object supports,
// return false if no pod spec is supported, or return an error.
UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
// EditorEnvs returns a group of environment variables that the edit command
// can range over in order to determine if the user has specified an editor
// of their choice.
EditorEnvs func() []string
// PrintObjectSpecificMessage prints object-specific messages on the provided writer
PrintObjectSpecificMessage func(obj runtime.Object, out io.Writer)
}
const (
@ -146,12 +159,14 @@ const (
ServiceV2GeneratorName = "service/v2"
ServiceAccountV1GeneratorName = "serviceaccount/v1"
HorizontalPodAutoscalerV1Beta1GeneratorName = "horizontalpodautoscaler/v1beta1"
HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1"
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
JobV1Beta1GeneratorName = "job/v1beta1"
JobV1GeneratorName = "job/v1"
NamespaceV1GeneratorName = "namespace/v1"
SecretV1GeneratorName = "secret/v1"
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
SecretForTLSV1GeneratorName = "secret-for-tls/v1"
ConfigMapV1GeneratorName = "configmap/v1"
)
@ -171,6 +186,7 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
}
generators["autoscale"] = map[string]kubectl.Generator{
HorizontalPodAutoscalerV1Beta1GeneratorName: kubectl.HorizontalPodAutoscalerV1Beta1{},
HorizontalPodAutoscalerV1GeneratorName: kubectl.HorizontalPodAutoscalerV1{},
}
generators["namespace"] = map[string]kubectl.Generator{
NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{},
@ -181,6 +197,10 @@ func DefaultGenerators(cmdName string) map[string]kubectl.Generator {
generators["secret-for-docker-registry"] = map[string]kubectl.Generator{
SecretForDockerRegistryV1GeneratorName: kubectl.SecretForDockerRegistryGeneratorV1{},
}
generators["secret-for-tls"] = map[string]kubectl.Generator{
SecretForTLSV1GeneratorName: kubectl.SecretForTLSGeneratorV1{},
}
return generators[cmdName]
}
@ -242,7 +262,18 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
client, err := clients.ClientForVersion(&unversioned.GroupVersion{Version: "v1"})
CheckErr(err)
versions, gvks, err := GetThirdPartyGroupVersions(client.Discovery())
var versions []unversioned.GroupVersion
var gvks []unversioned.GroupVersionKind
retries := 3
for i := 0; i < retries; i++ {
versions, gvks, err = GetThirdPartyGroupVersions(client.Discovery())
// Retry if we got a NotFound error, because user may delete
// a thirdparty group when the GetThirdPartyGroupVersions is
// running.
if err == nil || !apierrors.IsNotFound(err) {
break
}
}
CheckErr(err)
if len(versions) > 0 {
priorityMapper, ok := mapper.RESTMapper.(meta.PriorityRESTMapper)
@ -288,13 +319,15 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
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: metrics.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: metrics.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
{Group: federation.GroupName, Version: meta.AnyVersion, Kind: meta.AnyKind},
},
}
return priorityRESTMapper, api.Scheme
@ -319,6 +352,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
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:
@ -327,6 +362,10 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
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
default:
if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
return nil, fmt.Errorf("unknown api group/version: %s", gvk.String())
@ -338,12 +377,22 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
gv := gvk.GroupVersion()
cfg.GroupVersion = &gv
cfg.APIPath = "/apis"
cfg.Codec = thirdpartyresourcedata.NewCodec(c.ExtensionsClient.RESTClient.Codec(), gvk.Kind)
cfg.Codec = thirdpartyresourcedata.NewCodec(c.ExtensionsClient.RESTClient.Codec(), gvk)
cfg.NegotiatedSerializer = thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, gvk.Kind, gv, gv)
return restclient.RESTClientFor(cfg)
}
},
Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
if mapping.GroupVersionKind.Group == federation.GroupName {
fedClientSet, err := clients.FederationClientSetForVersion(&mappingVersion)
if err != nil {
return nil, err
}
if mapping.GroupVersionKind.Kind == "Cluster" {
return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil
}
}
client, err := clients.ClientForVersion(&mappingVersion)
if err != nil {
return nil, err
@ -354,10 +403,14 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind)
},
Decoder: func(toInternal bool) runtime.Decoder {
var decoder runtime.Decoder
if toInternal {
return api.Codecs.UniversalDecoder()
decoder = api.Codecs.UniversalDecoder()
} else {
decoder = api.Codecs.UniversalDeserializer()
}
return api.Codecs.UniversalDeserializer()
return thirdpartyresourcedata.NewDecoder(decoder, "")
},
JSONEncoder: func() runtime.Encoder {
return api.Codecs.LegacyCodec(registered.EnabledVersions()...)
@ -395,11 +448,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
default:
gvk, err := api.Scheme.ObjectKind(object)
gvks, _, err := api.Scheme.ObjectKinds(object)
if err != nil {
return "", err
}
return "", fmt.Errorf("cannot extract pod selector from %v", gvk)
return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0])
}
},
PortsForObject: func(object runtime.Object) ([]string, error) {
@ -416,11 +469,32 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
case *extensions.ReplicaSet:
return getPorts(t.Spec.Template.Spec), nil
default:
gvk, err := api.Scheme.ObjectKind(object)
gvks, _, err := api.Scheme.ObjectKinds(object)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("cannot extract ports from %v", gvk)
return nil, fmt.Errorf("cannot extract ports from %v", gvks[0])
}
},
ProtocolsForObject: func(object runtime.Object) (map[string]string, error) {
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
switch t := object.(type) {
case *api.ReplicationController:
return getProtocols(t.Spec.Template.Spec), nil
case *api.Pod:
return getProtocols(t.Spec), nil
case *api.Service:
return getServiceProtocols(t.Spec), nil
case *extensions.Deployment:
return getProtocols(t.Spec.Template.Spec), nil
case *extensions.ReplicaSet:
return getProtocols(t.Spec.Template.Spec), nil
default:
gvks, _, err := api.Scheme.ObjectKinds(object)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0])
}
},
LabelsForObject: func(object runtime.Object) (map[string]string, error) {
@ -478,11 +552,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil
default:
gvk, err := api.Scheme.ObjectKind(object)
gvks, _, err := api.Scheme.ObjectKinds(object)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("cannot get the logs from %v", gvk)
return nil, fmt.Errorf("cannot get the logs from %v", gvks[0])
}
},
PauseObject: func(object runtime.Object) (bool, error) {
@ -500,11 +574,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
_, err := c.Extensions().Deployments(t.Namespace).Update(t)
return false, err
default:
gvk, err := api.Scheme.ObjectKind(object)
gvks, _, err := api.Scheme.ObjectKinds(object)
if err != nil {
return false, err
}
return false, fmt.Errorf("cannot pause %v", gvk)
return false, fmt.Errorf("cannot pause %v", gvks[0])
}
},
ResumeObject: func(object runtime.Object) (bool, error) {
@ -522,11 +596,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
_, err := c.Extensions().Deployments(t.Namespace).Update(t)
return false, err
default:
gvk, err := api.Scheme.ObjectKind(object)
gvks, _, err := api.Scheme.ObjectKinds(object)
if err != nil {
return false, err
}
return false, fmt.Errorf("cannot resume %v", gvk)
return false, fmt.Errorf("cannot resume %v", gvks[0])
}
},
Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) {
@ -562,6 +636,14 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), client)
},
StatusViewer: func(mapping *meta.RESTMapping) (kubectl.StatusViewer, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
client, err := clients.ClientForVersion(&mappingVersion)
if err != nil {
return nil, err
}
return kubectl.StatusViewerFor(mapping.GroupVersionKind.GroupKind(), client)
},
Validator: func(validate bool, cacheDir string) (validation.Schema, error) {
if validate {
client, err := clients.ClientForVersion(nil)
@ -576,8 +658,13 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
dir = path.Join(cacheDir, version.String())
}
fedClient, err := clients.FederationClientForVersion(nil)
if err != nil {
return nil, err
}
return &clientSwaggerSchema{
c: client,
fedc: fedClient,
cacheDir: dir,
mapper: api.RESTMapper,
}, nil
@ -646,16 +733,68 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
case *api.Pod:
return t, nil
default:
gvk, err := api.Scheme.ObjectKind(object)
gvks, _, err := api.Scheme.ObjectKinds(object)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("cannot attach to %v: not implemented", gvk)
return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0])
}
},
// UpdatePodSpecForObject update the pod specification for the provided object
UpdatePodSpecForObject: func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
switch t := obj.(type) {
case *api.Pod:
return true, fn(&t.Spec)
case *api.ReplicationController:
if t.Spec.Template == nil {
t.Spec.Template = &api.PodTemplateSpec{}
}
return true, fn(&t.Spec.Template.Spec)
case *extensions.Deployment:
return true, fn(&t.Spec.Template.Spec)
case *extensions.DaemonSet:
return true, fn(&t.Spec.Template.Spec)
case *extensions.ReplicaSet:
return true, fn(&t.Spec.Template.Spec)
case *apps.PetSet:
return true, fn(&t.Spec.Template.Spec)
case *batch.Job:
return true, fn(&t.Spec.Template.Spec)
default:
return false, fmt.Errorf("the object is not a pod or does not have a pod template")
}
},
EditorEnvs: func() []string {
return []string{"KUBE_EDITOR", "EDITOR"}
},
PrintObjectSpecificMessage: func(obj runtime.Object, out io.Writer) {
switch obj := obj.(type) {
case *api.Service:
if obj.Spec.Type == api.ServiceTypeNodePort {
msg := fmt.Sprintf(
`You have exposed your service on an external port on all nodes in your
cluster. If you want to expose this service to the external internet, you may
need to set up firewall rules for the service port(s) (%s) to serve traffic.
See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details.
`,
makePortsString(obj.Spec.Ports, true))
out.Write([]byte(msg))
}
if _, ok := obj.Annotations[service.AnnotationLoadBalancerSourceRangesKey]; ok {
msg := fmt.Sprintf(
`You are using service annotation [service.beta.kubernetes.io/load-balancer-source-ranges].
It has been promoted to field [loadBalancerSourceRanges] in service spec. This annotation will be deprecated in the future.
Please use the loadBalancerSourceRanges field instead.
See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details.
`)
out.Write([]byte(msg))
}
}
},
}
}
@ -734,6 +873,20 @@ func (f *Factory) BindExternalFlags(flags *pflag.FlagSet) {
flags.AddGoFlagSet(flag.CommandLine)
}
func makePortsString(ports []api.ServicePort, useNodePort bool) string {
pieces := make([]string, len(ports))
for ix := range ports {
var port int32
if useNodePort {
port = ports[ix].NodePort
} else {
port = ports[ix].Port
}
pieces[ix] = fmt.Sprintf("%s:%d", strings.ToLower(string(ports[ix].Protocol)), port)
}
return strings.Join(pieces, ",")
}
func getPorts(spec api.PodSpec) []string {
result := []string{}
for _, container := range spec.Containers {
@ -744,6 +897,16 @@ func getPorts(spec api.PodSpec) []string {
return result
}
func getProtocols(spec api.PodSpec) map[string]string {
result := make(map[string]string)
for _, container := range spec.Containers {
for _, port := range container.Ports {
result[strconv.Itoa(int(port.ContainerPort))] = string(port.Protocol)
}
}
return result
}
// Extracts the ports exposed by a service from the given service spec.
func getServicePorts(spec api.ServiceSpec) []string {
result := []string{}
@ -753,8 +916,18 @@ func getServicePorts(spec api.ServiceSpec) []string {
return result
}
// Extracts the protocols exposed by a service from the given service spec.
func getServiceProtocols(spec api.ServiceSpec) map[string]string {
result := make(map[string]string)
for _, servicePort := range spec.Ports {
result[strconv.Itoa(int(servicePort.Port))] = string(servicePort.Protocol)
}
return result
}
type clientSwaggerSchema struct {
c *client.Client
fedc *restclient.RESTClient
cacheDir string
mapper meta.RESTMapper
}
@ -815,7 +988,7 @@ func writeSchemaFile(schemaData []byte, cacheDir, cacheFile, prefix, groupVersio
return nil
}
func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string) (err error) {
func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cacheDir string, delegate validation.Schema) (err error) {
var schemaData []byte
var firstSeen bool
fullDir, err := substituteUserHome(cacheDir)
@ -836,7 +1009,7 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac
return err
}
}
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData, delegate)
if err != nil {
return err
}
@ -849,7 +1022,7 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac
if err != nil {
return err
}
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData, delegate)
if err != nil {
return err
}
@ -888,20 +1061,32 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
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)
return getSchemaAndValidate(c.c.AutoscalingClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == policy.GroupName {
if c.c.PolicyClient == nil {
return errors.New("unable to validate: no policy client")
}
return getSchemaAndValidate(c.c.PolicyClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == apps.GroupName {
if c.c.AppsClient == nil {
return errors.New("unable to validate: no autoscaling client")
}
return getSchemaAndValidate(c.c.AppsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir)
return getSchemaAndValidate(c.c.AppsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == 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)
return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if gvk.Group == rbac.GroupName {
if c.c.RbacClient == nil {
return errors.New("unable to validate: no rbac client")
}
return getSchemaAndValidate(c.c.RbacClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
// Don't attempt to validate third party objects
@ -911,9 +1096,15 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
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)
return getSchemaAndValidate(c.c.ExtensionsClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
return getSchemaAndValidate(c.c.RESTClient, data, "api", gvk.GroupVersion().String(), c.cacheDir)
if gvk.Group == federation.GroupName {
if c.fedc == nil {
return errors.New("unable to validate: no federation client")
}
return getSchemaAndValidate(c.fedc, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
}
return getSchemaAndValidate(c.c.RESTClient, data, "api", gvk.GroupVersion().String(), c.cacheDir, c)
}
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
@ -972,12 +1163,12 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
// PrintObject prints an api object given command line flags to modify the output format
func (f *Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error {
gvk, err := api.Scheme.ObjectKind(obj)
gvks, _, err := api.Scheme.ObjectKinds(obj)
if err != nil {
return err
}
mapping, err := mapper.RESTMapping(gvk.GroupKind())
mapping, err := mapper.RESTMapping(gvks[0].GroupKind())
if err != nil {
return err
}

View file

@ -31,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/typed/discovery"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/kubectl"
@ -333,7 +334,12 @@ func AddValidateFlags(cmd *cobra.Command) {
}
func AddRecursiveFlag(cmd *cobra.Command, value *bool) {
cmd.Flags().BoolVarP(value, "recursive", "R", *value, "If true, process directory recursively.")
cmd.Flags().BoolVarP(value, "recursive", "R", *value, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
}
// AddDryRunFlag adds dry-run flag to a command. Usually used by mutations.
func AddDryRunFlag(cmd *cobra.Command) {
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
}
func AddApplyAnnotationFlags(cmd *cobra.Command) {
@ -344,7 +350,7 @@ func AddApplyAnnotationFlags(cmd *cobra.Command) {
// TODO: need to take a pass at other generator commands to use this set of flags
func AddGeneratorFlags(cmd *cobra.Command, defaultGenerator string) {
cmd.Flags().String("generator", defaultGenerator, "The name of the API generator to use.")
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
AddDryRunFlag(cmd)
}
func ReadConfigDataFromReader(reader io.Reader, source string) ([]byte, error) {
@ -433,6 +439,10 @@ func GetRecordFlag(cmd *cobra.Command) bool {
return GetFlagBool(cmd, "record")
}
func GetDryRunFlag(cmd *cobra.Command) bool {
return GetFlagBool(cmd, "dry-run")
}
// RecordChangeCause annotate change-cause to input runtime object.
func RecordChangeCause(obj runtime.Object, changeCause string) error {
accessor, err := meta.Accessor(obj)
@ -479,6 +489,10 @@ func ShouldRecord(cmd *cobra.Command, info *resource.Info) bool {
return GetRecordFlag(cmd) || ContainsChangeCause(info)
}
// GetThirdPartyGroupVersions returns the thirdparty "group/versions"s and
// resources supported by the server. A user may delete a thirdparty resource
// when this function is running, so this function may return a "NotFound" error
// due to the race.
func GetThirdPartyGroupVersions(discovery discovery.DiscoveryInterface) ([]unversioned.GroupVersion, []unversioned.GroupVersionKind, error) {
result := []unversioned.GroupVersion{}
gvks := []unversioned.GroupVersionKind{}
@ -528,3 +542,72 @@ func GetIncludeThirdPartyAPIs(cmd *cobra.Command) bool {
func AddInclude3rdPartyFlags(cmd *cobra.Command) {
cmd.Flags().Bool("include-extended-apis", true, "If true, include definitions of new APIs via calls to the API server. [default true]")
}
// GetResourcesAndPairs retrieves resources and "KEY=VALUE or KEY-" pair args from given args
func GetResourcesAndPairs(args []string, pairType string) (resources []string, pairArgs []string, err error) {
foundPair := false
for _, s := range args {
nonResource := strings.Contains(s, "=") || strings.HasSuffix(s, "-")
switch {
case !foundPair && nonResource:
foundPair = true
fallthrough
case foundPair && nonResource:
pairArgs = append(pairArgs, s)
case !foundPair && !nonResource:
resources = append(resources, s)
case foundPair && !nonResource:
err = fmt.Errorf("all resources must be specified before %s changes: %s", pairType, s)
return
}
}
return
}
// ParsePairs retrieves new and remove pairs (if supportRemove is true) from "KEY=VALUE or KEY-" pair args
func ParsePairs(pairArgs []string, pairType string, supportRemove bool) (newPairs map[string]string, removePairs []string, err error) {
newPairs = map[string]string{}
if supportRemove {
removePairs = []string{}
}
var invalidBuf bytes.Buffer
for _, pairArg := range pairArgs {
if strings.Index(pairArg, "=") != -1 {
parts := strings.SplitN(pairArg, "=", 2)
if len(parts) != 2 || len(parts[1]) == 0 {
if invalidBuf.Len() > 0 {
invalidBuf.WriteString(", ")
}
invalidBuf.WriteString(fmt.Sprintf(pairArg))
} else {
newPairs[parts[0]] = parts[1]
}
} else if supportRemove && strings.HasSuffix(pairArg, "-") {
removePairs = append(removePairs, pairArg[:len(pairArg)-1])
} else {
if invalidBuf.Len() > 0 {
invalidBuf.WriteString(", ")
}
invalidBuf.WriteString(fmt.Sprintf(pairArg))
}
}
if invalidBuf.Len() > 0 {
err = fmt.Errorf("invalid %s format: %s", pairType, invalidBuf.String())
return
}
return
}
// MaybeConvertObject attempts to convert an object to a specific group/version. If the object is
// a third party resource it is simply passed through.
func MaybeConvertObject(obj runtime.Object, gv unversioned.GroupVersion, converter runtime.ObjectConvertor) (runtime.Object, error) {
switch obj.(type) {
case *extensions.ThirdPartyResourceData:
// conversion is not supported for 3rd party objects
return obj, nil
default:
return converter.ConvertToVersion(obj, gv)
}
}

View file

@ -29,6 +29,8 @@ import (
"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"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/resource"
@ -101,6 +103,7 @@ func describerMap(c *client.Client) map[unversioned.GroupKind]Describer {
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)},
@ -277,7 +280,7 @@ func DescribeResourceQuotas(quotas *api.ResourceQuotaList, w io.Writer) {
for _, q := range quotas.Items {
fmt.Fprintf(w, "\n Name:\t%s\n", q.Name)
if len(q.Spec.Scopes) > 0 {
scopes := []string{}
scopes := make([]string, 0, len(q.Spec.Scopes))
for _, scope := range q.Spec.Scopes {
scopes = append(scopes, string(scope))
}
@ -294,7 +297,7 @@ func DescribeResourceQuotas(quotas *api.ResourceQuotaList, w io.Writer) {
fmt.Fprintf(w, " Resource\tUsed\tHard\n")
fmt.Fprint(w, " --------\t---\t---\n")
resources := []api.ResourceName{}
resources := make([]api.ResourceName, 0, len(q.Status.Hard))
for resource := range q.Status.Hard {
resources = append(resources, resource)
}
@ -430,7 +433,7 @@ func describeQuota(resourceQuota *api.ResourceQuota) (string, error) {
fmt.Fprintf(out, "Name:\t%s\n", resourceQuota.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", resourceQuota.Namespace)
if len(resourceQuota.Spec.Scopes) > 0 {
scopes := []string{}
scopes := make([]string, 0, len(resourceQuota.Spec.Scopes))
for _, scope := range resourceQuota.Spec.Scopes {
scopes = append(scopes, string(scope))
}
@ -446,7 +449,7 @@ func describeQuota(resourceQuota *api.ResourceQuota) (string, error) {
fmt.Fprintf(out, "Resource\tUsed\tHard\n")
fmt.Fprintf(out, "--------\t----\t----\n")
resources := []api.ResourceName{}
resources := make([]api.ResourceName, 0, len(resourceQuota.Status.Hard))
for resource := range resourceQuota.Status.Hard {
resources = append(resources, resource)
}
@ -524,7 +527,10 @@ func describePod(pod *api.Pod, events *api.EventList) (string, error) {
}
fmt.Fprintf(out, "IP:\t%s\n", pod.Status.PodIP)
fmt.Fprintf(out, "Controllers:\t%s\n", printControllers(pod.Annotations))
describeContainers(pod.Spec.Containers, pod.Status.ContainerStatuses, EnvValueRetriever(pod), out, "")
if len(pod.Spec.InitContainers) > 0 {
describeContainers("Init Containers", pod.Spec.InitContainers, pod.Status.InitContainerStatuses, EnvValueRetriever(pod), out, "")
}
describeContainers("Containers", pod.Spec.Containers, pod.Status.ContainerStatuses, EnvValueRetriever(pod), out, "")
if len(pod.Status.Conditions) > 0 {
fmt.Fprint(out, "Conditions:\n Type\tStatus\n")
for _, c := range pod.Status.Conditions {
@ -534,6 +540,7 @@ func describePod(pod *api.Pod, events *api.EventList) (string, error) {
}
}
describeVolumes(pod.Spec.Volumes, out, "")
fmt.Fprintf(out, "QoS Tier:\t%s\n", qosutil.GetPodQos(pod))
if events != nil {
DescribeEvents(events, out)
}
@ -694,7 +701,12 @@ func printRBDVolumeSource(rbd *api.RBDVolumeSource, out io.Writer) {
func printDownwardAPIVolumeSource(d *api.DownwardAPIVolumeSource, out io.Writer) {
fmt.Fprintf(out, " Type:\tDownwardAPI (a volume populated by information about the pod)\n Items:\n")
for _, mapping := range d.Items {
fmt.Fprintf(out, " %v -> %v\n", mapping.FieldRef.FieldPath, mapping.Path)
if mapping.FieldRef != nil {
fmt.Fprintf(out, " %v -> %v\n", mapping.FieldRef.FieldPath, mapping.Path)
}
if mapping.ResourceFieldRef != nil {
fmt.Fprintf(out, " %v -> %v\n", mapping.ResourceFieldRef.Resource, mapping.Path)
}
}
}
@ -712,6 +724,11 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe
storage := pv.Spec.Capacity[api.ResourceStorage]
var events *api.EventList
if describerSettings.ShowEvents {
events, _ = d.Events(namespace).Search(pv)
}
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", pv.Name)
printLabelsMultiline(out, "Labels", pv.Labels)
@ -744,6 +761,10 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe
printRBDVolumeSource(pv.Spec.RBD, out)
}
if events != nil {
DescribeEvents(events, out)
}
return nil
})
}
@ -769,6 +790,8 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, descri
capacity = storage.String()
}
events, _ := d.Events(namespace).Search(pvc)
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", pvc.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", pvc.Namespace)
@ -777,17 +800,25 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, descri
printLabelsMultiline(out, "Labels", pvc.Labels)
fmt.Fprintf(out, "Capacity:\t%s\n", capacity)
fmt.Fprintf(out, "Access Modes:\t%s\n", accessModes)
if events != nil {
DescribeEvents(events, out)
}
return nil
})
}
// TODO: Do a better job at indenting, maybe by using a prefix writer
func describeContainers(containers []api.Container, containerStatuses []api.ContainerStatus, resolverFn EnvVarResolverFunc, out io.Writer, space string) {
func describeContainers(label string, containers []api.Container, containerStatuses []api.ContainerStatus, resolverFn EnvVarResolverFunc, out io.Writer, space string) {
statuses := map[string]api.ContainerStatus{}
for _, status := range containerStatuses {
statuses[status.Name] = status
}
fmt.Fprintf(out, "%sContainers:\n", space)
if len(containers) == 0 {
fmt.Fprintf(out, "%s%s: <none>\n", space, label)
} else {
fmt.Fprintf(out, "%s%s:\n", space, label)
}
for _, container := range containers {
status, ok := statuses[container.Name]
nameIndent := ""
@ -822,25 +853,20 @@ func describeContainers(containers []api.Container, containerStatuses []api.Cont
}
}
resourceToQoS := qosutil.GetQoS(&container)
if len(resourceToQoS) > 0 {
fmt.Fprintf(out, " QoS Tier:\n")
}
for resource, qos := range resourceToQoS {
fmt.Fprintf(out, " %s:\t%s\n", resource, qos)
}
if len(container.Resources.Limits) > 0 {
resources := container.Resources
if len(resources.Limits) > 0 {
fmt.Fprintf(out, " Limits:\n")
}
for name, quantity := range container.Resources.Limits {
for _, name := range SortedResourceNames(resources.Limits) {
quantity := resources.Limits[name]
fmt.Fprintf(out, " %s:\t%s\n", name, quantity.String())
}
if len(container.Resources.Requests) > 0 {
if len(resources.Requests) > 0 {
fmt.Fprintf(out, " Requests:\n")
}
for name, quantity := range container.Resources.Requests {
for _, name := range SortedResourceNames(resources.Requests) {
quantity := resources.Requests[name]
fmt.Fprintf(out, " %s:\t%s\n", name, quantity.String())
}
@ -879,6 +905,12 @@ func describeContainers(containers []api.Container, containerStatuses []api.Cont
valueFrom = resolverFn(e)
}
fmt.Fprintf(out, " %s:\t%s (%s:%s)\n", e.Name, valueFrom, e.ValueFrom.FieldRef.APIVersion, e.ValueFrom.FieldRef.FieldPath)
case e.ValueFrom.ResourceFieldRef != nil:
valueFrom, err := fieldpath.ExtractContainerResourceValue(e.ValueFrom.ResourceFieldRef, &container)
if err != nil {
valueFrom = ""
}
fmt.Fprintf(out, " %s:\t%s (%s)\n", e.Name, valueFrom, e.ValueFrom.ResourceFieldRef.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:
@ -889,7 +921,7 @@ func describeContainers(containers []api.Container, containerStatuses []api.Cont
}
func describeContainerPorts(cPorts []api.ContainerPort) string {
ports := []string{}
ports := make([]string, 0, len(cPorts))
for _, cPort := range cPorts {
ports = append(ports, fmt.Sprintf("%d/%s", cPort.ContainerPort, cPort.Protocol))
}
@ -1037,7 +1069,10 @@ func DescribePodTemplate(template *api.PodTemplateSpec, out io.Writer) {
if len(template.Spec.ServiceAccountName) > 0 {
fmt.Fprintf(out, " Service Account:\t%s\n", template.Spec.ServiceAccountName)
}
describeContainers(template.Spec.Containers, nil, nil, out, " ")
if len(template.Spec.InitContainers) > 0 {
describeContainers("Init Containers", template.Spec.InitContainers, nil, nil, out, " ")
}
describeContainers("Containers", template.Spec.Containers, nil, nil, out, " ")
describeVolumes(template.Spec.Volumes, out, " ")
}
@ -1268,7 +1303,7 @@ func (i *IngressDescriber) describeIngress(ing *extensions.Ingress, describerSet
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%v\n", ing.Name)
fmt.Fprintf(out, "Namespace:\t%v\n", ing.Namespace)
fmt.Fprintf(out, "Address:\t%v\n", loadBalancerStatusStringer(ing.Status.LoadBalancer))
fmt.Fprintf(out, "Address:\t%v\n", loadBalancerStatusStringer(ing.Status.LoadBalancer, true))
def := ing.Spec.Backend
ns := ing.Namespace
if def == nil {
@ -1286,14 +1321,23 @@ func (i *IngressDescriber) describeIngress(ing *extensions.Ingress, describerSet
}
fmt.Fprint(out, "Rules:\n Host\tPath\tBackends\n")
fmt.Fprint(out, " ----\t----\t--------\n")
count := 0
for _, rules := range ing.Spec.Rules {
if rules.HTTP == nil {
continue
}
fmt.Fprintf(out, " %s\t\n", rules.Host)
for _, path := range rules.HTTP.Paths {
fmt.Fprintf(out, " \t%s \t%s (%s)\n", path.Path, backendStringer(&path.Backend), i.describeBackend(ing.Namespace, &path.Backend))
count++
host := rules.Host
if len(host) == 0 {
host = "*"
}
fmt.Fprintf(out, " %s\t\n", host)
for _, path := range rules.HTTP.Paths {
fmt.Fprintf(out, " \t%s \t%s (%s)\n", path.Path, backendStringer(&path.Backend), i.describeBackend(ns, &path.Backend))
}
}
if count == 0 {
fmt.Fprintf(out, " %s\t%s \t%s (%s)\n", "*", "*", backendStringer(def), i.describeBackend(ns, def))
}
describeIngressAnnotations(out, ing.Annotations)
@ -1437,7 +1481,7 @@ func describeEndpoints(ep *api.Endpoints, events *api.EventList) (string, error)
for i := range ep.Subsets {
subset := &ep.Subsets[i]
addresses := []string{}
addresses := make([]string, 0, len(subset.Addresses))
for _, addr := range subset.Addresses {
addresses = append(addresses, addr.IP)
}
@ -1447,7 +1491,7 @@ func describeEndpoints(ep *api.Endpoints, events *api.EventList) (string, error)
}
fmt.Fprintf(out, " Addresses:\t%s\n", addressesString)
notReadyAddresses := []string{}
notReadyAddresses := make([]string, 0, len(subset.NotReadyAddresses))
for _, addr := range subset.NotReadyAddresses {
notReadyAddresses = append(notReadyAddresses, addr.IP)
}
@ -1605,6 +1649,7 @@ func describeNode(node *api.Node, nodeNonTerminatedPodsList *api.PodList, events
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", node.Name)
printLabelsMultiline(out, "Labels", node.Labels)
printTaintsInAnnotationMultiline(out, "Taints", node.Annotations)
fmt.Fprintf(out, "CreationTimestamp:\t%s\n", node.CreationTimestamp.Time.Format(time.RFC1123Z))
fmt.Fprintf(out, "Phase:\t%v\n", node.Status.Phase)
if len(node.Status.Conditions) > 0 {
@ -1620,16 +1665,31 @@ func describeNode(node *api.Node, nodeNonTerminatedPodsList *api.PodList, events
c.Message)
}
}
var addresses []string
addresses := make([]string, 0, len(node.Status.Addresses))
for _, address := range node.Status.Addresses {
addresses = append(addresses, address.Address)
}
printResourceList := func(resourceList api.ResourceList) {
resources := make([]api.ResourceName, 0, len(resourceList))
for resource := range resourceList {
resources = append(resources, resource)
}
sort.Sort(SortableResourceNames(resources))
for _, resource := range resources {
value := resourceList[resource]
fmt.Fprintf(out, " %s:\t%s\n", resource, value.String())
}
}
fmt.Fprintf(out, "Addresses:\t%s\n", strings.Join(addresses, ","))
if len(node.Status.Capacity) > 0 {
fmt.Fprintf(out, "Capacity:\n")
for resource, value := range node.Status.Capacity {
fmt.Fprintf(out, " %s:\t%s\n", resource, value.String())
}
printResourceList(node.Status.Capacity)
}
if len(node.Status.Allocatable) > 0 {
fmt.Fprintf(out, "Allocatable:\n")
printResourceList(node.Status.Allocatable)
}
fmt.Fprintf(out, "System Info:\n")
@ -1638,6 +1698,8 @@ func describeNode(node *api.Node, nodeNonTerminatedPodsList *api.PodList, events
fmt.Fprintf(out, " Boot ID:\t%s\n", node.Status.NodeInfo.BootID)
fmt.Fprintf(out, " Kernel Version:\t%s\n", node.Status.NodeInfo.KernelVersion)
fmt.Fprintf(out, " OS Image:\t%s\n", node.Status.NodeInfo.OSImage)
fmt.Fprintf(out, " Operating System:\t%s\n", node.Status.NodeInfo.OperatingSystem)
fmt.Fprintf(out, " Architecture:\t%s\n", node.Status.NodeInfo.Architecture)
fmt.Fprintf(out, " Container Runtime Version:\t%s\n", node.Status.NodeInfo.ContainerRuntimeVersion)
fmt.Fprintf(out, " Kubelet Version:\t%s\n", node.Status.NodeInfo.KubeletVersion)
fmt.Fprintf(out, " Kube-Proxy Version:\t%s\n", node.Status.NodeInfo.KubeProxyVersion)
@ -1710,7 +1772,7 @@ type HorizontalPodAutoscalerDescriber struct {
}
func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
hpa, err := d.client.Extensions().HorizontalPodAutoscalers(namespace).Get(name)
hpa, err := d.client.Autoscaling().HorizontalPodAutoscalers(namespace).Get(name)
if err != nil {
return "", err
}
@ -1720,12 +1782,11 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, desc
printLabelsMultiline(out, "Labels", hpa.Labels)
printLabelsMultiline(out, "Annotations", hpa.Annotations)
fmt.Fprintf(out, "CreationTimestamp:\t%s\n", hpa.CreationTimestamp.Time.Format(time.RFC1123Z))
fmt.Fprintf(out, "Reference:\t%s/%s/%s\n",
hpa.Spec.ScaleRef.Kind,
hpa.Spec.ScaleRef.Name,
hpa.Spec.ScaleRef.Subresource)
if hpa.Spec.CPUUtilization != nil {
fmt.Fprintf(out, "Target CPU utilization:\t%d%%\n", hpa.Spec.CPUUtilization.TargetPercentage)
fmt.Fprintf(out, "Reference:\t%s/%s\n",
hpa.Spec.ScaleTargetRef.Kind,
hpa.Spec.ScaleTargetRef.Name)
if hpa.Spec.TargetCPUUtilizationPercentage != nil {
fmt.Fprintf(out, "Target CPU utilization:\t%d%%\n", *hpa.Spec.TargetCPUUtilizationPercentage)
fmt.Fprintf(out, "Current CPU utilization:\t")
if hpa.Status.CurrentCPUUtilizationPercentage != nil {
fmt.Fprintf(out, "%d%%\n", *hpa.Status.CurrentCPUUtilizationPercentage)
@ -1741,9 +1802,9 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, desc
fmt.Fprintf(out, "Max replicas:\t%d\n", hpa.Spec.MaxReplicas)
// TODO: switch to scale subresource once the required code is submitted.
if strings.ToLower(hpa.Spec.ScaleRef.Kind) == "replicationcontroller" {
if strings.ToLower(hpa.Spec.ScaleTargetRef.Kind) == "replicationcontroller" {
fmt.Fprintf(out, "ReplicationController pods:\t")
rc, err := d.client.ReplicationControllers(hpa.Namespace).Get(hpa.Spec.ScaleRef.Name)
rc, err := d.client.ReplicationControllers(hpa.Namespace).Get(hpa.Spec.ScaleTargetRef.Name)
if err == nil {
fmt.Fprintf(out, "%d current / %d desired\n", rc.Status.Replicas, rc.Spec.Replicas)
} else {
@ -1765,16 +1826,21 @@ func describeNodeResource(nodeNonTerminatedPodsList *api.PodList, node *api.Node
fmt.Fprintf(out, "Non-terminated Pods:\t(%d in total)\n", len(nodeNonTerminatedPodsList.Items))
fmt.Fprint(out, " Namespace\tName\t\tCPU Requests\tCPU Limits\tMemory Requests\tMemory Limits\n")
fmt.Fprint(out, " ---------\t----\t\t------------\t----------\t---------------\t-------------\n")
allocatable := node.Status.Capacity
if len(node.Status.Allocatable) > 0 {
allocatable = node.Status.Allocatable
}
for _, pod := range nodeNonTerminatedPodsList.Items {
req, limit, err := api.PodRequestsAndLimits(&pod)
if err != nil {
return err
}
cpuReq, cpuLimit, memoryReq, memoryLimit := req[api.ResourceCPU], limit[api.ResourceCPU], req[api.ResourceMemory], limit[api.ResourceMemory]
fractionCpuReq := float64(cpuReq.MilliValue()) / float64(node.Status.Capacity.Cpu().MilliValue()) * 100
fractionCpuLimit := float64(cpuLimit.MilliValue()) / float64(node.Status.Capacity.Cpu().MilliValue()) * 100
fractionMemoryReq := float64(memoryReq.MilliValue()) / float64(node.Status.Capacity.Memory().MilliValue()) * 100
fractionMemoryLimit := float64(memoryLimit.MilliValue()) / float64(node.Status.Capacity.Memory().MilliValue()) * 100
fractionCpuReq := float64(cpuReq.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
fractionCpuLimit := float64(cpuLimit.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
fractionMemoryReq := float64(memoryReq.Value()) / float64(allocatable.Memory().Value()) * 100
fractionMemoryLimit := float64(memoryLimit.Value()) / float64(allocatable.Memory().Value()) * 100
fmt.Fprintf(out, " %s\t%s\t\t%s (%d%%)\t%s (%d%%)\t%s (%d%%)\t%s (%d%%)\n", pod.Namespace, pod.Name,
cpuReq.String(), int64(fractionCpuReq), cpuLimit.String(), int64(fractionCpuLimit),
memoryReq.String(), int64(fractionMemoryReq), memoryLimit.String(), int64(fractionMemoryLimit))
@ -1787,10 +1853,10 @@ func describeNodeResource(nodeNonTerminatedPodsList *api.PodList, node *api.Node
return err
}
cpuReqs, cpuLimits, memoryReqs, memoryLimits := reqs[api.ResourceCPU], limits[api.ResourceCPU], reqs[api.ResourceMemory], limits[api.ResourceMemory]
fractionCpuReqs := float64(cpuReqs.MilliValue()) / float64(node.Status.Capacity.Cpu().MilliValue()) * 100
fractionCpuLimits := float64(cpuLimits.MilliValue()) / float64(node.Status.Capacity.Cpu().MilliValue()) * 100
fractionMemoryReqs := float64(memoryReqs.MilliValue()) / float64(node.Status.Capacity.Memory().MilliValue()) * 100
fractionMemoryLimits := float64(memoryLimits.MilliValue()) / float64(node.Status.Capacity.Memory().MilliValue()) * 100
fractionCpuReqs := float64(cpuReqs.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
fractionCpuLimits := float64(cpuLimits.MilliValue()) / float64(allocatable.Cpu().MilliValue()) * 100
fractionMemoryReqs := float64(memoryReqs.Value()) / float64(allocatable.Memory().Value()) * 100
fractionMemoryLimits := float64(memoryLimits.Value()) / float64(allocatable.Memory().Value()) * 100
fmt.Fprintf(out, " %s (%d%%)\t%s (%d%%)\t%s (%d%%)\t%s (%d%%)\n",
cpuReqs.String(), int64(fractionCpuReqs), cpuLimits.String(), int64(fractionCpuLimits),
memoryReqs.String(), int64(fractionMemoryReqs), memoryLimits.String(), int64(fractionMemoryLimits))
@ -1821,15 +1887,17 @@ func getPodsTotalRequestsAndLimits(podList *api.PodList) (reqs map[api.ResourceN
for podReqName, podReqValue := range podReqs {
if value, ok := reqs[podReqName]; !ok {
reqs[podReqName] = *podReqValue.Copy()
} else if err = value.Add(podReqValue); err != nil {
return nil, nil, err
} else {
value.Add(podReqValue)
reqs[podReqName] = value
}
}
for podLimitName, podLimitValue := range podLimits {
if value, ok := limits[podLimitName]; !ok {
limits[podLimitName] = *podLimitValue.Copy()
} else if err = value.Add(podLimitValue); err != nil {
return nil, nil, err
} else {
value.Add(podLimitValue)
limits[podLimitName] = value
}
}
}
@ -1884,12 +1952,9 @@ func (dd *DeploymentDescriber) Describe(namespace, name string, describerSetting
ru := d.Spec.Strategy.RollingUpdate
fmt.Fprintf(out, "RollingUpdateStrategy:\t%s max unavailable, %s max surge\n", ru.MaxUnavailable.String(), ru.MaxSurge.String())
}
oldRSs, _, err := deploymentutil.GetOldReplicaSets(d, dd)
oldRSs, _, newRS, err := deploymentutil.GetAllReplicaSets(d, dd)
if err == nil {
fmt.Fprintf(out, "OldReplicaSets:\t%s\n", printReplicaSetsByLabels(oldRSs))
}
newRS, err := deploymentutil.GetNewReplicaSet(d, dd)
if err == nil {
var newRSs []*extensions.ReplicaSet
if newRS != nil {
newRSs = append(newRSs, newRS)
@ -1936,7 +2001,7 @@ func getDaemonSetsForLabels(c client.DaemonSetInterface, labelsToMatch labels.La
func printReplicationControllersByLabels(matchingRCs []*api.ReplicationController) string {
// Format the matching RC's into strings.
var rcStrings []string
rcStrings := make([]string, 0, len(matchingRCs))
for _, controller := range matchingRCs {
rcStrings = append(rcStrings, fmt.Sprintf("%s (%d/%d replicas created)", controller.Name, controller.Status.Replicas, controller.Spec.Replicas))
}
@ -1950,7 +2015,7 @@ func printReplicationControllersByLabels(matchingRCs []*api.ReplicationControlle
func printReplicaSetsByLabels(matchingRSs []*extensions.ReplicaSet) string {
// Format the matching ReplicaSets into strings.
var rsStrings []string
rsStrings := make([]string, 0, len(matchingRSs))
for _, rs := range matchingRSs {
rsStrings = append(rsStrings, fmt.Sprintf("%s (%d/%d replicas created)", rs.Name, rs.Status.Replicas, rs.Spec.Replicas))
}
@ -2015,9 +2080,92 @@ func describeConfigMap(configMap *api.ConfigMap) (string, error) {
})
}
type ClusterDescriber struct {
fed_clientset.Interface
}
func (d *ClusterDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
cluster, err := d.Federation().Clusters().Get(name)
if err != nil {
return "", err
}
return describeCluster(cluster)
}
func describeCluster(cluster *federation.Cluster) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", cluster.Name)
fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(cluster.Labels))
fmt.Fprintf(out, "ServerAddressByClientCIDRs:\n ClientCIDR\tServerAddress\n")
fmt.Fprintf(out, " ----\t----\n")
for _, cidrAddr := range cluster.Spec.ServerAddressByClientCIDRs {
fmt.Fprintf(out, " %v \t%v\n\n", cidrAddr.ClientCIDR, cidrAddr.ServerAddress)
}
if len(cluster.Status.Conditions) > 0 {
fmt.Fprint(out, "Conditions:\n Type\tStatus\tLastUpdateTime\tLastTransitionTime\tReason\tMessage\n")
fmt.Fprint(out, " ----\t------\t-----------------\t------------------\t------\t-------\n")
for _, c := range cluster.Status.Conditions {
fmt.Fprintf(out, " %v \t%v \t%s \t%s \t%v \t%v\n",
c.Type,
c.Status,
c.LastProbeTime.Time.Format(time.RFC1123Z),
c.LastTransitionTime.Time.Format(time.RFC1123Z),
c.Reason,
c.Message)
}
}
fmt.Fprintf(out, "Version:\t%s\n", cluster.Status.Version)
if len(cluster.Status.Capacity) > 0 {
fmt.Fprintf(out, "Capacity:\n")
for resource, value := range cluster.Status.Capacity {
fmt.Fprintf(out, " %s:\t%s\n", resource, value.String())
}
}
if len(cluster.Status.Allocatable) > 0 {
fmt.Fprintf(out, "Allocatable:\n")
for resource, value := range cluster.Status.Allocatable {
fmt.Fprintf(out, " %s:\t%s\n", resource, value.String())
}
}
return nil
})
}
// NetworkPolicyDescriber generates information about a NetworkPolicy
type NetworkPolicyDescriber struct {
client.Interface
}
func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
c := d.Extensions().NetworkPolicies(namespace)
networkPolicy, err := c.Get(name)
if err != nil {
return "", err
}
return describeNetworkPolicy(networkPolicy)
}
func describeNetworkPolicy(networkPolicy *extensions.NetworkPolicy) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", networkPolicy.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", networkPolicy.Namespace)
printLabelsMultiline(out, "Labels", networkPolicy.Labels)
printLabelsMultiline(out, "Annotations", networkPolicy.Annotations)
return nil
})
}
// newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types.
func newErrNoDescriber(types ...reflect.Type) error {
names := []string{}
names := make([]string, 0, len(types))
for _, t := range types {
names = append(names, t.String())
}
@ -2056,7 +2204,7 @@ func (d *Describers) DescribeObject(exact interface{}, extra ...interface{}) (st
return fns[0].Describe(exact, extra...)
}
types := []reflect.Type{}
types := make([]reflect.Type, 0, len(extra))
for _, obj := range extra {
types = append(types, reflect.TypeOf(obj))
}
@ -2081,14 +2229,15 @@ func (d *Describers) Add(fns ...interface{}) error {
if ft.Kind() != reflect.Func {
return fmt.Errorf("expected func, got: %v", ft)
}
if ft.NumIn() == 0 {
numIn := ft.NumIn()
if numIn == 0 {
return fmt.Errorf("expected at least one 'in' params, got: %v", ft)
}
if ft.NumOut() != 2 {
return fmt.Errorf("expected two 'out' params - (string, error), got: %v", ft)
}
types := []reflect.Type{}
for i := 0; i < ft.NumIn(); i++ {
types := make([]reflect.Type, 0, numIn)
for i := 0; i < numIn; i++ {
types = append(types, ft.In(i))
}
if ft.Out(0) != reflect.TypeOf(string("")) {
@ -2186,3 +2335,42 @@ func printLabelsMultilineWithIndent(out io.Writer, initialIndent, title, innerIn
i++
}
}
// printTaintsMultiline prints multiple taints with a proper alignment.
func printTaintsInAnnotationMultiline(out io.Writer, title string, annotations map[string]string) {
taints, err := api.GetTaintsFromNodeAnnotations(annotations)
if err != nil {
taints = []api.Taint{}
}
printTaintsMultilineWithIndent(out, "", title, "\t", taints)
}
// printTaintsMultilineWithIndent prints multiple taints with a user-defined alignment.
func printTaintsMultilineWithIndent(out io.Writer, initialIndent, title, innerIndent string, taints []api.Taint) {
fmt.Fprintf(out, "%s%s:%s", initialIndent, title, innerIndent)
if taints == nil || len(taints) == 0 {
fmt.Fprintln(out, "<none>")
return
}
// to print taints in the sorted order
keys := make([]string, 0, len(taints))
for _, taint := range taints {
keys = append(keys, taint.Key)
}
sort.Strings(keys)
for i, key := range keys {
for _, taint := range taints {
if taint.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)
i++
}
}
}
}

View file

@ -134,6 +134,34 @@ func MakeParams(cmd *cobra.Command, params []GeneratorParam) map[string]interfac
return result
}
func MakeProtocols(protocols map[string]string) string {
out := []string{}
for key, value := range protocols {
out = append(out, fmt.Sprintf("%s/%s", key, value))
}
return strings.Join(out, ",")
}
func ParseProtocols(protocols interface{}) (map[string]string, error) {
protocolsString, isString := protocols.(string)
if !isString {
return nil, fmt.Errorf("expected string, found %v", protocols)
}
if len(protocolsString) == 0 {
return nil, fmt.Errorf("no protocols passed")
}
portProtocolMap := map[string]string{}
protocolsSlice := strings.Split(protocolsString, ",")
for ix := range protocolsSlice {
portProtocol := strings.Split(protocolsSlice[ix], "/")
if len(portProtocol) != 2 {
return nil, fmt.Errorf("unexpected port protocol mapping: %s", protocolsSlice[ix])
}
portProtocolMap[portProtocol[0]] = portProtocol[1]
}
return portProtocolMap, nil
}
func MakeLabels(labels map[string]string) string {
out := []string{}
for key, value := range labels {

View file

@ -19,8 +19,6 @@ package kubectl
import (
"fmt"
"io"
"sort"
"strconv"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
@ -29,7 +27,7 @@ import (
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/runtime"
deploymentutil "k8s.io/kubernetes/pkg/util/deployment"
"k8s.io/kubernetes/pkg/util/errors"
sliceutil "k8s.io/kubernetes/pkg/util/slice"
)
const (
@ -68,15 +66,14 @@ func (h *DeploymentHistoryViewer) History(namespace, name string) (HistoryInfo,
if err != nil {
return historyInfo, fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
}
_, allOldRSs, err := deploymentutil.GetOldReplicaSets(deployment, h.c)
_, allOldRSs, newRS, err := deploymentutil.GetAllReplicaSets(deployment, h.c)
if err != nil {
return historyInfo, fmt.Errorf("failed to retrieve old replica sets from deployment %s: %v", name, err)
return historyInfo, fmt.Errorf("failed to retrieve replica sets from deployment %s: %v", name, err)
}
newRS, err := deploymentutil.GetNewReplicaSet(deployment, h.c)
if err != nil {
return historyInfo, fmt.Errorf("failed to retrieve new replica set from deployment %s: %v", name, err)
allRSs := allOldRSs
if newRS != nil {
allRSs = append(allRSs, newRS)
}
allRSs := append(allOldRSs, newRS)
for _, rs := range allRSs {
v, err := deploymentutil.Revision(rs)
if err != nil {
@ -100,30 +97,24 @@ func PrintRolloutHistory(historyInfo HistoryInfo, resource, name string) (string
return fmt.Sprintf("No rollout history found in %s %q", resource, name), nil
}
// Sort the revisionToChangeCause map by revision
var revisions []string
for k := range historyInfo.RevisionToTemplate {
revisions = append(revisions, strconv.FormatInt(k, 10))
revisions := make([]int64, 0, len(historyInfo.RevisionToTemplate))
for r := range historyInfo.RevisionToTemplate {
revisions = append(revisions, r)
}
sort.Strings(revisions)
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")
errs := []error{}
for _, r := range revisions {
// Find the change-cause of revision r
r64, err := strconv.ParseInt(r, 10, 64)
if err != nil {
errs = append(errs, err)
continue
}
changeCause := historyInfo.RevisionToTemplate[r64].Annotations[ChangeCauseAnnotation]
changeCause := historyInfo.RevisionToTemplate[r].Annotations[ChangeCauseAnnotation]
if len(changeCause) == 0 {
changeCause = "<none>"
}
fmt.Fprintf(out, "%s\t%s\n", r, changeCause)
fmt.Fprintf(out, "%d\t%s\n", r, changeCause)
}
return errors.NewAggregate(errs)
return nil
})
}

View file

@ -43,7 +43,7 @@ type NamespaceInfo struct {
}
func listOfImages(spec *api.PodSpec) []string {
var images []string
images := make([]string, 0, len(spec.Containers))
for _, container := range spec.Containers {
images = append(images, container.Image)
}
@ -64,7 +64,7 @@ func NewThirdPartyResourceMapper(gvs []unversioned.GroupVersion, gvks []unversio
}, nil
}
}
groupVersions := []string{}
groupVersions := make([]string, 0, len(gvs))
for ix := range gvs {
groupVersions = append(groupVersions, gvs[ix].String())
}

View file

@ -36,6 +36,8 @@ import (
var FileExtensions = []string{".json", ".yaml", ".yml"}
var InputExtensions = append(FileExtensions, "stdin")
const defaultHttpGetAttempts int = 3
// Builder provides convenience functions for taking arguments and parameters
// from the command line and converting them to a list of resources to iterate
// over using the Visitor interface.
@ -69,6 +71,8 @@ type Builder struct {
singleResourceType bool
continueOnError bool
singular bool
export bool
schema validation.Schema
@ -109,8 +113,11 @@ func (b *Builder) FilenameParam(enforceNamespace, recursive bool, paths ...strin
b.errs = append(b.errs, fmt.Errorf("the URL passed to filename %q is not valid: %v", s, err))
continue
}
b.URL(url)
b.URL(defaultHttpGetAttempts, url)
default:
if !recursive {
b.singular = true
}
b.Path(recursive, s)
}
}
@ -123,11 +130,12 @@ func (b *Builder) FilenameParam(enforceNamespace, recursive bool, paths ...strin
}
// URL accepts a number of URLs directly.
func (b *Builder) URL(urls ...*url.URL) *Builder {
func (b *Builder) URL(httpAttemptCount int, urls ...*url.URL) *Builder {
for _, u := range urls {
b.paths = append(b.paths, &URLVisitor{
URL: u,
StreamVisitor: NewStreamVisitor(nil, b.mapper, u.String(), b.schema),
URL: u,
StreamVisitor: NewStreamVisitor(nil, b.mapper, u.String(), b.schema),
HttpAttemptCount: httpAttemptCount,
})
}
return b
@ -543,7 +551,12 @@ func (b *Builder) visitorResult() *Result {
// visit items specified by resource and name
if len(b.resourceTuples) != 0 {
isSingular := len(b.resourceTuples) == 1
// if b.singular is false, this could be by default, so double-check length
// of resourceTuples to determine if in fact it is singular or not
isSingular := b.singular
if !isSingular {
isSingular = len(b.resourceTuples) == 1
}
if len(b.paths) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}

View file

@ -22,7 +22,6 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata"
"k8s.io/kubernetes/pkg/runtime"
)
@ -57,9 +56,12 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
}
var obj runtime.Object
var versioned runtime.Object
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
obj, err = runtime.Decode(thirdpartyresourcedata.NewDecoder(nil, gvk.Kind), data)
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()
}
@ -96,7 +98,7 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
// if the object cannot be introspected. Name and namespace will be set into Info
// if the mapping's MetadataAccessor can retrieve them.
func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []unversioned.GroupVersionKind) (*Info, error) {
groupVersionKinds, err := m.ObjectKinds(obj)
groupVersionKinds, _, err := m.ObjectKinds(obj)
if err != nil {
return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err)
}

View file

@ -211,7 +211,7 @@ func (r *Result) Watch(resourceVersion string) (watch.Interface, error) {
// the objects as children, or if only a single Object is present, as that object. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func AsVersionedObject(infos []*Info, forceList bool, version string, encoder runtime.Encoder) (runtime.Object, error) {
func AsVersionedObject(infos []*Info, forceList bool, version unversioned.GroupVersion, encoder runtime.Encoder) (runtime.Object, error) {
objects, err := AsVersionedObjects(infos, version, encoder)
if err != nil {
return nil, err
@ -222,7 +222,7 @@ func AsVersionedObject(infos []*Info, forceList bool, version string, encoder ru
object = objects[0]
} else {
object = &api.List{Items: objects}
converted, err := tryConvert(api.Scheme, object, version, registered.GroupOrDie(api.GroupName).GroupVersion.Version)
converted, err := tryConvert(api.Scheme, object, version, registered.GroupOrDie(api.GroupName).GroupVersion)
if err != nil {
return nil, err
}
@ -234,7 +234,7 @@ func AsVersionedObject(infos []*Info, forceList bool, version string, encoder ru
// AsVersionedObjects converts a list of infos into versioned objects. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present.
func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder) ([]runtime.Object, error) {
func AsVersionedObjects(infos []*Info, version unversioned.GroupVersion, encoder runtime.Encoder) ([]runtime.Object, error) {
objects := []runtime.Object{}
for _, info := range infos {
if info.Object == nil {
@ -250,8 +250,8 @@ func AsVersionedObjects(infos []*Info, version string, encoder runtime.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 len(version) > 0 {
if _, err := api.Scheme.ObjectKind(info.Object); runtime.IsNotRegisteredError(err) {
if !version.IsEmpty() {
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)
if err != nil {
@ -263,7 +263,7 @@ func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder)
}
}
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion().String())
converted, err := tryConvert(info.Mapping.ObjectConvertor, info.Object, version, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil {
return nil, err
}
@ -274,10 +274,10 @@ func AsVersionedObjects(infos []*Info, version string, encoder runtime.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 ...string) (runtime.Object, error) {
func tryConvert(convertor runtime.ObjectConvertor, object runtime.Object, versions ...unversioned.GroupVersion) (runtime.Object, error) {
var last error
for _, version := range versions {
if len(version) == 0 {
if version.IsEmpty() {
return object, nil
}
obj, err := convertor.ConvertToVersion(object, version)

View file

@ -24,6 +24,7 @@ import (
"net/url"
"os"
"path/filepath"
"time"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
@ -221,22 +222,69 @@ func ValidateSchema(data []byte, schema validation.Schema) error {
type URLVisitor struct {
URL *url.URL
*StreamVisitor
HttpAttemptCount int
}
func (v *URLVisitor) Visit(fn VisitorFunc) error {
res, err := http.Get(v.URL.String())
body, err := readHttpWithRetries(httpgetImpl, time.Second, v.URL.String(), v.HttpAttemptCount)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != 200 {
return fmt.Errorf("unable to read URL %q, server reported %d %s", v.URL, res.StatusCode, res.Status)
}
v.StreamVisitor.Reader = res.Body
defer body.Close()
v.StreamVisitor.Reader = body
return v.StreamVisitor.Visit(fn)
}
// readHttpWithRetries tries to http.Get the v.URL retries times before giving up.
func readHttpWithRetries(get httpget, duration time.Duration, u string, attempts int) (io.ReadCloser, error) {
var err error
var body io.ReadCloser
if attempts <= 0 {
return nil, fmt.Errorf("http attempts must be greater than 0, was %d", attempts)
}
for i := 0; i < attempts; i++ {
var statusCode int
var status string
if i > 0 {
time.Sleep(duration)
}
// Try to get the URL
statusCode, status, body, err = get(u)
// Retry Errors
if err != nil {
continue
}
// Error - Set the error condition from the StatusCode
if statusCode != 200 {
err = fmt.Errorf("unable to read URL %q, server reported %s, status code=%d", u, status, statusCode)
}
if statusCode >= 500 && statusCode < 600 {
// Retry 500's
continue
} else {
// Don't retry other StatusCodes
break
}
}
return body, err
}
// httpget Defines function to retrieve a url and return the results. Exists for unit test stubbing.
type httpget func(url string) (int, string, io.ReadCloser, error)
// httpgetImpl Implements a function to retrieve a url and return the results.
func httpgetImpl(url string) (int, string, io.ReadCloser, error) {
resp, err := http.Get(url)
if err != nil {
return 0, "", nil, err
}
return resp.StatusCode, resp.Status, resp.Body, nil
}
// DecoratedVisitor will invoke the decorators in order prior to invoking the visitor function
// passed to Visit. An error will terminate the visit.
type DecoratedVisitor struct {

View file

@ -32,12 +32,15 @@ import (
"github.com/ghodss/yaml"
"github.com/golang/glog"
"k8s.io/kubernetes/federation/apis/federation"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
@ -51,6 +54,7 @@ const (
tabwriterPadding = 3
tabwriterPadChar = ' '
tabwriterFlags = 0
loadBalancerWidth = 16
)
// GetPrinter takes a format type, an optional format argument. It will return true
@ -67,7 +71,8 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
printer = &YAMLPrinter{}
case "name":
printer = &NamePrinter{
Typer: runtime.ObjectTyperToTyper(api.Scheme),
// TODO: this is wrong, these should be provided as an argument to GetPrinter
Typer: api.Scheme,
Decoder: api.Codecs.UniversalDecoder(),
}
case "template", "go-template":
@ -181,7 +186,7 @@ func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
if version.IsEmpty() {
continue
}
converted, err := p.convertor.ConvertToVersion(obj, version.String())
converted, err := p.convertor.ConvertToVersion(obj, version)
if runtime.IsNotRegisteredError(err) {
continue
}
@ -201,14 +206,12 @@ func (p *VersionedPrinter) HandledResources() []string {
// NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object.
type NamePrinter struct {
Decoder runtime.Decoder
Typer runtime.Typer
Typer runtime.ObjectTyper
}
// 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 {
gvk, _, _ := p.Typer.ObjectKind(obj)
if meta.IsListType(obj) {
items, err := meta.ExtractList(obj)
if err != nil {
@ -234,10 +237,9 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
}
}
if gvk != nil {
if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil {
// TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper
_, resource := meta.KindToResource(*gvk)
_, resource := meta.KindToResource(gvks[0])
fmt.Fprintf(w, "%s/%s\n", resource.Resource, name)
} else {
fmt.Fprintf(w, "<unknown>/%s\n", name)
@ -418,12 +420,12 @@ var replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
var replicaSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
var jobColumns = []string{"NAME", "DESIRED", "SUCCESSFUL", "AGE"}
var serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "AGE"}
var ingressColumns = []string{"NAME", "RULE", "BACKEND", "ADDRESS", "AGE"}
var ingressColumns = []string{"NAME", "HOSTS", "ADDRESS", "PORTS", "AGE"}
var petSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
var endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"}
var nodeColumns = []string{"NAME", "STATUS", "AGE"}
var daemonSetColumns = []string{"NAME", "DESIRED", "CURRENT", "NODE-SELECTOR", "AGE"}
var eventColumns = []string{"FIRSTSEEN", "LASTSEEN", "COUNT", "NAME", "KIND", "SUBOBJECT", "TYPE", "REASON", "SOURCE", "MESSAGE"}
var eventColumns = []string{"LASTSEEN", "FIRSTSEEN", "COUNT", "NAME", "KIND", "SUBOBJECT", "TYPE", "REASON", "SOURCE", "MESSAGE"}
var limitRangeColumns = []string{"NAME", "AGE"}
var resourceQuotaColumns = []string{"NAME", "AGE"}
var namespaceColumns = []string{"NAME", "STATUS", "AGE"}
@ -433,6 +435,10 @@ var persistentVolumeColumns = []string{"NAME", "CAPACITY", "ACCESSMODES", "STATU
var persistentVolumeClaimColumns = []string{"NAME", "STATUS", "VOLUME", "CAPACITY", "ACCESSMODES", "AGE"}
var componentStatusColumns = []string{"NAME", "STATUS", "MESSAGE", "ERROR"}
var thirdPartyResourceColumns = []string{"NAME", "DESCRIPTION", "VERSION(S)"}
var roleColumns = []string{"NAME", "AGE"}
var roleBindingColumns = []string{"NAME", "AGE"}
var clusterRoleColumns = []string{"NAME", "AGE"}
var clusterRoleBindingColumns = []string{"NAME", "AGE"}
// TODO: consider having 'KIND' for third party resource data
var thirdPartyResourceDataColumns = []string{"NAME", "LABELS", "DATA"}
@ -441,6 +447,8 @@ 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 networkPolicyColumns = []string{"NAME", "POD-SELECTOR", "AGE"}
// addDefaultHandlers adds print handlers for default Kubernetes types.
func (h *HumanReadablePrinter) addDefaultHandlers() {
@ -496,6 +504,18 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(podSecurityPolicyColumns, printPodSecurityPolicyList)
h.Handler(thirdPartyResourceDataColumns, printThirdPartyResourceData)
h.Handler(thirdPartyResourceDataColumns, printThirdPartyResourceDataList)
h.Handler(clusterColumns, printCluster)
h.Handler(clusterColumns, printClusterList)
h.Handler(networkPolicyColumns, printNetworkPolicy)
h.Handler(networkPolicyColumns, printNetworkPolicyList)
h.Handler(roleColumns, printRole)
h.Handler(roleColumns, printRoleList)
h.Handler(roleBindingColumns, printRoleBinding)
h.Handler(roleBindingColumns, printRoleBindingList)
h.Handler(clusterRoleColumns, printClusterRole)
h.Handler(clusterRoleColumns, printClusterRoleList)
h.Handler(clusterRoleBindingColumns, printClusterRoleBinding)
h.Handler(clusterRoleBindingColumns, printClusterRoleBindingList)
}
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
@ -593,22 +613,51 @@ func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error {
reason = pod.Status.Reason
}
for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- {
container := pod.Status.ContainerStatuses[i]
restarts += int(container.RestartCount)
if container.State.Waiting != nil && container.State.Waiting.Reason != "" {
reason = container.State.Waiting.Reason
} else if container.State.Terminated != nil && container.State.Terminated.Reason != "" {
reason = container.State.Terminated.Reason
} else if container.State.Terminated != nil && container.State.Terminated.Reason == "" {
if container.State.Terminated.Signal != 0 {
reason = fmt.Sprintf("Signal:%d", container.State.Terminated.Signal)
initializing := false
for i := range pod.Status.InitContainerStatuses {
container := pod.Status.InitContainerStatuses[i]
switch {
case container.State.Terminated != nil && container.State.Terminated.ExitCode == 0:
continue
case container.State.Terminated != nil:
// initialization is failed
if len(container.State.Terminated.Reason) == 0 {
if container.State.Terminated.Signal != 0 {
reason = fmt.Sprintf("Init:Signal:%d", container.State.Terminated.Signal)
} else {
reason = fmt.Sprintf("Init:ExitCode:%d", container.State.Terminated.ExitCode)
}
} else {
reason = fmt.Sprintf("ExitCode:%d", container.State.Terminated.ExitCode)
reason = "Init:" + container.State.Terminated.Reason
}
initializing = true
case container.State.Waiting != nil && len(container.State.Waiting.Reason) > 0 && container.State.Waiting.Reason != "PodInitializing":
reason = "Init:" + container.State.Waiting.Reason
initializing = true
default:
reason = fmt.Sprintf("Init:%d/%d", i, len(pod.Spec.InitContainers))
initializing = true
}
break
}
if !initializing {
for i := len(pod.Status.ContainerStatuses) - 1; i >= 0; i-- {
container := pod.Status.ContainerStatuses[i]
restarts += int(container.RestartCount)
if container.State.Waiting != nil && container.State.Waiting.Reason != "" {
reason = container.State.Waiting.Reason
} else if container.State.Terminated != nil && container.State.Terminated.Reason != "" {
reason = container.State.Terminated.Reason
} else if container.State.Terminated != nil && container.State.Terminated.Reason == "" {
if container.State.Terminated.Signal != 0 {
reason = fmt.Sprintf("Signal:%d", container.State.Terminated.Signal)
} else {
reason = fmt.Sprintf("ExitCode:%d", container.State.Terminated.ExitCode)
}
} else if container.Ready && container.State.Running != nil {
readyContainers++
}
} else if container.Ready && container.State.Running != nil {
readyContainers++
}
}
if pod.DeletionTimestamp != nil {
@ -633,17 +682,22 @@ func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error {
if options.Wide {
nodeName := pod.Spec.NodeName
if _, err := fmt.Fprintf(w, "\t%s",
podIP := pod.Status.PodIP
if podIP == "" {
podIP = "<none>"
}
if _, err := fmt.Fprintf(w, "\t%s\t%s",
podIP,
nodeName,
); err != nil {
return err
}
}
if _, err := fmt.Fprint(w, appendLabels(pod.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(pod.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, pod.Labels)); err != nil {
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, pod.Labels)); err != nil {
return err
}
@ -679,10 +733,10 @@ func printPodTemplate(pod *api.PodTemplate, w io.Writer, options PrintOptions) e
if _, err := fmt.Fprintf(w, "\t%s", labels.FormatLabels(pod.Template.Labels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(pod.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(pod.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, pod.Labels)); err != nil {
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, pod.Labels)); err != nil {
return err
}
@ -729,10 +783,10 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ
return err
}
}
if _, err := fmt.Fprint(w, appendLabels(controller.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(controller.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, controller.Labels)); err != nil {
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, controller.Labels)); err != nil {
return err
}
@ -777,10 +831,10 @@ func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options PrintOption
return err
}
}
if _, err := fmt.Fprint(w, appendLabels(rs.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(rs.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, rs.Labels)); err != nil {
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, rs.Labels)); err != nil {
return err
}
@ -796,6 +850,38 @@ func printReplicaSetList(list *extensions.ReplicaSetList, w io.Writer, options P
return nil
}
func printCluster(c *federation.Cluster, w io.Writer, options PrintOptions) error {
var statuses []string
for _, condition := range c.Status.Conditions {
if condition.Status == api.ConditionTrue {
statuses = append(statuses, string(condition.Type))
} else {
statuses = append(statuses, "Not"+string(condition.Type))
}
}
if len(statuses) == 0 {
statuses = append(statuses, "Unknown")
}
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n",
c.Name,
strings.Join(statuses, ","),
c.Status.Version,
translateTimestamp(c.CreationTimestamp),
); err != nil {
return err
}
return nil
}
func printClusterList(list *federation.ClusterList, w io.Writer, options PrintOptions) error {
for _, rs := range list.Items {
if err := printCluster(&rs, w, options); err != nil {
return err
}
}
return nil
}
func printJob(job *batch.Job, w io.Writer, options PrintOptions) error {
name := job.Name
namespace := job.Namespace
@ -839,10 +925,10 @@ func printJob(job *batch.Job, w io.Writer, options PrintOptions) error {
return err
}
}
if _, err := fmt.Fprint(w, appendLabels(job.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(job.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, job.Labels)); err != nil {
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, job.Labels)); err != nil {
return err
}
@ -858,19 +944,26 @@ func printJobList(list *batch.JobList, w io.Writer, options PrintOptions) error
return nil
}
// loadBalancerStatusStringer behaves just like a string interface and converts the given status to a string.
func loadBalancerStatusStringer(s api.LoadBalancerStatus) string {
// 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 {
ingress := s.Ingress
result := []string{}
for i := range ingress {
if ingress[i].IP != "" {
result = append(result, ingress[i].IP)
} else if ingress[i].Hostname != "" {
result = append(result, ingress[i].Hostname)
}
}
return strings.Join(result, ",")
r := strings.Join(result, ",")
if !wide && len(r) > loadBalancerWidth {
r = r[0:(loadBalancerWidth-3)] + "..."
}
return r
}
func getServiceExternalIP(svc *api.Service) string {
func getServiceExternalIP(svc *api.Service, wide bool) string {
switch svc.Spec.Type {
case api.ServiceTypeClusterIP:
if len(svc.Spec.ExternalIPs) > 0 {
@ -883,7 +976,7 @@ func getServiceExternalIP(svc *api.Service) string {
}
return "<nodes>"
case api.ServiceTypeLoadBalancer:
lbIps := loadBalancerStatusStringer(svc.Status.LoadBalancer)
lbIps := loadBalancerStatusStringer(svc.Status.LoadBalancer, wide)
if len(svc.Spec.ExternalIPs) > 0 {
result := append(strings.Split(lbIps, ","), svc.Spec.ExternalIPs...)
return strings.Join(result, ",")
@ -910,7 +1003,7 @@ func printService(svc *api.Service, w io.Writer, options PrintOptions) error {
namespace := svc.Namespace
internalIP := svc.Spec.ClusterIP
externalIP := getServiceExternalIP(svc)
externalIP := getServiceExternalIP(svc, options.Wide)
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
@ -931,10 +1024,10 @@ func printService(svc *api.Service, w io.Writer, options PrintOptions) error {
return err
}
}
if _, err := fmt.Fprint(w, appendLabels(svc.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(svc.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, svc.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, svc.Labels))
return err
}
@ -955,11 +1048,39 @@ func backendStringer(backend *extensions.IngressBackend) string {
return fmt.Sprintf("%v:%v", backend.ServiceName, backend.ServicePort.String())
}
func formatHosts(rules []extensions.IngressRule) string {
list := []string{}
max := 3
more := false
for _, rule := range rules {
if len(list) == max {
more = true
}
if !more && len(rule.Host) != 0 {
list = append(list, rule.Host)
}
}
if len(list) == 0 {
return "*"
}
ret := strings.Join(list, ",")
if more {
return fmt.Sprintf("%s + %d more...", ret, len(rules)-max)
}
return ret
}
func formatPorts(tls []extensions.IngressTLS) string {
if len(tls) != 0 {
return "80, 443"
}
return "80"
}
func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions) error {
name := ingress.Name
namespace := ingress.Namespace
hostRules := ingress.Spec.Rules
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
return err
@ -968,49 +1089,20 @@ func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions
if _, err := fmt.Fprintf(w, "%s\t%v\t%v\t%v\t%s",
name,
"-",
backendStringer(ingress.Spec.Backend),
loadBalancerStatusStringer(ingress.Status.LoadBalancer),
formatHosts(ingress.Spec.Rules),
loadBalancerStatusStringer(ingress.Status.LoadBalancer, options.Wide),
formatPorts(ingress.Spec.TLS),
translateTimestamp(ingress.CreationTimestamp),
); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(ingress.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(ingress.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, ingress.Labels)); err != nil {
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, ingress.Labels)); err != nil {
return err
}
// Lay out all the rules on separate lines if use wide output.
// TODO(AdoHe): improve ingress output
extraLinePrefix := ""
if options.WithNamespace {
extraLinePrefix = "\t"
}
for _, rules := range hostRules {
if rules.HTTP == nil {
continue
}
_, err := fmt.Fprintf(w, "%s\t%v\t", extraLinePrefix, rules.Host)
if err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabelTabs(options.ColumnLabels)); err != nil {
return err
}
for _, rule := range rules.HTTP.Paths {
_, err := fmt.Fprintf(w, "%s\t%v\t%v", extraLinePrefix, rule.Path, backendStringer(&rule.Backend))
if err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabelTabs(options.ColumnLabels)); err != nil {
return err
}
}
}
return nil
}
@ -1051,10 +1143,10 @@ func printPetSet(ps *apps.PetSet, w io.Writer, options PrintOptions) error {
return err
}
}
if _, err := fmt.Fprint(w, appendLabels(ps.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(ps.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, ps.Labels)); err != nil {
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, ps.Labels)); err != nil {
return err
}
@ -1106,10 +1198,10 @@ func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options PrintOptions)
return err
}
}
if _, err := fmt.Fprint(w, appendLabels(ds.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(ds.Labels, options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, ds.Labels)); err != nil {
if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, ds.Labels)); err != nil {
return err
}
@ -1137,10 +1229,10 @@ func printEndpoints(endpoints *api.Endpoints, w io.Writer, options PrintOptions)
if _, err := fmt.Fprintf(w, "%s\t%s\t%s", name, formatEndpoints(endpoints, nil), translateTimestamp(endpoints.CreationTimestamp)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(endpoints.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(endpoints.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, endpoints.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, endpoints.Labels))
return err
}
@ -1161,10 +1253,10 @@ func printNamespace(item *api.Namespace, w io.Writer, options PrintOptions) erro
if _, err := fmt.Fprintf(w, "%s\t%s\t%s", item.Name, item.Status.Phase, translateTimestamp(item.CreationTimestamp)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(item.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, item.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, item.Labels))
return err
}
@ -1189,10 +1281,10 @@ func printSecret(item *api.Secret, w io.Writer, options PrintOptions) error {
if _, err := fmt.Fprintf(w, "%s\t%s\t%v\t%s", name, item.Type, len(item.Data), translateTimestamp(item.CreationTimestamp)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(item.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, item.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, item.Labels))
return err
}
@ -1218,10 +1310,10 @@ func printServiceAccount(item *api.ServiceAccount, w io.Writer, options PrintOpt
if _, err := fmt.Fprintf(w, "%s\t%d\t%s", name, len(item.Secrets), translateTimestamp(item.CreationTimestamp)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(item.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, item.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, item.Labels))
return err
}
@ -1266,10 +1358,10 @@ func printNode(node *api.Node, w io.Writer, options PrintOptions) error {
return err
}
// Display caller specify column labels first.
if _, err := fmt.Fprint(w, appendLabels(node.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(node.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, node.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, node.Labels))
return err
}
@ -1310,10 +1402,10 @@ func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, options PrintO
); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(pv.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(pv.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, pv.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, pv.Labels))
return err
}
@ -1349,10 +1441,10 @@ func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, opt
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s", name, phase, pvc.Spec.VolumeName, capacity, accessModes, translateTimestamp(pvc.CreationTimestamp)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(pvc.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(pvc.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, pvc.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, pvc.Labels))
return err
}
@ -1385,8 +1477,8 @@ func printEvent(event *api.Event, w io.Writer, options PrintOptions) error {
if _, err := fmt.Fprintf(
w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s",
FirstTimestamp,
LastTimestamp,
FirstTimestamp,
event.Count,
event.InvolvedObject.Name,
event.InvolvedObject.Kind,
@ -1398,10 +1490,10 @@ func printEvent(event *api.Event, w io.Writer, options PrintOptions) error {
); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(event.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(event.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, event.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, event.Labels))
return err
}
@ -1417,27 +1509,7 @@ func printEventList(list *api.EventList, w io.Writer, options PrintOptions) erro
}
func printLimitRange(limitRange *api.LimitRange, w io.Writer, options PrintOptions) error {
name := limitRange.Name
namespace := limitRange.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
return err
}
}
if _, err := fmt.Fprintf(
w, "%s\t%s",
name,
translateTimestamp(limitRange.CreationTimestamp),
); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(limitRange.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, limitRange.Labels))
return err
return printObjectMeta(limitRange.ObjectMeta, w, options, true)
}
// Prints the LimitRangeList in a human-friendly format.
@ -1450,30 +1522,32 @@ func printLimitRangeList(list *api.LimitRangeList, w io.Writer, options PrintOpt
return nil
}
func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, options PrintOptions) error {
name := resourceQuota.Name
namespace := resourceQuota.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
// printObjectMeta prints the object metadata of a given resource.
func printObjectMeta(meta api.ObjectMeta, w io.Writer, options PrintOptions, namespaced bool) error {
if namespaced && options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", meta.Namespace); err != nil {
return err
}
}
if _, err := fmt.Fprintf(
w, "%s\t%s",
name,
translateTimestamp(resourceQuota.CreationTimestamp),
meta.Name,
translateTimestamp(meta.CreationTimestamp),
); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(resourceQuota.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(meta.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, resourceQuota.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, meta.Labels))
return err
}
func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, options PrintOptions) error {
return printObjectMeta(resourceQuota.ObjectMeta, w, options, true)
}
// Prints the ResourceQuotaList in a human-friendly format.
func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer, options PrintOptions) error {
for i := range list.Items {
@ -1484,6 +1558,62 @@ func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer, options Pr
return nil
}
func printRole(role *rbac.Role, w io.Writer, options PrintOptions) error {
return printObjectMeta(role.ObjectMeta, w, options, true)
}
// Prints the Role in a human-friendly format.
func printRoleList(list *rbac.RoleList, w io.Writer, options PrintOptions) error {
for i := range list.Items {
if err := printRole(&list.Items[i], w, options); err != nil {
return err
}
}
return nil
}
func printRoleBinding(roleBinding *rbac.RoleBinding, w io.Writer, options PrintOptions) error {
return printObjectMeta(roleBinding.ObjectMeta, w, options, true)
}
// Prints the RoleBinding in a human-friendly format.
func printRoleBindingList(list *rbac.RoleBindingList, w io.Writer, options PrintOptions) error {
for i := range list.Items {
if err := printRoleBinding(&list.Items[i], w, options); err != nil {
return err
}
}
return nil
}
func printClusterRole(clusterRole *rbac.ClusterRole, w io.Writer, options PrintOptions) error {
return printObjectMeta(clusterRole.ObjectMeta, w, options, false)
}
// Prints the ClusterRole in a human-friendly format.
func printClusterRoleList(list *rbac.ClusterRoleList, w io.Writer, options PrintOptions) error {
for i := range list.Items {
if err := printClusterRole(&list.Items[i], w, options); err != nil {
return err
}
}
return nil
}
func printClusterRoleBinding(clusterRoleBinding *rbac.ClusterRoleBinding, w io.Writer, options PrintOptions) error {
return printObjectMeta(clusterRoleBinding.ObjectMeta, w, options, false)
}
// Prints the ClusterRoleBinding in a human-friendly format.
func printClusterRoleBindingList(list *rbac.ClusterRoleBindingList, w io.Writer, options PrintOptions) error {
for i := range list.Items {
if err := printClusterRoleBinding(&list.Items[i], w, options); err != nil {
return err
}
}
return nil
}
func printComponentStatus(item *api.ComponentStatus, w io.Writer, options PrintOptions) error {
if options.WithNamespace {
return fmt.Errorf("componentStatus is not namespaced")
@ -1507,10 +1637,10 @@ func printComponentStatus(item *api.ComponentStatus, w io.Writer, options PrintO
if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s", item.Name, status, message, error); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(item.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(item.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, item.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, item.Labels))
return err
}
@ -1591,10 +1721,10 @@ func printDeployment(deployment *extensions.Deployment, w io.Writer, options Pri
if _, err := fmt.Fprintf(w, "%s\t%d\t%d\t%d\t%d\t%s", deployment.Name, desiredReplicas, currentReplicas, updatedReplicas, availableReplicas, age); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(deployment.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(deployment.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, deployment.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, deployment.Labels))
return err
}
@ -1607,16 +1737,15 @@ func printDeploymentList(list *extensions.DeploymentList, w io.Writer, options P
return nil
}
func printHorizontalPodAutoscaler(hpa *extensions.HorizontalPodAutoscaler, w io.Writer, options PrintOptions) error {
func printHorizontalPodAutoscaler(hpa *autoscaling.HorizontalPodAutoscaler, w io.Writer, options PrintOptions) error {
namespace := hpa.Namespace
name := hpa.Name
reference := fmt.Sprintf("%s/%s/%s",
hpa.Spec.ScaleRef.Kind,
hpa.Spec.ScaleRef.Name,
hpa.Spec.ScaleRef.Subresource)
reference := fmt.Sprintf("%s/%s",
hpa.Spec.ScaleTargetRef.Kind,
hpa.Spec.ScaleTargetRef.Name)
target := "<unset>"
if hpa.Spec.CPUUtilization != nil {
target = fmt.Sprintf("%d%%", hpa.Spec.CPUUtilization.TargetPercentage)
if hpa.Spec.TargetCPUUtilizationPercentage != nil {
target = fmt.Sprintf("%d%%", *hpa.Spec.TargetCPUUtilizationPercentage)
}
current := "<waiting>"
if hpa.Status.CurrentCPUUtilizationPercentage != nil {
@ -1644,14 +1773,14 @@ func printHorizontalPodAutoscaler(hpa *extensions.HorizontalPodAutoscaler, w io.
); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(hpa.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(hpa.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, hpa.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, hpa.Labels))
return err
}
func printHorizontalPodAutoscalerList(list *extensions.HorizontalPodAutoscalerList, w io.Writer, options PrintOptions) error {
func printHorizontalPodAutoscalerList(list *autoscaling.HorizontalPodAutoscalerList, w io.Writer, options PrintOptions) error {
for i := range list.Items {
if err := printHorizontalPodAutoscaler(&list.Items[i], w, options); err != nil {
return err
@ -1672,10 +1801,10 @@ func printConfigMap(configMap *api.ConfigMap, w io.Writer, options PrintOptions)
if _, err := fmt.Fprintf(w, "%s\t%v\t%s", name, len(configMap.Data), translateTimestamp(configMap.CreationTimestamp)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(configMap.Labels, options.ColumnLabels)); err != nil {
if _, err := fmt.Fprint(w, AppendLabels(configMap.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, configMap.Labels))
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, configMap.Labels))
return err
}
@ -1689,9 +1818,9 @@ func printConfigMapList(list *api.ConfigMapList, w io.Writer, options PrintOptio
}
func printPodSecurityPolicy(item *extensions.PodSecurityPolicy, w io.Writer, options PrintOptions) error {
_, err := fmt.Fprintf(w, "%s\t%t\t%v\t%v\t%s\t%s\n", item.Name, item.Spec.Privileged,
item.Spec.Capabilities, item.Spec.Volumes, item.Spec.SELinux.Rule,
item.Spec.RunAsUser.Rule)
_, err := fmt.Fprintf(w, "%s\t%t\t%v\t%s\t%s\t%s\t%s\t%t\t%v\n", item.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)
return err
}
@ -1705,7 +1834,35 @@ func printPodSecurityPolicyList(list *extensions.PodSecurityPolicyList, w io.Wri
return nil
}
func appendLabels(itemLabels map[string]string, columnLabels []string) string {
func printNetworkPolicy(networkPolicy *extensions.NetworkPolicy, w io.Writer, options PrintOptions) error {
name := networkPolicy.Name
namespace := networkPolicy.Namespace
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
return err
}
}
if _, err := fmt.Fprintf(w, "%s\t%v\t%s", name, unversioned.FormatLabelSelector(&networkPolicy.Spec.PodSelector), translateTimestamp(networkPolicy.CreationTimestamp)); err != nil {
return err
}
if _, err := fmt.Fprint(w, AppendLabels(networkPolicy.Labels, options.ColumnLabels)); err != nil {
return err
}
_, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, networkPolicy.Labels))
return err
}
func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, options PrintOptions) error {
for i := range list.Items {
if err := printNetworkPolicy(&list.Items[i], w, options); err != nil {
return err
}
}
return nil
}
func AppendLabels(itemLabels map[string]string, columnLabels []string) string {
var buffer bytes.Buffer
for _, cl := range columnLabels {
@ -1722,7 +1879,7 @@ func appendLabels(itemLabels map[string]string, columnLabels []string) string {
// Append all labels to a single column. We need this even when show-labels flag* is
// false, since this adds newline delimiter to the end of each row.
func appendAllLabels(showLabels bool, itemLabels map[string]string) string {
func AppendAllLabels(showLabels bool, itemLabels map[string]string) string {
var buffer bytes.Buffer
if showLabels {
@ -1736,7 +1893,7 @@ func appendAllLabels(showLabels bool, itemLabels map[string]string) string {
// Append a set of tabs for each label column. We need this in the case where
// we have extra lines so that the tabwriter will still line things up.
func appendLabelTabs(columnLabels []string) string {
func AppendLabelTabs(columnLabels []string) string {
var buffer bytes.Buffer
for range columnLabels {
@ -1780,7 +1937,7 @@ func formatLabelHeaders(columnLabels []string) []string {
func formatWideHeaders(wide bool, t reflect.Type) []string {
if wide {
if t.String() == "*api.Pod" || t.String() == "*api.PodList" {
return []string{"NODE"}
return []string{"IP", "NODE"}
}
if t.String() == "*api.ReplicationController" || t.String() == "*api.ReplicationControllerList" {
return []string{"CONTAINER(S)", "IMAGE(S)", "SELECTOR"}

View file

@ -83,6 +83,10 @@ type RollingUpdaterConfig struct {
// further, ensuring that total number of pods running at any time during
// the update is atmost 130% of desired pods.
MaxSurge intstr.IntOrString
// OnProgress is invoked if set during each scale cycle, to allow the caller to perform additional logic or
// abort the scale. If an error is returned the cleanup method will not be invoked. The percentage value
// is a synthetic "progress" calculation that represents the approximate percentage completion.
OnProgress func(oldRc, newRc *api.ReplicationController, percentage int) error
}
// RollingUpdaterCleanupPolicy is a cleanup action to take after the
@ -217,6 +221,26 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
fmt.Fprintf(out, "Scaling up %s from %d to %d, scaling down %s from %d to 0 (keep %d pods available, don't exceed %d pods)\n",
newRc.Name, newRc.Spec.Replicas, desired, oldRc.Name, oldRc.Spec.Replicas, minAvailable, desired+maxSurge)
// give a caller incremental notification and allow them to exit early
goal := desired - newRc.Spec.Replicas
if goal < 0 {
goal = -goal
}
progress := func(complete bool) error {
if config.OnProgress == nil {
return nil
}
progress := desired - newRc.Spec.Replicas
if progress < 0 {
progress = -progress
}
percentage := 100
if !complete && goal > 0 {
percentage = int((goal - progress) * 100 / goal)
}
return config.OnProgress(oldRc, newRc, percentage)
}
// Scale newRc and oldRc until newRc has the desired number of replicas and
// oldRc has 0 replicas.
progressDeadline := time.Now().UnixNano() + config.Timeout.Nanoseconds()
@ -232,6 +256,11 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
}
newRc = scaledRc
// notify the caller if necessary
if err := progress(false); err != nil {
return err
}
// Wait between scaling operations for things to settle.
time.Sleep(config.UpdatePeriod)
@ -242,6 +271,11 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
}
oldRc = scaledRc
// notify the caller if necessary
if err := progress(false); err != nil {
return err
}
// If we are making progress, continue to advance the progress deadline.
// Otherwise, time out with an error.
progressMade := (newRc.Spec.Replicas != newReplicas) || (oldRc.Spec.Replicas != oldReplicas)
@ -252,6 +286,11 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
}
}
// notify the caller if necessary
if err := progress(true); err != nil {
return err
}
// Housekeeping and cleanup policy execution.
return r.cleanup(oldRc, newRc, config)
}

57
vendor/k8s.io/kubernetes/pkg/kubectl/rollout_status.go generated vendored Normal file
View file

@ -0,0 +1,57 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
)
// StatusViewer provides an interface for resources that provides rollout status.
type StatusViewer interface {
Status(namespace, name string) (string, bool, error)
}
func StatusViewerFor(kind unversioned.GroupKind, c client.Interface) (StatusViewer, error) {
switch kind {
case extensions.Kind("Deployment"):
return &DeploymentStatusViewer{c.Extensions()}, nil
}
return nil, fmt.Errorf("no status viewer has been implemented for %v", kind)
}
type DeploymentStatusViewer struct {
c client.ExtensionsInterface
}
// Status returns a message describing deployment status, and a bool value indicating if the status is considered done
func (s *DeploymentStatusViewer) Status(namespace, name string) (string, bool, error) {
deployment, err := s.c.Deployments(namespace).Get(name)
if err != nil {
return "", false, err
}
if deployment.Generation <= deployment.Status.ObservedGeneration {
if deployment.Status.UpdatedReplicas == deployment.Spec.Replicas {
return fmt.Sprintf("deployment %s successfully rolled out\n", name), true, nil
}
return fmt.Sprintf("Waiting for rollout to finish: %d out of %d new replicas have been updated...\n", deployment.Status.UpdatedReplicas, deployment.Spec.Replicas), false, nil
}
return fmt.Sprintf("Waiting for deployment spec update to be observed...\n"), false, nil
}

View file

@ -434,7 +434,7 @@ func populateResourceList(spec string) (api.ResourceList, error) {
if err != nil {
return nil, err
}
result[resourceName] = *resourceQuantity
result[resourceName] = resourceQuantity
}
return result, nil
}
@ -458,7 +458,7 @@ func populateV1ResourceList(spec string) (v1.ResourceList, error) {
if err != nil {
return nil, err
}
result[resourceName] = *resourceQuantity
result[resourceName] = resourceQuantity
}
return result, nil
}
@ -827,7 +827,7 @@ func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object,
}
func parseEnvs(envArray []string) ([]api.EnvVar, error) {
envs := []api.EnvVar{}
envs := make([]api.EnvVar, 0, len(envArray))
for _, env := range envArray {
pos := strings.Index(env, "=")
if pos == -1 {

124
vendor/k8s.io/kubernetes/pkg/kubectl/secret_for_tls.go generated vendored Normal file
View file

@ -0,0 +1,124 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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 (
"crypto/tls"
"fmt"
"io/ioutil"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
)
// SecretForTLSGeneratorV1 supports stable generation of a TLS secret.
type SecretForTLSGeneratorV1 struct {
// Name is the name of this TLS secret.
Name string
// Key is the path to the user's private key.
Key string
// Cert is the path to the user's public key certificate.
Cert string
}
// Ensure it supports the generator pattern that uses parameter injection
var _ Generator = &SecretForTLSGeneratorV1{}
// Ensure it supports the generator pattern that uses parameters specified during construction
var _ StructuredGenerator = &SecretForTLSGeneratorV1{}
// Generate returns a secret using the specified parameters
func (s SecretForTLSGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
err := ValidateParams(s.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 := &SecretForTLSGeneratorV1{
Name: params["name"],
Key: params["key"],
Cert: params["cert"],
}
return delegate.StructuredGenerate()
}
// StructuredGenerate outputs a secret object using the configured fields
func (s SecretForTLSGeneratorV1) StructuredGenerate() (runtime.Object, error) {
if err := s.validate(); err != nil {
return nil, err
}
tlsCrt, err := readFile(s.Cert)
if err != nil {
return nil, err
}
tlsKey, err := readFile(s.Key)
if err != nil {
return nil, err
}
secret := &api.Secret{}
secret.Name = s.Name
secret.Type = api.SecretTypeTLS
secret.Data = map[string][]byte{}
secret.Data[api.TLSCertKey] = []byte(tlsCrt)
secret.Data[api.TLSPrivateKeyKey] = []byte(tlsKey)
return secret, nil
}
// readFile just reads a file into a byte array.
func readFile(file string) ([]byte, error) {
b, err := ioutil.ReadFile(file)
if err != nil {
return []byte{}, fmt.Errorf("Cannot read file %v, %v", file, err)
}
return b, nil
}
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
func (s SecretForTLSGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{
{"name", true},
{"key", true},
{"cert", true},
}
}
// validate validates required fields are set to support structured generation
func (s SecretForTLSGeneratorV1) validate() error {
// TODO: This is not strictly necessary. We can generate a self signed cert
// if no key/cert is given. The only requiredment is that we either get both
// or none. See test/e2e/ingress_utils for self signed cert generation.
if len(s.Key) == 0 {
return fmt.Errorf("key must be specified.")
}
if len(s.Cert) == 0 {
return fmt.Errorf("certificate must be specified.")
}
if _, err := tls.LoadX509KeyPair(s.Cert, s.Key); err != nil {
return fmt.Errorf("failed to load key pair %v", err)
}
// TODO: Add more validation.
// 1. If the certificate contains intermediates, it is a valid chain.
// 2. Format etc.
return nil
}

View file

@ -65,6 +65,9 @@ func paramNames() []GeneratorParam {
{"load-balancer-ip", false},
{"type", false},
{"protocol", false},
// protocols will be used to keep port-protocol mapping derived from
// exposed object
{"protocols", false},
{"container-port", false}, // alias of target-port
{"target-port", false},
{"port-name", false},
@ -112,6 +115,15 @@ func generate(genericParams map[string]interface{}) (runtime.Object, error) {
// Leave the port unnamed.
servicePortName = ""
}
protocolsString, found := params["protocols"]
var portProtocolMap map[string]string
if found && len(protocolsString) > 0 {
portProtocolMap, err = ParseProtocols(protocolsString)
if err != nil {
return nil, err
}
}
// ports takes precedence over port since it will be
// specified only when the user hasn't specified a port
// via --port and the exposed object has multiple ports.
@ -122,6 +134,7 @@ func generate(genericParams map[string]interface{}) (runtime.Object, error) {
return nil, fmt.Errorf("'port' is a required parameter.")
}
}
portStringSlice := strings.Split(portString, ",")
for i, stillPortString := range portStringSlice {
port, err := strconv.Atoi(stillPortString)
@ -134,10 +147,26 @@ func generate(genericParams map[string]interface{}) (runtime.Object, error) {
if len(portStringSlice) > 1 {
name = fmt.Sprintf("port-%d", i+1)
}
protocol := params["protocol"]
switch {
case len(protocol) == 0 && len(portProtocolMap) == 0:
// Default to TCP, what the flag was doing previously.
protocol = "TCP"
case len(protocol) > 0 && len(portProtocolMap) > 0:
// User has specified the --protocol while exposing a multiprotocol resource
// We should stomp multiple protocols with the one specified ie. do nothing
case len(protocol) == 0 && len(portProtocolMap) > 0:
// no --protocol and we expose a multiprotocol resource
protocol = "TCP" // have the default so we can stay sane
if exposeProtocol, found := portProtocolMap[stillPortString]; found {
protocol = exposeProtocol
}
}
ports = append(ports, api.ServicePort{
Name: name,
Port: int32(port),
Protocol: api.Protocol(params["protocol"]),
Protocol: api.Protocol(protocol),
})
}

View file

@ -17,7 +17,10 @@ limitations under the License.
package kubectl
import (
"sort"
"k8s.io/kubernetes/pkg/api"
qosutil "k8s.io/kubernetes/pkg/kubelet/qos/util"
)
type SortableResourceNames []api.ResourceName
@ -34,6 +37,16 @@ func (list SortableResourceNames) Less(i, j int) bool {
return list[i] < list[j]
}
// SortedResourceNames returns the sorted resource names of a resource list.
func SortedResourceNames(list api.ResourceList) []api.ResourceName {
resources := make([]api.ResourceName, 0, len(list))
for res := range list {
resources = append(resources, res)
}
sort.Sort(SortableResourceNames(resources))
return resources
}
type SortableResourceQuotas []api.ResourceQuota
func (list SortableResourceQuotas) Len() int {
@ -47,3 +60,13 @@ func (list SortableResourceQuotas) Swap(i, j int) {
func (list SortableResourceQuotas) Less(i, j int) bool {
return list[i].Name < list[j].Name
}
// SortedQoSResourceNames returns the sorted resource names of a QoS list.
func SortedQoSResourceNames(list qosutil.QoSList) []api.ResourceName {
resources := make([]api.ResourceName, 0, len(list))
for res := range list {
resources = append(resources, res)
}
sort.Sort(SortableResourceNames(resources))
return resources
}

View file

@ -23,8 +23,10 @@ import (
"sort"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/integer"
"k8s.io/kubernetes/pkg/util/jsonpath"
"github.com/golang/glog"
@ -153,6 +155,29 @@ func isLess(i, j reflect.Value) (bool, error) {
return i.String() < j.String(), nil
case reflect.Ptr:
return isLess(i.Elem(), j.Elem())
case reflect.Struct:
// sort unversioned.Time
in := i.Interface()
if t, ok := in.(unversioned.Time); ok {
return t.Before(j.Interface().(unversioned.Time)), nil
}
// fallback to the fields comparison
for idx := 0; idx < i.NumField(); idx++ {
less, err := isLess(i.Field(idx), j.Field(idx))
if err != nil || !less {
return less, err
}
}
return true, nil
case reflect.Array, reflect.Slice:
// note: the length of i and j may be different
for idx := 0; idx < integer.IntMin(i.Len(), j.Len()); idx++ {
less, err := isLess(i.Index(idx), j.Index(idx))
if err != nil || !less {
return less, err
}
}
return true, nil
default:
return false, fmt.Errorf("unsortable type: %v", i.Kind())
}

View file

@ -378,7 +378,7 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati
// Use observedGeneration to determine if the deployment controller noticed the pause.
if err := deploymentutil.WaitForObservedDeployment(func() (*extensions.Deployment, error) {
return deployments.Get(name)
}, deployment.Generation, 10*time.Millisecond, 1*time.Minute); err != nil {
}, deployment.Generation, 1*time.Second, 1*time.Minute); err != nil {
return err
}
@ -396,7 +396,8 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati
errList := []error{}
for _, rc := range rsList.Items {
if err := rsReaper.Stop(rc.Namespace, rc.Name, timeout, gracePeriod); err != nil {
if !errors.IsNotFound(err) {
scaleGetErr, ok := err.(*ScaleError)
if !errors.IsNotFound(err) || ok && !errors.IsNotFound(scaleGetErr.ActualError) {
errList = append(errList, err)
}
}