github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/image/store.go (about)

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