view.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. package container // import "github.com/docker/docker/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. memdb "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. return txn.Insert(memdbNamesTable, nameAssociation{name: name, containerID: containerID})
  171. })
  172. }
  173. // ReleaseName releases the reserved name
  174. // Once released, a name can be reserved again
  175. func (db *memDB) ReleaseName(name string) error {
  176. return db.withTxn(func(txn *memdb.Txn) error {
  177. return txn.Delete(memdbNamesTable, nameAssociation{name: name})
  178. })
  179. }
  180. type memdbView struct {
  181. txn *memdb.Txn
  182. }
  183. // All returns a all items in this snapshot. Returned objects must never be modified.
  184. func (v *memdbView) All() ([]Snapshot, error) {
  185. var all []Snapshot
  186. iter, err := v.txn.Get(memdbContainersTable, memdbIDIndex)
  187. if err != nil {
  188. return nil, err
  189. }
  190. for {
  191. item := iter.Next()
  192. if item == nil {
  193. break
  194. }
  195. snapshot := v.transform(item.(*Container))
  196. all = append(all, *snapshot)
  197. }
  198. return all, nil
  199. }
  200. // Get returns an item by id. Returned objects must never be modified.
  201. func (v *memdbView) Get(id string) (*Snapshot, error) {
  202. s, err := v.txn.First(memdbContainersTable, memdbIDIndex, id)
  203. if err != nil {
  204. return nil, err
  205. }
  206. if s == nil {
  207. return nil, NoSuchContainerError{id: id}
  208. }
  209. return v.transform(s.(*Container)), nil
  210. }
  211. // getNames lists all the reserved names for the given container ID.
  212. func (v *memdbView) getNames(containerID string) []string {
  213. iter, err := v.txn.Get(memdbNamesTable, memdbContainerIDIndex, containerID)
  214. if err != nil {
  215. return nil
  216. }
  217. var names []string
  218. for {
  219. item := iter.Next()
  220. if item == nil {
  221. break
  222. }
  223. names = append(names, item.(nameAssociation).name)
  224. }
  225. return names
  226. }
  227. // GetID returns the container ID that the passed in name is reserved to.
  228. func (v *memdbView) GetID(name string) (string, error) {
  229. s, err := v.txn.First(memdbNamesTable, memdbIDIndex, name)
  230. if err != nil {
  231. return "", err
  232. }
  233. if s == nil {
  234. return "", ErrNameNotReserved
  235. }
  236. return s.(nameAssociation).containerID, nil
  237. }
  238. // GetAllNames returns all registered names.
  239. func (v *memdbView) GetAllNames() map[string][]string {
  240. iter, err := v.txn.Get(memdbNamesTable, memdbContainerIDIndex)
  241. if err != nil {
  242. return nil
  243. }
  244. out := make(map[string][]string)
  245. for {
  246. item := iter.Next()
  247. if item == nil {
  248. break
  249. }
  250. assoc := item.(nameAssociation)
  251. out[assoc.containerID] = append(out[assoc.containerID], assoc.name)
  252. }
  253. return out
  254. }
  255. // transform maps a (deep) copied Container object to what queries need.
  256. // A lock on the Container is not held because these are immutable deep copies.
  257. func (v *memdbView) transform(container *Container) *Snapshot {
  258. health := types.NoHealthcheck
  259. if container.Health != nil {
  260. health = container.Health.Status()
  261. }
  262. snapshot := &Snapshot{
  263. Container: types.Container{
  264. ID: container.ID,
  265. Names: v.getNames(container.ID),
  266. ImageID: container.ImageID.String(),
  267. Ports: []types.Port{},
  268. Mounts: container.GetMountPoints(),
  269. State: container.State.StateString(),
  270. Status: container.State.String(),
  271. Created: container.Created.Unix(),
  272. },
  273. CreatedAt: container.Created,
  274. StartedAt: container.StartedAt,
  275. Name: container.Name,
  276. Pid: container.Pid,
  277. Managed: container.Managed,
  278. ExposedPorts: make(nat.PortSet),
  279. PortBindings: make(nat.PortSet),
  280. Health: health,
  281. Running: container.Running,
  282. Paused: container.Paused,
  283. ExitCode: container.ExitCode(),
  284. }
  285. if snapshot.Names == nil {
  286. // Dead containers will often have no name, so make sure the response isn't null
  287. snapshot.Names = []string{}
  288. }
  289. if container.HostConfig != nil {
  290. snapshot.Container.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
  291. snapshot.HostConfig.Isolation = string(container.HostConfig.Isolation)
  292. for binding := range container.HostConfig.PortBindings {
  293. snapshot.PortBindings[binding] = struct{}{}
  294. }
  295. }
  296. if container.Config != nil {
  297. snapshot.Image = container.Config.Image
  298. snapshot.Labels = container.Config.Labels
  299. for exposed := range container.Config.ExposedPorts {
  300. snapshot.ExposedPorts[exposed] = struct{}{}
  301. }
  302. }
  303. if len(container.Args) > 0 {
  304. var args []string
  305. for _, arg := range container.Args {
  306. if strings.Contains(arg, " ") {
  307. args = append(args, fmt.Sprintf("'%s'", arg))
  308. } else {
  309. args = append(args, arg)
  310. }
  311. }
  312. argsAsString := strings.Join(args, " ")
  313. snapshot.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
  314. } else {
  315. snapshot.Command = container.Path
  316. }
  317. snapshot.Ports = []types.Port{}
  318. networks := make(map[string]*network.EndpointSettings)
  319. if container.NetworkSettings != nil {
  320. for name, netw := range container.NetworkSettings.Networks {
  321. if netw == nil || netw.EndpointSettings == nil {
  322. continue
  323. }
  324. networks[name] = &network.EndpointSettings{
  325. EndpointID: netw.EndpointID,
  326. Gateway: netw.Gateway,
  327. IPAddress: netw.IPAddress,
  328. IPPrefixLen: netw.IPPrefixLen,
  329. IPv6Gateway: netw.IPv6Gateway,
  330. GlobalIPv6Address: netw.GlobalIPv6Address,
  331. GlobalIPv6PrefixLen: netw.GlobalIPv6PrefixLen,
  332. MacAddress: netw.MacAddress,
  333. NetworkID: netw.NetworkID,
  334. }
  335. if netw.IPAMConfig != nil {
  336. networks[name].IPAMConfig = &network.EndpointIPAMConfig{
  337. IPv4Address: netw.IPAMConfig.IPv4Address,
  338. IPv6Address: netw.IPAMConfig.IPv6Address,
  339. }
  340. }
  341. }
  342. for port, bindings := range container.NetworkSettings.Ports {
  343. p, err := nat.ParsePort(port.Port())
  344. if err != nil {
  345. logrus.Warnf("invalid port map %+v", err)
  346. continue
  347. }
  348. if len(bindings) == 0 {
  349. snapshot.Ports = append(snapshot.Ports, types.Port{
  350. PrivatePort: uint16(p),
  351. Type: port.Proto(),
  352. })
  353. continue
  354. }
  355. for _, binding := range bindings {
  356. h, err := nat.ParsePort(binding.HostPort)
  357. if err != nil {
  358. logrus.Warnf("invalid host port map %+v", err)
  359. continue
  360. }
  361. snapshot.Ports = append(snapshot.Ports, types.Port{
  362. PrivatePort: uint16(p),
  363. PublicPort: uint16(h),
  364. Type: port.Proto(),
  365. IP: binding.HostIP,
  366. })
  367. }
  368. }
  369. }
  370. snapshot.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
  371. return snapshot
  372. }
  373. // containerByIDIndexer is used to extract the ID field from Container types.
  374. // memdb.StringFieldIndex can not be used since ID is a field from an embedded struct.
  375. type containerByIDIndexer struct{}
  376. // FromObject implements the memdb.SingleIndexer interface for Container objects
  377. func (e *containerByIDIndexer) FromObject(obj interface{}) (bool, []byte, error) {
  378. c, ok := obj.(*Container)
  379. if !ok {
  380. return false, nil, fmt.Errorf("%T is not a Container", obj)
  381. }
  382. // Add the null character as a terminator
  383. v := c.ID + "\x00"
  384. return true, []byte(v), nil
  385. }
  386. // FromArgs implements the memdb.Indexer interface
  387. func (e *containerByIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
  388. if len(args) != 1 {
  389. return nil, fmt.Errorf("must provide only a single argument")
  390. }
  391. arg, ok := args[0].(string)
  392. if !ok {
  393. return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  394. }
  395. // Add the null character as a terminator
  396. arg += "\x00"
  397. return []byte(arg), nil
  398. }
  399. // namesByNameIndexer is used to index container name associations by name.
  400. type namesByNameIndexer struct{}
  401. func (e *namesByNameIndexer) FromObject(obj interface{}) (bool, []byte, error) {
  402. n, ok := obj.(nameAssociation)
  403. if !ok {
  404. return false, nil, fmt.Errorf(`%T does not have type "nameAssociation"`, obj)
  405. }
  406. // Add the null character as a terminator
  407. return true, []byte(n.name + "\x00"), nil
  408. }
  409. func (e *namesByNameIndexer) FromArgs(args ...interface{}) ([]byte, error) {
  410. if len(args) != 1 {
  411. return nil, fmt.Errorf("must provide only a single argument")
  412. }
  413. arg, ok := args[0].(string)
  414. if !ok {
  415. return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  416. }
  417. // Add the null character as a terminator
  418. arg += "\x00"
  419. return []byte(arg), nil
  420. }
  421. // namesByContainerIDIndexer is used to index container names by container ID.
  422. type namesByContainerIDIndexer struct{}
  423. func (e *namesByContainerIDIndexer) FromObject(obj interface{}) (bool, []byte, error) {
  424. n, ok := obj.(nameAssociation)
  425. if !ok {
  426. return false, nil, fmt.Errorf(`%T does not have type "nameAssociation"`, obj)
  427. }
  428. // Add the null character as a terminator
  429. return true, []byte(n.containerID + "\x00"), nil
  430. }
  431. func (e *namesByContainerIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
  432. if len(args) != 1 {
  433. return nil, fmt.Errorf("must provide only a single argument")
  434. }
  435. arg, ok := args[0].(string)
  436. if !ok {
  437. return nil, fmt.Errorf("argument must be a string: %#v", args[0])
  438. }
  439. // Add the null character as a terminator
  440. arg += "\x00"
  441. return []byte(arg), nil
  442. }