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