view.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. package container
  2. import (
  3. "fmt"
  4. "strings"
  5. "time"
  6. "github.com/Sirupsen/logrus"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/network"
  9. "github.com/docker/docker/pkg/registrar"
  10. "github.com/docker/go-connections/nat"
  11. "github.com/hashicorp/go-memdb"
  12. )
  13. const (
  14. memdbTable = "containers"
  15. memdbIDIndex = "id"
  16. )
  17. // Snapshot is a read only view for Containers. It holds all information necessary to serve container queries in a
  18. // versioned ACID in-memory store.
  19. type Snapshot struct {
  20. types.Container
  21. // additional info queries need to filter on
  22. // preserve nanosec resolution for queries
  23. CreatedAt time.Time
  24. StartedAt time.Time
  25. Name string
  26. Pid int
  27. ExitCode int
  28. Running bool
  29. Paused bool
  30. Managed bool
  31. ExposedPorts nat.PortSet
  32. PortBindings nat.PortSet
  33. Health string
  34. HostConfig struct {
  35. Isolation string
  36. }
  37. }
  38. // ViewDB provides an in-memory transactional (ACID) container Store
  39. type ViewDB interface {
  40. Snapshot(nameIndex *registrar.Registrar) View
  41. Save(*Container) error
  42. Delete(*Container) error
  43. }
  44. // View can be used by readers to avoid locking
  45. type View interface {
  46. All() ([]Snapshot, error)
  47. Get(id string) (*Snapshot, error)
  48. }
  49. var schema = &memdb.DBSchema{
  50. Tables: map[string]*memdb.TableSchema{
  51. memdbTable: {
  52. Name: memdbTable,
  53. Indexes: map[string]*memdb.IndexSchema{
  54. memdbIDIndex: {
  55. Name: memdbIDIndex,
  56. Unique: true,
  57. Indexer: &containerByIDIndexer{},
  58. },
  59. },
  60. },
  61. },
  62. }
  63. type memDB struct {
  64. store *memdb.MemDB
  65. }
  66. // NewViewDB provides the default implementation, with the default schema
  67. func NewViewDB() (ViewDB, error) {
  68. store, err := memdb.NewMemDB(schema)
  69. if err != nil {
  70. return nil, err
  71. }
  72. return &memDB{store: store}, nil
  73. }
  74. // Snapshot provides a consistent read-only View of the database
  75. func (db *memDB) Snapshot(index *registrar.Registrar) View {
  76. return &memdbView{
  77. txn: db.store.Txn(false),
  78. nameIndex: index.GetAll(),
  79. }
  80. }
  81. // Save atomically updates the in-memory store state for a Container.
  82. // Only read only (deep) copies of containers may be passed in.
  83. func (db *memDB) Save(c *Container) error {
  84. txn := db.store.Txn(true)
  85. defer txn.Commit()
  86. return txn.Insert(memdbTable, c)
  87. }
  88. // Delete removes an item by ID
  89. func (db *memDB) Delete(c *Container) error {
  90. txn := db.store.Txn(true)
  91. defer txn.Commit()
  92. return txn.Delete(memdbTable, NewBaseContainer(c.ID, c.Root))
  93. }
  94. type memdbView struct {
  95. txn *memdb.Txn
  96. nameIndex map[string][]string
  97. }
  98. // All returns a all items in this snapshot. Returned objects must never be modified.
  99. func (v *memdbView) All() ([]Snapshot, error) {
  100. var all []Snapshot
  101. iter, err := v.txn.Get(memdbTable, memdbIDIndex)
  102. if err != nil {
  103. return nil, err
  104. }
  105. for {
  106. item := iter.Next()
  107. if item == nil {
  108. break
  109. }
  110. snapshot := v.transform(item.(*Container))
  111. all = append(all, *snapshot)
  112. }
  113. return all, nil
  114. }
  115. // Get returns an item by id. Returned objects must never be modified.
  116. func (v *memdbView) Get(id string) (*Snapshot, error) {
  117. s, err := v.txn.First(memdbTable, memdbIDIndex, id)
  118. if err != nil {
  119. return nil, err
  120. }
  121. return v.transform(s.(*Container)), nil
  122. }
  123. // transform maps a (deep) copied Container object to what queries need.
  124. // A lock on the Container is not held because these are immutable deep copies.
  125. func (v *memdbView) transform(container *Container) *Snapshot {
  126. snapshot := &Snapshot{
  127. Container: types.Container{
  128. ID: container.ID,
  129. Names: v.nameIndex[container.ID],
  130. ImageID: container.ImageID.String(),
  131. Ports: []types.Port{},
  132. Mounts: container.GetMountPoints(),
  133. State: container.State.StateString(),
  134. Status: container.State.String(),
  135. Created: container.Created.Unix(),
  136. },
  137. CreatedAt: container.Created,
  138. StartedAt: container.StartedAt,
  139. Name: container.Name,
  140. Pid: container.Pid,
  141. Managed: container.Managed,
  142. ExposedPorts: make(nat.PortSet),
  143. PortBindings: make(nat.PortSet),
  144. Health: container.HealthString(),
  145. Running: container.Running,
  146. Paused: container.Paused,
  147. ExitCode: container.ExitCode(),
  148. }
  149. if snapshot.Names == nil {
  150. // Dead containers will often have no name, so make sure the response isn't null
  151. snapshot.Names = []string{}
  152. }
  153. if container.HostConfig != nil {
  154. snapshot.Container.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
  155. snapshot.HostConfig.Isolation = string(container.HostConfig.Isolation)
  156. for binding := range container.HostConfig.PortBindings {
  157. snapshot.PortBindings[binding] = struct{}{}
  158. }
  159. }
  160. if container.Config != nil {
  161. snapshot.Image = container.Config.Image
  162. snapshot.Labels = container.Config.Labels
  163. for exposed := range container.Config.ExposedPorts {
  164. snapshot.ExposedPorts[exposed] = struct{}{}
  165. }
  166. }
  167. if len(container.Args) > 0 {
  168. args := []string{}
  169. for _, arg := range container.Args {
  170. if strings.Contains(arg, " ") {
  171. args = append(args, fmt.Sprintf("'%s'", arg))
  172. } else {
  173. args = append(args, arg)
  174. }
  175. }
  176. argsAsString := strings.Join(args, " ")
  177. snapshot.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
  178. } else {
  179. snapshot.Command = container.Path
  180. }
  181. snapshot.Ports = []types.Port{}
  182. networks := make(map[string]*network.EndpointSettings)
  183. if container.NetworkSettings != nil {
  184. for name, netw := range container.NetworkSettings.Networks {
  185. if netw == nil || netw.EndpointSettings == nil {
  186. continue
  187. }
  188. networks[name] = &network.EndpointSettings{
  189. EndpointID: netw.EndpointID,
  190. Gateway: netw.Gateway,
  191. IPAddress: netw.IPAddress,
  192. IPPrefixLen: netw.IPPrefixLen,
  193. IPv6Gateway: netw.IPv6Gateway,
  194. GlobalIPv6Address: netw.GlobalIPv6Address,
  195. GlobalIPv6PrefixLen: netw.GlobalIPv6PrefixLen,
  196. MacAddress: netw.MacAddress,
  197. NetworkID: netw.NetworkID,
  198. }
  199. if netw.IPAMConfig != nil {
  200. networks[name].IPAMConfig = &network.EndpointIPAMConfig{
  201. IPv4Address: netw.IPAMConfig.IPv4Address,
  202. IPv6Address: netw.IPAMConfig.IPv6Address,
  203. }
  204. }
  205. }
  206. for port, bindings := range container.NetworkSettings.Ports {
  207. p, err := nat.ParsePort(port.Port())
  208. if err != nil {
  209. logrus.Warnf("invalid port map %+v", err)
  210. continue
  211. }
  212. if len(bindings) == 0 {
  213. snapshot.Ports = append(snapshot.Ports, types.Port{
  214. PrivatePort: uint16(p),
  215. Type: port.Proto(),
  216. })
  217. continue
  218. }
  219. for _, binding := range bindings {
  220. h, err := nat.ParsePort(binding.HostPort)
  221. if err != nil {
  222. logrus.Warnf("invalid host port map %+v", err)
  223. continue
  224. }
  225. snapshot.Ports = append(snapshot.Ports, types.Port{
  226. PrivatePort: uint16(p),
  227. PublicPort: uint16(h),
  228. Type: port.Proto(),
  229. IP: binding.HostIP,
  230. })
  231. }
  232. }
  233. }
  234. snapshot.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
  235. return snapshot
  236. }
  237. // containerByIDIndexer is used to extract the ID field from Container types.
  238. // memdb.StringFieldIndex can not be used since ID is a field from an embedded struct.
  239. type containerByIDIndexer struct{}
  240. // FromObject implements the memdb.SingleIndexer interface for Container objects
  241. func (e *containerByIDIndexer) FromObject(obj interface{}) (bool, []byte, error) {
  242. c, ok := obj.(*Container)
  243. if !ok {
  244. return false, nil, fmt.Errorf("%T is not a Container", obj)
  245. }
  246. // Add the null character as a terminator
  247. v := c.ID + "\x00"
  248. return true, []byte(v), nil
  249. }
  250. // FromArgs implements the memdb.Indexer interface
  251. func (e *containerByIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
  252. if len(args) != 1 {
  253. return nil, fmt.Errorf("must provide only a single argument")
  254. }
  255. arg, ok := args[0].(string)
  256. if !ok {
  257. return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  258. }
  259. // Add the null character as a terminator
  260. arg += "\x00"
  261. return []byte(arg), nil
  262. }