kruise-tools/pkg/cmd/get/get.go

422 lines
16 KiB
Go

/*
Copyright 2025 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"fmt"
"time"
kruiseappsv1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1"
kruiseappsv1beta1 "github.com/openkruise/kruise-api/apps/v1beta1"
kruisepolicyv1alpha1 "github.com/openkruise/kruise-api/policy/v1alpha1"
rolloutv1alpha1 "github.com/openkruise/kruise-rollout-api/rollouts/v1alpha1"
rolloutv1beta1 "github.com/openkruise/kruise-rollout-api/rollouts/v1beta1"
internalpolymorphichelpers "github.com/openkruise/kruise-tools/pkg/internal/polymorphichelpers"
"github.com/spf13/cobra"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
var (
getLong = templates.Examples(i18n.T(`
Display one or many resources related to kruise.`))
getExample = templates.Examples(i18n.T(`
# List all resources in the default namespace
kubectl-kruise get all
# List all resources in the specific namespace
kubectl-kruise get all -n namespace
# Watch all resources in the default namespace
kubectl-kruise get all -w`))
)
type GetOptions struct {
genericclioptions.IOStreams
Builder func() *resource.Builder
Resources []string
Namespace string
EnforceNamespace bool
GetViewerFn internalpolymorphichelpers.GetViewerFunc
}
func NewCmdGet(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := &GetOptions{IOStreams: streams}
cmd := &cobra.Command{
Use: "get all",
DisableFlagsInUseLine: true,
Short: i18n.T("Display one or many resources"),
Long: getLong,
Example: getExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, args))
cmdutil.CheckErr(o.Run())
},
}
cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", o.Namespace, "If present, the namespace scope for this CLI request")
return cmd
}
func (o *GetOptions) Complete(f cmdutil.Factory, args []string) error {
var err error
if len(args) == 0 {
return fmt.Errorf("required resource not specified")
}
o.Resources = args
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}
o.GetViewerFn = internalpolymorphichelpers.GetViewerFn
o.Builder = f.NewBuilder
return nil
}
func (o *GetOptions) Run() error {
if len(o.Resources) == 0 {
return fmt.Errorf("you must specify the type of resource to get")
}
if o.Resources[0] == "all" {
resourceTypes := []string{
"clonesets.apps.kruise.io",
"statefulsets.apps.kruise.io",
"daemonsets.apps.kruise.io",
"rollouts.rollouts.kruise.io",
"broadcastjobs.apps.kruise.io",
"containerrecreaterequests.apps.kruise.io",
"advancedcronjobs.apps.kruise.io",
"resourcedistributions.apps.kruise.io",
"uniteddeployments.apps.kruise.io",
"sidecarsets.apps.kruise.io",
"podprobemarkers.apps.kruise.io",
"imagepulljobs.apps.kruise.io",
"podunavailablebudgets.policy.kruise.io",
}
for _, resourceType := range resourceTypes {
b := o.Builder().
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
NamespaceParam(o.Namespace).DefaultNamespace().
ResourceTypeOrNameArgs(true, resourceType).
ContinueOnError().
Latest().
Flatten()
if resourceType == "sidecarsets.apps.kruise.io" {
b = b.AllNamespaces(true)
}
infos, err := b.Do().Infos()
if err != nil {
return err
}
if len(infos) > 0 {
// Print resource type header and table header only if there are resources
fmt.Fprintf(o.Out, "\n%s:\n", resourceType)
switch resourceType {
case "clonesets.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-14s\t%-18s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "UPDATED", "UPDATED_READY", "UPDATED_AVAILABLE", "READY", "TOTAL", "AGE")
case "statefulsets.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "CURRENT", "UPDATED", "READY", "AGE")
case "daemonsets.apps.kruise.io":
fmt.Fprintf(o.Out, "%-8s\t%-8s\t%-8s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "CURRENT", "READY", "UP-TO-DATE", "AVAILABLE", "NODE SELECTOR", "AGE")
case "rollouts.rollouts.kruise.io":
fmt.Fprintf(o.Out, "%-20s\t%-8s\t%-12s\t%-12s\t%-40s\t%-s\n",
"NAME", "STATUS", "CANARY_STEP", "CANARY_STATE", "MESSAGE", "AGE")
case "broadcastjobs.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "ACTIVE", "SUCCEEDED", "FAILED", "AGE")
case "containerrecreaterequests.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "PHASE", "COMPLETED", "FAILED", "AGE")
case "advancedcronjobs.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "SCHEDULE", "SUSPEND", "ACTIVE", "LAST SCHEDULE", "AGE")
case "resourcedistributions.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "TARGETS", "SUCCEEDED", "FAILED", "AGE")
case "uniteddeployments.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "UPDATED", "READY", "AVAILABLE", "AGE")
case "sidecarsets.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "MATCHED", "UPDATED", "READY", "INJECTED", "AGE")
case "podprobemarkers.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-s\n",
"NAME", "TARGETS", "PROBES", "AGE")
case "imagepulljobs.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "PHASE", "COMPLETED", "FAILED", "TOTAL", "AGE")
case "podunavailablebudgets.policy.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "MAXUNAVAILABLE", "UNAVAILABLE", "DISRUPTIONS", "TARGETS", "AGE")
}
for _, info := range infos {
if err := o.printResourceInfo(info.Object, resourceType); err != nil {
return err
}
}
}
}
return nil
}
// Handle single resource type
b := o.Builder().
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
NamespaceParam(o.Namespace).DefaultNamespace().
ResourceTypeOrNameArgs(true, o.Resources...).
ContinueOnError().
Latest().
Flatten()
infos, err := b.Do().Infos()
if err != nil {
return err
}
if len(infos) > 0 {
switch o.Resources[0] {
case "clonesets.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-14s\t%-18s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "UPDATED", "UPDATED_READY", "UPDATED_AVAILABLE", "READY", "TOTAL", "AGE")
case "statefulsets.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "CURRENT", "UPDATED", "READY", "AGE")
case "daemonsets.apps.kruise.io":
fmt.Fprintf(o.Out, "%-8s\t%-8s\t%-8s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "CURRENT", "READY", "UP-TO-DATE", "AVAILABLE", "NODE SELECTOR", "AGE")
case "rollouts.rollouts.kruise.io":
fmt.Fprintf(o.Out, "%-20s\t%-8s\t%-12s\t%-12s\t%-40s\t%-s\n",
"NAME", "STATUS", "CANARY_STEP", "CANARY_STATE", "MESSAGE", "AGE")
case "broadcastjobs.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "ACTIVE", "SUCCEEDED", "FAILED", "AGE")
case "containerrecreaterequests.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "PHASE", "COMPLETED", "FAILED", "AGE")
case "advancedcronjobs.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "SCHEDULE", "SUSPEND", "ACTIVE", "LAST SCHEDULE", "AGE")
case "resourcedistributions.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "TARGETS", "SUCCEEDED", "FAILED", "AGE")
case "uniteddeployments.apps.kruise.io":
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-8s\t%-s\n",
"NAME", "DESIRED", "UPDATED", "READY", "AVAILABLE", "AGE")
}
for _, info := range infos {
if err := o.printResourceInfo(info.Object, o.Resources[0]); err != nil {
return err
}
}
}
return nil
}
func (o *GetOptions) printResourceInfo(obj runtime.Object, resourceType string) error {
metaObj, ok := obj.(v1.Object)
if !ok {
return fmt.Errorf("object does not implement v1.Object interface")
}
name := metaObj.GetName()
age := time.Since(metaObj.GetCreationTimestamp().Time).Round(time.Second)
// Print based on resource type
switch resourceType {
case "clonesets.apps.kruise.io":
cloneset, ok := obj.(*kruiseappsv1alpha1.CloneSet)
if !ok {
return fmt.Errorf("object is not a CloneSet")
}
desired := cloneset.Spec.Replicas
updated := cloneset.Status.UpdatedReplicas
updatedReady := cloneset.Status.UpdatedReadyReplicas
updatedAvailable := cloneset.Status.UpdatedAvailableReplicas
ready := cloneset.Status.ReadyReplicas
total := cloneset.Status.Replicas
fmt.Fprintf(o.Out, "%-12s\t%-8d\t%-8d\t%-14d\t%-18d\t%-8d\t%-8d\t%-s\n",
name, *desired, updated, updatedReady, updatedAvailable, ready, total, age)
case "statefulsets.apps.kruise.io":
statefulset, ok := obj.(*kruiseappsv1beta1.StatefulSet)
if !ok {
return fmt.Errorf("object is not a StatefulSet")
}
desired := statefulset.Spec.Replicas
current := statefulset.Status.CurrentReplicas
updated := statefulset.Status.UpdatedReplicas
ready := statefulset.Status.ReadyReplicas
fmt.Fprintf(o.Out, "%-12s\t%-8d\t%-8d\t%-8d\t%-8d\t%-s\n",
name, *desired, current, updated, ready, age)
case "daemonsets.apps.kruise.io":
daemonset, ok := obj.(*kruiseappsv1alpha1.DaemonSet)
if !ok {
return fmt.Errorf("object is not a DaemonSet")
}
desired := daemonset.Status.DesiredNumberScheduled
current := daemonset.Status.CurrentNumberScheduled
ready := daemonset.Status.NumberReady
updated := daemonset.Status.UpdatedNumberScheduled
available := daemonset.Status.NumberAvailable
nodeSelector := daemonset.Spec.Template.Spec.NodeSelector
fmt.Fprintf(o.Out, "%-8s\t%-8d\t%-8d\t%-8d\t%-8d\t%-8d\t%-8s\t%-s\n",
name, desired, current, ready, updated, available, fmt.Sprintf("%v", nodeSelector), age)
case "rollouts.rollouts.kruise.io":
rollout, ok := obj.(*rolloutv1beta1.Rollout)
if !ok {
rolloutV1alpha1, ok := obj.(*rolloutv1alpha1.Rollout)
if !ok {
return fmt.Errorf("object is not a Rollout")
}
status := rolloutV1alpha1.Status.Phase
canaryStep := int32(0)
canaryState := string(rolloutv1alpha1.CanaryStepStateCompleted)
message := rolloutV1alpha1.Status.Message
if rolloutV1alpha1.Status.CanaryStatus != nil {
canaryStep = rolloutV1alpha1.Status.CanaryStatus.CurrentStepIndex
canaryState = string(rolloutV1alpha1.Status.CanaryStatus.CurrentStepState)
}
fmt.Fprintf(o.Out, "%-20s\t%-8s\t%-12d\t%-12s\t%-40s\t%-s\n",
name, status, canaryStep, canaryState, message, age)
return nil
}
status := rollout.Status.Phase
canaryStep := int32(0)
canaryState := string(rolloutv1beta1.CanaryStepStateCompleted)
message := rollout.Status.Message
if rollout.Status.CanaryStatus != nil {
canaryStep = rollout.Status.CanaryStatus.CurrentStepIndex
canaryState = string(rollout.Status.CanaryStatus.CurrentStepState)
}
fmt.Fprintf(o.Out, "%-20s\t%-8s\t%-12d\t%-12s\t%-40s\t%-s\n",
name, status, canaryStep, canaryState, message, age)
case "broadcastjobs.apps.kruise.io":
broadcastjob, ok := obj.(*kruiseappsv1alpha1.BroadcastJob)
if !ok {
return fmt.Errorf("object is not a BroadcastJob")
}
desired := broadcastjob.Spec.Parallelism
active := broadcastjob.Status.Active
successful := broadcastjob.Status.Succeeded
failed := broadcastjob.Status.Failed
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8d\t%-8d\t%-8d\t%-s\n",
name, desired.String(), active, successful, failed, age)
case "containerrecreaterequests.apps.kruise.io":
containerrecreaterequest, ok := obj.(*kruiseappsv1alpha1.ContainerRecreateRequest)
if !ok {
return fmt.Errorf("object is not a ContainerRecreateRequest")
}
phase := containerrecreaterequest.Status.Phase
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-s\n",
name, phase, "-", "-", age)
case "advancedcronjobs.apps.kruise.io":
advancedcronjob, ok := obj.(*kruiseappsv1alpha1.AdvancedCronJob)
if !ok {
return fmt.Errorf("object is not a AdvancedCronJob")
}
schedule := advancedcronjob.Spec.Schedule
suspend := advancedcronjob.Spec.Paused
active := len(advancedcronjob.Status.Active)
lastSchedule := advancedcronjob.Status.LastScheduleTime
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8v\t%-8d\t%-8s\t%-s\n",
name, schedule, suspend, active, lastSchedule, age)
case "resourcedistributions.apps.kruise.io":
_, ok := obj.(*kruiseappsv1alpha1.ResourceDistribution)
if !ok {
return fmt.Errorf("object is not a ResourceDistribution")
}
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8s\t%-8s\t%-s\n",
name, "-", "-", "-", age)
case "uniteddeployments.apps.kruise.io":
uniteddeployment, ok := obj.(*kruiseappsv1alpha1.UnitedDeployment)
if !ok {
return fmt.Errorf("object is not a UnitedDeployment")
}
desired := uniteddeployment.Spec.Replicas
updated := uniteddeployment.Status.UpdatedReplicas
ready := uniteddeployment.Status.ReadyReplicas
total := uniteddeployment.Status.Replicas
fmt.Fprintf(o.Out, "%-12s\t%-8d\t%-8d\t%-8d\t%-8d\t%-s\n",
name, *desired, updated, ready, total, age)
case "sidecarsets.apps.kruise.io":
sidecarset, ok := obj.(*kruiseappsv1alpha1.SidecarSet)
if !ok {
return fmt.Errorf("object is not a SidecarSet")
}
matched := sidecarset.Status.MatchedPods
updated := sidecarset.Status.UpdatedPods
ready := sidecarset.Status.ReadyPods
fmt.Fprintf(o.Out, "%-12s\t%-8d\t%-8d\t%-8d\t%-8s\t%-s\n",
name, matched, updated, ready, "-", age)
case "podprobemarkers.apps.kruise.io":
podprobemarker, ok := obj.(*kruiseappsv1alpha1.PodProbeMarker)
if !ok {
return fmt.Errorf("object is not a PodProbeMarker")
}
targets := len(podprobemarker.Spec.Selector.MatchLabels)
probes := len(podprobemarker.Spec.Probes)
fmt.Fprintf(o.Out, "%-12s\t%-8d\t%-8d\t%-s\n",
name, targets, probes, age)
case "imagepulljobs.apps.kruise.io":
imagepulljob, ok := obj.(*kruiseappsv1alpha1.ImagePullJob)
if !ok {
return fmt.Errorf("object is not a ImagePullJob")
}
completed := imagepulljob.Status.Succeeded
failed := imagepulljob.Status.Failed
total := imagepulljob.Status.Desired
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8d\t%-8d\t%-8d\t%-s\n",
name, "-", completed, failed, total, age)
case "podunavailablebudgets.policy.kruise.io":
pub, ok := obj.(*kruisepolicyv1alpha1.PodUnavailableBudget)
if !ok {
return fmt.Errorf("object is not a PodUnavailableBudget")
}
maxUnavailable := pub.Spec.MaxUnavailable
unavailable := pub.Status.UnavailablePods
disruptions := pub.Status.DisruptedPods
targets := len(pub.Spec.Selector.MatchLabels)
fmt.Fprintf(o.Out, "%-12s\t%-8s\t%-8d\t%-8d\t%-8d\t%-s\n",
name, maxUnavailable.String(), len(unavailable), len(disruptions), targets, age)
}
return nil
}