list.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. package daemon
  2. import (
  3. "errors"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "github.com/Sirupsen/logrus"
  8. "github.com/docker/docker/container"
  9. "github.com/docker/docker/image"
  10. "github.com/docker/docker/volume"
  11. "github.com/docker/engine-api/types"
  12. "github.com/docker/engine-api/types/filters"
  13. networktypes "github.com/docker/engine-api/types/network"
  14. "github.com/docker/go-connections/nat"
  15. )
  16. var acceptedVolumeFilterTags = map[string]bool{
  17. "dangling": true,
  18. "name": true,
  19. "driver": true,
  20. }
  21. var acceptedPsFilterTags = map[string]bool{
  22. "ancestor": true,
  23. "before": true,
  24. "exited": true,
  25. "id": true,
  26. "isolation": true,
  27. "label": true,
  28. "name": true,
  29. "status": true,
  30. "since": true,
  31. "volume": true,
  32. "network": 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.Container, *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. // this is used for --filter=before= and --before=, the latter is deprecated.
  70. beforeFilter *container.Container
  71. // sinceFilter is a filter to stop the filtering when the iterator arrive to the given container
  72. // this is used for --filter=since= and --since=, the latter is deprecated.
  73. sinceFilter *container.Container
  74. // ContainerListOptions is the filters set by the user
  75. *types.ContainerListOptions
  76. }
  77. // Containers returns the list of containers to show given the user's filtering.
  78. func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
  79. return daemon.reduceContainers(config, daemon.transformContainer)
  80. }
  81. // ListContainersForNode returns all containerID that match the specified nodeID
  82. func (daemon *Daemon) ListContainersForNode(nodeID string) []string {
  83. var ids []string
  84. for _, c := range daemon.List() {
  85. if c.Config.Labels["com.docker.swarm.node.id"] == nodeID {
  86. ids = append(ids, c.ID)
  87. }
  88. }
  89. return ids
  90. }
  91. func (daemon *Daemon) filterByNameIDMatches(ctx *listContext) []*container.Container {
  92. idSearch := false
  93. names := ctx.filters.Get("name")
  94. ids := ctx.filters.Get("id")
  95. if len(names)+len(ids) == 0 {
  96. // if name or ID filters are not in use, return to
  97. // standard behavior of walking the entire container
  98. // list from the daemon's in-memory store
  99. return daemon.List()
  100. }
  101. // idSearch will determine if we limit name matching to the IDs
  102. // matched from any IDs which were specified as filters
  103. if len(ids) > 0 {
  104. idSearch = true
  105. }
  106. matches := make(map[string]bool)
  107. // find ID matches; errors represent "not found" and can be ignored
  108. for _, id := range ids {
  109. if fullID, err := daemon.idIndex.Get(id); err == nil {
  110. matches[fullID] = true
  111. }
  112. }
  113. // look for name matches; if ID filtering was used, then limit the
  114. // search space to the matches map only; errors represent "not found"
  115. // and can be ignored
  116. if len(names) > 0 {
  117. for id, idNames := range ctx.names {
  118. // if ID filters were used and no matches on that ID were
  119. // found, continue to next ID in the list
  120. if idSearch && !matches[id] {
  121. continue
  122. }
  123. for _, eachName := range idNames {
  124. if ctx.filters.Match("name", eachName) {
  125. matches[id] = true
  126. }
  127. }
  128. }
  129. }
  130. cntrs := make([]*container.Container, 0, len(matches))
  131. for id := range matches {
  132. cntrs = append(cntrs, daemon.containers.Get(id))
  133. }
  134. return cntrs
  135. }
  136. // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
  137. func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
  138. containers := []*types.Container{}
  139. ctx, err := daemon.foldFilter(config)
  140. if err != nil {
  141. return nil, err
  142. }
  143. // fastpath to only look at a subset of containers if specific name
  144. // or ID matches were provided by the user--otherwise we potentially
  145. // end up locking and querying many more containers than intended
  146. containerList := daemon.filterByNameIDMatches(ctx)
  147. for _, container := range containerList {
  148. t, err := daemon.reducePsContainer(container, ctx, reducer)
  149. if err != nil {
  150. if err != errStopIteration {
  151. return nil, err
  152. }
  153. break
  154. }
  155. if t != nil {
  156. containers = append(containers, t)
  157. ctx.idx++
  158. }
  159. }
  160. return containers, nil
  161. }
  162. // reducePsContainer is the basic representation for a container as expected by the ps command.
  163. func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *listContext, reducer containerReducer) (*types.Container, error) {
  164. container.Lock()
  165. defer container.Unlock()
  166. // filter containers to return
  167. action := includeContainerInList(container, ctx)
  168. switch action {
  169. case excludeContainer:
  170. return nil, nil
  171. case stopIteration:
  172. return nil, errStopIteration
  173. }
  174. // transform internal container struct into api structs
  175. return reducer(container, ctx)
  176. }
  177. // foldFilter generates the container filter based on the user's filtering options.
  178. func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listContext, error) {
  179. psFilters := config.Filter
  180. if err := psFilters.Validate(acceptedPsFilterTags); err != nil {
  181. return nil, err
  182. }
  183. var filtExited []int
  184. err := psFilters.WalkValues("exited", func(value string) error {
  185. code, err := strconv.Atoi(value)
  186. if err != nil {
  187. return err
  188. }
  189. filtExited = append(filtExited, code)
  190. return nil
  191. })
  192. if err != nil {
  193. return nil, err
  194. }
  195. err = psFilters.WalkValues("status", func(value string) error {
  196. if !container.IsValidStateString(value) {
  197. return fmt.Errorf("Unrecognised filter value for status: %s", value)
  198. }
  199. config.All = true
  200. return nil
  201. })
  202. if err != nil {
  203. return nil, err
  204. }
  205. var beforeContFilter, sinceContFilter *container.Container
  206. err = psFilters.WalkValues("before", func(value string) error {
  207. beforeContFilter, err = daemon.GetContainer(value)
  208. return err
  209. })
  210. if err != nil {
  211. return nil, err
  212. }
  213. err = psFilters.WalkValues("since", func(value string) error {
  214. sinceContFilter, err = daemon.GetContainer(value)
  215. return err
  216. })
  217. if err != nil {
  218. return nil, err
  219. }
  220. imagesFilter := map[image.ID]bool{}
  221. var ancestorFilter bool
  222. if psFilters.Include("ancestor") {
  223. ancestorFilter = true
  224. psFilters.WalkValues("ancestor", func(ancestor string) error {
  225. id, err := daemon.GetImageID(ancestor)
  226. if err != nil {
  227. logrus.Warnf("Error while looking up for image %v", ancestor)
  228. return nil
  229. }
  230. if imagesFilter[id] {
  231. // Already seen this ancestor, skip it
  232. return nil
  233. }
  234. // Then walk down the graph and put the imageIds in imagesFilter
  235. populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children)
  236. return nil
  237. })
  238. }
  239. return &listContext{
  240. filters: psFilters,
  241. ancestorFilter: ancestorFilter,
  242. images: imagesFilter,
  243. exitAllowed: filtExited,
  244. beforeFilter: beforeContFilter,
  245. sinceFilter: sinceContFilter,
  246. ContainerListOptions: config,
  247. names: daemon.nameIndex.GetAll(),
  248. }, nil
  249. }
  250. // includeContainerInList decides whether a container should be included in the output or not based in the filter.
  251. // It also decides if the iteration should be stopped or not.
  252. func includeContainerInList(container *container.Container, ctx *listContext) iterationAction {
  253. // Do not include container if it's in the list before the filter container.
  254. // Set the filter container to nil to include the rest of containers after this one.
  255. if ctx.beforeFilter != nil {
  256. if container.ID == ctx.beforeFilter.ID {
  257. ctx.beforeFilter = nil
  258. }
  259. return excludeContainer
  260. }
  261. // Stop iteration when the container arrives to the filter container
  262. if ctx.sinceFilter != nil {
  263. if container.ID == ctx.sinceFilter.ID {
  264. return stopIteration
  265. }
  266. }
  267. // Do not include container if it's stopped and we're not filters
  268. if !container.Running && !ctx.All && ctx.Limit <= 0 {
  269. return excludeContainer
  270. }
  271. // Do not include container if the name doesn't match
  272. if !ctx.filters.Match("name", container.Name) {
  273. return excludeContainer
  274. }
  275. // Do not include container if the id doesn't match
  276. if !ctx.filters.Match("id", container.ID) {
  277. return excludeContainer
  278. }
  279. // Do not include container if any of the labels don't match
  280. if !ctx.filters.MatchKVList("label", container.Config.Labels) {
  281. return excludeContainer
  282. }
  283. // Do not include container if isolation doesn't match
  284. if excludeContainer == excludeByIsolation(container, ctx) {
  285. return excludeContainer
  286. }
  287. // Stop iteration when the index is over the limit
  288. if ctx.Limit > 0 && ctx.idx == ctx.Limit {
  289. return stopIteration
  290. }
  291. // Do not include container if its exit code is not in the filter
  292. if len(ctx.exitAllowed) > 0 {
  293. shouldSkip := true
  294. for _, code := range ctx.exitAllowed {
  295. if code == container.ExitCode() && !container.Running && !container.StartedAt.IsZero() {
  296. shouldSkip = false
  297. break
  298. }
  299. }
  300. if shouldSkip {
  301. return excludeContainer
  302. }
  303. }
  304. // Do not include container if its status doesn't match the filter
  305. if !ctx.filters.Match("status", container.State.StateString()) {
  306. return excludeContainer
  307. }
  308. if ctx.filters.Include("volume") {
  309. volumesByName := make(map[string]*volume.MountPoint)
  310. for _, m := range container.MountPoints {
  311. if m.Name != "" {
  312. volumesByName[m.Name] = m
  313. } else {
  314. volumesByName[m.Source] = m
  315. }
  316. }
  317. volumeExist := fmt.Errorf("volume mounted in container")
  318. err := ctx.filters.WalkValues("volume", func(value string) error {
  319. if _, exist := container.MountPoints[value]; exist {
  320. return volumeExist
  321. }
  322. if _, exist := volumesByName[value]; exist {
  323. return volumeExist
  324. }
  325. return nil
  326. })
  327. if err != volumeExist {
  328. return excludeContainer
  329. }
  330. }
  331. if ctx.ancestorFilter {
  332. if len(ctx.images) == 0 {
  333. return excludeContainer
  334. }
  335. if !ctx.images[container.ImageID] {
  336. return excludeContainer
  337. }
  338. }
  339. networkExist := fmt.Errorf("container part of network")
  340. if ctx.filters.Include("network") {
  341. err := ctx.filters.WalkValues("network", func(value string) error {
  342. if _, ok := container.NetworkSettings.Networks[value]; ok {
  343. return networkExist
  344. }
  345. for _, nw := range container.NetworkSettings.Networks {
  346. if nw.NetworkID == value {
  347. return networkExist
  348. }
  349. }
  350. return nil
  351. })
  352. if err != networkExist {
  353. return excludeContainer
  354. }
  355. }
  356. return includeContainer
  357. }
  358. // transformContainer generates the container type expected by the docker ps command.
  359. func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) {
  360. newC := &types.Container{
  361. ID: container.ID,
  362. Names: ctx.names[container.ID],
  363. ImageID: container.ImageID.String(),
  364. }
  365. if newC.Names == nil {
  366. // Dead containers will often have no name, so make sure the response isn't null
  367. newC.Names = []string{}
  368. }
  369. image := container.Config.Image // if possible keep the original ref
  370. if image != container.ImageID.String() {
  371. id, err := daemon.GetImageID(image)
  372. if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE {
  373. return nil, err
  374. }
  375. if err != nil || id != container.ImageID {
  376. image = container.ImageID.String()
  377. }
  378. }
  379. newC.Image = image
  380. if len(container.Args) > 0 {
  381. args := []string{}
  382. for _, arg := range container.Args {
  383. if strings.Contains(arg, " ") {
  384. args = append(args, fmt.Sprintf("'%s'", arg))
  385. } else {
  386. args = append(args, arg)
  387. }
  388. }
  389. argsAsString := strings.Join(args, " ")
  390. newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
  391. } else {
  392. newC.Command = container.Path
  393. }
  394. newC.Created = container.Created.Unix()
  395. newC.State = container.State.StateString()
  396. newC.Status = container.State.String()
  397. newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
  398. // copy networks to avoid races
  399. networks := make(map[string]*networktypes.EndpointSettings)
  400. for name, network := range container.NetworkSettings.Networks {
  401. if network == nil {
  402. continue
  403. }
  404. networks[name] = &networktypes.EndpointSettings{
  405. EndpointID: network.EndpointID,
  406. Gateway: network.Gateway,
  407. IPAddress: network.IPAddress,
  408. IPPrefixLen: network.IPPrefixLen,
  409. IPv6Gateway: network.IPv6Gateway,
  410. GlobalIPv6Address: network.GlobalIPv6Address,
  411. GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
  412. MacAddress: network.MacAddress,
  413. NetworkID: network.NetworkID,
  414. }
  415. if network.IPAMConfig != nil {
  416. networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{
  417. IPv4Address: network.IPAMConfig.IPv4Address,
  418. IPv6Address: network.IPAMConfig.IPv6Address,
  419. }
  420. }
  421. }
  422. newC.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
  423. newC.Ports = []types.Port{}
  424. for port, bindings := range container.NetworkSettings.Ports {
  425. p, err := nat.ParsePort(port.Port())
  426. if err != nil {
  427. return nil, err
  428. }
  429. if len(bindings) == 0 {
  430. newC.Ports = append(newC.Ports, types.Port{
  431. PrivatePort: p,
  432. Type: port.Proto(),
  433. })
  434. continue
  435. }
  436. for _, binding := range bindings {
  437. h, err := nat.ParsePort(binding.HostPort)
  438. if err != nil {
  439. return nil, err
  440. }
  441. newC.Ports = append(newC.Ports, types.Port{
  442. PrivatePort: p,
  443. PublicPort: h,
  444. Type: port.Proto(),
  445. IP: binding.HostIP,
  446. })
  447. }
  448. }
  449. if ctx.Size {
  450. sizeRw, sizeRootFs := daemon.getSize(container)
  451. newC.SizeRw = sizeRw
  452. newC.SizeRootFs = sizeRootFs
  453. }
  454. newC.Labels = container.Config.Labels
  455. newC.Mounts = addMountPoints(container)
  456. return newC, nil
  457. }
  458. // Volumes lists known volumes, using the filter to restrict the range
  459. // of volumes returned.
  460. func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, []string, error) {
  461. var (
  462. volumesOut []*types.Volume
  463. )
  464. volFilters, err := filters.FromParam(filter)
  465. if err != nil {
  466. return nil, nil, err
  467. }
  468. if err := volFilters.Validate(acceptedVolumeFilterTags); err != nil {
  469. return nil, nil, err
  470. }
  471. volumes, warnings, err := daemon.volumes.List()
  472. if err != nil {
  473. return nil, nil, err
  474. }
  475. filterVolumes, err := daemon.filterVolumes(volumes, volFilters)
  476. if err != nil {
  477. return nil, nil, err
  478. }
  479. for _, v := range filterVolumes {
  480. apiV := volumeToAPIType(v)
  481. if vv, ok := v.(interface {
  482. CachedPath() string
  483. }); ok {
  484. apiV.Mountpoint = vv.CachedPath()
  485. } else {
  486. apiV.Mountpoint = v.Path()
  487. }
  488. volumesOut = append(volumesOut, apiV)
  489. }
  490. return volumesOut, warnings, nil
  491. }
  492. // filterVolumes filters volume list according to user specified filter
  493. // and returns user chosen volumes
  494. func (daemon *Daemon) filterVolumes(vols []volume.Volume, filter filters.Args) ([]volume.Volume, error) {
  495. // if filter is empty, return original volume list
  496. if filter.Len() == 0 {
  497. return vols, nil
  498. }
  499. var retVols []volume.Volume
  500. for _, vol := range vols {
  501. if filter.Include("name") {
  502. if !filter.Match("name", vol.Name()) {
  503. continue
  504. }
  505. }
  506. if filter.Include("driver") {
  507. if !filter.Match("driver", vol.DriverName()) {
  508. continue
  509. }
  510. }
  511. retVols = append(retVols, vol)
  512. }
  513. danglingOnly := false
  514. if filter.Include("dangling") {
  515. if filter.ExactMatch("dangling", "true") || filter.ExactMatch("dangling", "1") {
  516. danglingOnly = true
  517. } else if !filter.ExactMatch("dangling", "false") && !filter.ExactMatch("dangling", "0") {
  518. return nil, fmt.Errorf("Invalid filter 'dangling=%s'", filter.Get("dangling"))
  519. }
  520. retVols = daemon.volumes.FilterByUsed(retVols, !danglingOnly)
  521. }
  522. return retVols, nil
  523. }
  524. func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
  525. if !ancestorMap[imageID] {
  526. for _, id := range getChildren(imageID) {
  527. populateImageFilterByParents(ancestorMap, id, getChildren)
  528. }
  529. ancestorMap[imageID] = true
  530. }
  531. }