view.go 13 KB

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