list.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. package daemon // import "github.com/docker/docker/daemon"
  2. import (
  3. "fmt"
  4. "sort"
  5. "strconv"
  6. "strings"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/filters"
  9. "github.com/docker/docker/container"
  10. "github.com/docker/docker/daemon/images"
  11. "github.com/docker/docker/errdefs"
  12. "github.com/docker/docker/image"
  13. "github.com/docker/go-connections/nat"
  14. "github.com/pkg/errors"
  15. "github.com/sirupsen/logrus"
  16. )
  17. var acceptedPsFilterTags = map[string]bool{
  18. "ancestor": true,
  19. "before": true,
  20. "exited": true,
  21. "id": true,
  22. "isolation": true,
  23. "label": true,
  24. "name": true,
  25. "status": true,
  26. "health": true,
  27. "since": true,
  28. "volume": true,
  29. "network": true,
  30. "is-task": true,
  31. "publish": true,
  32. "expose": true,
  33. }
  34. // iterationAction represents possible outcomes happening during the container iteration.
  35. type iterationAction int
  36. // containerReducer represents a reducer for a container.
  37. // Returns the object to serialize by the api.
  38. type containerReducer func(*container.Snapshot, *listContext) (*types.Container, error)
  39. const (
  40. // includeContainer is the action to include a container in the reducer.
  41. includeContainer iterationAction = iota
  42. // excludeContainer is the action to exclude a container in the reducer.
  43. excludeContainer
  44. // stopIteration is the action to stop iterating over the list of containers.
  45. stopIteration
  46. )
  47. // errStopIteration makes the iterator to stop without returning an error.
  48. var errStopIteration = errors.New("container list iteration stopped")
  49. // List returns an array of all containers registered in the daemon.
  50. func (daemon *Daemon) List() []*container.Container {
  51. return daemon.containers.List()
  52. }
  53. // listContext is the daemon generated filtering to iterate over containers.
  54. // This is created based on the user specification from types.ContainerListOptions.
  55. type listContext struct {
  56. // idx is the container iteration index for this context
  57. idx int
  58. // ancestorFilter tells whether it should check ancestors or not
  59. ancestorFilter bool
  60. // names is a list of container names to filter with
  61. names map[string][]string
  62. // images is a list of images to filter with
  63. images map[image.ID]bool
  64. // filters is a collection of arguments to filter with, specified by the user
  65. filters filters.Args
  66. // exitAllowed is a list of exit codes allowed to filter with
  67. exitAllowed []int
  68. // beforeFilter is a filter to ignore containers that appear before the one given
  69. beforeFilter *container.Snapshot
  70. // sinceFilter is a filter to stop the filtering when the iterator arrives to the given container
  71. sinceFilter *container.Snapshot
  72. // taskFilter tells if we should filter based on whether a container is part of a task
  73. taskFilter bool
  74. // isTask tells us if we should filter container that is a task (true) or not (false)
  75. isTask bool
  76. // publish is a list of published ports to filter with
  77. publish map[nat.Port]bool
  78. // expose is a list of exposed ports to filter with
  79. expose map[nat.Port]bool
  80. // ContainerListOptions is the filters set by the user
  81. *types.ContainerListOptions
  82. }
  83. // byCreatedDescending is a temporary type used to sort a list of containers by creation time.
  84. type byCreatedDescending []container.Snapshot
  85. func (r byCreatedDescending) Len() int { return len(r) }
  86. func (r byCreatedDescending) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
  87. func (r byCreatedDescending) Less(i, j int) bool {
  88. return r[j].CreatedAt.UnixNano() < r[i].CreatedAt.UnixNano()
  89. }
  90. // Containers returns the list of containers to show given the user's filtering.
  91. func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
  92. return daemon.reduceContainers(config, daemon.refreshImage)
  93. }
  94. func (daemon *Daemon) filterByNameIDMatches(view container.View, ctx *listContext) ([]container.Snapshot, error) {
  95. idSearch := false
  96. names := ctx.filters.Get("name")
  97. ids := ctx.filters.Get("id")
  98. if len(names)+len(ids) == 0 {
  99. // if name or ID filters are not in use, return to
  100. // standard behavior of walking the entire container
  101. // list from the daemon's in-memory store
  102. all, err := view.All()
  103. sort.Sort(byCreatedDescending(all))
  104. return all, err
  105. }
  106. // idSearch will determine if we limit name matching to the IDs
  107. // matched from any IDs which were specified as filters
  108. if len(ids) > 0 {
  109. idSearch = true
  110. }
  111. matches := make(map[string]bool)
  112. // find ID matches; errors represent "not found" and can be ignored
  113. for _, id := range ids {
  114. if fullID, err := daemon.idIndex.Get(id); err == nil {
  115. matches[fullID] = true
  116. }
  117. }
  118. // look for name matches; if ID filtering was used, then limit the
  119. // search space to the matches map only; errors represent "not found"
  120. // and can be ignored
  121. if len(names) > 0 {
  122. for id, idNames := range ctx.names {
  123. // if ID filters were used and no matches on that ID were
  124. // found, continue to next ID in the list
  125. if idSearch && !matches[id] {
  126. continue
  127. }
  128. for _, eachName := range idNames {
  129. // match both on container name with, and without slash-prefix
  130. if ctx.filters.Match("name", eachName) || ctx.filters.Match("name", strings.TrimPrefix(eachName, "/")) {
  131. matches[id] = true
  132. }
  133. }
  134. }
  135. }
  136. cntrs := make([]container.Snapshot, 0, len(matches))
  137. for id := range matches {
  138. c, err := view.Get(id)
  139. switch err.(type) {
  140. case nil:
  141. cntrs = append(cntrs, *c)
  142. case container.NoSuchContainerError:
  143. // ignore error
  144. default:
  145. return nil, err
  146. }
  147. }
  148. // Restore sort-order after filtering
  149. // Created gives us nanosec resolution for sorting
  150. sort.Sort(byCreatedDescending(cntrs))
  151. return cntrs, nil
  152. }
  153. // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
  154. func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
  155. if err := config.Filters.Validate(acceptedPsFilterTags); err != nil {
  156. return nil, err
  157. }
  158. var (
  159. view = daemon.containersReplica.Snapshot()
  160. containers = []*types.Container{}
  161. )
  162. ctx, err := daemon.foldFilter(view, config)
  163. if err != nil {
  164. return nil, err
  165. }
  166. // fastpath to only look at a subset of containers if specific name
  167. // or ID matches were provided by the user--otherwise we potentially
  168. // end up querying many more containers than intended
  169. containerList, err := daemon.filterByNameIDMatches(view, ctx)
  170. if err != nil {
  171. return nil, err
  172. }
  173. for i := range containerList {
  174. t, err := daemon.reducePsContainer(&containerList[i], ctx, reducer)
  175. if err != nil {
  176. if err != errStopIteration {
  177. return nil, err
  178. }
  179. break
  180. }
  181. if t != nil {
  182. containers = append(containers, t)
  183. ctx.idx++
  184. }
  185. }
  186. return containers, nil
  187. }
  188. // reducePsContainer is the basic representation for a container as expected by the ps command.
  189. func (daemon *Daemon) reducePsContainer(container *container.Snapshot, ctx *listContext, reducer containerReducer) (*types.Container, error) {
  190. // filter containers to return
  191. switch includeContainerInList(container, ctx) {
  192. case excludeContainer:
  193. return nil, nil
  194. case stopIteration:
  195. return nil, errStopIteration
  196. }
  197. // transform internal container struct into api structs
  198. newC, err := reducer(container, ctx)
  199. if err != nil {
  200. return nil, err
  201. }
  202. // release lock because size calculation is slow
  203. if ctx.Size {
  204. sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(newC.ID)
  205. newC.SizeRw = sizeRw
  206. newC.SizeRootFs = sizeRootFs
  207. }
  208. return newC, nil
  209. }
  210. // foldFilter generates the container filter based on the user's filtering options.
  211. func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerListOptions) (*listContext, error) {
  212. psFilters := config.Filters
  213. var filtExited []int
  214. err := psFilters.WalkValues("exited", func(value string) error {
  215. code, err := strconv.Atoi(value)
  216. if err != nil {
  217. return err
  218. }
  219. filtExited = append(filtExited, code)
  220. return nil
  221. })
  222. if err != nil {
  223. return nil, err
  224. }
  225. err = psFilters.WalkValues("status", func(value string) error {
  226. if !container.IsValidStateString(value) {
  227. return invalidFilter{"status", value}
  228. }
  229. config.All = true
  230. return nil
  231. })
  232. if err != nil {
  233. return nil, err
  234. }
  235. var taskFilter, isTask bool
  236. if psFilters.Contains("is-task") {
  237. if psFilters.ExactMatch("is-task", "true") {
  238. taskFilter = true
  239. isTask = true
  240. } else if psFilters.ExactMatch("is-task", "false") {
  241. taskFilter = true
  242. isTask = false
  243. } else {
  244. return nil, invalidFilter{"is-task", psFilters.Get("is-task")}
  245. }
  246. }
  247. err = psFilters.WalkValues("health", func(value string) error {
  248. if !container.IsValidHealthString(value) {
  249. return errdefs.InvalidParameter(errors.Errorf("Unrecognised filter value for health: %s", value))
  250. }
  251. return nil
  252. })
  253. if err != nil {
  254. return nil, err
  255. }
  256. var beforeContFilter, sinceContFilter *container.Snapshot
  257. err = psFilters.WalkValues("before", func(value string) error {
  258. beforeContFilter, err = idOrNameFilter(view, value)
  259. return err
  260. })
  261. if err != nil {
  262. return nil, err
  263. }
  264. err = psFilters.WalkValues("since", func(value string) error {
  265. sinceContFilter, err = idOrNameFilter(view, value)
  266. return err
  267. })
  268. if err != nil {
  269. return nil, err
  270. }
  271. imagesFilter := map[image.ID]bool{}
  272. var ancestorFilter bool
  273. if psFilters.Contains("ancestor") {
  274. ancestorFilter = true
  275. psFilters.WalkValues("ancestor", func(ancestor string) error {
  276. img, err := daemon.imageService.GetImage(ancestor, nil)
  277. if err != nil {
  278. logrus.Warnf("Error while looking up for image %v", ancestor)
  279. return nil
  280. }
  281. if imagesFilter[img.ID()] {
  282. // Already seen this ancestor, skip it
  283. return nil
  284. }
  285. // Then walk down the graph and put the imageIds in imagesFilter
  286. populateImageFilterByParents(imagesFilter, img.ID(), daemon.imageService.Children)
  287. return nil
  288. })
  289. }
  290. publishFilter := map[nat.Port]bool{}
  291. err = psFilters.WalkValues("publish", portOp("publish", publishFilter))
  292. if err != nil {
  293. return nil, err
  294. }
  295. exposeFilter := map[nat.Port]bool{}
  296. err = psFilters.WalkValues("expose", portOp("expose", exposeFilter))
  297. if err != nil {
  298. return nil, err
  299. }
  300. return &listContext{
  301. filters: psFilters,
  302. ancestorFilter: ancestorFilter,
  303. images: imagesFilter,
  304. exitAllowed: filtExited,
  305. beforeFilter: beforeContFilter,
  306. sinceFilter: sinceContFilter,
  307. taskFilter: taskFilter,
  308. isTask: isTask,
  309. publish: publishFilter,
  310. expose: exposeFilter,
  311. ContainerListOptions: config,
  312. names: view.GetAllNames(),
  313. }, nil
  314. }
  315. func idOrNameFilter(view container.View, value string) (*container.Snapshot, error) {
  316. filter, err := view.Get(value)
  317. switch err.(type) {
  318. case container.NoSuchContainerError:
  319. // Try name search instead
  320. found := ""
  321. for id, idNames := range view.GetAllNames() {
  322. for _, eachName := range idNames {
  323. if strings.TrimPrefix(value, "/") == strings.TrimPrefix(eachName, "/") {
  324. if found != "" && found != id {
  325. return nil, err
  326. }
  327. found = id
  328. }
  329. }
  330. }
  331. if found != "" {
  332. filter, err = view.Get(found)
  333. }
  334. }
  335. return filter, err
  336. }
  337. func portOp(key string, filter map[nat.Port]bool) func(value string) error {
  338. return func(value string) error {
  339. if strings.Contains(value, ":") {
  340. return fmt.Errorf("filter for '%s' should not contain ':': %s", key, value)
  341. }
  342. // support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
  343. proto, port := nat.SplitProtoPort(value)
  344. start, end, err := nat.ParsePortRange(port)
  345. if err != nil {
  346. return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
  347. }
  348. for i := start; i <= end; i++ {
  349. p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
  350. if err != nil {
  351. return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
  352. }
  353. filter[p] = true
  354. }
  355. return nil
  356. }
  357. }
  358. // includeContainerInList decides whether a container should be included in the output or not based in the filter.
  359. // It also decides if the iteration should be stopped or not.
  360. func includeContainerInList(container *container.Snapshot, ctx *listContext) iterationAction {
  361. // Do not include container if it's in the list before the filter container.
  362. // Set the filter container to nil to include the rest of containers after this one.
  363. if ctx.beforeFilter != nil {
  364. if container.ID == ctx.beforeFilter.ID {
  365. ctx.beforeFilter = nil
  366. }
  367. return excludeContainer
  368. }
  369. // Stop iteration when the container arrives to the filter container
  370. if ctx.sinceFilter != nil {
  371. if container.ID == ctx.sinceFilter.ID {
  372. return stopIteration
  373. }
  374. }
  375. // Do not include container if it's stopped and we're not filters
  376. if !container.Running && !ctx.All && ctx.Limit <= 0 {
  377. return excludeContainer
  378. }
  379. // Do not include container if the name doesn't match
  380. if !ctx.filters.Match("name", container.Name) && !ctx.filters.Match("name", strings.TrimPrefix(container.Name, "/")) {
  381. return excludeContainer
  382. }
  383. // Do not include container if the id doesn't match
  384. if !ctx.filters.Match("id", container.ID) {
  385. return excludeContainer
  386. }
  387. if ctx.taskFilter {
  388. if ctx.isTask != container.Managed {
  389. return excludeContainer
  390. }
  391. }
  392. // Do not include container if any of the labels don't match
  393. if !ctx.filters.MatchKVList("label", container.Labels) {
  394. return excludeContainer
  395. }
  396. // Do not include container if isolation doesn't match
  397. if excludeContainer == excludeByIsolation(container, ctx) {
  398. return excludeContainer
  399. }
  400. // Stop iteration when the index is over the limit
  401. if ctx.Limit > 0 && ctx.idx == ctx.Limit {
  402. return stopIteration
  403. }
  404. // Do not include container if its exit code is not in the filter
  405. if len(ctx.exitAllowed) > 0 {
  406. shouldSkip := true
  407. for _, code := range ctx.exitAllowed {
  408. if code == container.ExitCode && !container.Running && !container.StartedAt.IsZero() {
  409. shouldSkip = false
  410. break
  411. }
  412. }
  413. if shouldSkip {
  414. return excludeContainer
  415. }
  416. }
  417. // Do not include container if its status doesn't match the filter
  418. if !ctx.filters.Match("status", container.State) {
  419. return excludeContainer
  420. }
  421. // Do not include container if its health doesn't match the filter
  422. if !ctx.filters.ExactMatch("health", container.Health) {
  423. return excludeContainer
  424. }
  425. if ctx.filters.Contains("volume") {
  426. volumesByName := make(map[string]types.MountPoint)
  427. for _, m := range container.Mounts {
  428. if m.Name != "" {
  429. volumesByName[m.Name] = m
  430. } else {
  431. volumesByName[m.Source] = m
  432. }
  433. }
  434. volumesByDestination := make(map[string]types.MountPoint)
  435. for _, m := range container.Mounts {
  436. if m.Destination != "" {
  437. volumesByDestination[m.Destination] = m
  438. }
  439. }
  440. volumeExist := fmt.Errorf("volume mounted in container")
  441. err := ctx.filters.WalkValues("volume", func(value string) error {
  442. if _, exist := volumesByDestination[value]; exist {
  443. return volumeExist
  444. }
  445. if _, exist := volumesByName[value]; exist {
  446. return volumeExist
  447. }
  448. return nil
  449. })
  450. if err != volumeExist {
  451. return excludeContainer
  452. }
  453. }
  454. if ctx.ancestorFilter {
  455. if len(ctx.images) == 0 {
  456. return excludeContainer
  457. }
  458. if !ctx.images[image.ID(container.ImageID)] {
  459. return excludeContainer
  460. }
  461. }
  462. var (
  463. networkExist = errors.New("container part of network")
  464. noNetworks = errors.New("container is not part of any networks")
  465. )
  466. if ctx.filters.Contains("network") {
  467. err := ctx.filters.WalkValues("network", func(value string) error {
  468. if container.NetworkSettings == nil {
  469. return noNetworks
  470. }
  471. if _, ok := container.NetworkSettings.Networks[value]; ok {
  472. return networkExist
  473. }
  474. for _, nw := range container.NetworkSettings.Networks {
  475. if nw == nil {
  476. continue
  477. }
  478. if strings.HasPrefix(nw.NetworkID, value) {
  479. return networkExist
  480. }
  481. }
  482. return nil
  483. })
  484. if err != networkExist {
  485. return excludeContainer
  486. }
  487. }
  488. if len(ctx.expose) > 0 || len(ctx.publish) > 0 {
  489. var (
  490. shouldSkip = true
  491. publishedPort nat.Port
  492. exposedPort nat.Port
  493. )
  494. for _, port := range container.Ports {
  495. publishedPort = nat.Port(fmt.Sprintf("%d/%s", port.PublicPort, port.Type))
  496. exposedPort = nat.Port(fmt.Sprintf("%d/%s", port.PrivatePort, port.Type))
  497. if ok := ctx.publish[publishedPort]; ok {
  498. shouldSkip = false
  499. break
  500. } else if ok := ctx.expose[exposedPort]; ok {
  501. shouldSkip = false
  502. break
  503. }
  504. }
  505. if shouldSkip {
  506. return excludeContainer
  507. }
  508. }
  509. return includeContainer
  510. }
  511. // refreshImage checks if the Image ref still points to the correct ID, and updates the ref to the actual ID when it doesn't
  512. func (daemon *Daemon) refreshImage(s *container.Snapshot, ctx *listContext) (*types.Container, error) {
  513. c := s.Container
  514. image := s.Image // keep the original ref if still valid (hasn't changed)
  515. if image != s.ImageID {
  516. img, err := daemon.imageService.GetImage(image, nil)
  517. if _, isDNE := err.(images.ErrImageDoesNotExist); err != nil && !isDNE {
  518. return nil, err
  519. }
  520. if err != nil || img.ImageID() != s.ImageID {
  521. // ref changed, we need to use original ID
  522. image = s.ImageID
  523. }
  524. }
  525. c.Image = image
  526. return &c, nil
  527. }
  528. func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
  529. if !ancestorMap[imageID] {
  530. for _, id := range getChildren(imageID) {
  531. populateImageFilterByParents(ancestorMap, id, getChildren)
  532. }
  533. ancestorMap[imageID] = true
  534. }
  535. }