list.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. package daemon
  2. import (
  3. "errors"
  4. "fmt"
  5. "sort"
  6. "strconv"
  7. "strings"
  8. "github.com/Sirupsen/logrus"
  9. "github.com/docker/docker/api/types"
  10. "github.com/docker/docker/api/types/filters"
  11. networktypes "github.com/docker/docker/api/types/network"
  12. "github.com/docker/docker/container"
  13. "github.com/docker/docker/image"
  14. "github.com/docker/docker/volume"
  15. "github.com/docker/go-connections/nat"
  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.Container, *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.Container
  76. // sinceFilter is a filter to stop the filtering when the iterator arrive to the given container
  77. sinceFilter *container.Container
  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. // byContainerCreated is a temporary type used to sort a list of containers by creation time.
  90. type byContainerCreated []*container.Container
  91. func (r byContainerCreated) Len() int { return len(r) }
  92. func (r byContainerCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
  93. func (r byContainerCreated) Less(i, j int) bool {
  94. return r[i].Created.UnixNano() < r[j].Created.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.transformContainer)
  99. }
  100. func (daemon *Daemon) filterByNameIDMatches(ctx *listContext) []*container.Container {
  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. return daemon.List()
  109. }
  110. // idSearch will determine if we limit name matching to the IDs
  111. // matched from any IDs which were specified as filters
  112. if len(ids) > 0 {
  113. idSearch = true
  114. }
  115. matches := make(map[string]bool)
  116. // find ID matches; errors represent "not found" and can be ignored
  117. for _, id := range ids {
  118. if fullID, err := daemon.idIndex.Get(id); err == nil {
  119. matches[fullID] = true
  120. }
  121. }
  122. // look for name matches; if ID filtering was used, then limit the
  123. // search space to the matches map only; errors represent "not found"
  124. // and can be ignored
  125. if len(names) > 0 {
  126. for id, idNames := range ctx.names {
  127. // if ID filters were used and no matches on that ID were
  128. // found, continue to next ID in the list
  129. if idSearch && !matches[id] {
  130. continue
  131. }
  132. for _, eachName := range idNames {
  133. if ctx.filters.Match("name", eachName) {
  134. matches[id] = true
  135. }
  136. }
  137. }
  138. }
  139. cntrs := make([]*container.Container, 0, len(matches))
  140. for id := range matches {
  141. if c := daemon.containers.Get(id); c != nil {
  142. cntrs = append(cntrs, c)
  143. }
  144. }
  145. // Restore sort-order after filtering
  146. // Created gives us nanosec resolution for sorting
  147. sort.Sort(sort.Reverse(byContainerCreated(cntrs)))
  148. return cntrs
  149. }
  150. // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
  151. func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
  152. var (
  153. containers = []*types.Container{}
  154. )
  155. ctx, err := daemon.foldFilter(config)
  156. if err != nil {
  157. return nil, err
  158. }
  159. // fastpath to only look at a subset of containers if specific name
  160. // or ID matches were provided by the user--otherwise we potentially
  161. // end up locking and querying many more containers than intended
  162. containerList := daemon.filterByNameIDMatches(ctx)
  163. for _, container := range containerList {
  164. t, err := daemon.reducePsContainer(container, ctx, reducer)
  165. if err != nil {
  166. if err != errStopIteration {
  167. return nil, err
  168. }
  169. break
  170. }
  171. if t != nil {
  172. containers = append(containers, t)
  173. ctx.idx++
  174. }
  175. }
  176. return containers, nil
  177. }
  178. // reducePsContainer is the basic representation for a container as expected by the ps command.
  179. func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *listContext, reducer containerReducer) (*types.Container, error) {
  180. container.Lock()
  181. defer container.Unlock()
  182. // filter containers to return
  183. action := includeContainerInList(container, ctx)
  184. switch action {
  185. case excludeContainer:
  186. return nil, nil
  187. case stopIteration:
  188. return nil, errStopIteration
  189. }
  190. // transform internal container struct into api structs
  191. return reducer(container, ctx)
  192. }
  193. // foldFilter generates the container filter based on the user's filtering options.
  194. func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listContext, error) {
  195. psFilters := config.Filters
  196. if err := psFilters.Validate(acceptedPsFilterTags); err != nil {
  197. return nil, err
  198. }
  199. var filtExited []int
  200. err := psFilters.WalkValues("exited", func(value string) error {
  201. code, err := strconv.Atoi(value)
  202. if err != nil {
  203. return err
  204. }
  205. filtExited = append(filtExited, code)
  206. return nil
  207. })
  208. if err != nil {
  209. return nil, err
  210. }
  211. err = psFilters.WalkValues("status", func(value string) error {
  212. if !container.IsValidStateString(value) {
  213. return fmt.Errorf("Unrecognised filter value for status: %s", value)
  214. }
  215. config.All = true
  216. return nil
  217. })
  218. if err != nil {
  219. return nil, err
  220. }
  221. var taskFilter, isTask bool
  222. if psFilters.Include("is-task") {
  223. if psFilters.ExactMatch("is-task", "true") {
  224. taskFilter = true
  225. isTask = true
  226. } else if psFilters.ExactMatch("is-task", "false") {
  227. taskFilter = true
  228. isTask = false
  229. } else {
  230. return nil, fmt.Errorf("Invalid filter 'is-task=%s'", psFilters.Get("is-task"))
  231. }
  232. }
  233. err = psFilters.WalkValues("health", func(value string) error {
  234. if !container.IsValidHealthString(value) {
  235. return fmt.Errorf("Unrecognised filter value for health: %s", value)
  236. }
  237. return nil
  238. })
  239. if err != nil {
  240. return nil, err
  241. }
  242. var beforeContFilter, sinceContFilter *container.Container
  243. err = psFilters.WalkValues("before", func(value string) error {
  244. beforeContFilter, err = daemon.GetContainer(value)
  245. return err
  246. })
  247. if err != nil {
  248. return nil, err
  249. }
  250. err = psFilters.WalkValues("since", func(value string) error {
  251. sinceContFilter, err = daemon.GetContainer(value)
  252. return err
  253. })
  254. if err != nil {
  255. return nil, err
  256. }
  257. imagesFilter := map[image.ID]bool{}
  258. var ancestorFilter bool
  259. if psFilters.Include("ancestor") {
  260. ancestorFilter = true
  261. psFilters.WalkValues("ancestor", func(ancestor string) error {
  262. id, err := daemon.GetImageID(ancestor)
  263. if err != nil {
  264. logrus.Warnf("Error while looking up for image %v", ancestor)
  265. return nil
  266. }
  267. if imagesFilter[id] {
  268. // Already seen this ancestor, skip it
  269. return nil
  270. }
  271. // Then walk down the graph and put the imageIds in imagesFilter
  272. populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children)
  273. return nil
  274. })
  275. }
  276. publishFilter := map[nat.Port]bool{}
  277. err = psFilters.WalkValues("publish", func(value string) error {
  278. if strings.Contains(value, ":") {
  279. return fmt.Errorf("filter for 'publish' should not contain ':': %v", value)
  280. }
  281. //support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
  282. proto, port := nat.SplitProtoPort(value)
  283. start, end, err := nat.ParsePortRange(port)
  284. if err != nil {
  285. return fmt.Errorf("error while looking up for publish %v: %s", value, err)
  286. }
  287. for i := start; i <= end; i++ {
  288. p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
  289. if err != nil {
  290. return fmt.Errorf("error while looking up for publish %v: %s", value, err)
  291. }
  292. publishFilter[p] = true
  293. }
  294. return nil
  295. })
  296. if err != nil {
  297. return nil, err
  298. }
  299. exposeFilter := map[nat.Port]bool{}
  300. err = psFilters.WalkValues("expose", func(value string) error {
  301. if strings.Contains(value, ":") {
  302. return fmt.Errorf("filter for 'expose' should not contain ':': %v", value)
  303. }
  304. //support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
  305. proto, port := nat.SplitProtoPort(value)
  306. start, end, err := nat.ParsePortRange(port)
  307. if err != nil {
  308. return fmt.Errorf("error while looking up for 'expose' %v: %s", value, err)
  309. }
  310. for i := start; i <= end; i++ {
  311. p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
  312. if err != nil {
  313. return fmt.Errorf("error while looking up for 'expose' %v: %s", value, err)
  314. }
  315. exposeFilter[p] = true
  316. }
  317. return nil
  318. })
  319. if err != nil {
  320. return nil, err
  321. }
  322. return &listContext{
  323. filters: psFilters,
  324. ancestorFilter: ancestorFilter,
  325. images: imagesFilter,
  326. exitAllowed: filtExited,
  327. beforeFilter: beforeContFilter,
  328. sinceFilter: sinceContFilter,
  329. taskFilter: taskFilter,
  330. isTask: isTask,
  331. publish: publishFilter,
  332. expose: exposeFilter,
  333. ContainerListOptions: config,
  334. names: daemon.nameIndex.GetAll(),
  335. }, nil
  336. }
  337. // includeContainerInList decides whether a container should be included in the output or not based in the filter.
  338. // It also decides if the iteration should be stopped or not.
  339. func includeContainerInList(container *container.Container, ctx *listContext) iterationAction {
  340. // Do not include container if it's in the list before the filter container.
  341. // Set the filter container to nil to include the rest of containers after this one.
  342. if ctx.beforeFilter != nil {
  343. if container.ID == ctx.beforeFilter.ID {
  344. ctx.beforeFilter = nil
  345. }
  346. return excludeContainer
  347. }
  348. // Stop iteration when the container arrives to the filter container
  349. if ctx.sinceFilter != nil {
  350. if container.ID == ctx.sinceFilter.ID {
  351. return stopIteration
  352. }
  353. }
  354. // Do not include container if it's stopped and we're not filters
  355. if !container.Running && !ctx.All && ctx.Limit <= 0 {
  356. return excludeContainer
  357. }
  358. // Do not include container if the name doesn't match
  359. if !ctx.filters.Match("name", container.Name) {
  360. return excludeContainer
  361. }
  362. // Do not include container if the id doesn't match
  363. if !ctx.filters.Match("id", container.ID) {
  364. return excludeContainer
  365. }
  366. if ctx.taskFilter {
  367. if ctx.isTask != container.Managed {
  368. return excludeContainer
  369. }
  370. }
  371. // Do not include container if any of the labels don't match
  372. if !ctx.filters.MatchKVList("label", container.Config.Labels) {
  373. return excludeContainer
  374. }
  375. // Do not include container if isolation doesn't match
  376. if excludeContainer == excludeByIsolation(container, ctx) {
  377. return excludeContainer
  378. }
  379. // Stop iteration when the index is over the limit
  380. if ctx.Limit > 0 && ctx.idx == ctx.Limit {
  381. return stopIteration
  382. }
  383. // Do not include container if its exit code is not in the filter
  384. if len(ctx.exitAllowed) > 0 {
  385. shouldSkip := true
  386. for _, code := range ctx.exitAllowed {
  387. if code == container.ExitCode() && !container.Running && !container.StartedAt.IsZero() {
  388. shouldSkip = false
  389. break
  390. }
  391. }
  392. if shouldSkip {
  393. return excludeContainer
  394. }
  395. }
  396. // Do not include container if its status doesn't match the filter
  397. if !ctx.filters.Match("status", container.State.StateString()) {
  398. return excludeContainer
  399. }
  400. // Do not include container if its health doesn't match the filter
  401. if !ctx.filters.ExactMatch("health", container.State.HealthString()) {
  402. return excludeContainer
  403. }
  404. if ctx.filters.Include("volume") {
  405. volumesByName := make(map[string]*volume.MountPoint)
  406. for _, m := range container.MountPoints {
  407. if m.Name != "" {
  408. volumesByName[m.Name] = m
  409. } else {
  410. volumesByName[m.Source] = m
  411. }
  412. }
  413. volumeExist := fmt.Errorf("volume mounted in container")
  414. err := ctx.filters.WalkValues("volume", func(value string) error {
  415. if _, exist := container.MountPoints[value]; exist {
  416. return volumeExist
  417. }
  418. if _, exist := volumesByName[value]; exist {
  419. return volumeExist
  420. }
  421. return nil
  422. })
  423. if err != volumeExist {
  424. return excludeContainer
  425. }
  426. }
  427. if ctx.ancestorFilter {
  428. if len(ctx.images) == 0 {
  429. return excludeContainer
  430. }
  431. if !ctx.images[container.ImageID] {
  432. return excludeContainer
  433. }
  434. }
  435. networkExist := fmt.Errorf("container part of network")
  436. if ctx.filters.Include("network") {
  437. err := ctx.filters.WalkValues("network", func(value string) error {
  438. if _, ok := container.NetworkSettings.Networks[value]; ok {
  439. return networkExist
  440. }
  441. for _, nw := range container.NetworkSettings.Networks {
  442. if nw.EndpointSettings == nil {
  443. continue
  444. }
  445. if nw.NetworkID == value {
  446. return networkExist
  447. }
  448. }
  449. return nil
  450. })
  451. if err != networkExist {
  452. return excludeContainer
  453. }
  454. }
  455. if len(ctx.publish) > 0 {
  456. shouldSkip := true
  457. for port := range ctx.publish {
  458. if _, ok := container.HostConfig.PortBindings[port]; ok {
  459. shouldSkip = false
  460. break
  461. }
  462. }
  463. if shouldSkip {
  464. return excludeContainer
  465. }
  466. }
  467. if len(ctx.expose) > 0 {
  468. shouldSkip := true
  469. for port := range ctx.expose {
  470. if _, ok := container.Config.ExposedPorts[port]; ok {
  471. shouldSkip = false
  472. break
  473. }
  474. }
  475. if shouldSkip {
  476. return excludeContainer
  477. }
  478. }
  479. return includeContainer
  480. }
  481. // transformContainer generates the container type expected by the docker ps command.
  482. func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) {
  483. newC := &types.Container{
  484. ID: container.ID,
  485. Names: ctx.names[container.ID],
  486. ImageID: container.ImageID.String(),
  487. }
  488. if newC.Names == nil {
  489. // Dead containers will often have no name, so make sure the response isn't null
  490. newC.Names = []string{}
  491. }
  492. image := container.Config.Image // if possible keep the original ref
  493. if image != container.ImageID.String() {
  494. id, err := daemon.GetImageID(image)
  495. if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE {
  496. return nil, err
  497. }
  498. if err != nil || id != container.ImageID {
  499. image = container.ImageID.String()
  500. }
  501. }
  502. newC.Image = image
  503. if len(container.Args) > 0 {
  504. args := []string{}
  505. for _, arg := range container.Args {
  506. if strings.Contains(arg, " ") {
  507. args = append(args, fmt.Sprintf("'%s'", arg))
  508. } else {
  509. args = append(args, arg)
  510. }
  511. }
  512. argsAsString := strings.Join(args, " ")
  513. newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
  514. } else {
  515. newC.Command = container.Path
  516. }
  517. newC.Created = container.Created.Unix()
  518. newC.State = container.State.StateString()
  519. newC.Status = container.State.String()
  520. newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
  521. // copy networks to avoid races
  522. networks := make(map[string]*networktypes.EndpointSettings)
  523. for name, network := range container.NetworkSettings.Networks {
  524. if network == nil || network.EndpointSettings == nil {
  525. continue
  526. }
  527. networks[name] = &networktypes.EndpointSettings{
  528. EndpointID: network.EndpointID,
  529. Gateway: network.Gateway,
  530. IPAddress: network.IPAddress,
  531. IPPrefixLen: network.IPPrefixLen,
  532. IPv6Gateway: network.IPv6Gateway,
  533. GlobalIPv6Address: network.GlobalIPv6Address,
  534. GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
  535. MacAddress: network.MacAddress,
  536. NetworkID: network.NetworkID,
  537. }
  538. if network.IPAMConfig != nil {
  539. networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{
  540. IPv4Address: network.IPAMConfig.IPv4Address,
  541. IPv6Address: network.IPAMConfig.IPv6Address,
  542. }
  543. }
  544. }
  545. newC.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
  546. newC.Ports = []types.Port{}
  547. for port, bindings := range container.NetworkSettings.Ports {
  548. p, err := nat.ParsePort(port.Port())
  549. if err != nil {
  550. return nil, err
  551. }
  552. if len(bindings) == 0 {
  553. newC.Ports = append(newC.Ports, types.Port{
  554. PrivatePort: uint16(p),
  555. Type: port.Proto(),
  556. })
  557. continue
  558. }
  559. for _, binding := range bindings {
  560. h, err := nat.ParsePort(binding.HostPort)
  561. if err != nil {
  562. return nil, err
  563. }
  564. newC.Ports = append(newC.Ports, types.Port{
  565. PrivatePort: uint16(p),
  566. PublicPort: uint16(h),
  567. Type: port.Proto(),
  568. IP: binding.HostIP,
  569. })
  570. }
  571. }
  572. if ctx.Size {
  573. sizeRw, sizeRootFs := daemon.getSize(container)
  574. newC.SizeRw = sizeRw
  575. newC.SizeRootFs = sizeRootFs
  576. }
  577. newC.Labels = container.Config.Labels
  578. newC.Mounts = addMountPoints(container)
  579. return newC, nil
  580. }
  581. // Volumes lists known volumes, using the filter to restrict the range
  582. // of volumes returned.
  583. func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, []string, error) {
  584. var (
  585. volumesOut []*types.Volume
  586. )
  587. volFilters, err := filters.FromParam(filter)
  588. if err != nil {
  589. return nil, nil, err
  590. }
  591. if err := volFilters.Validate(acceptedVolumeFilterTags); err != nil {
  592. return nil, nil, err
  593. }
  594. volumes, warnings, err := daemon.volumes.List()
  595. if err != nil {
  596. return nil, nil, err
  597. }
  598. filterVolumes, err := daemon.filterVolumes(volumes, volFilters)
  599. if err != nil {
  600. return nil, nil, err
  601. }
  602. for _, v := range filterVolumes {
  603. apiV := volumeToAPIType(v)
  604. if vv, ok := v.(interface {
  605. CachedPath() string
  606. }); ok {
  607. apiV.Mountpoint = vv.CachedPath()
  608. } else {
  609. apiV.Mountpoint = v.Path()
  610. }
  611. volumesOut = append(volumesOut, apiV)
  612. }
  613. return volumesOut, warnings, nil
  614. }
  615. // filterVolumes filters volume list according to user specified filter
  616. // and returns user chosen volumes
  617. func (daemon *Daemon) filterVolumes(vols []volume.Volume, filter filters.Args) ([]volume.Volume, error) {
  618. // if filter is empty, return original volume list
  619. if filter.Len() == 0 {
  620. return vols, nil
  621. }
  622. var retVols []volume.Volume
  623. for _, vol := range vols {
  624. if filter.Include("name") {
  625. if !filter.Match("name", vol.Name()) {
  626. continue
  627. }
  628. }
  629. if filter.Include("driver") {
  630. if !filter.ExactMatch("driver", vol.DriverName()) {
  631. continue
  632. }
  633. }
  634. if filter.Include("label") {
  635. v, ok := vol.(volume.DetailedVolume)
  636. if !ok {
  637. continue
  638. }
  639. if !filter.MatchKVList("label", v.Labels()) {
  640. continue
  641. }
  642. }
  643. retVols = append(retVols, vol)
  644. }
  645. danglingOnly := false
  646. if filter.Include("dangling") {
  647. if filter.ExactMatch("dangling", "true") || filter.ExactMatch("dangling", "1") {
  648. danglingOnly = true
  649. } else if !filter.ExactMatch("dangling", "false") && !filter.ExactMatch("dangling", "0") {
  650. return nil, fmt.Errorf("Invalid filter 'dangling=%s'", filter.Get("dangling"))
  651. }
  652. retVols = daemon.volumes.FilterByUsed(retVols, !danglingOnly)
  653. }
  654. return retVols, nil
  655. }
  656. func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
  657. if !ancestorMap[imageID] {
  658. for _, id := range getChildren(imageID) {
  659. populateImageFilterByParents(ancestorMap, id, getChildren)
  660. }
  661. ancestorMap[imageID] = true
  662. }
  663. }