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 }