Add VNFS image cache of oci registry based images
This commit is contained in:
182
internal/pkg/oci/puller.go
Normal file
182
internal/pkg/oci/puller.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/copy"
|
||||
"github.com/containers/image/docker"
|
||||
"github.com/containers/image/oci/layout"
|
||||
"github.com/containers/image/signature"
|
||||
"github.com/containers/image/types"
|
||||
imgSpecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/opencontainers/umoci"
|
||||
"github.com/opencontainers/umoci/oci/layer"
|
||||
)
|
||||
|
||||
type pullerOpt func(*puller) error
|
||||
|
||||
func optSetBlobCachePath(path string) pullerOpt {
|
||||
return func(p *puller) error {
|
||||
p.blobCachePath = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func optSetTmpDirPath(path string) pullerOpt {
|
||||
return func(p *puller) error {
|
||||
p.tmpDirPath = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func optSetSystemContext(s *types.SystemContext) pullerOpt {
|
||||
return func(p *puller) error {
|
||||
p.sysCtx = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type puller struct {
|
||||
id string
|
||||
blobCachePath string
|
||||
tmpDirPath string
|
||||
sysCtx *types.SystemContext
|
||||
}
|
||||
|
||||
func newPuller(opts ...pullerOpt) (*puller, error) {
|
||||
p := &puller{
|
||||
// default to a sensible value, but caller should set this with opts
|
||||
blobCachePath: filepath.Join(defaultCachePath, blobPrefix),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
if err := o(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// stripScheme prepares the docker uri for external library parsing and returns an error
|
||||
// if it detects a malformed schema
|
||||
func stripScheme(uri string) (string, error) {
|
||||
if !strings.HasPrefix(uri, "docker://") {
|
||||
return "", fmt.Errorf("unsupported uri schema: %q", uri)
|
||||
}
|
||||
|
||||
return strings.TrimPrefix(uri, "docker:"), nil
|
||||
}
|
||||
|
||||
// generateID stores and returns a unique identifier derived from the sha256sum of the image manifest
|
||||
func (p *puller) generateID(ctx context.Context, uri string) (string, error) {
|
||||
cleanURI, err := stripScheme(uri)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ref, err := docker.ParseReference(cleanURI)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
src, err := ref.NewImageSource(ctx, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
manifestBytes, _, err := src.GetManifest(ctx, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
p.id = fmt.Sprintf("sha256:%x", sha256.Sum256(manifestBytes))
|
||||
return p.id, nil
|
||||
}
|
||||
|
||||
func (p *puller) pull(ctx context.Context, uri, dst string) (err error) {
|
||||
cleanURI, err := stripScheme(uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
srcRef, err := docker.ParseReference(cleanURI)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse uri: %v", err)
|
||||
}
|
||||
|
||||
cacheRef, err := layout.ParseReference(p.blobCachePath + ":" + p.id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate local oci reference: %v", err)
|
||||
}
|
||||
|
||||
// Create a wide open oci image signature policy
|
||||
policy := &signature.Policy{Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}}
|
||||
policyCtx, err := signature.NewPolicyContext(policy)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create policy context: %v", err)
|
||||
}
|
||||
|
||||
// copy to cache location
|
||||
_, err = copy.Image(ctx, policyCtx, cacheRef, srcRef, ©.Options{
|
||||
ReportWriter: os.Stdout,
|
||||
SourceCtx: p.sysCtx,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// defaults to $TMPDIR or /tmp
|
||||
tmpDir, err := ioutil.TempDir(p.tmpDirPath, "oci-bundle-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// create an oci bundle our tmpdir to avoid issues with umoci.UnpackRootfs()
|
||||
tmpRef, err := layout.ParseReference(tmpDir + ":" + "tmp")
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate local oci reference: %v", err)
|
||||
}
|
||||
|
||||
// copy to temporary location
|
||||
_, err = copy.Image(ctx, policyCtx, tmpRef, cacheRef, ©.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmp, err := tmpRef.NewImageSource(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestBytes, _, err := tmp.GetManifest(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var manifest imgSpecs.Manifest
|
||||
if err := json.Unmarshal(manifestBytes, &manifest); err != nil {
|
||||
return fmt.Errorf("unable to unmarshall mafinest json: %v", err)
|
||||
}
|
||||
|
||||
eng, err := umoci.OpenLayout(tmpDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open oci layout: %v", err)
|
||||
}
|
||||
|
||||
var mo layer.MapOptions
|
||||
err = layer.UnpackRootfs(ctx, eng, dst, manifest, &mo, nil, imgSpecs.Descriptor{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to unpack rootfs: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user