Add --chunk-size support to kubectl describe

Kubernetes-commit: 8ad6fd6ddbf6a8e0f2cdca17583af38cf30c2bb2
This commit is contained in:
Katrina Verey 2021-04-20 11:24:35 -07:00 committed by Kubernetes Publisher
parent d0eb368b00
commit c31d6154d6
5 changed files with 230 additions and 68 deletions

View File

@ -92,6 +92,7 @@ func NewCmdDescribe(parent string, f cmdutil.Factory, streams genericclioptions.
FilenameOptions: &resource.FilenameOptions{},
DescriberSettings: &describe.DescriberSettings{
ShowEvents: true,
ChunkSize: cmdutil.DefaultChunkSize,
},
CmdParent: parent,
@ -115,6 +116,7 @@ func NewCmdDescribe(parent string, f cmdutil.Factory, streams genericclioptions.
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
cmd.Flags().BoolVar(&o.DescriberSettings.ShowEvents, "show-events", o.DescriberSettings.ShowEvents, "If true, display events related to the described object.")
cmdutil.AddChunkSizeFlag(cmd, &o.DescriberSettings.ChunkSize)
return cmd
}
@ -156,6 +158,7 @@ func (o *DescribeOptions) Run() error {
FilenameParam(o.EnforceNamespace, o.FilenameOptions).
LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(true, o.BuilderArgs...).
RequestChunksOf(o.DescriberSettings.ChunkSize).
Flatten().
Do()
err := r.Err()
@ -220,6 +223,7 @@ func (o *DescribeOptions) DescribeMatchingResources(originalError error, resourc
NamespaceParam(o.Namespace).DefaultNamespace().
ResourceTypeOrNameArgs(true, resource).
SingleResourceType().
RequestChunksOf(o.DescriberSettings.ChunkSize).
Flatten().
Do()
mapping, err := r.ResourceMapping()

View File

@ -216,6 +216,32 @@ func TestDescribeObjectSkipEvents(t *testing.T) {
}
}
func TestDescribeObjectChunkSize(t *testing.T) {
d := &testDescriber{Output: "test output"}
oldFn := describe.DescriberFn
defer func() {
describe.DescriberFn = oldFn
}()
describe.DescriberFn = d.describerFor
pods, _, _ := cmdtesting.TestData()
tf := cmdtesting.NewTestFactory().WithNamespace("test")
defer tf.Cleanup()
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)},
}
cmd := NewCmdDescribe("kubectl", tf, genericclioptions.NewTestIOStreamsDiscard())
cmd.Flags().Set("chunk-size", "100")
cmd.Run(cmd, []string{"pods"})
if d.Settings.ChunkSize != 100 {
t.Errorf("ChunkSize = 100 expected, got ChunkSize = %v", d.Settings.ChunkSize)
}
}
func TestDescribeHelpMessage(t *testing.T) {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()

View File

@ -33,6 +33,7 @@ import (
"unicode"
"github.com/fatih/camelcase"
"k8s.io/apimachinery/pkg/runtime"
appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
@ -65,6 +66,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/cli-runtime/pkg/genericclioptions"
runtimeresource "k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/dynamic"
clientset "k8s.io/client-go/kubernetes"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
@ -284,7 +286,7 @@ func (g *genericDescriber) Describe(namespace, name string, describerSettings De
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = g.events.Events(namespace).Search(scheme.Scheme, obj)
events, _ = searchEvents(g.events, obj, describerSettings.ChunkSize)
}
return tabbedString(func(out io.Writer) error {
@ -403,7 +405,17 @@ func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings
if err != nil {
return "", err
}
resourceQuotaList, err := d.CoreV1().ResourceQuotas(name).List(context.TODO(), metav1.ListOptions{})
resourceQuotaList := &corev1.ResourceQuotaList{}
err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
func(options metav1.ListOptions) (runtime.Object, error) {
newList, err := d.CoreV1().ResourceQuotas(name).List(context.TODO(), options)
if err != nil {
return nil, runtimeresource.EnhanceListError(err, options, corev1.ResourceQuotas.String())
}
resourceQuotaList.Items = append(resourceQuotaList.Items, newList.Items...)
return newList, nil
})
if err != nil {
if errors.IsNotFound(err) {
// Server does not support resource quotas.
@ -413,7 +425,17 @@ func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings
return "", err
}
}
limitRangeList, err := d.CoreV1().LimitRanges(name).List(context.TODO(), metav1.ListOptions{})
limitRangeList := &corev1.LimitRangeList{}
err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
func(options metav1.ListOptions) (runtime.Object, error) {
newList, err := d.CoreV1().LimitRanges(name).List(context.TODO(), options)
if err != nil {
return nil, runtimeresource.EnhanceListError(err, options, "limitranges")
}
limitRangeList.Items = append(limitRangeList.Items, newList.Items...)
return newList, nil
})
if err != nil {
if errors.IsNotFound(err) {
// Server does not support limit ranges.
@ -674,9 +696,22 @@ func (d *PodDescriber) Describe(namespace, name string, describerSettings Descri
if describerSettings.ShowEvents {
eventsInterface := d.CoreV1().Events(namespace)
selector := eventsInterface.GetFieldSelector(&name, &namespace, nil, nil)
options := metav1.ListOptions{FieldSelector: selector.String()}
events, err2 := eventsInterface.List(context.TODO(), options)
if describerSettings.ShowEvents && err2 == nil && len(events.Items) > 0 {
initialOpts := metav1.ListOptions{
FieldSelector: selector.String(),
Limit: describerSettings.ChunkSize,
}
events := &corev1.EventList{}
err2 := runtimeresource.FollowContinue(&initialOpts,
func(options metav1.ListOptions) (runtime.Object, error) {
newList, err := eventsInterface.List(context.TODO(), options)
if err != nil {
return nil, runtimeresource.EnhanceListError(err, options, "events")
}
events.Items = append(events.Items, newList.Items...)
return newList, nil
})
if err2 == nil && len(events.Items) > 0 {
return tabbedString(func(out io.Writer) error {
w := NewPrefixWriter(out)
w.Write(LEVEL_0, "Pod '%v': error '%v', but found events.\n", name, err)
@ -697,7 +732,7 @@ func (d *PodDescriber) Describe(namespace, name string, describerSettings Descri
if _, isMirrorPod := pod.Annotations[corev1.MirrorPodAnnotationKey]; isMirrorPod {
ref.UID = types.UID(pod.Annotations[corev1.MirrorPodAnnotationKey])
}
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, ref)
events, _ = searchEvents(d.CoreV1(), ref, describerSettings.ChunkSize)
}
}
@ -1382,7 +1417,7 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, pv)
events, _ = searchEvents(d.CoreV1(), pv, describerSettings.ChunkSize)
}
return describePersistentVolume(pv, events)
@ -1530,18 +1565,18 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, descri
pc := d.CoreV1().Pods(namespace)
pods, err := getPodsForPVC(pc, pvc.Name)
pods, err := getPodsForPVC(pc, pvc.Name, describerSettings)
if err != nil {
return "", err
}
events, _ := d.CoreV1().Events(namespace).Search(scheme.Scheme, pvc)
events, _ := searchEvents(d.CoreV1(), pvc, describerSettings.ChunkSize)
return describePersistentVolumeClaim(pvc, events, pods)
}
func getPodsForPVC(c corev1client.PodInterface, pvcName string) ([]corev1.Pod, error) {
nsPods, err := c.List(context.TODO(), metav1.ListOptions{})
func getPodsForPVC(c corev1client.PodInterface, pvcName string, settings DescriberSettings) ([]corev1.Pod, error) {
nsPods, err := getPodsInChunks(c, metav1.ListOptions{Limit: settings.ChunkSize})
if err != nil {
return []corev1.Pod{}, err
}
@ -2000,14 +2035,15 @@ func (d *ReplicationControllerDescriber) Describe(namespace, name string, descri
return "", err
}
running, waiting, succeeded, failed, err := getPodStatusForController(pc, labels.SelectorFromSet(controller.Spec.Selector), controller.UID)
selector := labels.SelectorFromSet(controller.Spec.Selector)
running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector, controller.UID, describerSettings)
if err != nil {
return "", err
}
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, controller)
events, _ = searchEvents(d.CoreV1(), controller, describerSettings.ChunkSize)
}
return describeReplicationController(controller, events, running, waiting, succeeded, failed)
@ -2080,11 +2116,11 @@ func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings
return "", err
}
running, waiting, succeeded, failed, getPodErr := getPodStatusForController(pc, selector, rs.UID)
running, waiting, succeeded, failed, getPodErr := getPodStatusForController(pc, selector, rs.UID, describerSettings)
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, rs)
events, _ = searchEvents(d.CoreV1(), rs, describerSettings.ChunkSize)
}
return describeReplicaSet(rs, events, running, waiting, succeeded, failed, getPodErr)
@ -2136,7 +2172,7 @@ func (d *JobDescriber) Describe(namespace, name string, describerSettings Descri
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, job)
events, _ = searchEvents(d.CoreV1(), job, describerSettings.ChunkSize)
}
return describeJob(job, events)
@ -2219,7 +2255,7 @@ func (d *CronJobDescriber) Describe(namespace, name string, describerSettings De
cronJob, err := d.client.BatchV1().CronJobs(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = d.client.CoreV1().Events(namespace).Search(scheme.Scheme, cronJob)
events, _ = searchEvents(d.client.CoreV1(), cronJob, describerSettings.ChunkSize)
}
return describeCronJob(cronJob, events)
}
@ -2230,7 +2266,7 @@ func (d *CronJobDescriber) Describe(namespace, name string, describerSettings De
return "", err
}
if describerSettings.ShowEvents {
events, _ = d.client.CoreV1().Events(namespace).Search(scheme.Scheme, cronJob)
events, _ = searchEvents(d.client.CoreV1(), cronJobBeta, describerSettings.ChunkSize)
}
return describeCronJobBeta(cronJobBeta, events)
}
@ -2399,14 +2435,14 @@ func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings
if err != nil {
return "", err
}
running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector, daemon.UID)
running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector, daemon.UID, describerSettings)
if err != nil {
return "", err
}
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, daemon)
events, _ = searchEvents(d.CoreV1(), daemon, describerSettings.ChunkSize)
}
return describeDaemonSet(daemon, events, running, waiting, succeeded, failed)
@ -2490,14 +2526,14 @@ func (i *IngressDescriber) Describe(namespace, name string, describerSettings De
netV1, err := i.client.NetworkingV1().Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = i.client.CoreV1().Events(namespace).Search(scheme.Scheme, netV1)
events, _ = searchEvents(i.client.CoreV1(), netV1, describerSettings.ChunkSize)
}
return i.describeIngressV1(netV1, events)
}
netV1beta1, err := i.client.NetworkingV1beta1().Ingresses(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = i.client.CoreV1().Events(namespace).Search(scheme.Scheme, netV1beta1)
events, _ = searchEvents(i.client.CoreV1(), netV1beta1, describerSettings.ChunkSize)
}
return i.describeIngressV1beta1(netV1beta1, events)
}
@ -2703,14 +2739,14 @@ func (i *IngressClassDescriber) Describe(namespace, name string, describerSettin
netV1, err := i.client.NetworkingV1().IngressClasses().Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = i.client.CoreV1().Events(namespace).Search(scheme.Scheme, netV1)
events, _ = searchEvents(i.client.CoreV1(), netV1, describerSettings.ChunkSize)
}
return i.describeIngressClassV1(netV1, events)
}
netV1beta1, err := i.client.NetworkingV1beta1().IngressClasses().Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = i.client.CoreV1().Events(namespace).Search(scheme.Scheme, netV1beta1)
events, _ = searchEvents(i.client.CoreV1(), netV1beta1, describerSettings.ChunkSize)
}
return i.describeIngressClassV1beta1(netV1beta1, events)
}
@ -2779,7 +2815,7 @@ func (d *ServiceDescriber) Describe(namespace, name string, describerSettings De
endpoints, _ := d.CoreV1().Endpoints(namespace).Get(context.TODO(), name, metav1.GetOptions{})
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, service)
events, _ = searchEvents(d.CoreV1(), service, describerSettings.ChunkSize)
}
return describeService(service, endpoints, events)
}
@ -2898,7 +2934,7 @@ func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, ep)
events, _ = searchEvents(d.CoreV1(), ep, describerSettings.ChunkSize)
}
return describeEndpoints(ep, events)
@ -2970,7 +3006,7 @@ func (d *EndpointSliceDescriber) Describe(namespace, name string, describerSetti
epsV1, err := d.DiscoveryV1().EndpointSlices(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, epsV1)
events, _ = searchEvents(d.CoreV1(), epsV1, describerSettings.ChunkSize)
}
return describeEndpointSliceV1(epsV1, events)
}
@ -2981,7 +3017,7 @@ func (d *EndpointSliceDescriber) Describe(namespace, name string, describerSetti
}
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, epsV1beta1)
events, _ = searchEvents(d.CoreV1(), epsV1beta1, describerSettings.ChunkSize)
}
return describeEndpointSliceV1beta1(epsV1beta1, events)
@ -3159,7 +3195,16 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
// missingSecrets is the set of all secrets present in the
// serviceAccount but not present in the set of existing secrets.
missingSecrets := sets.NewString()
secrets, err := d.CoreV1().Secrets(namespace).List(context.TODO(), metav1.ListOptions{})
secrets := corev1.SecretList{}
err = runtimeresource.FollowContinue(&metav1.ListOptions{Limit: describerSettings.ChunkSize},
func(options metav1.ListOptions) (runtime.Object, error) {
newList, err := d.CoreV1().Secrets(namespace).List(context.TODO(), options)
if err != nil {
return nil, runtimeresource.EnhanceListError(err, options, corev1.ResourceSecrets.String())
}
secrets.Items = append(secrets.Items, newList.Items...)
return newList, nil
})
// errors are tolerated here in order to describe the serviceAccount with all
// of the secrets that it references, even if those secrets cannot be fetched.
@ -3193,7 +3238,7 @@ func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSett
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = d.CoreV1().Events(namespace).Search(scheme.Scheme, serviceAccount)
events, _ = searchEvents(d.CoreV1(), serviceAccount, describerSettings.ChunkSize)
}
return describeServiceAccount(serviceAccount, tokens, missingSecrets, events)
@ -3436,7 +3481,11 @@ func (d *NodeDescriber) Describe(namespace, name string, describerSettings Descr
// in a policy aware setting, users may have access to a node, but not all pods
// in that case, we note that the user does not have access to the pods
canViewPods := true
nodeNonTerminatedPodsList, err := d.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{FieldSelector: fieldSelector.String()})
initialOpts := metav1.ListOptions{
FieldSelector: fieldSelector.String(),
Limit: describerSettings.ChunkSize,
}
nodeNonTerminatedPodsList, err := getPodsInChunks(d.CoreV1().Pods(namespace), initialOpts)
if err != nil {
if !errors.IsForbidden(err) {
return "", err
@ -3451,7 +3500,7 @@ func (d *NodeDescriber) Describe(namespace, name string, describerSettings Descr
} else {
// TODO: We haven't decided the namespace for Node object yet.
ref.UID = types.UID(ref.Name)
events, _ = d.CoreV1().Events("").Search(scheme.Scheme, ref)
events, _ = searchEvents(d.CoreV1(), ref, describerSettings.ChunkSize)
}
}
@ -3596,14 +3645,14 @@ func (p *StatefulSetDescriber) Describe(namespace, name string, describerSetting
return "", err
}
running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector, ps.UID)
running, waiting, succeeded, failed, err := getPodStatusForController(pc, selector, ps.UID, describerSettings)
if err != nil {
return "", err
}
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = p.client.CoreV1().Events(namespace).Search(scheme.Scheme, ps)
events, _ = searchEvents(p.client.CoreV1(), ps, describerSettings.ChunkSize)
}
return describeStatefulSet(ps, selector, events, running, waiting, succeeded, failed)
@ -3664,7 +3713,7 @@ func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, de
signerName = csr.Spec.SignerName
username = csr.Spec.Username
if describerSettings.ShowEvents {
events, _ = p.client.CoreV1().Events(namespace).Search(scheme.Scheme, csr)
events, _ = searchEvents(p.client.CoreV1(), csr, describerSettings.ChunkSize)
}
} else if csr, err := p.client.CertificatesV1beta1().CertificateSigningRequests().Get(context.TODO(), name, metav1.GetOptions{}); err == nil {
crBytes = csr.Spec.Request
@ -3679,7 +3728,7 @@ func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, de
}
username = csr.Spec.Username
if describerSettings.ShowEvents {
events, _ = p.client.CoreV1().Events(namespace).Search(scheme.Scheme, csr)
events, _ = searchEvents(p.client.CoreV1(), csr, describerSettings.ChunkSize)
}
} else {
return "", err
@ -3764,7 +3813,7 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, desc
hpaV2beta2, err := d.client.AutoscalingV2beta2().HorizontalPodAutoscalers(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = d.client.CoreV1().Events(namespace).Search(scheme.Scheme, hpaV2beta2)
events, _ = searchEvents(d.client.CoreV1(), hpaV2beta2, describerSettings.ChunkSize)
}
return describeHorizontalPodAutoscalerV2beta2(hpaV2beta2, events, d)
}
@ -3772,7 +3821,7 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, desc
hpaV1, err := d.client.AutoscalingV1().HorizontalPodAutoscalers(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err == nil {
if describerSettings.ShowEvents {
events, _ = d.client.CoreV1().Events(namespace).Search(scheme.Scheme, hpaV1)
events, _ = searchEvents(d.client.CoreV1(), hpaV1, describerSettings.ChunkSize)
}
return describeHorizontalPodAutoscalerV1(hpaV1, events, d)
}
@ -4115,19 +4164,28 @@ func (dd *DeploymentDescriber) Describe(namespace, name string, describerSetting
if err != nil {
return "", err
}
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = searchEvents(dd.client.CoreV1(), d, describerSettings.ChunkSize)
}
var oldRSs, newRSs []*appsv1.ReplicaSet
if oldResult, _, newResult, err := deploymentutil.GetAllReplicaSetsInChunks(d, dd.client.AppsV1(), describerSettings.ChunkSize); err == nil {
oldRSs = oldResult
if newResult != nil {
newRSs = append(newRSs, newResult)
}
}
return describeDeployment(d, oldRSs, newRSs, events)
}
func describeDeployment(d *appsv1.Deployment, oldRSs []*appsv1.ReplicaSet, newRSs []*appsv1.ReplicaSet, events *corev1.EventList) (string, error) {
selector, err := metav1.LabelSelectorAsSelector(d.Spec.Selector)
if err != nil {
return "", err
}
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = dd.client.CoreV1().Events(namespace).Search(scheme.Scheme, d)
}
return describeDeployment(d, selector, d.DeepCopy(), events, dd)
}
func describeDeployment(d *appsv1.Deployment, selector labels.Selector, internalDeployment *appsv1.Deployment, events *corev1.EventList, dd *DeploymentDescriber) (string, error) {
return tabbedString(func(out io.Writer) error {
w := NewPrefixWriter(out)
w.Write(LEVEL_0, "Name:\t%s\n", d.ObjectMeta.Name)
@ -4143,7 +4201,7 @@ func describeDeployment(d *appsv1.Deployment, selector labels.Selector, internal
ru := d.Spec.Strategy.RollingUpdate
w.Write(LEVEL_0, "RollingUpdateStrategy:\t%s max unavailable, %s max surge\n", ru.MaxUnavailable.String(), ru.MaxSurge.String())
}
DescribePodTemplate(&internalDeployment.Spec.Template, w)
DescribePodTemplate(&d.Spec.Template, w)
if len(d.Status.Conditions) > 0 {
w.Write(LEVEL_0, "Conditions:\n Type\tStatus\tReason\n")
w.Write(LEVEL_1, "----\t------\t------\n")
@ -4151,13 +4209,9 @@ func describeDeployment(d *appsv1.Deployment, selector labels.Selector, internal
w.Write(LEVEL_1, "%v \t%v\t%v\n", c.Type, c.Status, c.Reason)
}
}
oldRSs, _, newRS, err := deploymentutil.GetAllReplicaSets(d, dd.client.AppsV1())
if err == nil {
if len(oldRSs) > 0 || len(newRSs) > 0 {
w.Write(LEVEL_0, "OldReplicaSets:\t%s\n", printReplicaSetsByLabels(oldRSs))
var newRSs []*appsv1.ReplicaSet
if newRS != nil {
newRSs = append(newRSs, newRS)
}
w.Write(LEVEL_0, "NewReplicaSet:\t%s\n", printReplicaSetsByLabels(newRSs))
}
if events != nil {
@ -4182,9 +4236,10 @@ func printReplicaSetsByLabels(matchingRSs []*appsv1.ReplicaSet) string {
return list
}
func getPodStatusForController(c corev1client.PodInterface, selector labels.Selector, uid types.UID) (running, waiting, succeeded, failed int, err error) {
options := metav1.ListOptions{LabelSelector: selector.String()}
rcPods, err := c.List(context.TODO(), options)
func getPodStatusForController(c corev1client.PodInterface, selector labels.Selector, uid types.UID, settings DescriberSettings) (
running, waiting, succeeded, failed int, err error) {
initialOpts := metav1.ListOptions{LabelSelector: selector.String(), Limit: settings.ChunkSize}
rcPods, err := getPodsInChunks(c, initialOpts)
if err != nil {
return
}
@ -4208,6 +4263,20 @@ func getPodStatusForController(c corev1client.PodInterface, selector labels.Sele
return
}
func getPodsInChunks(c corev1client.PodInterface, initialOpts metav1.ListOptions) (*corev1.PodList, error) {
podList := &corev1.PodList{}
err := runtimeresource.FollowContinue(&initialOpts,
func(options metav1.ListOptions) (runtime.Object, error) {
newList, err := c.List(context.TODO(), options)
if err != nil {
return nil, runtimeresource.EnhanceListError(err, options, corev1.ResourcePods.String())
}
podList.Items = append(podList.Items, newList.Items...)
return newList, nil
})
return podList, err
}
// ConfigMapDescriber generates information about a ConfigMap
type ConfigMapDescriber struct {
clientset.Interface
@ -4234,7 +4303,7 @@ func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings
w.Write(LEVEL_0, "%s\n", string(v))
}
if describerSettings.ShowEvents {
events, err := d.CoreV1().Events(namespace).Search(scheme.Scheme, configMap)
events, err := searchEvents(d.CoreV1(), configMap, describerSettings.ChunkSize)
if err != nil {
return err
}
@ -4415,7 +4484,7 @@ func (s *StorageClassDescriber) Describe(namespace, name string, describerSettin
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = s.CoreV1().Events(namespace).Search(scheme.Scheme, sc)
events, _ = searchEvents(s.CoreV1(), sc, describerSettings.ChunkSize)
}
return describeStorageClass(sc, events)
@ -4467,7 +4536,7 @@ func (c *CSINodeDescriber) Describe(namespace, name string, describerSettings De
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = c.CoreV1().Events(namespace).Search(scheme.Scheme, csi)
events, _ = searchEvents(c.CoreV1(), csi, describerSettings.ChunkSize)
}
return describeCSINode(csi, events)
@ -4549,7 +4618,7 @@ func (p *PodDisruptionBudgetDescriber) Describe(namespace, name string, describe
if err == nil {
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = p.CoreV1().Events(namespace).Search(scheme.Scheme, pdbv1)
events, _ = searchEvents(p.CoreV1(), pdbv1, describerSettings.ChunkSize)
}
return describePodDisruptionBudgetV1(pdbv1, events)
}
@ -4561,7 +4630,7 @@ func (p *PodDisruptionBudgetDescriber) Describe(namespace, name string, describe
if err == nil {
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = p.CoreV1().Events(namespace).Search(scheme.Scheme, pdbv1beta1)
events, _ = searchEvents(p.CoreV1(), pdbv1beta1, describerSettings.ChunkSize)
}
return describePodDisruptionBudgetV1beta1(pdbv1beta1, events)
}
@ -4642,7 +4711,7 @@ func (s *PriorityClassDescriber) Describe(namespace, name string, describerSetti
var events *corev1.EventList
if describerSettings.ShowEvents {
events, _ = s.CoreV1().Events(namespace).Search(scheme.Scheme, pc)
events, _ = searchEvents(s.CoreV1(), pc, describerSettings.ChunkSize)
}
return describePriorityClass(pc, events)
@ -5411,3 +5480,37 @@ func loadBalancerStatusStringer(s corev1.LoadBalancerStatus, wide bool) string {
}
return r
}
// searchEvents finds events about the specified object.
// It is very similar to CoreV1.Events.Search, but supports the Limit parameter.
func searchEvents(client corev1client.EventsGetter, objOrRef runtime.Object, limit int64) (*corev1.EventList, error) {
ref, err := reference.GetReference(scheme.Scheme, objOrRef)
if err != nil {
return nil, err
}
stringRefKind := string(ref.Kind)
var refKind *string
if len(stringRefKind) > 0 {
refKind = &stringRefKind
}
stringRefUID := string(ref.UID)
var refUID *string
if len(stringRefUID) > 0 {
refUID = &stringRefUID
}
e := client.Events(ref.Namespace)
fieldSelector := e.GetFieldSelector(&ref.Name, &ref.Namespace, refKind, refUID)
initialOpts := metav1.ListOptions{FieldSelector: fieldSelector.String(), Limit: limit}
eventList := &corev1.EventList{}
err = runtimeresource.FollowContinue(&initialOpts,
func(options metav1.ListOptions) (runtime.Object, error) {
newEvents, err := e.List(context.TODO(), options)
if err != nil {
return nil, runtimeresource.EnhanceListError(err, options, "events")
}
eventList.Items = append(eventList.Items, newEvents.Items...)
return newEvents, nil
})
return eventList, err
}

View File

@ -48,6 +48,7 @@ type ResourceDescriber interface {
// describer to control what is printed.
type DescriberSettings struct {
ShowEvents bool
ChunkSize int64
}
// ObjectDescriber is an interface for displaying arbitrary objects with extra

View File

@ -28,6 +28,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
intstrutil "k8s.io/apimachinery/pkg/util/intstr"
runtimeresource "k8s.io/cli-runtime/pkg/resource"
appsclient "k8s.io/client-go/kubernetes/typed/apps/v1"
)
@ -84,7 +85,22 @@ func Revision(obj runtime.Object) (int64, error) {
// with no pods, and the second set of old replica sets include all old replica sets. The third returned value
// is the new replica set, and it may be nil if it doesn't exist yet.
func GetAllReplicaSets(deployment *appsv1.Deployment, c appsclient.AppsV1Interface) ([]*appsv1.ReplicaSet, []*appsv1.ReplicaSet, *appsv1.ReplicaSet, error) {
rsList, err := listReplicaSets(deployment, rsListFromClient(c))
rsList, err := listReplicaSets(deployment, rsListFromClient(c), nil)
if err != nil {
return nil, nil, nil, err
}
newRS := findNewReplicaSet(deployment, rsList)
oldRSes, allOldRSes := findOldReplicaSets(deployment, rsList, newRS)
return oldRSes, allOldRSes, newRS, nil
}
// GetAllReplicaSetsInChunks is the same as GetAllReplicaSets, but accepts a chunk size argument.
// It returns the old and new replica sets targeted by the given Deployment. It gets PodList and
// ReplicaSetList from client interface. Note that the first set of old replica sets doesn't include the ones
// with no pods, and the second set of old replica sets include all old replica sets. The third returned value
// is the new replica set, and it may be nil if it doesn't exist yet.
func GetAllReplicaSetsInChunks(deployment *appsv1.Deployment, c appsclient.AppsV1Interface, chunkSize int64) ([]*appsv1.ReplicaSet, []*appsv1.ReplicaSet, *appsv1.ReplicaSet, error) {
rsList, err := listReplicaSets(deployment, rsListFromClient(c), &chunkSize)
if err != nil {
return nil, nil, nil, err
}
@ -95,8 +111,17 @@ func GetAllReplicaSets(deployment *appsv1.Deployment, c appsclient.AppsV1Interfa
// RsListFromClient returns an rsListFunc that wraps the given client.
func rsListFromClient(c appsclient.AppsV1Interface) rsListFunc {
return func(namespace string, options metav1.ListOptions) ([]*appsv1.ReplicaSet, error) {
rsList, err := c.ReplicaSets(namespace).List(context.TODO(), options)
return func(namespace string, initialOpts metav1.ListOptions) ([]*appsv1.ReplicaSet, error) {
rsList := &appsv1.ReplicaSetList{}
err := runtimeresource.FollowContinue(&initialOpts,
func(opts metav1.ListOptions) (runtime.Object, error) {
newRs, err := c.ReplicaSets(namespace).List(context.TODO(), opts)
if err != nil {
return nil, runtimeresource.EnhanceListError(err, opts, "replicasets")
}
rsList.Items = append(rsList.Items, newRs.Items...)
return newRs, nil
})
if err != nil {
return nil, err
}
@ -115,7 +140,7 @@ type rsListFunc func(string, metav1.ListOptions) ([]*appsv1.ReplicaSet, error)
// Note that this does NOT attempt to reconcile ControllerRef (adopt/orphan),
// because only the controller itself should do that.
// However, it does filter out anything whose ControllerRef doesn't match.
func listReplicaSets(deployment *appsv1.Deployment, getRSList rsListFunc) ([]*appsv1.ReplicaSet, error) {
func listReplicaSets(deployment *appsv1.Deployment, getRSList rsListFunc, chunkSize *int64) ([]*appsv1.ReplicaSet, error) {
// TODO: Right now we list replica sets by their labels. We should list them by selector, i.e. the replica set's selector
// should be a superset of the deployment's selector, see https://github.com/kubernetes/kubernetes/issues/19830.
namespace := deployment.Namespace
@ -124,6 +149,9 @@ func listReplicaSets(deployment *appsv1.Deployment, getRSList rsListFunc) ([]*ap
return nil, err
}
options := metav1.ListOptions{LabelSelector: selector.String()}
if chunkSize != nil {
options.Limit = *chunkSize
}
all, err := getRSList(namespace, options)
if err != nil {
return nil, err