123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- package container
- import (
- "fmt"
- "strings"
- "time"
- "github.com/Sirupsen/logrus"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/api/types/network"
- "github.com/docker/docker/pkg/registrar"
- "github.com/docker/go-connections/nat"
- "github.com/hashicorp/go-memdb"
- )
- const (
- memdbTable = "containers"
- memdbIDIndex = "id"
- )
- // Snapshot is a read only view for Containers. It holds all information necessary to serve container queries in a
- // versioned ACID in-memory store.
- type Snapshot struct {
- types.Container
- // additional info queries need to filter on
- // preserve nanosec resolution for queries
- CreatedAt time.Time
- StartedAt time.Time
- Name string
- Pid int
- ExitCode int
- Running bool
- Paused bool
- Managed bool
- ExposedPorts nat.PortSet
- PortBindings nat.PortSet
- Health string
- HostConfig struct {
- Isolation string
- }
- }
- // ViewDB provides an in-memory transactional (ACID) container Store
- type ViewDB interface {
- Snapshot(nameIndex *registrar.Registrar) View
- Save(*Container) error
- Delete(*Container) error
- }
- // View can be used by readers to avoid locking
- type View interface {
- All() ([]Snapshot, error)
- Get(id string) (*Snapshot, error)
- }
- var schema = &memdb.DBSchema{
- Tables: map[string]*memdb.TableSchema{
- memdbTable: {
- Name: memdbTable,
- Indexes: map[string]*memdb.IndexSchema{
- memdbIDIndex: {
- Name: memdbIDIndex,
- Unique: true,
- Indexer: &containerByIDIndexer{},
- },
- },
- },
- },
- }
- type memDB struct {
- store *memdb.MemDB
- }
- // NewViewDB provides the default implementation, with the default schema
- func NewViewDB() (ViewDB, error) {
- store, err := memdb.NewMemDB(schema)
- if err != nil {
- return nil, err
- }
- return &memDB{store: store}, nil
- }
- // Snapshot provides a consistent read-only View of the database
- func (db *memDB) Snapshot(index *registrar.Registrar) View {
- return &memdbView{
- txn: db.store.Txn(false),
- nameIndex: index.GetAll(),
- }
- }
- // Save atomically updates the in-memory store state for a Container.
- // Only read only (deep) copies of containers may be passed in.
- func (db *memDB) Save(c *Container) error {
- txn := db.store.Txn(true)
- defer txn.Commit()
- return txn.Insert(memdbTable, c)
- }
- // Delete removes an item by ID
- func (db *memDB) Delete(c *Container) error {
- txn := db.store.Txn(true)
- defer txn.Commit()
- return txn.Delete(memdbTable, NewBaseContainer(c.ID, c.Root))
- }
- type memdbView struct {
- txn *memdb.Txn
- nameIndex map[string][]string
- }
- // All returns a all items in this snapshot. Returned objects must never be modified.
- func (v *memdbView) All() ([]Snapshot, error) {
- var all []Snapshot
- iter, err := v.txn.Get(memdbTable, memdbIDIndex)
- if err != nil {
- return nil, err
- }
- for {
- item := iter.Next()
- if item == nil {
- break
- }
- snapshot := v.transform(item.(*Container))
- all = append(all, *snapshot)
- }
- return all, nil
- }
- // Get returns an item by id. Returned objects must never be modified.
- func (v *memdbView) Get(id string) (*Snapshot, error) {
- s, err := v.txn.First(memdbTable, memdbIDIndex, id)
- if err != nil {
- return nil, err
- }
- return v.transform(s.(*Container)), nil
- }
- // transform maps a (deep) copied Container object to what queries need.
- // A lock on the Container is not held because these are immutable deep copies.
- func (v *memdbView) transform(container *Container) *Snapshot {
- snapshot := &Snapshot{
- Container: types.Container{
- ID: container.ID,
- Names: v.nameIndex[container.ID],
- ImageID: container.ImageID.String(),
- Ports: []types.Port{},
- Mounts: container.GetMountPoints(),
- State: container.State.StateString(),
- Status: container.State.String(),
- Created: container.Created.Unix(),
- },
- CreatedAt: container.Created,
- StartedAt: container.StartedAt,
- Name: container.Name,
- Pid: container.Pid,
- Managed: container.Managed,
- ExposedPorts: make(nat.PortSet),
- PortBindings: make(nat.PortSet),
- Health: container.HealthString(),
- Running: container.Running,
- Paused: container.Paused,
- ExitCode: container.ExitCode(),
- }
- if snapshot.Names == nil {
- // Dead containers will often have no name, so make sure the response isn't null
- snapshot.Names = []string{}
- }
- if container.HostConfig != nil {
- snapshot.Container.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
- snapshot.HostConfig.Isolation = string(container.HostConfig.Isolation)
- for binding := range container.HostConfig.PortBindings {
- snapshot.PortBindings[binding] = struct{}{}
- }
- }
- if container.Config != nil {
- snapshot.Image = container.Config.Image
- snapshot.Labels = container.Config.Labels
- for exposed := range container.Config.ExposedPorts {
- snapshot.ExposedPorts[exposed] = struct{}{}
- }
- }
- if len(container.Args) > 0 {
- args := []string{}
- for _, arg := range container.Args {
- if strings.Contains(arg, " ") {
- args = append(args, fmt.Sprintf("'%s'", arg))
- } else {
- args = append(args, arg)
- }
- }
- argsAsString := strings.Join(args, " ")
- snapshot.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
- } else {
- snapshot.Command = container.Path
- }
- snapshot.Ports = []types.Port{}
- networks := make(map[string]*network.EndpointSettings)
- if container.NetworkSettings != nil {
- for name, netw := range container.NetworkSettings.Networks {
- if netw == nil || netw.EndpointSettings == nil {
- continue
- }
- networks[name] = &network.EndpointSettings{
- EndpointID: netw.EndpointID,
- Gateway: netw.Gateway,
- IPAddress: netw.IPAddress,
- IPPrefixLen: netw.IPPrefixLen,
- IPv6Gateway: netw.IPv6Gateway,
- GlobalIPv6Address: netw.GlobalIPv6Address,
- GlobalIPv6PrefixLen: netw.GlobalIPv6PrefixLen,
- MacAddress: netw.MacAddress,
- NetworkID: netw.NetworkID,
- }
- if netw.IPAMConfig != nil {
- networks[name].IPAMConfig = &network.EndpointIPAMConfig{
- IPv4Address: netw.IPAMConfig.IPv4Address,
- IPv6Address: netw.IPAMConfig.IPv6Address,
- }
- }
- }
- for port, bindings := range container.NetworkSettings.Ports {
- p, err := nat.ParsePort(port.Port())
- if err != nil {
- logrus.Warnf("invalid port map %+v", err)
- continue
- }
- if len(bindings) == 0 {
- snapshot.Ports = append(snapshot.Ports, types.Port{
- PrivatePort: uint16(p),
- Type: port.Proto(),
- })
- continue
- }
- for _, binding := range bindings {
- h, err := nat.ParsePort(binding.HostPort)
- if err != nil {
- logrus.Warnf("invalid host port map %+v", err)
- continue
- }
- snapshot.Ports = append(snapshot.Ports, types.Port{
- PrivatePort: uint16(p),
- PublicPort: uint16(h),
- Type: port.Proto(),
- IP: binding.HostIP,
- })
- }
- }
- }
- snapshot.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
- return snapshot
- }
- // containerByIDIndexer is used to extract the ID field from Container types.
- // memdb.StringFieldIndex can not be used since ID is a field from an embedded struct.
- type containerByIDIndexer struct{}
- // FromObject implements the memdb.SingleIndexer interface for Container objects
- func (e *containerByIDIndexer) FromObject(obj interface{}) (bool, []byte, error) {
- c, ok := obj.(*Container)
- if !ok {
- return false, nil, fmt.Errorf("%T is not a Container", obj)
- }
- // Add the null character as a terminator
- v := c.ID + "\x00"
- return true, []byte(v), nil
- }
- // FromArgs implements the memdb.Indexer interface
- func (e *containerByIDIndexer) FromArgs(args ...interface{}) ([]byte, error) {
- if len(args) != 1 {
- return nil, fmt.Errorf("must provide only a single argument")
- }
- arg, ok := args[0].(string)
- if !ok {
- return nil, fmt.Errorf("argument must be a string: %#v", args[0])
- }
- // Add the null character as a terminator
- arg += "\x00"
- return []byte(arg), nil
- }
|