...

Source file src/github.com/google/go-containerregistry/pkg/v1/cache/fs.go

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

     1  // Copyright 2021 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 cache
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"path/filepath"
    23  	"runtime"
    24  
    25  	v1 "github.com/google/go-containerregistry/pkg/v1"
    26  	"github.com/google/go-containerregistry/pkg/v1/tarball"
    27  )
    28  
    29  type fscache struct {
    30  	path string
    31  }
    32  
    33  // NewFilesystemCache returns a Cache implementation backed by files.
    34  func NewFilesystemCache(path string) Cache {
    35  	return &fscache{path}
    36  }
    37  
    38  func (fs *fscache) Put(l v1.Layer) (v1.Layer, error) {
    39  	digest, err := l.Digest()
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	diffID, err := l.DiffID()
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	return &layer{
    48  		Layer:  l,
    49  		path:   fs.path,
    50  		digest: digest,
    51  		diffID: diffID,
    52  	}, nil
    53  }
    54  
    55  type layer struct {
    56  	v1.Layer
    57  	path           string
    58  	digest, diffID v1.Hash
    59  }
    60  
    61  func (l *layer) create(h v1.Hash) (io.WriteCloser, error) {
    62  	if err := os.MkdirAll(l.path, 0700); err != nil {
    63  		return nil, err
    64  	}
    65  	return os.Create(cachepath(l.path, h))
    66  }
    67  
    68  func (l *layer) Compressed() (io.ReadCloser, error) {
    69  	f, err := l.create(l.digest)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	rc, err := l.Layer.Compressed()
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	return &readcloser{
    78  		t:      io.TeeReader(rc, f),
    79  		closes: []func() error{rc.Close, f.Close},
    80  	}, nil
    81  }
    82  
    83  func (l *layer) Uncompressed() (io.ReadCloser, error) {
    84  	f, err := l.create(l.diffID)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	rc, err := l.Layer.Uncompressed()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	return &readcloser{
    93  		t:      io.TeeReader(rc, f),
    94  		closes: []func() error{rc.Close, f.Close},
    95  	}, nil
    96  }
    97  
    98  type readcloser struct {
    99  	t      io.Reader
   100  	closes []func() error
   101  }
   102  
   103  func (rc *readcloser) Read(b []byte) (int, error) {
   104  	return rc.t.Read(b)
   105  }
   106  
   107  func (rc *readcloser) Close() error {
   108  	// Call all Close methods, even if any returned an error. Return the
   109  	// first returned error.
   110  	var err error
   111  	for _, c := range rc.closes {
   112  		lastErr := c()
   113  		if err == nil {
   114  			err = lastErr
   115  		}
   116  	}
   117  	return err
   118  }
   119  
   120  func (fs *fscache) Get(h v1.Hash) (v1.Layer, error) {
   121  	l, err := tarball.LayerFromFile(cachepath(fs.path, h))
   122  	if os.IsNotExist(err) {
   123  		return nil, ErrNotFound
   124  	}
   125  	if errors.Is(err, io.ErrUnexpectedEOF) {
   126  		// Delete and return ErrNotFound because the layer was incomplete.
   127  		if err := fs.Delete(h); err != nil {
   128  			return nil, err
   129  		}
   130  		return nil, ErrNotFound
   131  	}
   132  	return l, err
   133  }
   134  
   135  func (fs *fscache) Delete(h v1.Hash) error {
   136  	err := os.Remove(cachepath(fs.path, h))
   137  	if os.IsNotExist(err) {
   138  		return ErrNotFound
   139  	}
   140  	return err
   141  }
   142  
   143  func cachepath(path string, h v1.Hash) string {
   144  	var file string
   145  	if runtime.GOOS == "windows" {
   146  		file = fmt.Sprintf("%s-%s", h.Algorithm, h.Hex)
   147  	} else {
   148  		file = h.String()
   149  	}
   150  	return filepath.Join(path, file)
   151  }
   152  

View as plain text