1package main
2
3import (
4	"encoding/json"
5	"fmt"
6	"os"
7	"strconv"
8	"strings"
9	"time"
10
11	"github.com/docker/docker/api/types"
12	"github.com/docker/docker/api/types/container"
13	"github.com/docker/docker/integration-cli/checker"
14	"github.com/go-check/check"
15	"github.com/gotestyourself/gotestyourself/icmd"
16)
17
18func checkValidGraphDriver(c *check.C, name string) {
19	if name != "devicemapper" && name != "overlay" && name != "vfs" && name != "zfs" && name != "btrfs" && name != "aufs" {
20		c.Fatalf("%v is not a valid graph driver name", name)
21	}
22}
23
24func (s *DockerSuite) TestInspectImage(c *check.C) {
25	testRequires(c, DaemonIsLinux)
26	imageTest := "emptyfs"
27	// It is important that this ID remain stable. If a code change causes
28	// it to be different, this is equivalent to a cache bust when pulling
29	// a legacy-format manifest. If the check at the end of this function
30	// fails, fix the difference in the image serialization instead of
31	// updating this hash.
32	imageTestID := "sha256:11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d"
33	id := inspectField(c, imageTest, "Id")
34
35	c.Assert(id, checker.Equals, imageTestID)
36}
37
38func (s *DockerSuite) TestInspectInt64(c *check.C) {
39	dockerCmd(c, "run", "-d", "-m=300M", "--name", "inspectTest", "busybox", "true")
40	inspectOut := inspectField(c, "inspectTest", "HostConfig.Memory")
41	c.Assert(inspectOut, checker.Equals, "314572800")
42}
43
44func (s *DockerSuite) TestInspectDefault(c *check.C) {
45	//Both the container and image are named busybox. docker inspect will fetch the container JSON.
46	//If the container JSON is not available, it will go for the image JSON.
47
48	out, _ := dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "true")
49	containerID := strings.TrimSpace(out)
50
51	inspectOut := inspectField(c, "busybox", "Id")
52	c.Assert(strings.TrimSpace(inspectOut), checker.Equals, containerID)
53}
54
55func (s *DockerSuite) TestInspectStatus(c *check.C) {
56	out := runSleepingContainer(c, "-d")
57	out = strings.TrimSpace(out)
58
59	inspectOut := inspectField(c, out, "State.Status")
60	c.Assert(inspectOut, checker.Equals, "running")
61
62	// Windows does not support pause/unpause on Windows Server Containers.
63	// (RS1 does for Hyper-V Containers, but production CI is not setup for that)
64	if testEnv.DaemonPlatform() != "windows" {
65		dockerCmd(c, "pause", out)
66		inspectOut = inspectField(c, out, "State.Status")
67		c.Assert(inspectOut, checker.Equals, "paused")
68
69		dockerCmd(c, "unpause", out)
70		inspectOut = inspectField(c, out, "State.Status")
71		c.Assert(inspectOut, checker.Equals, "running")
72	}
73
74	dockerCmd(c, "stop", out)
75	inspectOut = inspectField(c, out, "State.Status")
76	c.Assert(inspectOut, checker.Equals, "exited")
77
78}
79
80func (s *DockerSuite) TestInspectTypeFlagContainer(c *check.C) {
81	//Both the container and image are named busybox. docker inspect will fetch container
82	//JSON State.Running field. If the field is true, it's a container.
83	runSleepingContainer(c, "--name=busybox", "-d")
84
85	formatStr := "--format={{.State.Running}}"
86	out, _ := dockerCmd(c, "inspect", "--type=container", formatStr, "busybox")
87	c.Assert(out, checker.Equals, "true\n") // not a container JSON
88}
89
90func (s *DockerSuite) TestInspectTypeFlagWithNoContainer(c *check.C) {
91	//Run this test on an image named busybox. docker inspect will try to fetch container
92	//JSON. Since there is no container named busybox and --type=container, docker inspect will
93	//not try to get the image JSON. It will throw an error.
94
95	dockerCmd(c, "run", "-d", "busybox", "true")
96
97	_, _, err := dockerCmdWithError("inspect", "--type=container", "busybox")
98	// docker inspect should fail, as there is no container named busybox
99	c.Assert(err, checker.NotNil)
100}
101
102func (s *DockerSuite) TestInspectTypeFlagWithImage(c *check.C) {
103	//Both the container and image are named busybox. docker inspect will fetch image
104	//JSON as --type=image. if there is no image with name busybox, docker inspect
105	//will throw an error.
106
107	dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "true")
108
109	out, _ := dockerCmd(c, "inspect", "--type=image", "busybox")
110	c.Assert(out, checker.Not(checker.Contains), "State") // not an image JSON
111}
112
113func (s *DockerSuite) TestInspectTypeFlagWithInvalidValue(c *check.C) {
114	//Both the container and image are named busybox. docker inspect will fail
115	//as --type=foobar is not a valid value for the flag.
116
117	dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "true")
118
119	out, exitCode, err := dockerCmdWithError("inspect", "--type=foobar", "busybox")
120	c.Assert(err, checker.NotNil, check.Commentf("%s", exitCode))
121	c.Assert(exitCode, checker.Equals, 1, check.Commentf("%s", err))
122	c.Assert(out, checker.Contains, "not a valid value for --type")
123}
124
125func (s *DockerSuite) TestInspectImageFilterInt(c *check.C) {
126	testRequires(c, DaemonIsLinux)
127	imageTest := "emptyfs"
128	out := inspectField(c, imageTest, "Size")
129
130	size, err := strconv.Atoi(out)
131	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect size of the image: %s, %v", out, err))
132
133	//now see if the size turns out to be the same
134	formatStr := fmt.Sprintf("--format={{eq .Size %d}}", size)
135	out, _ = dockerCmd(c, "inspect", formatStr, imageTest)
136	result, err := strconv.ParseBool(strings.TrimSuffix(out, "\n"))
137	c.Assert(err, checker.IsNil)
138	c.Assert(result, checker.Equals, true)
139}
140
141func (s *DockerSuite) TestInspectContainerFilterInt(c *check.C) {
142	result := icmd.RunCmd(icmd.Cmd{
143		Command: []string{dockerBinary, "run", "-i", "-a", "stdin", "busybox", "cat"},
144		Stdin:   strings.NewReader("blahblah"),
145	})
146	result.Assert(c, icmd.Success)
147	out := result.Stdout()
148	id := strings.TrimSpace(out)
149
150	out = inspectField(c, id, "State.ExitCode")
151
152	exitCode, err := strconv.Atoi(out)
153	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect exitcode of the container: %s, %v", out, err))
154
155	//now get the exit code to verify
156	formatStr := fmt.Sprintf("--format={{eq .State.ExitCode %d}}", exitCode)
157	out, _ = dockerCmd(c, "inspect", formatStr, id)
158	inspectResult, err := strconv.ParseBool(strings.TrimSuffix(out, "\n"))
159	c.Assert(err, checker.IsNil)
160	c.Assert(inspectResult, checker.Equals, true)
161}
162
163func (s *DockerSuite) TestInspectImageGraphDriver(c *check.C) {
164	testRequires(c, DaemonIsLinux, Devicemapper)
165	imageTest := "emptyfs"
166	name := inspectField(c, imageTest, "GraphDriver.Name")
167
168	checkValidGraphDriver(c, name)
169
170	deviceID := inspectField(c, imageTest, "GraphDriver.Data.DeviceId")
171
172	_, err := strconv.Atoi(deviceID)
173	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect DeviceId of the image: %s, %v", deviceID, err))
174
175	deviceSize := inspectField(c, imageTest, "GraphDriver.Data.DeviceSize")
176
177	_, err = strconv.ParseUint(deviceSize, 10, 64)
178	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect DeviceSize of the image: %s, %v", deviceSize, err))
179}
180
181func (s *DockerSuite) TestInspectContainerGraphDriver(c *check.C) {
182	testRequires(c, DaemonIsLinux, Devicemapper)
183
184	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
185	out = strings.TrimSpace(out)
186
187	name := inspectField(c, out, "GraphDriver.Name")
188
189	checkValidGraphDriver(c, name)
190
191	imageDeviceID := inspectField(c, "busybox", "GraphDriver.Data.DeviceId")
192
193	deviceID := inspectField(c, out, "GraphDriver.Data.DeviceId")
194
195	c.Assert(imageDeviceID, checker.Not(checker.Equals), deviceID)
196
197	_, err := strconv.Atoi(deviceID)
198	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect DeviceId of the image: %s, %v", deviceID, err))
199
200	deviceSize := inspectField(c, out, "GraphDriver.Data.DeviceSize")
201
202	_, err = strconv.ParseUint(deviceSize, 10, 64)
203	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect DeviceSize of the image: %s, %v", deviceSize, err))
204}
205
206func (s *DockerSuite) TestInspectBindMountPoint(c *check.C) {
207	modifier := ",z"
208	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
209	if testEnv.DaemonPlatform() == "windows" {
210		modifier = ""
211		// Linux creates the host directory if it doesn't exist. Windows does not.
212		os.Mkdir(`c:\data`, os.ModeDir)
213	}
214
215	dockerCmd(c, "run", "-d", "--name", "test", "-v", prefix+slash+"data:"+prefix+slash+"data:ro"+modifier, "busybox", "cat")
216
217	vol := inspectFieldJSON(c, "test", "Mounts")
218
219	var mp []types.MountPoint
220	err := json.Unmarshal([]byte(vol), &mp)
221	c.Assert(err, checker.IsNil)
222
223	// check that there is only one mountpoint
224	c.Assert(mp, check.HasLen, 1)
225
226	m := mp[0]
227
228	c.Assert(m.Name, checker.Equals, "")
229	c.Assert(m.Driver, checker.Equals, "")
230	c.Assert(m.Source, checker.Equals, prefix+slash+"data")
231	c.Assert(m.Destination, checker.Equals, prefix+slash+"data")
232	if testEnv.DaemonPlatform() != "windows" { // Windows does not set mode
233		c.Assert(m.Mode, checker.Equals, "ro"+modifier)
234	}
235	c.Assert(m.RW, checker.Equals, false)
236}
237
238func (s *DockerSuite) TestInspectNamedMountPoint(c *check.C) {
239	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
240
241	dockerCmd(c, "run", "-d", "--name", "test", "-v", "data:"+prefix+slash+"data", "busybox", "cat")
242
243	vol := inspectFieldJSON(c, "test", "Mounts")
244
245	var mp []types.MountPoint
246	err := json.Unmarshal([]byte(vol), &mp)
247	c.Assert(err, checker.IsNil)
248
249	// check that there is only one mountpoint
250	c.Assert(mp, checker.HasLen, 1)
251
252	m := mp[0]
253
254	c.Assert(m.Name, checker.Equals, "data")
255	c.Assert(m.Driver, checker.Equals, "local")
256	c.Assert(m.Source, checker.Not(checker.Equals), "")
257	c.Assert(m.Destination, checker.Equals, prefix+slash+"data")
258	c.Assert(m.RW, checker.Equals, true)
259}
260
261// #14947
262func (s *DockerSuite) TestInspectTimesAsRFC3339Nano(c *check.C) {
263	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
264	id := strings.TrimSpace(out)
265	startedAt := inspectField(c, id, "State.StartedAt")
266	finishedAt := inspectField(c, id, "State.FinishedAt")
267	created := inspectField(c, id, "Created")
268
269	_, err := time.Parse(time.RFC3339Nano, startedAt)
270	c.Assert(err, checker.IsNil)
271	_, err = time.Parse(time.RFC3339Nano, finishedAt)
272	c.Assert(err, checker.IsNil)
273	_, err = time.Parse(time.RFC3339Nano, created)
274	c.Assert(err, checker.IsNil)
275
276	created = inspectField(c, "busybox", "Created")
277
278	_, err = time.Parse(time.RFC3339Nano, created)
279	c.Assert(err, checker.IsNil)
280}
281
282// #15633
283func (s *DockerSuite) TestInspectLogConfigNoType(c *check.C) {
284	dockerCmd(c, "create", "--name=test", "--log-opt", "max-file=42", "busybox")
285	var logConfig container.LogConfig
286
287	out := inspectFieldJSON(c, "test", "HostConfig.LogConfig")
288
289	err := json.NewDecoder(strings.NewReader(out)).Decode(&logConfig)
290	c.Assert(err, checker.IsNil, check.Commentf("%v", out))
291
292	c.Assert(logConfig.Type, checker.Equals, "json-file")
293	c.Assert(logConfig.Config["max-file"], checker.Equals, "42", check.Commentf("%v", logConfig))
294}
295
296func (s *DockerSuite) TestInspectNoSizeFlagContainer(c *check.C) {
297
298	//Both the container and image are named busybox. docker inspect will fetch container
299	//JSON SizeRw and SizeRootFs field. If there is no flag --size/-s, there are no size fields.
300
301	runSleepingContainer(c, "--name=busybox", "-d")
302
303	formatStr := "--format={{.SizeRw}},{{.SizeRootFs}}"
304	out, _ := dockerCmd(c, "inspect", "--type=container", formatStr, "busybox")
305	c.Assert(strings.TrimSpace(out), check.Equals, "<nil>,<nil>", check.Commentf("Expected not to display size info: %s", out))
306}
307
308func (s *DockerSuite) TestInspectSizeFlagContainer(c *check.C) {
309	runSleepingContainer(c, "--name=busybox", "-d")
310
311	formatStr := "--format='{{.SizeRw}},{{.SizeRootFs}}'"
312	out, _ := dockerCmd(c, "inspect", "-s", "--type=container", formatStr, "busybox")
313	sz := strings.Split(out, ",")
314
315	c.Assert(strings.TrimSpace(sz[0]), check.Not(check.Equals), "<nil>")
316	c.Assert(strings.TrimSpace(sz[1]), check.Not(check.Equals), "<nil>")
317}
318
319func (s *DockerSuite) TestInspectTemplateError(c *check.C) {
320	// Template parsing error for both the container and image.
321
322	runSleepingContainer(c, "--name=container1", "-d")
323
324	out, _, err := dockerCmdWithError("inspect", "--type=container", "--format='Format container: {{.ThisDoesNotExist}}'", "container1")
325	c.Assert(err, check.Not(check.IsNil))
326	c.Assert(out, checker.Contains, "Template parsing error")
327
328	out, _, err = dockerCmdWithError("inspect", "--type=image", "--format='Format container: {{.ThisDoesNotExist}}'", "busybox")
329	c.Assert(err, check.Not(check.IsNil))
330	c.Assert(out, checker.Contains, "Template parsing error")
331}
332
333func (s *DockerSuite) TestInspectJSONFields(c *check.C) {
334	runSleepingContainer(c, "--name=busybox", "-d")
335	out, _, err := dockerCmdWithError("inspect", "--type=container", "--format={{.HostConfig.Dns}}", "busybox")
336
337	c.Assert(err, check.IsNil)
338	c.Assert(out, checker.Equals, "[]\n")
339}
340
341func (s *DockerSuite) TestInspectByPrefix(c *check.C) {
342	id := inspectField(c, "busybox", "Id")
343	c.Assert(id, checker.HasPrefix, "sha256:")
344
345	id2 := inspectField(c, id[:12], "Id")
346	c.Assert(id, checker.Equals, id2)
347
348	id3 := inspectField(c, strings.TrimPrefix(id, "sha256:")[:12], "Id")
349	c.Assert(id, checker.Equals, id3)
350}
351
352func (s *DockerSuite) TestInspectStopWhenNotFound(c *check.C) {
353	runSleepingContainer(c, "--name=busybox1", "-d")
354	runSleepingContainer(c, "--name=busybox2", "-d")
355	result := dockerCmdWithResult("inspect", "--type=container", "--format='{{.Name}}'", "busybox1", "busybox2", "missing")
356
357	c.Assert(result.Error, checker.Not(check.IsNil))
358	c.Assert(result.Stdout(), checker.Contains, "busybox1")
359	c.Assert(result.Stdout(), checker.Contains, "busybox2")
360	c.Assert(result.Stderr(), checker.Contains, "Error: No such container: missing")
361
362	// test inspect would not fast fail
363	result = dockerCmdWithResult("inspect", "--type=container", "--format='{{.Name}}'", "missing", "busybox1", "busybox2")
364
365	c.Assert(result.Error, checker.Not(check.IsNil))
366	c.Assert(result.Stdout(), checker.Contains, "busybox1")
367	c.Assert(result.Stdout(), checker.Contains, "busybox2")
368	c.Assert(result.Stderr(), checker.Contains, "Error: No such container: missing")
369}
370
371func (s *DockerSuite) TestInspectHistory(c *check.C) {
372	dockerCmd(c, "run", "--name=testcont", "busybox", "echo", "hello")
373	dockerCmd(c, "commit", "-m", "test comment", "testcont", "testimg")
374	out, _, err := dockerCmdWithError("inspect", "--format='{{.Comment}}'", "testimg")
375	c.Assert(err, check.IsNil)
376	c.Assert(out, checker.Contains, "test comment")
377}
378
379func (s *DockerSuite) TestInspectContainerNetworkDefault(c *check.C) {
380	testRequires(c, DaemonIsLinux)
381
382	contName := "test1"
383	dockerCmd(c, "run", "--name", contName, "-d", "busybox", "top")
384	netOut, _ := dockerCmd(c, "network", "inspect", "--format={{.ID}}", "bridge")
385	out := inspectField(c, contName, "NetworkSettings.Networks")
386	c.Assert(out, checker.Contains, "bridge")
387	out = inspectField(c, contName, "NetworkSettings.Networks.bridge.NetworkID")
388	c.Assert(strings.TrimSpace(out), checker.Equals, strings.TrimSpace(netOut))
389}
390
391func (s *DockerSuite) TestInspectContainerNetworkCustom(c *check.C) {
392	testRequires(c, DaemonIsLinux)
393
394	netOut, _ := dockerCmd(c, "network", "create", "net1")
395	dockerCmd(c, "run", "--name=container1", "--net=net1", "-d", "busybox", "top")
396	out := inspectField(c, "container1", "NetworkSettings.Networks")
397	c.Assert(out, checker.Contains, "net1")
398	out = inspectField(c, "container1", "NetworkSettings.Networks.net1.NetworkID")
399	c.Assert(strings.TrimSpace(out), checker.Equals, strings.TrimSpace(netOut))
400}
401
402func (s *DockerSuite) TestInspectRootFS(c *check.C) {
403	out, _, err := dockerCmdWithError("inspect", "busybox")
404	c.Assert(err, check.IsNil)
405
406	var imageJSON []types.ImageInspect
407	err = json.Unmarshal([]byte(out), &imageJSON)
408	c.Assert(err, checker.IsNil)
409
410	c.Assert(len(imageJSON[0].RootFS.Layers), checker.GreaterOrEqualThan, 1)
411}
412
413func (s *DockerSuite) TestInspectAmpersand(c *check.C) {
414	testRequires(c, DaemonIsLinux)
415
416	name := "test"
417	out, _ := dockerCmd(c, "run", "--name", name, "--env", `TEST_ENV="soanni&rtr"`, "busybox", "env")
418	c.Assert(out, checker.Contains, `soanni&rtr`)
419	out, _ = dockerCmd(c, "inspect", name)
420	c.Assert(out, checker.Contains, `soanni&rtr`)
421}
422
423func (s *DockerSuite) TestInspectPlugin(c *check.C) {
424	testRequires(c, DaemonIsLinux, IsAmd64, Network)
425	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
426	c.Assert(err, checker.IsNil)
427
428	out, _, err := dockerCmdWithError("inspect", "--type", "plugin", "--format", "{{.Name}}", pNameWithTag)
429	c.Assert(err, checker.IsNil)
430	c.Assert(strings.TrimSpace(out), checker.Equals, pNameWithTag)
431
432	out, _, err = dockerCmdWithError("inspect", "--format", "{{.Name}}", pNameWithTag)
433	c.Assert(err, checker.IsNil)
434	c.Assert(strings.TrimSpace(out), checker.Equals, pNameWithTag)
435
436	// Even without tag the inspect still work
437	out, _, err = dockerCmdWithError("inspect", "--type", "plugin", "--format", "{{.Name}}", pNameWithTag)
438	c.Assert(err, checker.IsNil)
439	c.Assert(strings.TrimSpace(out), checker.Equals, pNameWithTag)
440
441	out, _, err = dockerCmdWithError("inspect", "--format", "{{.Name}}", pNameWithTag)
442	c.Assert(err, checker.IsNil)
443	c.Assert(strings.TrimSpace(out), checker.Equals, pNameWithTag)
444
445	_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
446	c.Assert(err, checker.IsNil)
447
448	out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
449	c.Assert(err, checker.IsNil)
450	c.Assert(out, checker.Contains, pNameWithTag)
451}
452
453// Test case for 29185
454func (s *DockerSuite) TestInspectUnknownObject(c *check.C) {
455	// This test should work on both Windows and Linux
456	out, _, err := dockerCmdWithError("inspect", "foobar")
457	c.Assert(err, checker.NotNil)
458	c.Assert(out, checker.Contains, "Error: No such object: foobar")
459	c.Assert(err.Error(), checker.Contains, "Error: No such object: foobar")
460}
461
462func (s *DockerSuite) TestInspectInvalidReference(c *check.C) {
463	// This test should work on both Windows and Linux
464	out, _, err := dockerCmdWithError("inspect", "FooBar")
465	c.Assert(err, checker.NotNil)
466	c.Assert(out, checker.Contains, "no such image: FooBar")
467}
468