github.com/cdoern/storage@v1.12.13/containers.go (about)

     1  package storage
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"time"
    10  
    11  	"github.com/containers/storage/pkg/idtools"
    12  	"github.com/containers/storage/pkg/ioutils"
    13  	"github.com/containers/storage/pkg/stringid"
    14  	"github.com/containers/storage/pkg/truncindex"
    15  	digest "github.com/opencontainers/go-digest"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // A Container is a reference to a read-write layer with metadata.
    20  type Container struct {
    21  	// ID is either one which was specified at create-time, or a random
    22  	// value which was generated by the library.
    23  	ID string `json:"id"`
    24  
    25  	// Names is an optional set of user-defined convenience values.  The
    26  	// container can be referred to by its ID or any of its names.  Names
    27  	// are unique among containers.
    28  	Names []string `json:"names,omitempty"`
    29  
    30  	// ImageID is the ID of the image which was used to create the container.
    31  	ImageID string `json:"image"`
    32  
    33  	// LayerID is the ID of the read-write layer for the container itself.
    34  	// It is assumed that the image's top layer is the parent of the container's
    35  	// read-write layer.
    36  	LayerID string `json:"layer"`
    37  
    38  	// Metadata is data we keep for the convenience of the caller.  It is not
    39  	// expected to be large, since it is kept in memory.
    40  	Metadata string `json:"metadata,omitempty"`
    41  
    42  	// BigDataNames is a list of names of data items that we keep for the
    43  	// convenience of the caller.  They can be large, and are only in
    44  	// memory when being read from or written to disk.
    45  	BigDataNames []string `json:"big-data-names,omitempty"`
    46  
    47  	// BigDataSizes maps the names in BigDataNames to the sizes of the data
    48  	// that has been stored, if they're known.
    49  	BigDataSizes map[string]int64 `json:"big-data-sizes,omitempty"`
    50  
    51  	// BigDataDigests maps the names in BigDataNames to the digests of the
    52  	// data that has been stored, if they're known.
    53  	BigDataDigests map[string]digest.Digest `json:"big-data-digests,omitempty"`
    54  
    55  	// Created is the datestamp for when this container was created.  Older
    56  	// versions of the library did not track this information, so callers
    57  	// will likely want to use the IsZero() method to verify that a value
    58  	// is set before using it.
    59  	Created time.Time `json:"created,omitempty"`
    60  
    61  	// UIDMap and GIDMap are used for setting up a container's root
    62  	// filesystem for use inside of a user namespace where UID mapping is
    63  	// being used.
    64  	UIDMap []idtools.IDMap `json:"uidmap,omitempty"`
    65  	GIDMap []idtools.IDMap `json:"gidmap,omitempty"`
    66  
    67  	Flags map[string]interface{} `json:"flags,omitempty"`
    68  }
    69  
    70  // ContainerStore provides bookkeeping for information about Containers.
    71  type ContainerStore interface {
    72  	FileBasedStore
    73  	MetadataStore
    74  	ContainerBigDataStore
    75  	FlaggableStore
    76  
    77  	// Create creates a container that has a specified ID (or generates a
    78  	// random one if an empty value is supplied) and optional names,
    79  	// based on the specified image, using the specified layer as its
    80  	// read-write layer.
    81  	// The maps in the container's options structure are recorded for the
    82  	// convenience of the caller, nothing more.
    83  	Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (*Container, error)
    84  
    85  	// SetNames updates the list of names associated with the container
    86  	// with the specified ID.
    87  	SetNames(id string, names []string) error
    88  
    89  	// Get retrieves information about a container given an ID or name.
    90  	Get(id string) (*Container, error)
    91  
    92  	// Exists checks if there is a container with the given ID or name.
    93  	Exists(id string) bool
    94  
    95  	// Delete removes the record of the container.
    96  	Delete(id string) error
    97  
    98  	// Wipe removes records of all containers.
    99  	Wipe() error
   100  
   101  	// Lookup attempts to translate a name to an ID.  Most methods do this
   102  	// implicitly.
   103  	Lookup(name string) (string, error)
   104  
   105  	// Containers returns a slice enumerating the known containers.
   106  	Containers() ([]Container, error)
   107  }
   108  
   109  type containerStore struct {
   110  	lockfile   Locker
   111  	dir        string
   112  	containers []*Container
   113  	idindex    *truncindex.TruncIndex
   114  	byid       map[string]*Container
   115  	bylayer    map[string]*Container
   116  	byname     map[string]*Container
   117  }
   118  
   119  func copyContainer(c *Container) *Container {
   120  	return &Container{
   121  		ID:             c.ID,
   122  		Names:          copyStringSlice(c.Names),
   123  		ImageID:        c.ImageID,
   124  		LayerID:        c.LayerID,
   125  		Metadata:       c.Metadata,
   126  		BigDataNames:   copyStringSlice(c.BigDataNames),
   127  		BigDataSizes:   copyStringInt64Map(c.BigDataSizes),
   128  		BigDataDigests: copyStringDigestMap(c.BigDataDigests),
   129  		Created:        c.Created,
   130  		UIDMap:         copyIDMap(c.UIDMap),
   131  		GIDMap:         copyIDMap(c.GIDMap),
   132  		Flags:          copyStringInterfaceMap(c.Flags),
   133  	}
   134  }
   135  
   136  func (c *Container) MountLabel() string {
   137  	if label, ok := c.Flags["MountLabel"].(string); ok {
   138  		return label
   139  	}
   140  	return ""
   141  }
   142  
   143  func (c *Container) ProcessLabel() string {
   144  	if label, ok := c.Flags["ProcessLabel"].(string); ok {
   145  		return label
   146  	}
   147  	return ""
   148  }
   149  
   150  func (c *Container) MountOpts() []string {
   151  	if mountOpts, ok := c.Flags["MountOpts"].([]string); ok {
   152  		return mountOpts
   153  	}
   154  	return nil
   155  }
   156  
   157  func (r *containerStore) Containers() ([]Container, error) {
   158  	containers := make([]Container, len(r.containers))
   159  	for i := range r.containers {
   160  		containers[i] = *copyContainer(r.containers[i])
   161  	}
   162  	return containers, nil
   163  }
   164  
   165  func (r *containerStore) containerspath() string {
   166  	return filepath.Join(r.dir, "containers.json")
   167  }
   168  
   169  func (r *containerStore) datadir(id string) string {
   170  	return filepath.Join(r.dir, id)
   171  }
   172  
   173  func (r *containerStore) datapath(id, key string) string {
   174  	return filepath.Join(r.datadir(id), makeBigDataBaseName(key))
   175  }
   176  
   177  func (r *containerStore) Load() error {
   178  	needSave := false
   179  	rpath := r.containerspath()
   180  	data, err := ioutil.ReadFile(rpath)
   181  	if err != nil && !os.IsNotExist(err) {
   182  		return err
   183  	}
   184  	containers := []*Container{}
   185  	layers := make(map[string]*Container)
   186  	idlist := []string{}
   187  	ids := make(map[string]*Container)
   188  	names := make(map[string]*Container)
   189  	if err = json.Unmarshal(data, &containers); len(data) == 0 || err == nil {
   190  		idlist = make([]string, 0, len(containers))
   191  		for n, container := range containers {
   192  			idlist = append(idlist, container.ID)
   193  			ids[container.ID] = containers[n]
   194  			layers[container.LayerID] = containers[n]
   195  			for _, name := range container.Names {
   196  				if conflict, ok := names[name]; ok {
   197  					r.removeName(conflict, name)
   198  					needSave = true
   199  				}
   200  				names[name] = containers[n]
   201  			}
   202  		}
   203  	}
   204  	r.containers = containers
   205  	r.idindex = truncindex.NewTruncIndex(idlist)
   206  	r.byid = ids
   207  	r.bylayer = layers
   208  	r.byname = names
   209  	if needSave {
   210  		return r.Save()
   211  	}
   212  	return nil
   213  }
   214  
   215  func (r *containerStore) Save() error {
   216  	if !r.Locked() {
   217  		return errors.New("container store is not locked")
   218  	}
   219  	rpath := r.containerspath()
   220  	if err := os.MkdirAll(filepath.Dir(rpath), 0700); err != nil {
   221  		return err
   222  	}
   223  	jdata, err := json.Marshal(&r.containers)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	defer r.Touch()
   228  	return ioutils.AtomicWriteFile(rpath, jdata, 0600)
   229  }
   230  
   231  func newContainerStore(dir string) (ContainerStore, error) {
   232  	if err := os.MkdirAll(dir, 0700); err != nil {
   233  		return nil, err
   234  	}
   235  	lockfile, err := GetLockfile(filepath.Join(dir, "containers.lock"))
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	lockfile.Lock()
   240  	defer lockfile.Unlock()
   241  	cstore := containerStore{
   242  		lockfile:   lockfile,
   243  		dir:        dir,
   244  		containers: []*Container{},
   245  		byid:       make(map[string]*Container),
   246  		bylayer:    make(map[string]*Container),
   247  		byname:     make(map[string]*Container),
   248  	}
   249  	if err := cstore.Load(); err != nil {
   250  		return nil, err
   251  	}
   252  	return &cstore, nil
   253  }
   254  
   255  func (r *containerStore) lookup(id string) (*Container, bool) {
   256  	if container, ok := r.byid[id]; ok {
   257  		return container, ok
   258  	} else if container, ok := r.byname[id]; ok {
   259  		return container, ok
   260  	} else if container, ok := r.bylayer[id]; ok {
   261  		return container, ok
   262  	} else if longid, err := r.idindex.Get(id); err == nil {
   263  		if container, ok := r.byid[longid]; ok {
   264  			return container, ok
   265  		}
   266  	}
   267  	return nil, false
   268  }
   269  
   270  func (r *containerStore) ClearFlag(id string, flag string) error {
   271  	container, ok := r.lookup(id)
   272  	if !ok {
   273  		return ErrContainerUnknown
   274  	}
   275  	delete(container.Flags, flag)
   276  	return r.Save()
   277  }
   278  
   279  func (r *containerStore) SetFlag(id string, flag string, value interface{}) error {
   280  	container, ok := r.lookup(id)
   281  	if !ok {
   282  		return ErrContainerUnknown
   283  	}
   284  	if container.Flags == nil {
   285  		container.Flags = make(map[string]interface{})
   286  	}
   287  	container.Flags[flag] = value
   288  	return r.Save()
   289  }
   290  
   291  func (r *containerStore) Create(id string, names []string, image, layer, metadata string, options *ContainerOptions) (container *Container, err error) {
   292  	if id == "" {
   293  		id = stringid.GenerateRandomID()
   294  		_, idInUse := r.byid[id]
   295  		for idInUse {
   296  			id = stringid.GenerateRandomID()
   297  			_, idInUse = r.byid[id]
   298  		}
   299  	}
   300  	if _, idInUse := r.byid[id]; idInUse {
   301  		return nil, ErrDuplicateID
   302  	}
   303  	if options.MountOpts != nil {
   304  		options.Flags["MountOpts"] = append([]string{}, options.MountOpts...)
   305  	}
   306  	names = dedupeNames(names)
   307  	for _, name := range names {
   308  		if _, nameInUse := r.byname[name]; nameInUse {
   309  			return nil, errors.Wrapf(ErrDuplicateName,
   310  				fmt.Sprintf("the container name \"%s\" is already in use by \"%s\". You have to remove that container to be able to reuse that name.", name, r.byname[name].ID))
   311  		}
   312  	}
   313  	if err == nil {
   314  		container = &Container{
   315  			ID:             id,
   316  			Names:          names,
   317  			ImageID:        image,
   318  			LayerID:        layer,
   319  			Metadata:       metadata,
   320  			BigDataNames:   []string{},
   321  			BigDataSizes:   make(map[string]int64),
   322  			BigDataDigests: make(map[string]digest.Digest),
   323  			Created:        time.Now().UTC(),
   324  			Flags:          copyStringInterfaceMap(options.Flags),
   325  			UIDMap:         copyIDMap(options.UIDMap),
   326  			GIDMap:         copyIDMap(options.GIDMap),
   327  		}
   328  		r.containers = append(r.containers, container)
   329  		r.byid[id] = container
   330  		r.idindex.Add(id)
   331  		r.bylayer[layer] = container
   332  		for _, name := range names {
   333  			r.byname[name] = container
   334  		}
   335  		err = r.Save()
   336  		container = copyContainer(container)
   337  	}
   338  	return container, err
   339  }
   340  
   341  func (r *containerStore) Metadata(id string) (string, error) {
   342  	if container, ok := r.lookup(id); ok {
   343  		return container.Metadata, nil
   344  	}
   345  	return "", ErrContainerUnknown
   346  }
   347  
   348  func (r *containerStore) SetMetadata(id, metadata string) error {
   349  	if container, ok := r.lookup(id); ok {
   350  		container.Metadata = metadata
   351  		return r.Save()
   352  	}
   353  	return ErrContainerUnknown
   354  }
   355  
   356  func (r *containerStore) removeName(container *Container, name string) {
   357  	container.Names = stringSliceWithoutValue(container.Names, name)
   358  }
   359  
   360  func (r *containerStore) SetNames(id string, names []string) error {
   361  	names = dedupeNames(names)
   362  	if container, ok := r.lookup(id); ok {
   363  		for _, name := range container.Names {
   364  			delete(r.byname, name)
   365  		}
   366  		for _, name := range names {
   367  			if otherContainer, ok := r.byname[name]; ok {
   368  				r.removeName(otherContainer, name)
   369  			}
   370  			r.byname[name] = container
   371  		}
   372  		container.Names = names
   373  		return r.Save()
   374  	}
   375  	return ErrContainerUnknown
   376  }
   377  
   378  func (r *containerStore) Delete(id string) error {
   379  	container, ok := r.lookup(id)
   380  	if !ok {
   381  		return ErrContainerUnknown
   382  	}
   383  	id = container.ID
   384  	toDeleteIndex := -1
   385  	for i, candidate := range r.containers {
   386  		if candidate.ID == id {
   387  			toDeleteIndex = i
   388  			break
   389  		}
   390  	}
   391  	delete(r.byid, id)
   392  	r.idindex.Delete(id)
   393  	delete(r.bylayer, container.LayerID)
   394  	for _, name := range container.Names {
   395  		delete(r.byname, name)
   396  	}
   397  	if toDeleteIndex != -1 {
   398  		// delete the container at toDeleteIndex
   399  		if toDeleteIndex == len(r.containers)-1 {
   400  			r.containers = r.containers[:len(r.containers)-1]
   401  		} else {
   402  			r.containers = append(r.containers[:toDeleteIndex], r.containers[toDeleteIndex+1:]...)
   403  		}
   404  	}
   405  	if err := r.Save(); err != nil {
   406  		return err
   407  	}
   408  	if err := os.RemoveAll(r.datadir(id)); err != nil {
   409  		return err
   410  	}
   411  	return nil
   412  }
   413  
   414  func (r *containerStore) Get(id string) (*Container, error) {
   415  	if container, ok := r.lookup(id); ok {
   416  		return copyContainer(container), nil
   417  	}
   418  	return nil, ErrContainerUnknown
   419  }
   420  
   421  func (r *containerStore) Lookup(name string) (id string, err error) {
   422  	if container, ok := r.lookup(name); ok {
   423  		return container.ID, nil
   424  	}
   425  	return "", ErrContainerUnknown
   426  }
   427  
   428  func (r *containerStore) Exists(id string) bool {
   429  	_, ok := r.lookup(id)
   430  	return ok
   431  }
   432  
   433  func (r *containerStore) BigData(id, key string) ([]byte, error) {
   434  	if key == "" {
   435  		return nil, errors.Wrapf(ErrInvalidBigDataName, "can't retrieve container big data value for empty name")
   436  	}
   437  	c, ok := r.lookup(id)
   438  	if !ok {
   439  		return nil, ErrContainerUnknown
   440  	}
   441  	return ioutil.ReadFile(r.datapath(c.ID, key))
   442  }
   443  
   444  func (r *containerStore) BigDataSize(id, key string) (int64, error) {
   445  	if key == "" {
   446  		return -1, errors.Wrapf(ErrInvalidBigDataName, "can't retrieve size of container big data with empty name")
   447  	}
   448  	c, ok := r.lookup(id)
   449  	if !ok {
   450  		return -1, ErrContainerUnknown
   451  	}
   452  	if c.BigDataSizes == nil {
   453  		c.BigDataSizes = make(map[string]int64)
   454  	}
   455  	if size, ok := c.BigDataSizes[key]; ok {
   456  		return size, nil
   457  	}
   458  	if data, err := r.BigData(id, key); err == nil && data != nil {
   459  		if err = r.SetBigData(id, key, data); err == nil {
   460  			c, ok := r.lookup(id)
   461  			if !ok {
   462  				return -1, ErrContainerUnknown
   463  			}
   464  			if size, ok := c.BigDataSizes[key]; ok {
   465  				return size, nil
   466  			}
   467  		} else {
   468  			return -1, err
   469  		}
   470  	}
   471  	return -1, ErrSizeUnknown
   472  }
   473  
   474  func (r *containerStore) BigDataDigest(id, key string) (digest.Digest, error) {
   475  	if key == "" {
   476  		return "", errors.Wrapf(ErrInvalidBigDataName, "can't retrieve digest of container big data value with empty name")
   477  	}
   478  	c, ok := r.lookup(id)
   479  	if !ok {
   480  		return "", ErrContainerUnknown
   481  	}
   482  	if c.BigDataDigests == nil {
   483  		c.BigDataDigests = make(map[string]digest.Digest)
   484  	}
   485  	if d, ok := c.BigDataDigests[key]; ok {
   486  		return d, nil
   487  	}
   488  	if data, err := r.BigData(id, key); err == nil && data != nil {
   489  		if err = r.SetBigData(id, key, data); err == nil {
   490  			c, ok := r.lookup(id)
   491  			if !ok {
   492  				return "", ErrContainerUnknown
   493  			}
   494  			if d, ok := c.BigDataDigests[key]; ok {
   495  				return d, nil
   496  			}
   497  		} else {
   498  			return "", err
   499  		}
   500  	}
   501  	return "", ErrDigestUnknown
   502  }
   503  
   504  func (r *containerStore) BigDataNames(id string) ([]string, error) {
   505  	c, ok := r.lookup(id)
   506  	if !ok {
   507  		return nil, ErrContainerUnknown
   508  	}
   509  	return copyStringSlice(c.BigDataNames), nil
   510  }
   511  
   512  func (r *containerStore) SetBigData(id, key string, data []byte) error {
   513  	if key == "" {
   514  		return errors.Wrapf(ErrInvalidBigDataName, "can't set empty name for container big data item")
   515  	}
   516  	c, ok := r.lookup(id)
   517  	if !ok {
   518  		return ErrContainerUnknown
   519  	}
   520  	if err := os.MkdirAll(r.datadir(c.ID), 0700); err != nil {
   521  		return err
   522  	}
   523  	err := ioutils.AtomicWriteFile(r.datapath(c.ID, key), data, 0600)
   524  	if err == nil {
   525  		save := false
   526  		if c.BigDataSizes == nil {
   527  			c.BigDataSizes = make(map[string]int64)
   528  		}
   529  		oldSize, sizeOk := c.BigDataSizes[key]
   530  		c.BigDataSizes[key] = int64(len(data))
   531  		if c.BigDataDigests == nil {
   532  			c.BigDataDigests = make(map[string]digest.Digest)
   533  		}
   534  		oldDigest, digestOk := c.BigDataDigests[key]
   535  		newDigest := digest.Canonical.FromBytes(data)
   536  		c.BigDataDigests[key] = newDigest
   537  		if !sizeOk || oldSize != c.BigDataSizes[key] || !digestOk || oldDigest != newDigest {
   538  			save = true
   539  		}
   540  		addName := true
   541  		for _, name := range c.BigDataNames {
   542  			if name == key {
   543  				addName = false
   544  				break
   545  			}
   546  		}
   547  		if addName {
   548  			c.BigDataNames = append(c.BigDataNames, key)
   549  			save = true
   550  		}
   551  		if save {
   552  			err = r.Save()
   553  		}
   554  	}
   555  	return err
   556  }
   557  
   558  func (r *containerStore) Wipe() error {
   559  	ids := make([]string, 0, len(r.byid))
   560  	for id := range r.byid {
   561  		ids = append(ids, id)
   562  	}
   563  	for _, id := range ids {
   564  		if err := r.Delete(id); err != nil {
   565  			return err
   566  		}
   567  	}
   568  	return nil
   569  }
   570  
   571  func (r *containerStore) Lock() {
   572  	r.lockfile.Lock()
   573  }
   574  
   575  func (r *containerStore) RecursiveLock() {
   576  	r.lockfile.RecursiveLock()
   577  }
   578  
   579  func (r *containerStore) RLock() {
   580  	r.lockfile.RLock()
   581  }
   582  func (r *containerStore) Unlock() {
   583  	r.lockfile.Unlock()
   584  }
   585  
   586  func (r *containerStore) Touch() error {
   587  	return r.lockfile.Touch()
   588  }
   589  
   590  func (r *containerStore) Modified() (bool, error) {
   591  	return r.lockfile.Modified()
   592  }
   593  
   594  func (r *containerStore) IsReadWrite() bool {
   595  	return r.lockfile.IsReadWrite()
   596  }
   597  
   598  func (r *containerStore) TouchedSince(when time.Time) bool {
   599  	return r.lockfile.TouchedSince(when)
   600  }
   601  
   602  func (r *containerStore) Locked() bool {
   603  	return r.lockfile.Locked()
   604  }