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 }