volcano.sh/volcano@v1.9.0/pkg/cli/vjobs/view.go (about)

     1  /*
     2  Copyright 2019 The Volcano Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package vjobs
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"strings"
    26  
    27  	"github.com/spf13/cobra"
    28  
    29  	coreV1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/client-go/kubernetes"
    32  	"k8s.io/client-go/rest"
    33  
    34  	"volcano.sh/apis/pkg/apis/batch/v1alpha1"
    35  	"volcano.sh/apis/pkg/client/clientset/versioned"
    36  	"volcano.sh/volcano/pkg/cli/util"
    37  )
    38  
    39  type viewFlags struct {
    40  	util.CommonFlags
    41  
    42  	Namespace     string
    43  	JobName       string
    44  	SchedulerName string
    45  	allNamespace  bool
    46  	selector      string
    47  }
    48  
    49  const (
    50  	// Level0 is the level of print indent
    51  	Level0 = iota
    52  	// Level1 is the level of print indent
    53  	Level1
    54  	// Level2 is the level of print indent
    55  	Level2
    56  
    57  	// Name  name etc below key words are used in job print format
    58  	Name string = "Name"
    59  	// Creation create
    60  	Creation string = "Creation"
    61  	// Phase phase
    62  	Phase string = "Phase"
    63  	// Replicas  replicas
    64  	Replicas string = "Replicas"
    65  	// Min  minimum
    66  	Min string = "Min"
    67  	// Scheduler scheduler
    68  	Scheduler string = "Scheduler"
    69  	// Pending  pending
    70  	Pending string = "Pending"
    71  	// Running running
    72  	Running string = "Running"
    73  	// Succeeded success
    74  	Succeeded string = "Succeeded"
    75  	// Terminating terminating
    76  	Terminating string = "Terminating"
    77  	// Version version
    78  	Version string = "Version"
    79  	// Failed  failed
    80  	Failed string = "Failed"
    81  	// Unknown pod
    82  	Unknown string = "Unknown"
    83  	// RetryCount retry count
    84  	RetryCount string = "RetryCount"
    85  	// JobType  job type
    86  	JobType string = "JobType"
    87  	// Namespace job namespace
    88  	Namespace string = "Namespace"
    89  )
    90  
    91  var viewJobFlags = &viewFlags{}
    92  
    93  // InitViewFlags init the view command flags.
    94  func InitViewFlags(cmd *cobra.Command) {
    95  	util.InitFlags(cmd, &viewJobFlags.CommonFlags)
    96  
    97  	cmd.Flags().StringVarP(&viewJobFlags.Namespace, "namespace", "N", "default", "the namespace of job")
    98  	cmd.Flags().StringVarP(&viewJobFlags.JobName, "name", "n", "", "the name of job")
    99  	cmd.Flags().StringVarP(&viewJobFlags.SchedulerName, "scheduler", "S", "", "list job with specified scheduler name")
   100  	cmd.Flags().BoolVarP(&viewJobFlags.allNamespace, "all-namespaces", "", false, "list jobs in all namespaces")
   101  	cmd.Flags().StringVarP(&viewJobFlags.selector, "selector", "", "", "fuzzy matching jobName")
   102  }
   103  
   104  // ViewJob gives full details of the job.
   105  func ViewJob() error {
   106  	config, err := util.BuildConfig(viewJobFlags.Master, viewJobFlags.Kubeconfig)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	if viewJobFlags.JobName == "" {
   111  		err := ListJobs()
   112  		return err
   113  	}
   114  
   115  	jobClient := versioned.NewForConfigOrDie(config)
   116  	job, err := jobClient.BatchV1alpha1().Jobs(viewJobFlags.Namespace).Get(context.TODO(), viewJobFlags.JobName, metav1.GetOptions{})
   117  	if err != nil {
   118  		return err
   119  	}
   120  	if job == nil {
   121  		fmt.Printf("No resources found\n")
   122  		return nil
   123  	}
   124  	PrintJobInfo(job, os.Stdout)
   125  	PrintEvents(GetEvents(config, job), os.Stdout)
   126  	return nil
   127  }
   128  
   129  // PrintJobInfo print the job detailed info into writer.
   130  func PrintJobInfo(job *v1alpha1.Job, writer io.Writer) {
   131  	WriteLine(writer, Level0, "Name:       \t%s\n", job.Name)
   132  	WriteLine(writer, Level0, "Namespace:  \t%s\n", job.Namespace)
   133  	if len(job.Labels) > 0 {
   134  		label, _ := json.Marshal(job.Labels)
   135  		WriteLine(writer, Level0, "Labels:     \t%s\n", string(label))
   136  	} else {
   137  		WriteLine(writer, Level0, "Labels:     \t<none>\n")
   138  	}
   139  	if len(job.Annotations) > 0 {
   140  		annotation, _ := json.Marshal(job.Annotations)
   141  		WriteLine(writer, Level0, "Annotations:\t%s\n", string(annotation))
   142  	} else {
   143  		WriteLine(writer, Level0, "Annotations:\t<none>\n")
   144  	}
   145  	WriteLine(writer, Level0, "API Version:\t%s\n", job.APIVersion)
   146  	WriteLine(writer, Level0, "Kind:       \t%s\n", job.Kind)
   147  
   148  	WriteLine(writer, Level0, "Metadata:\n")
   149  	WriteLine(writer, Level1, "Creation Timestamp:\t%s\n", job.CreationTimestamp)
   150  	WriteLine(writer, Level1, "Generate Name:     \t%s\n", job.GenerateName)
   151  	WriteLine(writer, Level1, "Generation:        \t%d\n", job.Generation)
   152  	WriteLine(writer, Level1, "Resource Version:  \t%s\n", job.ResourceVersion)
   153  	WriteLine(writer, Level1, "UID:               \t%s\n", job.UID)
   154  
   155  	WriteLine(writer, Level0, "Spec:\n")
   156  	WriteLine(writer, Level1, "Min Available:     \t%d\n", job.Spec.MinAvailable)
   157  	WriteLine(writer, Level1, "Plugins:\n")
   158  	WriteLine(writer, Level2, "Env:\t%v\n", job.Spec.Plugins["env"])
   159  	WriteLine(writer, Level2, "Ssh:\t%v\n", job.Spec.Plugins["ssh"])
   160  	WriteLine(writer, Level1, "Scheduler Name:    \t%s\n", job.Spec.SchedulerName)
   161  	WriteLine(writer, Level1, "Tasks:\n")
   162  	for i := 0; i < len(job.Spec.Tasks); i++ {
   163  		WriteLine(writer, Level2, "Name:\t%s\n", job.Spec.Tasks[i].Name)
   164  		WriteLine(writer, Level2, "Replicas:\t%d\n", job.Spec.Tasks[i].Replicas)
   165  		WriteLine(writer, Level2, "Template:\n")
   166  		WriteLine(writer, Level2+1, "Metadata:\n")
   167  		WriteLine(writer, Level2+2, "Annotations:\n")
   168  		WriteLine(writer, Level2+3, "Cri . Cci . Io / Container - Type:          \t%s\n", job.Spec.Tasks[i].Template.ObjectMeta.Annotations["cri.cci.io/container-type"])
   169  		WriteLine(writer, Level2+3, "Kubernetes . Io / Availablezone:            \t%s\n", job.Spec.Tasks[i].Template.ObjectMeta.Annotations["kubernetes.io/availablezone"])
   170  		WriteLine(writer, Level2+3, "Network . Alpha . Kubernetes . Io / Network:\t%s\n", job.Spec.Tasks[i].Template.ObjectMeta.Annotations["network.alpha.kubernetes.io/network"])
   171  		WriteLine(writer, Level2+2, "Creation Timestamp:\t%s\n", job.Spec.Tasks[i].Template.ObjectMeta.CreationTimestamp)
   172  
   173  		WriteLine(writer, Level2+1, "Spec:\n")
   174  		WriteLine(writer, Level2+2, "Containers:\n")
   175  		for j := 0; j < len(job.Spec.Tasks[i].Template.Spec.Containers); j++ {
   176  			WriteLine(writer, Level2+3, "Command:\n")
   177  			for k := 0; k < len(job.Spec.Tasks[i].Template.Spec.Containers[j].Command); k++ {
   178  				WriteLine(writer, Level2+4, "%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Command[k])
   179  			}
   180  			WriteLine(writer, Level2+3, "Image:\t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Image)
   181  			WriteLine(writer, Level2+3, "Name: \t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Name)
   182  			WriteLine(writer, Level2+3, "Ports:\n")
   183  			for k := 0; k < len(job.Spec.Tasks[i].Template.Spec.Containers[j].Ports); k++ {
   184  				WriteLine(writer, Level2+4, "Container Port:\t%d\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Ports[k].ContainerPort)
   185  				WriteLine(writer, Level2+4, "Name:          \t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Ports[k].Name)
   186  			}
   187  			WriteLine(writer, Level2+3, "Resources:\n")
   188  			WriteLine(writer, Level2+4, "Limits:\n")
   189  			WriteLine(writer, Level2+5, "Cpu:   \t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Resources.Limits.Cpu())
   190  			WriteLine(writer, Level2+5, "Memory:\t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Resources.Limits.Memory())
   191  			WriteLine(writer, Level2+4, "Requests:\n")
   192  			WriteLine(writer, Level2+5, "Cpu:   \t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Resources.Requests.Cpu())
   193  			WriteLine(writer, Level2+5, "Memory:\t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Resources.Requests.Memory())
   194  			WriteLine(writer, Level2+4, "Working Dir:\t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].WorkingDir)
   195  		}
   196  		WriteLine(writer, Level2+2, "Image Pull Secrets:\n")
   197  		for j := 0; j < len(job.Spec.Tasks[i].Template.Spec.ImagePullSecrets); j++ {
   198  			WriteLine(writer, Level2+3, "Name:     \t%s\n", job.Spec.Tasks[i].Template.Spec.ImagePullSecrets[j].Name)
   199  		}
   200  		WriteLine(writer, Level2+2, "Restart Policy:   \t%s\n", job.Spec.Tasks[i].Template.Spec.RestartPolicy)
   201  	}
   202  
   203  	WriteLine(writer, Level0, "Status:\n")
   204  	if job.Status.Succeeded > 0 {
   205  		WriteLine(writer, Level1, "Succeeded:    \t%d\n", job.Status.Succeeded)
   206  	}
   207  	if job.Status.Pending > 0 {
   208  		WriteLine(writer, Level1, "Pending:      \t%d\n", job.Status.Pending)
   209  	}
   210  	if job.Status.Running > 0 {
   211  		WriteLine(writer, Level1, "Running:      \t%d\n", job.Status.Running)
   212  	}
   213  	if job.Status.Failed > 0 {
   214  		WriteLine(writer, Level1, "Failed:       \t%d\n", job.Status.Failed)
   215  	}
   216  	if job.Status.Terminating > 0 {
   217  		WriteLine(writer, Level1, "Terminating:  \t%d\n", job.Status.Terminating)
   218  	}
   219  	if job.Status.Unknown > 0 {
   220  		WriteLine(writer, Level1, "Unknown:      \t%d\n", job.Status.Unknown)
   221  	}
   222  	if job.Status.RetryCount > 0 {
   223  		WriteLine(writer, Level1, "RetryCount:   \t%d\n", job.Status.RetryCount)
   224  	}
   225  	if job.Status.MinAvailable > 0 {
   226  		WriteLine(writer, Level1, "Min Available:\t%d\n", job.Status.MinAvailable)
   227  	}
   228  	if job.Status.Version > 0 {
   229  		WriteLine(writer, Level1, "Version:      \t%d\n", job.Status.Version)
   230  	}
   231  
   232  	WriteLine(writer, Level1, "State:\n")
   233  	WriteLine(writer, Level2, "Phase:\t%s\n", job.Status.State.Phase)
   234  	if len(job.Status.ControlledResources) > 0 {
   235  		WriteLine(writer, Level1, "Controlled Resources:\n")
   236  		for key, value := range job.Status.ControlledResources {
   237  			WriteLine(writer, Level2, "%s: \t%s\n", key, value)
   238  		}
   239  	}
   240  }
   241  
   242  // PrintEvents print event info to writer.
   243  func PrintEvents(events []coreV1.Event, writer io.Writer) {
   244  	if len(events) > 0 {
   245  		WriteLine(writer, Level0, "%s:\n%-15s\t%-40s\t%-30s\t%-40s\t%s\n", "Events", "Type", "Reason", "Age", "Form", "Message")
   246  		WriteLine(writer, Level0, "%-15s\t%-40s\t%-30s\t%-40s\t%s\n", "-------", "-------", "-------", "-------", "-------")
   247  		for _, e := range events {
   248  			var interval string
   249  			if e.Count > 1 {
   250  				interval = fmt.Sprintf("%s (x%d over %s)", util.TranslateTimestampSince(e.LastTimestamp), e.Count, util.TranslateTimestampSince(e.FirstTimestamp))
   251  			} else {
   252  				interval = util.TranslateTimestampSince(e.FirstTimestamp)
   253  			}
   254  			EventSourceString := []string{e.Source.Component}
   255  			if len(e.Source.Host) > 0 {
   256  				EventSourceString = append(EventSourceString, e.Source.Host)
   257  			}
   258  			WriteLine(writer, Level0, "%-15v\t%-40v\t%-30s\t%-40s\t%v\n",
   259  				e.Type,
   260  				e.Reason,
   261  				interval,
   262  				strings.Join(EventSourceString, ", "),
   263  				strings.TrimSpace(e.Message),
   264  			)
   265  		}
   266  	} else {
   267  		WriteLine(writer, Level0, "Events: \t<none>\n")
   268  	}
   269  }
   270  
   271  // GetEvents get the job event by config.
   272  func GetEvents(config *rest.Config, job *v1alpha1.Job) []coreV1.Event {
   273  	kubernetes, err := kubernetes.NewForConfig(config)
   274  	if err != nil {
   275  		fmt.Printf("%v\n", err)
   276  		return nil
   277  	}
   278  	events, _ := kubernetes.CoreV1().Events(viewJobFlags.Namespace).List(context.TODO(), metav1.ListOptions{})
   279  	var jobEvents []coreV1.Event
   280  	for _, v := range events.Items {
   281  		if strings.HasPrefix(v.ObjectMeta.Name, job.Name+".") {
   282  			jobEvents = append(jobEvents, v)
   283  		}
   284  	}
   285  	return jobEvents
   286  }
   287  
   288  // WriteLine write lines with specified indent.
   289  func WriteLine(writer io.Writer, spaces int, content string, params ...interface{}) {
   290  	prefix := ""
   291  	for i := 0; i < spaces; i++ {
   292  		prefix += "  "
   293  	}
   294  	fmt.Fprintf(writer, prefix+content, params...)
   295  }
   296  
   297  // ListJobs lists all jobs details.
   298  func ListJobs() error {
   299  	config, err := util.BuildConfig(viewJobFlags.Master, viewJobFlags.Kubeconfig)
   300  	if err != nil {
   301  		return err
   302  	}
   303  	if viewJobFlags.allNamespace {
   304  		viewJobFlags.Namespace = ""
   305  	}
   306  	jobClient := versioned.NewForConfigOrDie(config)
   307  	jobs, err := jobClient.BatchV1alpha1().Jobs(viewJobFlags.Namespace).List(context.TODO(), metav1.ListOptions{})
   308  	if err != nil {
   309  		return err
   310  	}
   311  
   312  	if len(jobs.Items) == 0 {
   313  		fmt.Printf("No resources found\n")
   314  		return nil
   315  	}
   316  	PrintJobs(jobs, os.Stdout)
   317  
   318  	return nil
   319  }
   320  
   321  // PrintJobs prints all jobs details.
   322  func PrintJobs(jobs *v1alpha1.JobList, writer io.Writer) {
   323  	maxLenInfo := getMaxLen(jobs)
   324  
   325  	titleFormat := "%%-%ds%%-15s%%-12s%%-12s%%-12s%%-6s%%-10s%%-10s%%-12s%%-10s%%-12s%%-10s\n"
   326  	contentFormat := "%%-%ds%%-15s%%-12s%%-12s%%-12d%%-6d%%-10d%%-10d%%-12d%%-10d%%-12d%%-10d\n"
   327  
   328  	var err error
   329  	if viewJobFlags.allNamespace {
   330  		_, err = fmt.Fprintf(writer, fmt.Sprintf("%%-%ds"+titleFormat, maxLenInfo[1], maxLenInfo[0]),
   331  			Namespace, Name, Creation, Phase, JobType, Replicas, Min, Pending, Running, Succeeded, Failed, Unknown, RetryCount)
   332  	} else {
   333  		_, err = fmt.Fprintf(writer, fmt.Sprintf(titleFormat, maxLenInfo[0]),
   334  			Name, Creation, Phase, JobType, Replicas, Min, Pending, Running, Succeeded, Failed, Unknown, RetryCount)
   335  	}
   336  	if err != nil {
   337  		fmt.Printf("Failed to print list command result: %s.\n", err)
   338  	}
   339  
   340  	for _, job := range jobs.Items {
   341  		if viewJobFlags.SchedulerName != "" && viewJobFlags.SchedulerName != job.Spec.SchedulerName {
   342  			continue
   343  		}
   344  		if !strings.Contains(job.Name, viewJobFlags.selector) {
   345  			continue
   346  		}
   347  		replicas := int32(0)
   348  		for _, ts := range job.Spec.Tasks {
   349  			replicas += ts.Replicas
   350  		}
   351  		jobType := job.ObjectMeta.Labels[v1alpha1.JobTypeKey]
   352  		if jobType == "" {
   353  			jobType = "Batch"
   354  		}
   355  
   356  		if viewJobFlags.allNamespace {
   357  			_, err = fmt.Fprintf(writer, fmt.Sprintf("%%-%ds"+contentFormat, maxLenInfo[1], maxLenInfo[0]),
   358  				job.Namespace, job.Name, job.CreationTimestamp.Format("2006-01-02"), job.Status.State.Phase, jobType, replicas,
   359  				job.Status.MinAvailable, job.Status.Pending, job.Status.Running, job.Status.Succeeded, job.Status.Failed, job.Status.Unknown, job.Status.RetryCount)
   360  		} else {
   361  			_, err = fmt.Fprintf(writer, fmt.Sprintf(contentFormat, maxLenInfo[0]),
   362  				job.Name, job.CreationTimestamp.Format("2006-01-02"), job.Status.State.Phase, jobType, replicas,
   363  				job.Status.MinAvailable, job.Status.Pending, job.Status.Running, job.Status.Succeeded, job.Status.Failed, job.Status.Unknown, job.Status.RetryCount)
   364  		}
   365  		if err != nil {
   366  			fmt.Printf("Failed to print list command result: %s.\n", err)
   367  		}
   368  	}
   369  }
   370  
   371  func getMaxLen(jobs *v1alpha1.JobList) []int {
   372  	maxNameLen := len(Name)
   373  	maxNamespaceLen := len(Namespace)
   374  	for _, job := range jobs.Items {
   375  		if len(job.Name) > maxNameLen {
   376  			maxNameLen = len(job.Name)
   377  		}
   378  		if len(job.Namespace) > maxNamespaceLen {
   379  			maxNamespaceLen = len(job.Namespace)
   380  		}
   381  	}
   382  
   383  	return []int{maxNameLen + 3, maxNamespaceLen + 3}
   384  }