118 lines
2.1 KiB
Go
118 lines
2.1 KiB
Go
package oci
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/containers/image/v5/types"
|
|
)
|
|
|
|
const (
|
|
defaultCachePath = "/var/warewulf/container-cache/oci/"
|
|
blobPrefix = "blobs"
|
|
rootfsPrefix = "rootfs"
|
|
)
|
|
|
|
type CacheOpt func(*Cache) error
|
|
|
|
func OptSetCachePath(path string) CacheOpt {
|
|
return func(s *Cache) error {
|
|
s.path = path
|
|
return nil
|
|
}
|
|
}
|
|
|
|
type Cache struct {
|
|
path string
|
|
}
|
|
|
|
func (c *Cache) rootfsDir() string {
|
|
return filepath.Join(c.path, rootfsPrefix)
|
|
}
|
|
|
|
func (c *Cache) blobDir() string {
|
|
return filepath.Join(c.path, blobPrefix)
|
|
}
|
|
|
|
// checkEntry maps the given id a path within the cache. It will return an os.ErrNotExist if the entry path is empty.
|
|
func (c *Cache) checkEntry(id string) (string, error) {
|
|
path := filepath.Join(c.rootfsDir(), id)
|
|
fi, err := os.Stat(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !fi.IsDir() {
|
|
return "", fmt.Errorf("invalid entry %q is not a directory", path)
|
|
}
|
|
|
|
return path, nil
|
|
}
|
|
|
|
func (c *Cache) createEntry(id string) (string, error) {
|
|
path := filepath.Join(c.rootfsDir(), id)
|
|
if err := os.MkdirAll(path, 0755); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return path, nil
|
|
}
|
|
|
|
func NewCache(opts ...CacheOpt) (*Cache, error) {
|
|
s := &Cache{
|
|
path: filepath.Join(defaultCachePath),
|
|
}
|
|
|
|
for _, o := range opts {
|
|
if err := o(s); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if err := os.MkdirAll(s.path, 0755); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func (c *Cache) Pull(ctx context.Context, uri string, sysCtx *types.SystemContext) (string, error) {
|
|
p, err := NewPuller(
|
|
OptSetBlobCachePath(c.blobDir()),
|
|
OptSetSystemContext(sysCtx),
|
|
)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
id, err := p.GenerateID(ctx, uri)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// look up cache entry and return if it exists
|
|
path, err := c.checkEntry(id)
|
|
if err == nil {
|
|
return path, nil
|
|
} else if !os.IsNotExist(err) {
|
|
return "", err
|
|
}
|
|
|
|
// cache entry does not exist so we must create it
|
|
path, err = c.createEntry(id)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// populate entry
|
|
if err := p.Pull(ctx, uri, path); err != nil {
|
|
// clean up entry on error
|
|
os.RemoveAll(path)
|
|
return "", err
|
|
}
|
|
|
|
return path, nil
|
|
}
|