container_test.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. package formatter
  2. import (
  3. "bytes"
  4. "fmt"
  5. "strings"
  6. "testing"
  7. "time"
  8. "github.com/docker/docker/api/types"
  9. "github.com/docker/docker/pkg/stringid"
  10. )
  11. func TestContainerPsContext(t *testing.T) {
  12. containerID := stringid.GenerateRandomID()
  13. unix := time.Now().Add(-65 * time.Second).Unix()
  14. var ctx containerContext
  15. cases := []struct {
  16. container types.Container
  17. trunc bool
  18. expValue string
  19. expHeader string
  20. call func() string
  21. }{
  22. {types.Container{ID: containerID}, true, stringid.TruncateID(containerID), containerIDHeader, ctx.ID},
  23. {types.Container{ID: containerID}, false, containerID, containerIDHeader, ctx.ID},
  24. {types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", namesHeader, ctx.Names},
  25. {types.Container{Image: "ubuntu"}, true, "ubuntu", imageHeader, ctx.Image},
  26. {types.Container{Image: "verylongimagename"}, true, "verylongimagename", imageHeader, ctx.Image},
  27. {types.Container{Image: "verylongimagename"}, false, "verylongimagename", imageHeader, ctx.Image},
  28. {types.Container{
  29. Image: "a5a665ff33eced1e0803148700880edab4",
  30. ImageID: "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5",
  31. },
  32. true,
  33. "a5a665ff33ec",
  34. imageHeader,
  35. ctx.Image,
  36. },
  37. {types.Container{
  38. Image: "a5a665ff33eced1e0803148700880edab4",
  39. ImageID: "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5",
  40. },
  41. false,
  42. "a5a665ff33eced1e0803148700880edab4",
  43. imageHeader,
  44. ctx.Image,
  45. },
  46. {types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image},
  47. {types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command},
  48. {types.Container{Created: unix}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
  49. {types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports},
  50. {types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status},
  51. {types.Container{SizeRw: 10}, true, "10 B", sizeHeader, ctx.Size},
  52. {types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10 B (virtual 20 B)", sizeHeader, ctx.Size},
  53. {types.Container{}, true, "", labelsHeader, ctx.Labels},
  54. {types.Container{Labels: map[string]string{"cpu": "6", "storage": "ssd"}}, true, "cpu=6,storage=ssd", labelsHeader, ctx.Labels},
  55. {types.Container{Created: unix}, true, "About a minute", runningForHeader, ctx.RunningFor},
  56. {types.Container{
  57. Mounts: []types.MountPoint{
  58. {
  59. Name: "this-is-a-long-volume-name-and-will-be-truncated-if-trunc-is-set",
  60. Driver: "local",
  61. Source: "/a/path",
  62. },
  63. },
  64. }, true, "this-is-a-lo...", mountsHeader, ctx.Mounts},
  65. {types.Container{
  66. Mounts: []types.MountPoint{
  67. {
  68. Driver: "local",
  69. Source: "/a/path",
  70. },
  71. },
  72. }, false, "/a/path", mountsHeader, ctx.Mounts},
  73. {types.Container{
  74. Mounts: []types.MountPoint{
  75. {
  76. Name: "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203",
  77. Driver: "local",
  78. Source: "/a/path",
  79. },
  80. },
  81. }, false, "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203", mountsHeader, ctx.Mounts},
  82. }
  83. for _, c := range cases {
  84. ctx = containerContext{c: c.container, trunc: c.trunc}
  85. v := c.call()
  86. if strings.Contains(v, ",") {
  87. compareMultipleValues(t, v, c.expValue)
  88. } else if v != c.expValue {
  89. t.Fatalf("Expected %s, was %s\n", c.expValue, v)
  90. }
  91. h := ctx.fullHeader()
  92. if h != c.expHeader {
  93. t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
  94. }
  95. }
  96. c1 := types.Container{Labels: map[string]string{"com.docker.swarm.swarm-id": "33", "com.docker.swarm.node_name": "ubuntu"}}
  97. ctx = containerContext{c: c1, trunc: true}
  98. sid := ctx.Label("com.docker.swarm.swarm-id")
  99. node := ctx.Label("com.docker.swarm.node_name")
  100. if sid != "33" {
  101. t.Fatalf("Expected 33, was %s\n", sid)
  102. }
  103. if node != "ubuntu" {
  104. t.Fatalf("Expected ubuntu, was %s\n", node)
  105. }
  106. h := ctx.fullHeader()
  107. if h != "SWARM ID\tNODE NAME" {
  108. t.Fatalf("Expected %s, was %s\n", "SWARM ID\tNODE NAME", h)
  109. }
  110. c2 := types.Container{}
  111. ctx = containerContext{c: c2, trunc: true}
  112. label := ctx.Label("anything.really")
  113. if label != "" {
  114. t.Fatalf("Expected an empty string, was %s", label)
  115. }
  116. ctx = containerContext{c: c2, trunc: true}
  117. fullHeader := ctx.fullHeader()
  118. if fullHeader != "" {
  119. t.Fatalf("Expected fullHeader to be empty, was %s", fullHeader)
  120. }
  121. }
  122. func TestContainerContextWrite(t *testing.T) {
  123. unixTime := time.Now().AddDate(0, 0, -1).Unix()
  124. expectedTime := time.Unix(unixTime, 0).String()
  125. contexts := []struct {
  126. context ContainerContext
  127. expected string
  128. }{
  129. // Errors
  130. {
  131. ContainerContext{
  132. Context: Context{
  133. Format: "{{InvalidFunction}}",
  134. },
  135. },
  136. `Template parsing error: template: :1: function "InvalidFunction" not defined
  137. `,
  138. },
  139. {
  140. ContainerContext{
  141. Context: Context{
  142. Format: "{{nil}}",
  143. },
  144. },
  145. `Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
  146. `,
  147. },
  148. // Table Format
  149. {
  150. ContainerContext{
  151. Context: Context{
  152. Format: "table",
  153. },
  154. Size: true,
  155. },
  156. `CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
  157. containerID1 ubuntu "" 24 hours ago foobar_baz 0 B
  158. containerID2 ubuntu "" 24 hours ago foobar_bar 0 B
  159. `,
  160. },
  161. {
  162. ContainerContext{
  163. Context: Context{
  164. Format: "table",
  165. },
  166. },
  167. `CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  168. containerID1 ubuntu "" 24 hours ago foobar_baz
  169. containerID2 ubuntu "" 24 hours ago foobar_bar
  170. `,
  171. },
  172. {
  173. ContainerContext{
  174. Context: Context{
  175. Format: "table {{.Image}}",
  176. },
  177. },
  178. "IMAGE\nubuntu\nubuntu\n",
  179. },
  180. {
  181. ContainerContext{
  182. Context: Context{
  183. Format: "table {{.Image}}",
  184. },
  185. Size: true,
  186. },
  187. "IMAGE\nubuntu\nubuntu\n",
  188. },
  189. {
  190. ContainerContext{
  191. Context: Context{
  192. Format: "table {{.Image}}",
  193. Quiet: true,
  194. },
  195. },
  196. "IMAGE\nubuntu\nubuntu\n",
  197. },
  198. {
  199. ContainerContext{
  200. Context: Context{
  201. Format: "table",
  202. Quiet: true,
  203. },
  204. },
  205. "containerID1\ncontainerID2\n",
  206. },
  207. // Raw Format
  208. {
  209. ContainerContext{
  210. Context: Context{
  211. Format: "raw",
  212. },
  213. },
  214. fmt.Sprintf(`container_id: containerID1
  215. image: ubuntu
  216. command: ""
  217. created_at: %s
  218. status:
  219. names: foobar_baz
  220. labels:
  221. ports:
  222. container_id: containerID2
  223. image: ubuntu
  224. command: ""
  225. created_at: %s
  226. status:
  227. names: foobar_bar
  228. labels:
  229. ports:
  230. `, expectedTime, expectedTime),
  231. },
  232. {
  233. ContainerContext{
  234. Context: Context{
  235. Format: "raw",
  236. },
  237. Size: true,
  238. },
  239. fmt.Sprintf(`container_id: containerID1
  240. image: ubuntu
  241. command: ""
  242. created_at: %s
  243. status:
  244. names: foobar_baz
  245. labels:
  246. ports:
  247. size: 0 B
  248. container_id: containerID2
  249. image: ubuntu
  250. command: ""
  251. created_at: %s
  252. status:
  253. names: foobar_bar
  254. labels:
  255. ports:
  256. size: 0 B
  257. `, expectedTime, expectedTime),
  258. },
  259. {
  260. ContainerContext{
  261. Context: Context{
  262. Format: "raw",
  263. Quiet: true,
  264. },
  265. },
  266. "container_id: containerID1\ncontainer_id: containerID2\n",
  267. },
  268. // Custom Format
  269. {
  270. ContainerContext{
  271. Context: Context{
  272. Format: "{{.Image}}",
  273. },
  274. },
  275. "ubuntu\nubuntu\n",
  276. },
  277. {
  278. ContainerContext{
  279. Context: Context{
  280. Format: "{{.Image}}",
  281. },
  282. Size: true,
  283. },
  284. "ubuntu\nubuntu\n",
  285. },
  286. }
  287. for _, context := range contexts {
  288. containers := []types.Container{
  289. {ID: "containerID1", Names: []string{"/foobar_baz"}, Image: "ubuntu", Created: unixTime},
  290. {ID: "containerID2", Names: []string{"/foobar_bar"}, Image: "ubuntu", Created: unixTime},
  291. }
  292. out := bytes.NewBufferString("")
  293. context.context.Output = out
  294. context.context.Containers = containers
  295. context.context.Write()
  296. actual := out.String()
  297. if actual != context.expected {
  298. t.Fatalf("Expected \n%s, got \n%s", context.expected, actual)
  299. }
  300. // Clean buffer
  301. out.Reset()
  302. }
  303. }
  304. func TestContainerContextWriteWithNoContainers(t *testing.T) {
  305. out := bytes.NewBufferString("")
  306. containers := []types.Container{}
  307. contexts := []struct {
  308. context ContainerContext
  309. expected string
  310. }{
  311. {
  312. ContainerContext{
  313. Context: Context{
  314. Format: "{{.Image}}",
  315. Output: out,
  316. },
  317. },
  318. "",
  319. },
  320. {
  321. ContainerContext{
  322. Context: Context{
  323. Format: "table {{.Image}}",
  324. Output: out,
  325. },
  326. },
  327. "IMAGE\n",
  328. },
  329. {
  330. ContainerContext{
  331. Context: Context{
  332. Format: "{{.Image}}",
  333. Output: out,
  334. },
  335. Size: true,
  336. },
  337. "",
  338. },
  339. {
  340. ContainerContext{
  341. Context: Context{
  342. Format: "table {{.Image}}",
  343. Output: out,
  344. },
  345. Size: true,
  346. },
  347. "IMAGE\n",
  348. },
  349. {
  350. ContainerContext{
  351. Context: Context{
  352. Format: "table {{.Image}}\t{{.Size}}",
  353. Output: out,
  354. },
  355. },
  356. "IMAGE SIZE\n",
  357. },
  358. {
  359. ContainerContext{
  360. Context: Context{
  361. Format: "table {{.Image}}\t{{.Size}}",
  362. Output: out,
  363. },
  364. Size: true,
  365. },
  366. "IMAGE SIZE\n",
  367. },
  368. }
  369. for _, context := range contexts {
  370. context.context.Containers = containers
  371. context.context.Write()
  372. actual := out.String()
  373. if actual != context.expected {
  374. t.Fatalf("Expected \n%s, got \n%s", context.expected, actual)
  375. }
  376. // Clean buffer
  377. out.Reset()
  378. }
  379. }