github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/container/view.go (about)

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