list.go 18 KB

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