...

Source file src/github.com/google/go-containerregistry/pkg/v1/partial/with.go

Documentation: github.com/google/go-containerregistry/pkg/v1/partial

     1  // Copyright 2018 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package partial
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"fmt"
    21  	"io"
    22  
    23  	v1 "github.com/google/go-containerregistry/pkg/v1"
    24  	"github.com/google/go-containerregistry/pkg/v1/types"
    25  )
    26  
    27  // WithRawConfigFile defines the subset of v1.Image used by these helper methods
    28  type WithRawConfigFile interface {
    29  	// RawConfigFile returns the serialized bytes of this image's config file.
    30  	RawConfigFile() ([]byte, error)
    31  }
    32  
    33  // ConfigFile is a helper for implementing v1.Image
    34  func ConfigFile(i WithRawConfigFile) (*v1.ConfigFile, error) {
    35  	b, err := i.RawConfigFile()
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	return v1.ParseConfigFile(bytes.NewReader(b))
    40  }
    41  
    42  // ConfigName is a helper for implementing v1.Image
    43  func ConfigName(i WithRawConfigFile) (v1.Hash, error) {
    44  	b, err := i.RawConfigFile()
    45  	if err != nil {
    46  		return v1.Hash{}, err
    47  	}
    48  	h, _, err := v1.SHA256(bytes.NewReader(b))
    49  	return h, err
    50  }
    51  
    52  type configLayer struct {
    53  	hash    v1.Hash
    54  	content []byte
    55  }
    56  
    57  // Digest implements v1.Layer
    58  func (cl *configLayer) Digest() (v1.Hash, error) {
    59  	return cl.hash, nil
    60  }
    61  
    62  // DiffID implements v1.Layer
    63  func (cl *configLayer) DiffID() (v1.Hash, error) {
    64  	return cl.hash, nil
    65  }
    66  
    67  // Uncompressed implements v1.Layer
    68  func (cl *configLayer) Uncompressed() (io.ReadCloser, error) {
    69  	return io.NopCloser(bytes.NewBuffer(cl.content)), nil
    70  }
    71  
    72  // Compressed implements v1.Layer
    73  func (cl *configLayer) Compressed() (io.ReadCloser, error) {
    74  	return io.NopCloser(bytes.NewBuffer(cl.content)), nil
    75  }
    76  
    77  // Size implements v1.Layer
    78  func (cl *configLayer) Size() (int64, error) {
    79  	return int64(len(cl.content)), nil
    80  }
    81  
    82  func (cl *configLayer) MediaType() (types.MediaType, error) {
    83  	// Defaulting this to OCIConfigJSON as it should remain
    84  	// backwards compatible with DockerConfigJSON
    85  	return types.OCIConfigJSON, nil
    86  }
    87  
    88  var _ v1.Layer = (*configLayer)(nil)
    89  
    90  // withConfigLayer allows partial image implementations to provide a layer
    91  // for their config file.
    92  type withConfigLayer interface {
    93  	ConfigLayer() (v1.Layer, error)
    94  }
    95  
    96  // ConfigLayer implements v1.Layer from the raw config bytes.
    97  // This is so that clients (e.g. remote) can access the config as a blob.
    98  //
    99  // Images that want to return a specific layer implementation can implement
   100  // withConfigLayer.
   101  func ConfigLayer(i WithRawConfigFile) (v1.Layer, error) {
   102  	if wcl, ok := unwrap(i).(withConfigLayer); ok {
   103  		return wcl.ConfigLayer()
   104  	}
   105  
   106  	h, err := ConfigName(i)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	rcfg, err := i.RawConfigFile()
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	return &configLayer{
   115  		hash:    h,
   116  		content: rcfg,
   117  	}, nil
   118  }
   119  
   120  // WithConfigFile defines the subset of v1.Image used by these helper methods
   121  type WithConfigFile interface {
   122  	// ConfigFile returns this image's config file.
   123  	ConfigFile() (*v1.ConfigFile, error)
   124  }
   125  
   126  // DiffIDs is a helper for implementing v1.Image
   127  func DiffIDs(i WithConfigFile) ([]v1.Hash, error) {
   128  	cfg, err := i.ConfigFile()
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	return cfg.RootFS.DiffIDs, nil
   133  }
   134  
   135  // RawConfigFile is a helper for implementing v1.Image
   136  func RawConfigFile(i WithConfigFile) ([]byte, error) {
   137  	cfg, err := i.ConfigFile()
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	return json.Marshal(cfg)
   142  }
   143  
   144  // WithRawManifest defines the subset of v1.Image used by these helper methods
   145  type WithRawManifest interface {
   146  	// RawManifest returns the serialized bytes of this image's config file.
   147  	RawManifest() ([]byte, error)
   148  }
   149  
   150  // Digest is a helper for implementing v1.Image
   151  func Digest(i WithRawManifest) (v1.Hash, error) {
   152  	mb, err := i.RawManifest()
   153  	if err != nil {
   154  		return v1.Hash{}, err
   155  	}
   156  	digest, _, err := v1.SHA256(bytes.NewReader(mb))
   157  	return digest, err
   158  }
   159  
   160  // Manifest is a helper for implementing v1.Image
   161  func Manifest(i WithRawManifest) (*v1.Manifest, error) {
   162  	b, err := i.RawManifest()
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	return v1.ParseManifest(bytes.NewReader(b))
   167  }
   168  
   169  // WithManifest defines the subset of v1.Image used by these helper methods
   170  type WithManifest interface {
   171  	// Manifest returns this image's Manifest object.
   172  	Manifest() (*v1.Manifest, error)
   173  }
   174  
   175  // RawManifest is a helper for implementing v1.Image
   176  func RawManifest(i WithManifest) ([]byte, error) {
   177  	m, err := i.Manifest()
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	return json.Marshal(m)
   182  }
   183  
   184  // Size is a helper for implementing v1.Image
   185  func Size(i WithRawManifest) (int64, error) {
   186  	b, err := i.RawManifest()
   187  	if err != nil {
   188  		return -1, err
   189  	}
   190  	return int64(len(b)), nil
   191  }
   192  
   193  // FSLayers is a helper for implementing v1.Image
   194  func FSLayers(i WithManifest) ([]v1.Hash, error) {
   195  	m, err := i.Manifest()
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  	fsl := make([]v1.Hash, len(m.Layers))
   200  	for i, l := range m.Layers {
   201  		fsl[i] = l.Digest
   202  	}
   203  	return fsl, nil
   204  }
   205  
   206  // BlobSize is a helper for implementing v1.Image
   207  func BlobSize(i WithManifest, h v1.Hash) (int64, error) {
   208  	d, err := BlobDescriptor(i, h)
   209  	if err != nil {
   210  		return -1, err
   211  	}
   212  	return d.Size, nil
   213  }
   214  
   215  // BlobDescriptor is a helper for implementing v1.Image
   216  func BlobDescriptor(i WithManifest, h v1.Hash) (*v1.Descriptor, error) {
   217  	m, err := i.Manifest()
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	if m.Config.Digest == h {
   223  		return &m.Config, nil
   224  	}
   225  
   226  	for _, l := range m.Layers {
   227  		if l.Digest == h {
   228  			return &l, nil
   229  		}
   230  	}
   231  	return nil, fmt.Errorf("blob %v not found", h)
   232  }
   233  
   234  // WithManifestAndConfigFile defines the subset of v1.Image used by these helper methods
   235  type WithManifestAndConfigFile interface {
   236  	WithConfigFile
   237  
   238  	// Manifest returns this image's Manifest object.
   239  	Manifest() (*v1.Manifest, error)
   240  }
   241  
   242  // BlobToDiffID is a helper for mapping between compressed
   243  // and uncompressed blob hashes.
   244  func BlobToDiffID(i WithManifestAndConfigFile, h v1.Hash) (v1.Hash, error) {
   245  	blobs, err := FSLayers(i)
   246  	if err != nil {
   247  		return v1.Hash{}, err
   248  	}
   249  	diffIDs, err := DiffIDs(i)
   250  	if err != nil {
   251  		return v1.Hash{}, err
   252  	}
   253  	if len(blobs) != len(diffIDs) {
   254  		return v1.Hash{}, fmt.Errorf("mismatched fs layers (%d) and diff ids (%d)", len(blobs), len(diffIDs))
   255  	}
   256  	for i, blob := range blobs {
   257  		if blob == h {
   258  			return diffIDs[i], nil
   259  		}
   260  	}
   261  	return v1.Hash{}, fmt.Errorf("unknown blob %v", h)
   262  }
   263  
   264  // DiffIDToBlob is a helper for mapping between uncompressed
   265  // and compressed blob hashes.
   266  func DiffIDToBlob(wm WithManifestAndConfigFile, h v1.Hash) (v1.Hash, error) {
   267  	blobs, err := FSLayers(wm)
   268  	if err != nil {
   269  		return v1.Hash{}, err
   270  	}
   271  	diffIDs, err := DiffIDs(wm)
   272  	if err != nil {
   273  		return v1.Hash{}, err
   274  	}
   275  	if len(blobs) != len(diffIDs) {
   276  		return v1.Hash{}, fmt.Errorf("mismatched fs layers (%d) and diff ids (%d)", len(blobs), len(diffIDs))
   277  	}
   278  	for i, diffID := range diffIDs {
   279  		if diffID == h {
   280  			return blobs[i], nil
   281  		}
   282  	}
   283  	return v1.Hash{}, fmt.Errorf("unknown diffID %v", h)
   284  }
   285  
   286  // WithDiffID defines the subset of v1.Layer for exposing the DiffID method.
   287  type WithDiffID interface {
   288  	DiffID() (v1.Hash, error)
   289  }
   290  
   291  // withDescriptor allows partial layer implementations to provide a layer
   292  // descriptor to the partial image manifest builder. This allows partial
   293  // uncompressed layers to provide foreign layer metadata like URLs to the
   294  // uncompressed image manifest.
   295  type withDescriptor interface {
   296  	Descriptor() (*v1.Descriptor, error)
   297  }
   298  
   299  // Describable represents something for which we can produce a v1.Descriptor.
   300  type Describable interface {
   301  	Digest() (v1.Hash, error)
   302  	MediaType() (types.MediaType, error)
   303  	Size() (int64, error)
   304  }
   305  
   306  // Descriptor returns a v1.Descriptor given a Describable. It also encodes
   307  // some logic for unwrapping things that have been wrapped by
   308  // CompressedToLayer, UncompressedToLayer, CompressedToImage, or
   309  // UncompressedToImage.
   310  func Descriptor(d Describable) (*v1.Descriptor, error) {
   311  	// If Describable implements Descriptor itself, return that.
   312  	if wd, ok := unwrap(d).(withDescriptor); ok {
   313  		return wd.Descriptor()
   314  	}
   315  
   316  	// If all else fails, compute the descriptor from the individual methods.
   317  	var (
   318  		desc v1.Descriptor
   319  		err  error
   320  	)
   321  
   322  	if desc.Size, err = d.Size(); err != nil {
   323  		return nil, err
   324  	}
   325  	if desc.Digest, err = d.Digest(); err != nil {
   326  		return nil, err
   327  	}
   328  	if desc.MediaType, err = d.MediaType(); err != nil {
   329  		return nil, err
   330  	}
   331  	if wat, ok := d.(withArtifactType); ok {
   332  		if desc.ArtifactType, err = wat.ArtifactType(); err != nil {
   333  			return nil, err
   334  		}
   335  	} else {
   336  		if wrm, ok := d.(WithRawManifest); ok && desc.MediaType.IsImage() {
   337  			mf, _ := Manifest(wrm)
   338  			// Failing to parse as a manifest should just be ignored.
   339  			// The manifest might not be valid, and that's okay.
   340  			if mf != nil && !mf.Config.MediaType.IsConfig() {
   341  				desc.ArtifactType = string(mf.Config.MediaType)
   342  			}
   343  		}
   344  	}
   345  
   346  	return &desc, nil
   347  }
   348  
   349  type withArtifactType interface {
   350  	ArtifactType() (string, error)
   351  }
   352  
   353  type withUncompressedSize interface {
   354  	UncompressedSize() (int64, error)
   355  }
   356  
   357  // UncompressedSize returns the size of the Uncompressed layer. If the
   358  // underlying implementation doesn't implement UncompressedSize directly,
   359  // this will compute the uncompressedSize by reading everything returned
   360  // by Compressed(). This is potentially expensive and may consume the contents
   361  // for streaming layers.
   362  func UncompressedSize(l v1.Layer) (int64, error) {
   363  	// If the layer implements UncompressedSize itself, return that.
   364  	if wus, ok := unwrap(l).(withUncompressedSize); ok {
   365  		return wus.UncompressedSize()
   366  	}
   367  
   368  	// The layer doesn't implement UncompressedSize, we need to compute it.
   369  	rc, err := l.Uncompressed()
   370  	if err != nil {
   371  		return -1, err
   372  	}
   373  	defer rc.Close()
   374  
   375  	return io.Copy(io.Discard, rc)
   376  }
   377  
   378  type withExists interface {
   379  	Exists() (bool, error)
   380  }
   381  
   382  // Exists checks to see if a layer exists. This is a hack to work around the
   383  // mistakes of the partial package. Don't use this.
   384  func Exists(l v1.Layer) (bool, error) {
   385  	// If the layer implements Exists itself, return that.
   386  	if we, ok := unwrap(l).(withExists); ok {
   387  		return we.Exists()
   388  	}
   389  
   390  	// The layer doesn't implement Exists, so we hope that calling Compressed()
   391  	// is enough to trigger an error if the layer does not exist.
   392  	rc, err := l.Compressed()
   393  	if err != nil {
   394  		return false, err
   395  	}
   396  	defer rc.Close()
   397  
   398  	// We may want to try actually reading a single byte, but if we need to do
   399  	// that, we should just fix this hack.
   400  	return true, nil
   401  }
   402  
   403  // Recursively unwrap our wrappers so that we can check for the original implementation.
   404  // We might want to expose this?
   405  func unwrap(i any) any {
   406  	if ule, ok := i.(*uncompressedLayerExtender); ok {
   407  		return unwrap(ule.UncompressedLayer)
   408  	}
   409  	if cle, ok := i.(*compressedLayerExtender); ok {
   410  		return unwrap(cle.CompressedLayer)
   411  	}
   412  	if uie, ok := i.(*uncompressedImageExtender); ok {
   413  		return unwrap(uie.UncompressedImageCore)
   414  	}
   415  	if cie, ok := i.(*compressedImageExtender); ok {
   416  		return unwrap(cie.CompressedImageCore)
   417  	}
   418  	return i
   419  }
   420  
   421  // ArtifactType returns the artifact type for the given manifest.
   422  //
   423  // If the manifest reports its own artifact type, that's returned, otherwise
   424  // the manifest is parsed and, if successful, its config.mediaType is returned.
   425  func ArtifactType(w WithManifest) (string, error) {
   426  	if wat, ok := w.(withArtifactType); ok {
   427  		return wat.ArtifactType()
   428  	}
   429  	mf, _ := w.Manifest()
   430  	// Failing to parse as a manifest should just be ignored.
   431  	// The manifest might not be valid, and that's okay.
   432  	if mf != nil && !mf.Config.MediaType.IsConfig() {
   433  		return string(mf.Config.MediaType), nil
   434  	}
   435  	return "", nil
   436  }
   437  

View as plain text