volcano.sh/volcano@v1.9.0/pkg/cli/job/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 job
    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  	commonFlags
    41  
    42  	Namespace string
    43  	JobName   string
    44  }
    45  
    46  // level of print indent.
    47  const (
    48  	Level0 = iota
    49  	Level1
    50  	Level2
    51  )
    52  
    53  var viewJobFlags = &viewFlags{}
    54  
    55  // InitViewFlags init the view command flags.
    56  func InitViewFlags(cmd *cobra.Command) {
    57  	initFlags(cmd, &viewJobFlags.commonFlags)
    58  
    59  	cmd.Flags().StringVarP(&viewJobFlags.Namespace, "namespace", "n", "default", "the namespace of job")
    60  	cmd.Flags().StringVarP(&viewJobFlags.JobName, "name", "N", "", "the name of job")
    61  }
    62  
    63  // ViewJob gives full details of the job.
    64  func ViewJob() error {
    65  	config, err := util.BuildConfig(viewJobFlags.Master, viewJobFlags.Kubeconfig)
    66  	if err != nil {
    67  		return err
    68  	}
    69  	if viewJobFlags.JobName == "" {
    70  		err := fmt.Errorf("job name (specified by --name or -N) is mandatory to view a particular job")
    71  		return err
    72  	}
    73  
    74  	jobClient := versioned.NewForConfigOrDie(config)
    75  	job, err := jobClient.BatchV1alpha1().Jobs(viewJobFlags.Namespace).Get(context.TODO(), viewJobFlags.JobName, metav1.GetOptions{})
    76  	if err != nil {
    77  		return err
    78  	}
    79  	if job == nil {
    80  		fmt.Printf("No resources found\n")
    81  		return nil
    82  	}
    83  	PrintJobInfo(job, os.Stdout)
    84  	PrintEvents(GetEvents(config, job), os.Stdout)
    85  	return nil
    86  }
    87  
    88  // PrintJobInfo print the job detailed info into writer.
    89  func PrintJobInfo(job *v1alpha1.Job, writer io.Writer) {
    90  	WriteLine(writer, Level0, "Name:       \t%s\n", job.Name)
    91  	WriteLine(writer, Level0, "Namespace:  \t%s\n", job.Namespace)
    92  	if len(job.Labels) > 0 {
    93  		label, _ := json.Marshal(job.Labels)
    94  		WriteLine(writer, Level0, "Labels:     \t%s\n", string(label))
    95  	} else {
    96  		WriteLine(writer, Level0, "Labels:     \t<none>\n")
    97  	}
    98  	if len(job.Annotations) > 0 {
    99  		annotation, _ := json.Marshal(job.Annotations)
   100  		WriteLine(writer, Level0, "Annotations:\t%s\n", string(annotation))
   101  	} else {
   102  		WriteLine(writer, Level0, "Annotations:\t<none>\n")
   103  	}
   104  	WriteLine(writer, Level0, "API Version:\t%s\n", job.APIVersion)
   105  	WriteLine(writer, Level0, "Kind:       \t%s\n", job.Kind)
   106  
   107  	WriteLine(writer, Level0, "Metadata:\n")
   108  	WriteLine(writer, Level1, "Creation Timestamp:\t%s\n", job.CreationTimestamp)
   109  	WriteLine(writer, Level1, "Generate Name:     \t%s\n", job.GenerateName)
   110  	WriteLine(writer, Level1, "Generation:        \t%d\n", job.Generation)
   111  	WriteLine(writer, Level1, "Resource Version:  \t%s\n", job.ResourceVersion)
   112  	WriteLine(writer, Level1, "UID:               \t%s\n", job.UID)
   113  
   114  	WriteLine(writer, Level0, "Spec:\n")
   115  	WriteLine(writer, Level1, "Min Available:     \t%d\n", job.Spec.MinAvailable)
   116  	WriteLine(writer, Level1, "Plugins:\n")
   117  	WriteLine(writer, Level2, "Env:\t%v\n", job.Spec.Plugins["env"])
   118  	WriteLine(writer, Level2, "Ssh:\t%v\n", job.Spec.Plugins["ssh"])
   119  	WriteLine(writer, Level1, "Scheduler Name:    \t%s\n", job.Spec.SchedulerName)
   120  	WriteLine(writer, Level1, "Tasks:\n")
   121  	for i := 0; i < len(job.Spec.Tasks); i++ {
   122  		WriteLine(writer, Level2, "Name:\t%s\n", job.Spec.Tasks[i].Name)
   123  		WriteLine(writer, Level2, "Replicas:\t%d\n", job.Spec.Tasks[i].Replicas)
   124  		WriteLine(writer, Level2, "Template:\n")
   125  		WriteLine(writer, Level2+1, "Metadata:\n")
   126  		WriteLine(writer, Level2+2, "Annotations:\n")
   127  		WriteLine(writer, Level2+3, "Cri . Cci . Io / Container - Type:          \t%s\n", job.Spec.Tasks[i].Template.ObjectMeta.Annotations["cri.cci.io/container-type"])
   128  		WriteLine(writer, Level2+3, "Kubernetes . Io / Availablezone:            \t%s\n", job.Spec.Tasks[i].Template.ObjectMeta.Annotations["kubernetes.io/availablezone"])
   129  		WriteLine(writer, Level2+3, "Network . Alpha . Kubernetes . Io / Network:\t%s\n", job.Spec.Tasks[i].Template.ObjectMeta.Annotations["network.alpha.kubernetes.io/network"])
   130  		WriteLine(writer, Level2+2, "Creation Timestamp:\t%s\n", job.Spec.Tasks[i].Template.ObjectMeta.CreationTimestamp)
   131  
   132  		WriteLine(writer, Level2+1, "Spec:\n")
   133  		WriteLine(writer, Level2+2, "Containers:\n")
   134  		for j := 0; j < len(job.Spec.Tasks[i].Template.Spec.Containers); j++ {
   135  			WriteLine(writer, Level2+3, "Command:\n")
   136  			for k := 0; k < len(job.Spec.Tasks[i].Template.Spec.Containers[j].Command); k++ {
   137  				WriteLine(writer, Level2+4, "%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Command[k])
   138  			}
   139  			WriteLine(writer, Level2+3, "Image:\t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Image)
   140  			WriteLine(writer, Level2+3, "Name: \t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Name)
   141  			WriteLine(writer, Level2+3, "Ports:\n")
   142  			for k := 0; k < len(job.Spec.Tasks[i].Template.Spec.Containers[j].Ports); k++ {
   143  				WriteLine(writer, Level2+4, "Container Port:\t%d\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Ports[k].ContainerPort)
   144  				WriteLine(writer, Level2+4, "Name:          \t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Ports[k].Name)
   145  			}
   146  			WriteLine(writer, Level2+3, "Resources:\n")
   147  			WriteLine(writer, Level2+4, "Limits:\n")
   148  			WriteLine(writer, Level2+5, "Cpu:   \t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Resources.Limits.Cpu())
   149  			WriteLine(writer, Level2+5, "Memory:\t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Resources.Limits.Memory())
   150  			WriteLine(writer, Level2+4, "Requests:\n")
   151  			WriteLine(writer, Level2+5, "Cpu:   \t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Resources.Requests.Cpu())
   152  			WriteLine(writer, Level2+5, "Memory:\t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].Resources.Requests.Memory())
   153  			WriteLine(writer, Level2+4, "Working Dir:\t%s\n", job.Spec.Tasks[i].Template.Spec.Containers[j].WorkingDir)
   154  		}
   155  		WriteLine(writer, Level2+2, "Image Pull Secrets:\n")
   156  		for j := 0; j < len(job.Spec.Tasks[i].Template.Spec.ImagePullSecrets); j++ {
   157  			WriteLine(writer, Level2+3, "Name:     \t%s\n", job.Spec.Tasks[i].Template.Spec.ImagePullSecrets[j].Name)
   158  		}
   159  		WriteLine(writer, Level2+2, "Restart Policy:   \t%s\n", job.Spec.Tasks[i].Template.Spec.RestartPolicy)
   160  	}
   161  
   162  	WriteLine(writer, Level0, "Status:\n")
   163  	if job.Status.Succeeded > 0 {
   164  		WriteLine(writer, Level1, "Succeeded:    \t%d\n", job.Status.Succeeded)
   165  	}
   166  	if job.Status.Pending > 0 {
   167  		WriteLine(writer, Level1, "Pending:      \t%d\n", job.Status.Pending)
   168  	}
   169  	if job.Status.Running > 0 {
   170  		WriteLine(writer, Level1, "Running:      \t%d\n", job.Status.Running)
   171  	}
   172  	if job.Status.Failed > 0 {
   173  		WriteLine(writer, Level1, "Failed:       \t%d\n", job.Status.Failed)
   174  	}
   175  	if job.Status.Terminating > 0 {
   176  		WriteLine(writer, Level1, "Terminating:  \t%d\n", job.Status.Terminating)
   177  	}
   178  	if job.Status.Unknown > 0 {
   179  		WriteLine(writer, Level1, "Unknown:      \t%d\n", job.Status.Unknown)
   180  	}
   181  	if job.Status.RetryCount > 0 {
   182  		WriteLine(writer, Level1, "RetryCount:   \t%d\n", job.Status.RetryCount)
   183  	}
   184  	if job.Status.MinAvailable > 0 {
   185  		WriteLine(writer, Level1, "Min Available:\t%d\n", job.Status.MinAvailable)
   186  	}
   187  	if job.Status.Version > 0 {
   188  		WriteLine(writer, Level1, "Version:      \t%d\n", job.Status.Version)
   189  	}
   190  
   191  	WriteLine(writer, Level1, "State:\n")
   192  	WriteLine(writer, Level2, "Phase:\t%s\n", job.Status.State.Phase)
   193  	if len(job.Status.ControlledResources) > 0 {
   194  		WriteLine(writer, Level1, "Controlled Resources:\n")
   195  		for key, value := range job.Status.ControlledResources {
   196  			WriteLine(writer, Level2, "%s: \t%s\n", key, value)
   197  		}
   198  	}
   199  	if len(job.Status.Conditions) > 0 {
   200  		WriteLine(writer, Level1, "Conditions:\n    Status\tTransitionTime\n")
   201  		for _, c := range job.Status.Conditions {
   202  			WriteLine(writer, Level2, "%v \t%v \n",
   203  				c.Status,
   204  				c.LastTransitionTime)
   205  		}
   206  	}
   207  }
   208  
   209  // PrintEvents print event info to writer.
   210  func PrintEvents(events []coreV1.Event, writer io.Writer) {
   211  	if len(events) > 0 {
   212  		WriteLine(writer, Level0, "%s:\n%-15s\t%-40s\t%-30s\t%-40s\t%s\n", "Events", "Type", "Reason", "Age", "Form", "Message")
   213  		WriteLine(writer, Level0, "%-15s\t%-40s\t%-30s\t%-40s\t%s\n", "-------", "-------", "-------", "-------", "-------")
   214  		for _, e := range events {
   215  			var interval string
   216  			if e.Count > 1 {
   217  				interval = fmt.Sprintf("%s (x%d over %s)", translateTimestampSince(e.LastTimestamp), e.Count, translateTimestampSince(e.FirstTimestamp))
   218  			} else {
   219  				interval = translateTimestampSince(e.FirstTimestamp)
   220  			}
   221  			EventSourceString := []string{e.Source.Component}
   222  			if len(e.Source.Host) > 0 {
   223  				EventSourceString = append(EventSourceString, e.Source.Host)
   224  			}
   225  			WriteLine(writer, Level0, "%-15v\t%-40v\t%-30s\t%-40s\t%v\n",
   226  				e.Type,
   227  				e.Reason,
   228  				interval,
   229  				strings.Join(EventSourceString, ", "),
   230  				strings.TrimSpace(e.Message),
   231  			)
   232  		}
   233  	} else {
   234  		WriteLine(writer, Level0, "Events: \t<none>\n")
   235  	}
   236  }
   237  
   238  // GetEvents get the job event by config.
   239  func GetEvents(config *rest.Config, job *v1alpha1.Job) []coreV1.Event {
   240  	kubernetes, err := kubernetes.NewForConfig(config)
   241  	if err != nil {
   242  		fmt.Printf("%v\n", err)
   243  		return nil
   244  	}
   245  	events, _ := kubernetes.CoreV1().Events(viewJobFlags.Namespace).List(context.TODO(), metav1.ListOptions{})
   246  	var jobEvents []coreV1.Event
   247  	for _, v := range events.Items {
   248  		if strings.HasPrefix(v.ObjectMeta.Name, job.Name+".") {
   249  			jobEvents = append(jobEvents, v)
   250  		}
   251  	}
   252  	return jobEvents
   253  }
   254  
   255  // WriteLine write lines with specified indent.
   256  func WriteLine(writer io.Writer, spaces int, content string, params ...interface{}) {
   257  	prefix := ""
   258  	for i := 0; i < spaces; i++ {
   259  		prefix += "  "
   260  	}
   261  	fmt.Fprintf(writer, prefix+content, params...)
   262  }