list.go 18 KB

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