view.go 14 KB

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