github.com/rish1988/moby@v25.0.2+incompatible/image/store.go (about)

     1  package image // import "github.com/docker/docker/image"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/containerd/log"
    10  	"github.com/docker/docker/errdefs"
    11  	"github.com/docker/docker/layer"
    12  	"github.com/opencontainers/go-digest"
    13  	"github.com/opencontainers/go-digest/digestset"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // Store is an interface for creating and accessing images
    18  type Store interface {
    19  	Create(config []byte) (ID, error)
    20  	Get(id ID) (*Image, error)
    21  	Delete(id ID) ([]layer.Metadata, error)
    22  	Search(partialID string) (ID, error)
    23  	SetParent(id ID, parent ID) error
    24  	GetParent(id ID) (ID, error)
    25  	SetLastUpdated(id ID) error
    26  	GetLastUpdated(id ID) (time.Time, error)
    27  	Children(id ID) []ID
    28  	Map() map[ID]*Image
    29  	Heads() map[ID]*Image
    30  	Len() int
    31  }
    32  
    33  // LayerGetReleaser is a minimal interface for getting and releasing images.
    34  type LayerGetReleaser interface {
    35  	Get(layer.ChainID) (layer.Layer, error)
    36  	Release(layer.Layer) ([]layer.Metadata, error)
    37  }
    38  
    39  type imageMeta struct {
    40  	layer    layer.Layer
    41  	children map[ID]struct{}
    42  }
    43  
    44  type store struct {
    45  	sync.RWMutex
    46  	lss       LayerGetReleaser
    47  	images    map[ID]*imageMeta
    48  	fs        StoreBackend
    49  	digestSet *digestset.Set
    50  }
    51  
    52  // NewImageStore returns new store object for given set of layer stores
    53  func NewImageStore(fs StoreBackend, lss LayerGetReleaser) (Store, error) {
    54  	is := &store{
    55  		lss:       lss,
    56  		images:    make(map[ID]*imageMeta),
    57  		fs:        fs,
    58  		digestSet: digestset.NewSet(),
    59  	}
    60  
    61  	// load all current images and retain layers
    62  	if err := is.restore(); err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	return is, nil
    67  }
    68  
    69  func (is *store) restore() error {
    70  	// As the code below is run when restoring all images (which can be "many"),
    71  	// constructing the "log.G(ctx).WithFields" is deliberately not "DRY", as the
    72  	// logger is only used for error-cases, and we don't want to do allocations
    73  	// if we don't need it. The "f" type alias is here is just for convenience,
    74  	// and to make the code _slightly_ more DRY. See the discussion on GitHub;
    75  	// https://github.com/moby/moby/pull/44426#discussion_r1059519071
    76  	type f = log.Fields
    77  	err := is.fs.Walk(func(dgst digest.Digest) error {
    78  		img, err := is.Get(ID(dgst))
    79  		if err != nil {
    80  			log.G(context.TODO()).WithFields(f{"digest": dgst, "err": err}).Error("invalid image")
    81  			return nil
    82  		}
    83  		var l layer.Layer
    84  		if chainID := img.RootFS.ChainID(); chainID != "" {
    85  			if err := CheckOS(img.OperatingSystem()); err != nil {
    86  				log.G(context.TODO()).WithFields(f{"chainID": chainID, "os": img.OperatingSystem()}).Error("not restoring image with unsupported operating system")
    87  				return nil
    88  			}
    89  			l, err = is.lss.Get(chainID)
    90  			if err != nil {
    91  				if errors.Is(err, layer.ErrLayerDoesNotExist) {
    92  					log.G(context.TODO()).WithFields(f{"chainID": chainID, "os": img.OperatingSystem(), "err": err}).Error("not restoring image")
    93  					return nil
    94  				}
    95  				return err
    96  			}
    97  		}
    98  		if err := is.digestSet.Add(dgst); err != nil {
    99  			return err
   100  		}
   101  
   102  		is.images[ID(dgst)] = &imageMeta{
   103  			layer:    l,
   104  			children: make(map[ID]struct{}),
   105  		}
   106  
   107  		return nil
   108  	})
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	// Second pass to fill in children maps
   114  	for id := range is.images {
   115  		if parent, err := is.GetParent(id); err == nil {
   116  			if parentMeta := is.images[parent]; parentMeta != nil {
   117  				parentMeta.children[id] = struct{}{}
   118  			}
   119  		}
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  func (is *store) Create(config []byte) (ID, error) {
   126  	var img *Image
   127  	img, err := NewFromJSON(config)
   128  	if err != nil {
   129  		return "", err
   130  	}
   131  
   132  	// Must reject any config that references diffIDs from the history
   133  	// which aren't among the rootfs layers.
   134  	rootFSLayers := make(map[layer.DiffID]struct{})
   135  	for _, diffID := range img.RootFS.DiffIDs {
   136  		rootFSLayers[diffID] = struct{}{}
   137  	}
   138  
   139  	layerCounter := 0
   140  	for _, h := range img.History {
   141  		if !h.EmptyLayer {
   142  			layerCounter++
   143  		}
   144  	}
   145  	if layerCounter > len(img.RootFS.DiffIDs) {
   146  		return "", errdefs.InvalidParameter(errors.New("too many non-empty layers in History section"))
   147  	}
   148  
   149  	imageDigest, err := is.fs.Set(config)
   150  	if err != nil {
   151  		return "", errdefs.InvalidParameter(err)
   152  	}
   153  
   154  	is.Lock()
   155  	defer is.Unlock()
   156  
   157  	imageID := ID(imageDigest)
   158  	if _, exists := is.images[imageID]; exists {
   159  		return imageID, nil
   160  	}
   161  
   162  	layerID := img.RootFS.ChainID()
   163  
   164  	var l layer.Layer
   165  	if layerID != "" {
   166  		if err := CheckOS(img.OperatingSystem()); err != nil {
   167  			return "", err
   168  		}
   169  		l, err = is.lss.Get(layerID)
   170  		if err != nil {
   171  			return "", errdefs.InvalidParameter(errors.Wrapf(err, "failed to get layer %s", layerID))
   172  		}
   173  	}
   174  
   175  	is.images[imageID] = &imageMeta{
   176  		layer:    l,
   177  		children: make(map[ID]struct{}),
   178  	}
   179  
   180  	if err = is.digestSet.Add(imageDigest); err != nil {
   181  		delete(is.images, imageID)
   182  		return "", errdefs.InvalidParameter(err)
   183  	}
   184  
   185  	return imageID, nil
   186  }
   187  
   188  type imageNotFoundError string
   189  
   190  func (e imageNotFoundError) Error() string {
   191  	return "No such image: " + string(e)
   192  }
   193  
   194  func (imageNotFoundError) NotFound() {}
   195  
   196  func (is *store) Search(term string) (ID, error) {
   197  	dgst, err := is.digestSet.Lookup(term)
   198  	if err != nil {
   199  		if err == digestset.ErrDigestNotFound {
   200  			err = imageNotFoundError(term)
   201  		}
   202  		return "", errors.WithStack(err)
   203  	}
   204  	return ID(dgst), nil
   205  }
   206  
   207  func (is *store) Get(id ID) (*Image, error) {
   208  	// todo: Check if image is in images
   209  	// todo: Detect manual insertions and start using them
   210  	config, err := is.fs.Get(id.Digest())
   211  	if err != nil {
   212  		return nil, errdefs.NotFound(err)
   213  	}
   214  
   215  	img, err := NewFromJSON(config)
   216  	if err != nil {
   217  		return nil, errdefs.InvalidParameter(err)
   218  	}
   219  	img.computedID = id
   220  
   221  	img.Parent, err = is.GetParent(id)
   222  	if err != nil {
   223  		img.Parent = ""
   224  	}
   225  
   226  	return img, nil
   227  }
   228  
   229  func (is *store) Delete(id ID) ([]layer.Metadata, error) {
   230  	is.Lock()
   231  	defer is.Unlock()
   232  
   233  	imgMeta := is.images[id]
   234  	if imgMeta == nil {
   235  		return nil, errdefs.NotFound(fmt.Errorf("unrecognized image ID %s", id.String()))
   236  	}
   237  	_, err := is.Get(id)
   238  	if err != nil {
   239  		return nil, errdefs.NotFound(fmt.Errorf("unrecognized image %s, %v", id.String(), err))
   240  	}
   241  	for cID := range imgMeta.children {
   242  		is.fs.DeleteMetadata(cID.Digest(), "parent")
   243  	}
   244  	if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
   245  		delete(is.images[parent].children, id)
   246  	}
   247  
   248  	if err := is.digestSet.Remove(id.Digest()); err != nil {
   249  		log.G(context.TODO()).Errorf("error removing %s from digest set: %q", id, err)
   250  	}
   251  	delete(is.images, id)
   252  	is.fs.Delete(id.Digest())
   253  
   254  	if imgMeta.layer != nil {
   255  		return is.lss.Release(imgMeta.layer)
   256  	}
   257  	return nil, nil
   258  }
   259  
   260  func (is *store) SetParent(id, parentID ID) error {
   261  	is.Lock()
   262  	defer is.Unlock()
   263  	parentMeta := is.images[parentID]
   264  	if parentMeta == nil {
   265  		return errdefs.NotFound(fmt.Errorf("unknown parent image ID %s", parentID.String()))
   266  	}
   267  	if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
   268  		delete(is.images[parent].children, id)
   269  	}
   270  	parentMeta.children[id] = struct{}{}
   271  	return is.fs.SetMetadata(id.Digest(), "parent", []byte(parentID))
   272  }
   273  
   274  func (is *store) GetParent(id ID) (ID, error) {
   275  	d, err := is.fs.GetMetadata(id.Digest(), "parent")
   276  	if err != nil {
   277  		return "", errdefs.NotFound(err)
   278  	}
   279  	return ID(d), nil // todo: validate?
   280  }
   281  
   282  // SetLastUpdated time for the image ID to the current time
   283  func (is *store) SetLastUpdated(id ID) error {
   284  	lastUpdated := []byte(time.Now().Format(time.RFC3339Nano))
   285  	return is.fs.SetMetadata(id.Digest(), "lastUpdated", lastUpdated)
   286  }
   287  
   288  // GetLastUpdated time for the image ID
   289  func (is *store) GetLastUpdated(id ID) (time.Time, error) {
   290  	bytes, err := is.fs.GetMetadata(id.Digest(), "lastUpdated")
   291  	if err != nil || len(bytes) == 0 {
   292  		// No lastUpdated time
   293  		return time.Time{}, nil
   294  	}
   295  	return time.Parse(time.RFC3339Nano, string(bytes))
   296  }
   297  
   298  func (is *store) Children(id ID) []ID {
   299  	is.RLock()
   300  	defer is.RUnlock()
   301  
   302  	return is.children(id)
   303  }
   304  
   305  func (is *store) children(id ID) []ID {
   306  	var ids []ID
   307  	if is.images[id] != nil {
   308  		for id := range is.images[id].children {
   309  			ids = append(ids, id)
   310  		}
   311  	}
   312  	return ids
   313  }
   314  
   315  func (is *store) Heads() map[ID]*Image {
   316  	return is.imagesMap(false)
   317  }
   318  
   319  func (is *store) Map() map[ID]*Image {
   320  	return is.imagesMap(true)
   321  }
   322  
   323  func (is *store) imagesMap(all bool) map[ID]*Image {
   324  	is.RLock()
   325  	defer is.RUnlock()
   326  
   327  	images := make(map[ID]*Image)
   328  
   329  	for id := range is.images {
   330  		if !all && len(is.children(id)) > 0 {
   331  			continue
   332  		}
   333  		img, err := is.Get(id)
   334  		if err != nil {
   335  			log.G(context.TODO()).Errorf("invalid image access: %q, error: %q", id, err)
   336  			continue
   337  		}
   338  		images[id] = img
   339  	}
   340  	return images
   341  }
   342  
   343  func (is *store) Len() int {
   344  	is.RLock()
   345  	defer is.RUnlock()
   346  	return len(is.images)
   347  }