view.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. package container
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "time"
  7. "github.com/docker/docker/api/types"
  8. "github.com/docker/docker/api/types/network"
  9. "github.com/docker/go-connections/nat"
  10. "github.com/hashicorp/go-memdb"
  11. "github.com/sirupsen/logrus"
  12. )
  13. const (
  14. memdbContainersTable = "containers"
  15. memdbNamesTable = "names"
  16. memdbIDIndex = "id"
  17. memdbContainerIDIndex = "containerid"
  18. )
  19. var (
  20. // ErrNameReserved is an error which is returned when a name is requested to be reserved that already is reserved
  21. ErrNameReserved = errors.New("name is reserved")
  22. // ErrNameNotReserved is an error which is returned when trying to find a name that is not reserved
  23. ErrNameNotReserved = errors.New("name is not reserved")
  24. )
  25. // Snapshot is a read only view for Containers. It holds all information necessary to serve container queries in a
  26. // versioned ACID in-memory store.
  27. type Snapshot struct {
  28. types.Container
  29. // additional info queries need to filter on
  30. // preserve nanosec resolution for queries
  31. CreatedAt time.Time
  32. StartedAt time.Time
  33. Name string
  34. Pid int
  35. ExitCode int
  36. Running bool
  37. Paused bool
  38. Managed bool
  39. ExposedPorts nat.PortSet
  40. PortBindings nat.PortSet
  41. Health string
  42. HostConfig struct {
  43. Isolation string
  44. }
  45. }
  46. // nameAssociation associates a container id with a name.
  47. type nameAssociation struct {
  48. // name is the name to associate. Note that name is the primary key
  49. // ("id" in memdb).
  50. name string
  51. containerID string
  52. }
  53. // ViewDB provides an in-memory transactional (ACID) container Store
  54. type ViewDB interface {
  55. Snapshot() View
  56. Save(*Container) error
  57. Delete(*Container) error
  58. ReserveName(name, containerID string) error
  59. ReleaseName(name string) error
  60. }
  61. // View can be used by readers to avoid locking
  62. type View interface {
  63. All() ([]Snapshot, error)
  64. Get(id string) (*Snapshot, error)
  65. GetID(name string) (string, error)
  66. GetAllNames() map[string][]string
  67. }
  68. var schema = &memdb.DBSchema{
  69. Tables: map[string]*memdb.TableSchema{
  70. memdbContainersTable: {
  71. Name: memdbContainersTable,
  72. Indexes: map[string]*memdb.IndexSchema{
  73. memdbIDIndex: {
  74. Name: memdbIDIndex,
  75. Unique: true,
  76. Indexer: &containerByIDIndexer{},
  77. },
  78. },
  79. },
  80. memdbNamesTable: {
  81. Name: memdbNamesTable,
  82. Indexes: map[string]*memdb.IndexSchema{
  83. // Used for names, because "id" is the primary key in memdb.
  84. memdbIDIndex: {
  85. Name: memdbIDIndex,
  86. Unique: true,
  87. Indexer: &namesByNameIndexer{},
  88. },
  89. memdbContainerIDIndex: {
  90. Name: memdbContainerIDIndex,
  91. Indexer: &namesByContainerIDIndexer{},
  92. },
  93. },
  94. },
  95. },
  96. }
  97. type memDB struct {
  98. store *memdb.MemDB
  99. }
  100. // NoSuchContainerError indicates that the container wasn't found in the
  101. // database.
  102. type NoSuchContainerError struct {
  103. id string
  104. }
  105. // Error satisfies the error interface.
  106. func (e NoSuchContainerError) Error() string {
  107. return "no such container " + e.id
  108. }
  109. // NewViewDB provides the default implementation, with the default schema
  110. func NewViewDB() (ViewDB, error) {
  111. store, err := memdb.NewMemDB(schema)
  112. if err != nil {
  113. return nil, err
  114. }
  115. return &memDB{store: store}, nil
  116. }
  117. // Snapshot provides a consistent read-only View of the database
  118. func (db *memDB) Snapshot() View {
  119. return &memdbView{
  120. txn: db.store.Txn(false),
  121. }
  122. }
  123. func (db *memDB) withTxn(cb func(*memdb.Txn) error) error {
  124. txn := db.store.Txn(true)
  125. err := cb(txn)
  126. if err != nil {
  127. txn.Abort()
  128. return err
  129. }
  130. txn.Commit()
  131. return nil
  132. }
  133. // Save atomically updates the in-memory store state for a Container.
  134. // Only read only (deep) copies of containers may be passed in.
  135. func (db *memDB) Save(c *Container) error {
  136. return db.withTxn(func(txn *memdb.Txn) error {
  137. return txn.Insert(memdbContainersTable, c)
  138. })
  139. }
  140. // Delete removes an item by ID
  141. func (db *memDB) Delete(c *Container) error {
  142. return db.withTxn(func(txn *memdb.Txn) error {
  143. view := &memdbView{txn: txn}
  144. names := view.getNames(c.ID)
  145. for _, name := range names {
  146. txn.Delete(memdbNamesTable, nameAssociation{name: name})
  147. }
  148. // Ignore error - the container may not actually exist in the
  149. // db, but we still need to clean up associated names.
  150. txn.Delete(memdbContainersTable, NewBaseContainer(c.ID, c.Root))
  151. return nil
  152. })
  153. }
  154. // ReserveName registers a container ID to a name
  155. // ReserveName is idempotent
  156. // Attempting to reserve a container ID to a name that already exists results in an `ErrNameReserved`
  157. // A name reservation is globally unique
  158. func (db *memDB) ReserveName(name, containerID string) error {
  159. return db.withTxn(func(txn *memdb.Txn) error {
  160. s, err := txn.First(memdbNamesTable, memdbIDIndex, name)
  161. if err != nil {
  162. return err
  163. }
  164. if s != nil {
  165. if s.(nameAssociation).containerID != containerID {
  166. return ErrNameReserved
  167. }
  168. return nil
  169. }
  170. if err := txn.Insert(memdbNamesTable, nameAssociation{name: name, containerID: containerID}); err != nil {
  171. return err
  172. }
  173. return nil
  174. })
  175. }
  176. // ReleaseName releases the reserved name
  177. // Once released, a name can be reserved again
  178. func (db *memDB) ReleaseName(name string) error {
  179. return db.withTxn(func(txn *memdb.Txn) error {
  180. return txn.Delete(memdbNamesTable, nameAssociation{name: name})
  181. })
  182. }
  183. type memdbView struct {
  184. txn *memdb.Txn
  185. }
  186. // All returns a all items in this snapshot. Returned objects must never be modified.
  187. func (v *memdbView) All() ([]Snapshot, error) {
  188. var all []Snapshot
  189. iter, err := v.txn.Get(memdbContainersTable, memdbIDIndex)
  190. if err != nil {
  191. return nil, err
  192. }
  193. for {
  194. item := iter.Next()
  195. if item == nil {
  196. break
  197. }
  198. snapshot := v.transform(item.(*Container))
  199. all = append(all, *snapshot)
  200. }
  201. return all, nil
  202. }
  203. // Get returns an item by id. Returned objects must never be modified.
  204. func (v *memdbView) Get(id string) (*Snapshot, error) {
  205. s, err := v.txn.First(memdbContainersTable, memdbIDIndex, id)
  206. if err != nil {
  207. return nil, err
  208. }
  209. if s == nil {
  210. return nil, NoSuchContainerError{id: id}
  211. }
  212. return v.transform(s.(*Container)), nil
  213. }
  214. // getNames lists all the reserved names for the given container ID.
  215. func (v *memdbView) getNames(containerID string) []string {
  216. iter, err := v.txn.Get(memdbNamesTable, memdbContainerIDIndex, containerID)
  217. if err != nil {
  218. return nil
  219. }
  220. var names []string
  221. for {
  222. item := iter.Next()
  223. if item == nil {
  224. break
  225. }
  226. names = append(names, item.(nameAssociation).name)
  227. }
  228. return names
  229. }
  230. // GetID returns the container ID that the passed in name is reserved to.
  231. func (v *memdbView) GetID(name string) (string, error) {
  232. s, err := v.txn.First(memdbNamesTable, memdbIDIndex, name)
  233. if err != nil {
  234. return "", err
  235. }
  236. if s == nil {
  237. return "", ErrNameNotReserved
  238. }
  239. return s.(nameAssociation).containerID, nil
  240. }
  241. // GetAllNames returns all registered names.
  242. func (v *memdbView) GetAllNames() map[string][]string {
  243. iter, err := v.txn.Get(memdbNamesTable, memdbContainerIDIndex)
  244. if err != nil {
  245. return nil
  246. }
  247. out := make(map[string][]string)
  248. for {
  249. item := iter.Next()
  250. if item == nil {
  251. break
  252. }
  253. assoc := item.(nameAssociation)
  254. out[assoc.containerID] = append(out[assoc.containerID], assoc.name)
  255. }
  256. return out
  257. }
  258. // transform maps a (deep) copied Container object to what queries need.
  259. // A lock on the Container is not held because these are immutable deep copies.
  260. func (v *memdbView) transform(container *Container) *Snapshot {
  261. snapshot := &Snapshot{
  262. Container: types.Container{
  263. ID: container.ID,
  264. Names: v.getNames(container.ID),
  265. ImageID: container.ImageID.String(),
  266. Ports: []types.Port{},
  267. Mounts: container.GetMountPoints(),
  268. State: container.State.StateString(),
  269. Status: container.State.String(),
  270. Created: container.Created.Unix(),
  271. },
  272. CreatedAt: container.Created,
  273. StartedAt: container.StartedAt,
  274. Name: container.Name,
  275. Pid: container.Pid,
  276. Managed: container.Managed,
  277. ExposedPorts: make(nat.PortSet),
  278. PortBindings: make(nat.PortSet),
  279. Health: container.HealthString(),
  280. Running: container.Running,
  281. Paused: container.Paused,
  282. ExitCode: container.ExitCode(),
  283. }
  284. if snapshot.Names == nil {
  285. // Dead containers will often have no name, so make sure the response isn't null
  286. snapshot.Names = []string{}
  287. }
  288. if container.HostConfig != nil {
  289. snapshot.Container.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
  290. snapshot.HostConfig.Isolation = string(container.HostConfig.Isolation)
  291. for binding := range container.HostConfig.PortBindings {
  292. snapshot.PortBindings[binding] = struct{}{}
  293. }
  294. }
  295. if container.Config != nil {
  296. snapshot.Image = container.Config.Image
  297. snapshot.Labels = container.Config.Labels
  298. for exposed := range container.Config.ExposedPorts {
  299. snapshot.ExposedPorts[exposed] = struct{}{}
  300. }
  301. }
  302. if len(container.Args) > 0 {
  303. args := []string{}
  304. for _, arg := range container.Args {
  305. if strings.Contains(arg, " ") {
  306. args = append(args, fmt.Sprintf("'%s'", arg))
  307. } else {
  308. args = append(args, arg)
  309. }
  310. }
  311. argsAsString := strings.Join(args, " ")
  312. snapshot.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
  313. } else {
  314. snapshot.Command = container.Path
  315. }
  316. snapshot.Ports = []types.Port{}
  317. networks := make(map[string]*network.EndpointSettings)
  318. if container.NetworkSettings != nil {
  319. for name, netw := range container.NetworkSettings.Networks {
  320. if netw == nil || netw.EndpointSettings == nil {
  321. continue
  322. }
  323. networks[name] = &network.EndpointSettings{
  324. EndpointID: netw.EndpointID,
  325. Gateway: netw.Gateway,
  326. IPAddress: netw.IPAddress,
  327. IPPrefixLen: netw.IPPrefixLen,
  328. IPv6Gateway: netw.IPv6Gateway,
  329. GlobalIPv6Address: netw.GlobalIPv6Address,
  330. GlobalIPv6PrefixLen: netw.GlobalIPv6PrefixLen,
  331. MacAddress: netw.MacAddress,
  332. NetworkID: netw.NetworkID,
  333. }
  334. if netw.IPAMConfig != nil {
  335. networks[name].IPAMConfig = &network.EndpointIPAMConfig{
  336. IPv4Address: netw.IPAMConfig.IPv4Address,
  337. IPv6Address: netw.IPAMConfig.IPv6Address,
  338. }
  339. }
  340. }
  341. for port, bindings := range container.NetworkSettings.Ports {
  342. p, err := nat.ParsePort(port.Port())
  343. if err != nil {
  344. logrus.Warnf("invalid port map %+v", err)
  345. continue
  346. }
  347. if len(bindings) == 0 {
  348. snapshot.Ports = append(snapshot.Ports, types.Port{
  349. PrivatePort: uint16(p),
  350. Type: port.Proto(),
  351. })
  352. continue
  353. }
  354. for _, binding := range bindings {
  355. h, err := nat.ParsePort(binding.HostPort)
  356. if err != nil {
  357. logrus.Warnf("invalid host port map %+v", err)
  358. continue
  359. }
  360. snapshot.Ports = append(snapshot.Ports, types.Port{
  361. PrivatePort: uint16(p),
  362. PublicPort: uint16(h),
  363. Type: port.Proto(),
  364. IP: binding.HostIP,
  365. })
  366. }
  367. }
  368. }
  369. snapshot.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
  370. return snapshot
  371. }
  372. // containerByIDIndexer is used to extract the ID field from Container types.
  373. // memdb.StringFieldIndex can not be used since ID is a field from an embedded struct.
  374. type containerByIDIndexer struct{}
  375. // FromObject implements the memdb.SingleIndexer interface for Container objects
  376. func (e *containerByIDIndexer) FromObject(obj interface{}) (bool, []byte, error) {
  377. c, ok := obj.(*Container)
  378. if !ok {
  379. return false, nil, fmt.Errorf("%T is not a Container", obj)
  380. }
  381. // Add the null character as a terminator
  382. v := c.ID + "\x00"
  383. return true, []byte(v), nil
  384. }
  385. // FromArgs implements the memdb.Indexer interface
  386. func (e *containerByIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
  387. if len(args) != 1 {
  388. return nil, fmt.Errorf("must provide only a single argument")
  389. }
  390. arg, ok := args[0].(string)
  391. if !ok {
  392. return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  393. }
  394. // Add the null character as a terminator
  395. arg += "\x00"
  396. return []byte(arg), nil
  397. }
  398. // namesByNameIndexer is used to index container name associations by name.
  399. type namesByNameIndexer struct{}
  400. func (e *namesByNameIndexer) FromObject(obj interface{}) (bool, []byte, error) {
  401. n, ok := obj.(nameAssociation)
  402. if !ok {
  403. return false, nil, fmt.Errorf(`%T does not have type "nameAssociation"`, obj)
  404. }
  405. // Add the null character as a terminator
  406. return true, []byte(n.name + "\x00"), nil
  407. }
  408. func (e *namesByNameIndexer) FromArgs(args ...interface{}) ([]byte, error) {
  409. if len(args) != 1 {
  410. return nil, fmt.Errorf("must provide only a single argument")
  411. }
  412. arg, ok := args[0].(string)
  413. if !ok {
  414. return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  415. }
  416. // Add the null character as a terminator
  417. arg += "\x00"
  418. return []byte(arg), nil
  419. }
  420. // namesByContainerIDIndexer is used to index container names by container ID.
  421. type namesByContainerIDIndexer struct{}
  422. func (e *namesByContainerIDIndexer) FromObject(obj interface{}) (bool, []byte, error) {
  423. n, ok := obj.(nameAssociation)
  424. if !ok {
  425. return false, nil, fmt.Errorf(`%T does not have type "nameAssocation"`, obj)
  426. }
  427. // Add the null character as a terminator
  428. return true, []byte(n.containerID + "\x00"), nil
  429. }
  430. func (e *namesByContainerIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
  431. if len(args) != 1 {
  432. return nil, fmt.Errorf("must provide only a single argument")
  433. }
  434. arg, ok := args[0].(string)
  435. if !ok {
  436. return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  437. }
  438. // Add the null character as a terminator
  439. arg += "\x00"
  440. return []byte(arg), nil
  441. }