github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/manifest/store/store.go (about)

     1  package store
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/docker/cli/cli/manifest/types"
    12  	"github.com/docker/distribution/manifest/manifestlist"
    13  	"github.com/docker/distribution/reference"
    14  	digest "github.com/opencontainers/go-digest"
    15  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // Store manages local storage of image distribution manifests
    20  type Store interface {
    21  	Remove(listRef reference.Reference) error
    22  	Get(listRef reference.Reference, manifest reference.Reference) (types.ImageManifest, error)
    23  	GetList(listRef reference.Reference) ([]types.ImageManifest, error)
    24  	Save(listRef reference.Reference, manifest reference.Reference, image types.ImageManifest) error
    25  }
    26  
    27  // fsStore manages manifest files stored on the local filesystem
    28  type fsStore struct {
    29  	root string
    30  }
    31  
    32  // NewStore returns a new store for a local file path
    33  func NewStore(root string) Store {
    34  	return &fsStore{root: root}
    35  }
    36  
    37  // Remove a manifest list from local storage
    38  func (s *fsStore) Remove(listRef reference.Reference) error {
    39  	path := filepath.Join(s.root, makeFilesafeName(listRef.String()))
    40  	return os.RemoveAll(path)
    41  }
    42  
    43  // Get returns the local manifest
    44  func (s *fsStore) Get(listRef reference.Reference, manifest reference.Reference) (types.ImageManifest, error) {
    45  	filename := manifestToFilename(s.root, listRef.String(), manifest.String())
    46  	return s.getFromFilename(manifest, filename)
    47  }
    48  
    49  func (s *fsStore) getFromFilename(ref reference.Reference, filename string) (types.ImageManifest, error) {
    50  	bytes, err := ioutil.ReadFile(filename)
    51  	switch {
    52  	case os.IsNotExist(err):
    53  		return types.ImageManifest{}, newNotFoundError(ref.String())
    54  	case err != nil:
    55  		return types.ImageManifest{}, err
    56  	}
    57  	var manifestInfo struct {
    58  		types.ImageManifest
    59  
    60  		// Deprecated Fields, replaced by Descriptor
    61  		Digest   digest.Digest
    62  		Platform *manifestlist.PlatformSpec
    63  	}
    64  
    65  	if err := json.Unmarshal(bytes, &manifestInfo); err != nil {
    66  		return types.ImageManifest{}, err
    67  	}
    68  
    69  	// Compatibility with image manifests created before
    70  	// descriptor, newer versions omit Digest and Platform
    71  	if manifestInfo.Digest != "" {
    72  		mediaType, raw, err := manifestInfo.Payload()
    73  		if err != nil {
    74  			return types.ImageManifest{}, err
    75  		}
    76  		if dgst := digest.FromBytes(raw); dgst != manifestInfo.Digest {
    77  			return types.ImageManifest{}, errors.Errorf("invalid manifest file %v: image manifest digest mismatch (%v != %v)", filename, manifestInfo.Digest, dgst)
    78  		}
    79  		manifestInfo.ImageManifest.Descriptor = ocispec.Descriptor{
    80  			Digest:    manifestInfo.Digest,
    81  			Size:      int64(len(raw)),
    82  			MediaType: mediaType,
    83  			Platform:  types.OCIPlatform(manifestInfo.Platform),
    84  		}
    85  	}
    86  
    87  	return manifestInfo.ImageManifest, nil
    88  }
    89  
    90  // GetList returns all the local manifests for a transaction
    91  func (s *fsStore) GetList(listRef reference.Reference) ([]types.ImageManifest, error) {
    92  	filenames, err := s.listManifests(listRef.String())
    93  	switch {
    94  	case err != nil:
    95  		return nil, err
    96  	case filenames == nil:
    97  		return nil, newNotFoundError(listRef.String())
    98  	}
    99  
   100  	manifests := []types.ImageManifest{}
   101  	for _, filename := range filenames {
   102  		filename = filepath.Join(s.root, makeFilesafeName(listRef.String()), filename)
   103  		manifest, err := s.getFromFilename(listRef, filename)
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  		manifests = append(manifests, manifest)
   108  	}
   109  	return manifests, nil
   110  }
   111  
   112  // listManifests stored in a transaction
   113  func (s *fsStore) listManifests(transaction string) ([]string, error) {
   114  	transactionDir := filepath.Join(s.root, makeFilesafeName(transaction))
   115  	fileInfos, err := ioutil.ReadDir(transactionDir)
   116  	switch {
   117  	case os.IsNotExist(err):
   118  		return nil, nil
   119  	case err != nil:
   120  		return nil, err
   121  	}
   122  
   123  	filenames := []string{}
   124  	for _, info := range fileInfos {
   125  		filenames = append(filenames, info.Name())
   126  	}
   127  	return filenames, nil
   128  }
   129  
   130  // Save a manifest as part of a local manifest list
   131  func (s *fsStore) Save(listRef reference.Reference, manifest reference.Reference, image types.ImageManifest) error {
   132  	if err := s.createManifestListDirectory(listRef.String()); err != nil {
   133  		return err
   134  	}
   135  	filename := manifestToFilename(s.root, listRef.String(), manifest.String())
   136  	bytes, err := json.Marshal(image)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	return ioutil.WriteFile(filename, bytes, 0644)
   141  }
   142  
   143  func (s *fsStore) createManifestListDirectory(transaction string) error {
   144  	path := filepath.Join(s.root, makeFilesafeName(transaction))
   145  	return os.MkdirAll(path, 0755)
   146  }
   147  
   148  func manifestToFilename(root, manifestList, manifest string) string {
   149  	return filepath.Join(root, makeFilesafeName(manifestList), makeFilesafeName(manifest))
   150  }
   151  
   152  func makeFilesafeName(ref string) string {
   153  	fileName := strings.Replace(ref, ":", "-", -1)
   154  	return strings.Replace(fileName, "/", "_", -1)
   155  }
   156  
   157  type notFoundError struct {
   158  	object string
   159  }
   160  
   161  func newNotFoundError(ref string) *notFoundError {
   162  	return &notFoundError{object: ref}
   163  }
   164  
   165  func (n *notFoundError) Error() string {
   166  	return fmt.Sprintf("No such manifest: %s", n.object)
   167  }
   168  
   169  // NotFound interface
   170  func (n *notFoundError) NotFound() {}
   171  
   172  // IsNotFound returns true if the error is a not found error
   173  func IsNotFound(err error) bool {
   174  	_, ok := err.(notFound)
   175  	return ok
   176  }
   177  
   178  type notFound interface {
   179  	NotFound()
   180  }