github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/job_inspect.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/nomad/api" 8 "github.com/hashicorp/nomad/api/contexts" 9 "github.com/posener/complete" 10 ) 11 12 type JobInspectCommand struct { 13 Meta 14 } 15 16 func (c *JobInspectCommand) Help() string { 17 helpText := ` 18 Usage: nomad job inspect [options] <job> 19 Alias: nomad inspect 20 21 Inspect is used to see the specification of a submitted job. 22 23 When ACLs are enabled, this command requires a token with the 'read-job' and 24 'list-jobs' capabilities for the job's namespace. 25 26 General Options: 27 28 ` + generalOptionsUsage(usageOptsDefault) + ` 29 30 Inspect Options: 31 32 -version <job version> 33 Display the job at the given job version. 34 35 -json 36 Output the job in its JSON format. 37 38 -t 39 Format and display job using a Go template. 40 ` 41 return strings.TrimSpace(helpText) 42 } 43 44 func (c *JobInspectCommand) Synopsis() string { 45 return "Inspect a submitted job" 46 } 47 48 func (c *JobInspectCommand) AutocompleteFlags() complete.Flags { 49 return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), 50 complete.Flags{ 51 "-version": complete.PredictAnything, 52 "-json": complete.PredictNothing, 53 "-t": complete.PredictAnything, 54 }) 55 } 56 57 func (c *JobInspectCommand) AutocompleteArgs() complete.Predictor { 58 return complete.PredictFunc(func(a complete.Args) []string { 59 client, err := c.Meta.Client() 60 if err != nil { 61 return nil 62 } 63 64 resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Jobs, nil) 65 if err != nil { 66 return []string{} 67 } 68 return resp.Matches[contexts.Jobs] 69 }) 70 } 71 72 func (c *JobInspectCommand) Name() string { return "job inspect" } 73 74 func (c *JobInspectCommand) Run(args []string) int { 75 var json bool 76 var tmpl, versionStr string 77 78 flags := c.Meta.FlagSet(c.Name(), FlagSetClient) 79 flags.Usage = func() { c.Ui.Output(c.Help()) } 80 flags.BoolVar(&json, "json", false, "") 81 flags.StringVar(&tmpl, "t", "", "") 82 flags.StringVar(&versionStr, "version", "", "") 83 84 if err := flags.Parse(args); err != nil { 85 return 1 86 } 87 args = flags.Args() 88 89 // Get the HTTP client 90 client, err := c.Meta.Client() 91 if err != nil { 92 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 93 return 1 94 } 95 96 // If args not specified but output format is specified, format and output the jobs data list 97 if len(args) == 0 && (json || len(tmpl) > 0) { 98 jobs, _, err := client.Jobs().List(nil) 99 if err != nil { 100 c.Ui.Error(fmt.Sprintf("Error querying jobs: %v", err)) 101 return 1 102 } 103 104 out, err := Format(json, tmpl, jobs) 105 if err != nil { 106 c.Ui.Error(err.Error()) 107 return 1 108 } 109 110 c.Ui.Output(out) 111 return 0 112 } 113 114 // Check that we got exactly one job 115 if len(args) != 1 { 116 c.Ui.Error("This command takes one argument: <job>") 117 c.Ui.Error(commandErrorText(c)) 118 return 1 119 } 120 jobID := strings.TrimSpace(args[0]) 121 122 // Check if the job exists 123 jobs, _, err := client.Jobs().PrefixList(jobID) 124 if err != nil { 125 c.Ui.Error(fmt.Sprintf("Error inspecting job: %s", err)) 126 return 1 127 } 128 if len(jobs) == 0 { 129 c.Ui.Error(fmt.Sprintf("No job(s) with prefix or id %q found", jobID)) 130 return 1 131 } 132 if len(jobs) > 1 { 133 if (jobID != jobs[0].ID) || (c.allNamespaces() && jobs[0].ID == jobs[1].ID) { 134 c.Ui.Error(fmt.Sprintf("Prefix matched multiple jobs\n\n%s", createStatusListOutput(jobs, c.allNamespaces()))) 135 return 1 136 } 137 } 138 139 var version *uint64 140 if versionStr != "" { 141 v, _, err := parseVersion(versionStr) 142 if err != nil { 143 c.Ui.Error(fmt.Sprintf("Error parsing version value %q: %v", versionStr, err)) 144 return 1 145 } 146 147 version = &v 148 } 149 150 // Prefix lookup matched a single job 151 job, err := getJob(client, jobs[0].JobSummary.Namespace, jobs[0].ID, version) 152 if err != nil { 153 c.Ui.Error(fmt.Sprintf("Error inspecting job: %s", err)) 154 return 1 155 } 156 157 // If output format is specified, format and output the data 158 if json || len(tmpl) > 0 { 159 out, err := Format(json, tmpl, job) 160 if err != nil { 161 c.Ui.Error(err.Error()) 162 return 1 163 } 164 165 c.Ui.Output(out) 166 return 0 167 } 168 169 // Print the contents of the job 170 req := struct { 171 Job *api.Job 172 }{ 173 Job: job, 174 } 175 f, err := DataFormat("json", "") 176 if err != nil { 177 c.Ui.Error(fmt.Sprintf("Error getting formatter: %s", err)) 178 return 1 179 } 180 181 out, err := f.TransformData(req) 182 if err != nil { 183 c.Ui.Error(fmt.Sprintf("Error formatting the data: %s", err)) 184 return 1 185 } 186 c.Ui.Output(out) 187 return 0 188 } 189 190 // getJob retrieves the job optionally at a particular version. 191 func getJob(client *api.Client, namespace, jobID string, version *uint64) (*api.Job, error) { 192 var q *api.QueryOptions 193 if namespace != "" { 194 q = &api.QueryOptions{Namespace: namespace} 195 } 196 if version == nil { 197 job, _, err := client.Jobs().Info(jobID, q) 198 return job, err 199 } 200 201 versions, _, _, err := client.Jobs().Versions(jobID, false, q) 202 if err != nil { 203 return nil, err 204 } 205 206 for _, j := range versions { 207 if *j.Version != *version { 208 continue 209 } 210 return j, nil 211 } 212 213 return nil, fmt.Errorf("job %q with version %d couldn't be found", jobID, *version) 214 }