Merge pull request #379 from yanxuean/unpack

Use image.IsUnpacked
This commit is contained in:
Lantao Liu 2017-11-01 07:50:12 +01:00 committed by GitHub
commit 2cb1572667
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 174 additions and 256 deletions

View File

@ -21,10 +21,8 @@ import (
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
"github.com/opencontainers/image-spec/identity"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -35,19 +33,13 @@ func WithImageUnpack(i containerd.Image) containerd.NewContainerOpts {
if c.Snapshotter == "" { if c.Snapshotter == "" {
return errors.New("no snapshotter set for container") return errors.New("no snapshotter set for container")
} }
snapshotter := client.SnapshotService(c.Snapshotter) unpacked, err := i.IsUnpacked(ctx, c.Snapshotter)
diffIDs, err := i.RootFS(ctx)
if err != nil { if err != nil {
return errors.Wrap(err, "get image diff IDs") return errors.Wrap(err, "fail to check if image is unpacked")
} }
chainID := identity.ChainID(diffIDs) if unpacked {
_, err = snapshotter.Stat(ctx, chainID.String())
if err == nil {
return nil return nil
} }
if !errdefs.IsNotFound(err) {
return errors.Wrap(err, "stat snapshot")
}
// Unpack the snapshot. // Unpack the snapshot.
if err := i.Unpack(ctx, c.Snapshotter); err != nil { if err := i.Unpack(ctx, c.Snapshotter); err != nil {
return errors.Wrap(err, "unpack snapshot") return errors.Wrap(err, "unpack snapshot")

View File

@ -28,7 +28,6 @@ import (
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
containerdimages "github.com/containerd/containerd/images" containerdimages "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/snapshot"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
@ -99,7 +98,7 @@ func (c *criContainerdService) recover(ctx context.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to list images: %v", err) return fmt.Errorf("failed to list images: %v", err)
} }
images, err := loadImages(ctx, cImages, c.client.ContentStore(), c.client.SnapshotService(c.config.ContainerdConfig.Snapshotter)) images, err := loadImages(ctx, cImages, c.client.ContentStore(), c.config.ContainerdConfig.Snapshotter)
if err != nil { if err != nil {
return fmt.Errorf("failed to load images: %v", err) return fmt.Errorf("failed to load images: %v", err)
} }
@ -329,7 +328,7 @@ func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.S
// TODO(random-liu): Check whether image is unpacked, because containerd put image reference // TODO(random-liu): Check whether image is unpacked, because containerd put image reference
// into store before image is unpacked. // into store before image is unpacked.
func loadImages(ctx context.Context, cImages []containerd.Image, provider content.Provider, func loadImages(ctx context.Context, cImages []containerd.Image, provider content.Provider,
snapshotter snapshot.Snapshotter) ([]imagestore.Image, error) { snapshotter string) ([]imagestore.Image, error) {
// Group images by image id. // Group images by image id.
imageMap := make(map[string][]containerd.Image) imageMap := make(map[string][]containerd.Image)
for _, i := range cImages { for _, i := range cImages {
@ -355,6 +354,16 @@ func loadImages(ctx context.Context, cImages []containerd.Image, provider conten
glog.Warningf("The image content readiness for %q is not ok", i.Name()) glog.Warningf("The image content readiness for %q is not ok", i.Name())
continue continue
} }
// Checking existence of top-level snapshot for each image being recovered.
unpacked, err := i.IsUnpacked(ctx, snapshotter)
if err != nil {
glog.Warningf("Failed to Check whether image is unpacked for image %s: %v", i.Name(), err)
continue
}
if !unpacked {
glog.Warningf("The image %s is not unpacked.", i.Name())
// TODO(random-liu): Consider whether we should try unpack here.
}
info, err := getImageInfo(ctx, i, provider) info, err := getImageInfo(ctx, i, provider)
if err != nil { if err != nil {
@ -387,12 +396,6 @@ func loadImages(ctx context.Context, cImages []containerd.Image, provider conten
glog.Warningf("Invalid image reference %q", name) glog.Warningf("Invalid image reference %q", name)
} }
} }
// Checking existence of top-level snapshot for each image being recovered.
if _, err := snapshotter.Stat(ctx, image.ChainID); err != nil {
glog.Warningf("Failed to stat the top-level snapshot for image %+v: %v", image, err)
// TODO(random-liu): Consider whether we should try unpack here.
}
images = append(images, image) images = append(images, image)
} }
return images, nil return images, nil

View File

@ -2,7 +2,7 @@ github.com/blang/semver v3.1.0
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/containerd/cgroups f7dd103d3e4e696aa67152f6b4ddd1779a3455a9 github.com/containerd/cgroups f7dd103d3e4e696aa67152f6b4ddd1779a3455a9
github.com/containerd/containerd 04659d94051126f4fe4fcddf068550db447ff5a0 github.com/containerd/containerd fe3d9a70fabba8d73387db82585a2cbe07a5a68b
github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e github.com/containerd/continuity cf279e6ac893682272b4479d4c67fd3abf878b4e
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788 github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788

View File

@ -205,4 +205,4 @@ is released under the Apache 2.0 license. The README.md file, and files in the
"docs" folder are licensed under the Creative Commons Attribution 4.0 "docs" folder are licensed under the Creative Commons Attribution 4.0
International License under the terms and conditions set forth in the file International License under the terms and conditions set forth in the file
"LICENSE.docs". You may obtain a duplicate copy of the same license, titled "LICENSE.docs". You may obtain a duplicate copy of the same license, titled
CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/. CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/.

View File

@ -24,6 +24,7 @@ func dialer(address string, timeout time.Duration) (net.Conn, error) {
return winio.DialPipe(address, &timeout) return winio.DialPipe(address, &timeout)
} }
// DialAddress returns the dial address
func DialAddress(address string) string { func DialAddress(address string) string {
return address return address
} }

View File

@ -10,7 +10,7 @@ import (
"sync" "sync"
) )
// Resourcetype represents type of resource at a node // ResourceType represents type of resource at a node
type ResourceType uint8 type ResourceType uint8
// Node presents a resource which has a type and key, // Node presents a resource which has a type and key,
@ -145,10 +145,10 @@ func ConcurrentMark(ctx context.Context, root <-chan Node, refs func(context.Con
// Sweep removes all nodes returned through the channel which are not in // Sweep removes all nodes returned through the channel which are not in
// the reachable set by calling the provided remove function. // the reachable set by calling the provided remove function.
func Sweep(reachable map[Node]struct{}, all <-chan Node, remove func(Node) error) error { func Sweep(reachable map[Node]struct{}, all []Node, remove func(Node) error) error {
// All black objects are now reachable, and all white objects are // All black objects are now reachable, and all white objects are
// unreachable. Free those that are white! // unreachable. Free those that are white!
for node := range all { for _, node := range all {
if _, ok := reachable[node]; !ok { if _, ok := reachable[node]; !ok {
if err := remove(node); err != nil { if err := remove(node); err != nil {
return err return err

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/rootfs" "github.com/containerd/containerd/rootfs"
@ -30,6 +31,8 @@ type Image interface {
Size(ctx context.Context) (int64, error) Size(ctx context.Context) (int64, error)
// Config descriptor for the image. // Config descriptor for the image.
Config(ctx context.Context) (ocispec.Descriptor, error) Config(ctx context.Context) (ocispec.Descriptor, error)
// IsUnpacked returns whether or not an image is unpacked.
IsUnpacked(context.Context, string) (bool, error)
} }
var _ = (Image)(&image{}) var _ = (Image)(&image{})
@ -63,6 +66,26 @@ func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) {
return i.i.Config(ctx, provider, platforms.Default()) return i.i.Config(ctx, provider, platforms.Default())
} }
func (i *image) IsUnpacked(ctx context.Context, snapshotterName string) (bool, error) {
sn := i.client.SnapshotService(snapshotterName)
cs := i.client.ContentStore()
diffs, err := i.i.RootFS(ctx, cs, platforms.Default())
if err != nil {
return false, err
}
chainID := identity.ChainID(diffs)
_, err = sn.Stat(ctx, chainID.String())
if err == nil {
return true, nil
} else if !errdefs.IsNotFound(err) {
return false, err
}
return false, nil
}
func (i *image) Unpack(ctx context.Context, snapshotterName string) error { func (i *image) Unpack(ctx context.Context, snapshotterName string) error {
layers, err := i.getLayers(ctx, platforms.Default()) layers, err := i.getLayers(ctx, platforms.Default())
if err != nil { if err != nil {

View File

@ -190,6 +190,7 @@ func (m *DB) Update(fn func(*bolt.Tx) error) error {
return m.db.Update(fn) return m.db.Update(fn)
} }
// GarbageCollect starts garbage collection
func (m *DB) GarbageCollect(ctx context.Context) error { func (m *DB) GarbageCollect(ctx context.Context) error {
lt1 := time.Now() lt1 := time.Now()
m.wlock.Lock() m.wlock.Lock()
@ -198,41 +199,10 @@ func (m *DB) GarbageCollect(ctx context.Context) error {
log.G(ctx).WithField("d", time.Now().Sub(lt1)).Debug("metadata garbage collected") log.G(ctx).WithField("d", time.Now().Sub(lt1)).Debug("metadata garbage collected")
}() }()
var marked map[gc.Node]struct{} marked, err := m.getMarked(ctx)
if err := m.db.View(func(tx *bolt.Tx) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
roots := make(chan gc.Node)
errChan := make(chan error)
go func() {
defer close(errChan)
defer close(roots)
// Call roots
if err := scanRoots(ctx, tx, roots); err != nil {
cancel()
errChan <- err
}
}()
refs := func(ctx context.Context, n gc.Node, fn func(gc.Node)) error {
return references(ctx, tx, n, fn)
}
reachable, err := gc.ConcurrentMark(ctx, roots, refs)
if rerr := <-errChan; rerr != nil {
return rerr
}
if err != nil { if err != nil {
return err return err
} }
marked = reachable
return nil
}); err != nil {
return err
}
m.dirtyL.Lock() m.dirtyL.Lock()
defer m.dirtyL.Unlock() defer m.dirtyL.Unlock()
@ -241,15 +211,11 @@ func (m *DB) GarbageCollect(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
nodeC := make(chan gc.Node) rm := func(ctx context.Context, n gc.Node) error {
var scanErr error if _, ok := marked[n]; ok {
return nil
}
go func() {
defer close(nodeC)
scanErr = scanAll(ctx, tx, nodeC)
}()
rm := func(n gc.Node) error {
if n.Type == ResourceSnapshot { if n.Type == ResourceSnapshot {
if idx := strings.IndexRune(n.Key, '/'); idx > 0 { if idx := strings.IndexRune(n.Key, '/'); idx > 0 {
m.dirtySS[n.Key[:idx]] = struct{}{} m.dirtySS[n.Key[:idx]] = struct{}{}
@ -260,12 +226,8 @@ func (m *DB) GarbageCollect(ctx context.Context) error {
return remove(ctx, tx, n) return remove(ctx, tx, n)
} }
if err := gc.Sweep(marked, nodeC, rm); err != nil { if err := scanAll(ctx, tx, rm); err != nil {
return errors.Wrap(err, "failed to sweep") return errors.Wrap(err, "failed to scan and remove")
}
if scanErr != nil {
return errors.Wrap(scanErr, "failed to scan all")
} }
return nil return nil
@ -292,6 +254,54 @@ func (m *DB) GarbageCollect(ctx context.Context) error {
return nil return nil
} }
func (m *DB) getMarked(ctx context.Context) (map[gc.Node]struct{}, error) {
var marked map[gc.Node]struct{}
if err := m.db.View(func(tx *bolt.Tx) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
var (
nodes []gc.Node
wg sync.WaitGroup
roots = make(chan gc.Node)
)
wg.Add(1)
go func() {
defer wg.Done()
for n := range roots {
nodes = append(nodes, n)
}
}()
// Call roots
if err := scanRoots(ctx, tx, roots); err != nil {
cancel()
return err
}
close(roots)
wg.Wait()
refs := func(n gc.Node) ([]gc.Node, error) {
var sn []gc.Node
if err := references(ctx, tx, n, func(nn gc.Node) {
sn = append(sn, nn)
}); err != nil {
return nil, err
}
return sn, nil
}
reachable, err := gc.Tricolor(nodes, refs)
if err != nil {
return err
}
marked = reachable
return nil
}); err != nil {
return nil, err
}
return marked, nil
}
func (m *DB) cleanupSnapshotter(name string) { func (m *DB) cleanupSnapshotter(name string) {
ctx := context.Background() ctx := context.Background()
sn, ok := m.ss[name] sn, ok := m.ss[name]

View File

@ -12,10 +12,15 @@ import (
) )
const ( const (
// ResourceUnknown specifies an unknown resource
ResourceUnknown gc.ResourceType = iota ResourceUnknown gc.ResourceType = iota
// ResourceContent specifies a content resource
ResourceContent ResourceContent
// ResourceSnapshot specifies a snapshot resource
ResourceSnapshot ResourceSnapshot
// ResourceContainer specifies a container resource
ResourceContainer ResourceContainer
// ResourceTask specifies a task resource
ResourceTask ResourceTask
) )
@ -174,7 +179,7 @@ func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)
return nil return nil
} }
func scanAll(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc.Node) error) error {
v1bkt := tx.Bucket(bucketKeyVersion) v1bkt := tx.Bucket(bucketKeyVersion)
if v1bkt == nil { if v1bkt == nil {
return nil return nil
@ -201,12 +206,8 @@ func scanAll(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
if v != nil { if v != nil {
return nil return nil
} }
select { node := gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k))
case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)): return fn(ctx, node)
case <-ctx.Done():
return ctx.Err()
}
return nil
}) })
}); err != nil { }); err != nil {
return err return err
@ -222,12 +223,8 @@ func scanAll(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
if v != nil { if v != nil {
return nil return nil
} }
select { node := gcnode(ResourceContent, ns, string(k))
case nc <- gcnode(ResourceContent, ns, string(k)): return fn(ctx, node)
case <-ctx.Done():
return ctx.Err()
}
return nil
}); err != nil { }); err != nil {
return err return err
} }

View File

@ -1,83 +0,0 @@
package mount
// On Solaris we can't invoke the mount system call directly. First,
// the mount system call takes more than 6 arguments, and go doesn't
// support invoking system calls that take more than 6 arguments. Past
// that, the mount system call is a private interfaces. For example,
// the arguments and data structures passed to the kernel to create an
// nfs mount are private and can change at any time. The only public
// and stable interface for creating mounts on Solaris is the mount.8
// command, so we'll invoke that here.
import (
"bytes"
"errors"
"fmt"
"os/exec"
"strings"
"golang.org/x/sys/unix"
)
const (
mountCmd = "/usr/sbin/mount"
)
func doMount(arg ...string) error {
cmd := exec.Command(mountCmd, arg...)
/* Setup Stdin, Stdout, and Stderr */
stderr := new(bytes.Buffer)
cmd.Stdin = nil
cmd.Stdout = nil
cmd.Stderr = stderr
/*
* Run the command. If the command fails create a new error
* object to return that includes stderr output.
*/
err := cmd.Start()
if err != nil {
return err
}
err = cmd.Wait()
if err != nil {
return errors.New(fmt.Sprintf("%v: %s", err, stderr.String()))
}
return nil
}
func (m *Mount) Mount(target string) error {
var err error
if len(m.Options) == 0 {
err = doMount("-F", m.Type, m.Source, target)
} else {
err = doMount("-F", m.Type, "-o", strings.Join(m.Options, ","),
m.Source, target)
}
return err
}
func Unmount(mount string, flags int) error {
return unix.Unmount(mount, flags)
}
// UnmountAll repeatedly unmounts the given mount point until there
// are no mounts remaining (EINVAL is returned by mount), which is
// useful for undoing a stack of mounts on the same mount point.
func UnmountAll(mount string, flags int) error {
for {
if err := Unmount(mount, flags); err != nil {
// EINVAL is returned if the target is not a
// mount point, indicating that we are
// done. It can also indicate a few other
// things (such as invalid flags) which we
// unfortunately end up squelching here too.
if err == unix.EINVAL {
return nil
}
return err
}
}
}

View File

@ -3,17 +3,21 @@ package mount
import "github.com/pkg/errors" import "github.com/pkg/errors"
var ( var (
// ErrNotImplementOnWindows is returned when an action is not implemented for windows
ErrNotImplementOnWindows = errors.New("not implemented under windows") ErrNotImplementOnWindows = errors.New("not implemented under windows")
) )
// Mount to the provided target
func (m *Mount) Mount(target string) error { func (m *Mount) Mount(target string) error {
return ErrNotImplementOnWindows return ErrNotImplementOnWindows
} }
// Unmount the mount at the provided path
func Unmount(mount string, flags int) error { func Unmount(mount string, flags int) error {
return ErrNotImplementOnWindows return ErrNotImplementOnWindows
} }
// UnmountAll mounts at the provided path
func UnmountAll(mount string, flags int) error { func UnmountAll(mount string, flags int) error {
return ErrNotImplementOnWindows return ErrNotImplementOnWindows
} }

View File

@ -1,50 +0,0 @@
// +build solaris,cgo
package mount
/*
#include <stdio.h>
#include <stdlib.h>
#include <sys/mnttab.h>
*/
import "C"
import (
"fmt"
"unsafe"
)
// Self retrieves a list of mounts for the current running process.
func Self() ([]Info, error) {
path := C.CString(C.MNTTAB)
defer C.free(unsafe.Pointer(path))
mode := C.CString("r")
defer C.free(unsafe.Pointer(mode))
mnttab := C.fopen(path, mode)
if mnttab == nil {
return nil, fmt.Errorf("Failed to open %s", C.MNTTAB)
}
var out []Info
var mp C.struct_mnttab
ret := C.getmntent(mnttab, &mp)
for ret == 0 {
var mountinfo Info
mountinfo.Mountpoint = C.GoString(mp.mnt_mountp)
mountinfo.Source = C.GoString(mp.mnt_special)
mountinfo.FSType = C.GoString(mp.mnt_fstype)
mountinfo.Options = C.GoString(mp.mnt_mntopts)
out = append(out, mountinfo)
ret = C.getmntent(mnttab, &mp)
}
C.fclose(mnttab)
return out, nil
}
// PID collects the mounts for a specific process ID.
func PID(pid int) ([]Info, error) {
return nil, fmt.Errorf("mountinfo.PID is not implemented on solaris")
}

View File

@ -22,11 +22,11 @@ type InitContext struct {
Meta *Meta // plugins can fill in metadata at init. Meta *Meta // plugins can fill in metadata at init.
plugins *PluginSet plugins *Set
} }
// NewContext returns a new plugin InitContext // NewContext returns a new plugin InitContext
func NewContext(ctx context.Context, r *Registration, plugins *PluginSet, root, state string) *InitContext { func NewContext(ctx context.Context, r *Registration, plugins *Set, root, state string) *InitContext {
return &InitContext{ return &InitContext{
Context: log.WithModule(ctx, r.URI()), Context: log.WithModule(ctx, r.URI()),
Root: filepath.Join(root, r.URI()), Root: filepath.Join(root, r.URI()),
@ -72,26 +72,26 @@ func (p *Plugin) Instance() (interface{}, error) {
return p.instance, p.err return p.instance, p.err
} }
// PluginSet defines a plugin collection, used with InitContext. // Set defines a plugin collection, used with InitContext.
// //
// This maintains ordering and unique indexing over the set. // This maintains ordering and unique indexing over the set.
// //
// After iteratively instantiating plugins, this set should represent, the // After iteratively instantiating plugins, this set should represent, the
// ordered, initialization set of plugins for a containerd instance. // ordered, initialization set of plugins for a containerd instance.
type PluginSet struct { type Set struct {
ordered []*Plugin // order of initialization ordered []*Plugin // order of initialization
byTypeAndID map[Type]map[string]*Plugin byTypeAndID map[Type]map[string]*Plugin
} }
// NewPluginSet returns an initialized plugin set // NewPluginSet returns an initialized plugin set
func NewPluginSet() *PluginSet { func NewPluginSet() *Set {
return &PluginSet{ return &Set{
byTypeAndID: make(map[Type]map[string]*Plugin), byTypeAndID: make(map[Type]map[string]*Plugin),
} }
} }
// Add a plugin to the set // Add a plugin to the set
func (ps *PluginSet) Add(p *Plugin) error { func (ps *Set) Add(p *Plugin) error {
if byID, typeok := ps.byTypeAndID[p.Registration.Type]; !typeok { if byID, typeok := ps.byTypeAndID[p.Registration.Type]; !typeok {
ps.byTypeAndID[p.Registration.Type] = map[string]*Plugin{ ps.byTypeAndID[p.Registration.Type] = map[string]*Plugin{
p.Registration.ID: p, p.Registration.ID: p,
@ -107,7 +107,7 @@ func (ps *PluginSet) Add(p *Plugin) error {
} }
// Get returns the first plugin by its type // Get returns the first plugin by its type
func (ps *PluginSet) Get(t Type) (interface{}, error) { func (ps *Set) Get(t Type) (interface{}, error) {
for _, v := range ps.byTypeAndID[t] { for _, v := range ps.byTypeAndID[t] {
return v.Instance() return v.Instance()
} }

View File

@ -58,9 +58,13 @@ const (
// Registration contains information for registering a plugin // Registration contains information for registering a plugin
type Registration struct { type Registration struct {
// Type of the plugin
Type Type Type Type
// ID of the plugin
ID string ID string
// Config specific to the plugin
Config interface{} Config interface{}
// Requires is a list of plugins that the registered plugin requires to be available
Requires []Type Requires []Type
// InitFn is called when initializing a plugin. The registration and // InitFn is called when initializing a plugin. The registration and
@ -69,6 +73,7 @@ type Registration struct {
InitFn func(*InitContext) (interface{}, error) InitFn func(*InitContext) (interface{}, error)
} }
// Init the registered plugin
func (r *Registration) Init(ic *InitContext) *Plugin { func (r *Registration) Init(ic *InitContext) *Plugin {
p, err := r.InitFn(ic) p, err := r.InitFn(ic)
return &Plugin{ return &Plugin{

View File

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math/rand"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -215,13 +216,26 @@ func (c *Converter) fetchManifest(ctx context.Context, desc ocispec.Descriptor)
func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) error { func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) error {
log.G(ctx).Debug("fetch blob") log.G(ctx).Debug("fetch blob")
ref := remotes.MakeRefKey(ctx, desc) var (
ref = remotes.MakeRefKey(ctx, desc)
calc := newBlobStateCalculator() calc = newBlobStateCalculator()
retry = 16
)
tryit:
cw, err := c.contentStore.Writer(ctx, ref, desc.Size, desc.Digest) cw, err := c.contentStore.Writer(ctx, ref, desc.Size, desc.Digest)
if err != nil { if err != nil {
if !errdefs.IsAlreadyExists(err) { if errdefs.IsUnavailable(err) {
select {
case <-time.After(time.Millisecond * time.Duration(rand.Intn(retry))):
if retry < 2048 {
retry = retry << 1
}
goto tryit
case <-ctx.Done():
return err
}
} else if !errdefs.IsAlreadyExists(err) {
return err return err
} }

View File

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"math/rand"
"time" "time"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
@ -84,7 +85,7 @@ func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc
// of writer and abort if not updated recently. // of writer and abort if not updated recently.
select { select {
case <-time.After(time.Millisecond * time.Duration(retry)): case <-time.After(time.Millisecond * time.Duration(rand.Intn(retry))):
if retry < 2048 { if retry < 2048 {
retry = retry << 1 retry = retry << 1
} }

View File

@ -44,6 +44,6 @@ func (ra *remoteReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
return n, nil return n, nil
} }
func (rr *remoteReaderAt) Close() error { func (ra *remoteReaderAt) Close() error {
return nil return nil
} }

View File

@ -15,6 +15,7 @@ type remoteStore struct {
client contentapi.ContentClient client contentapi.ContentClient
} }
// NewStoreFromClient returns a new content store
func NewStoreFromClient(client contentapi.ContentClient) content.Store { func NewStoreFromClient(client contentapi.ContentClient) content.Store {
return &remoteStore{ return &remoteStore{
client: client, client: client,

View File

@ -9,7 +9,7 @@ import (
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// NewApplierFromClient returns a new Applier which communicates // NewDiffServiceFromClient returns a new diff service which communicates
// over a GRPC connection. // over a GRPC connection.
func NewDiffServiceFromClient(client diffapi.DiffClient) diff.Differ { func NewDiffServiceFromClient(client diffapi.DiffClient) diff.Differ {
return &remote{ return &remote{

View File

@ -13,6 +13,7 @@ type remoteStore struct {
client imagesapi.ImagesClient client imagesapi.ImagesClient
} }
// NewStoreFromClient returns a new image store client
func NewStoreFromClient(client imagesapi.ImagesClient) images.Store { func NewStoreFromClient(client imagesapi.ImagesClient) images.Store {
return &remoteStore{ return &remoteStore{
client: client, client: client,

View File

@ -10,6 +10,7 @@ import (
"github.com/gogo/protobuf/types" "github.com/gogo/protobuf/types"
) )
// NewStoreFromClient returns a new namespace store
func NewStoreFromClient(client api.NamespacesClient) namespaces.Store { func NewStoreFromClient(client api.NamespacesClient) namespaces.Store {
return &remote{client: client} return &remote{client: client}
} }

View File

@ -20,6 +20,9 @@ const (
KindCommitted KindCommitted
) )
// ParseKind parses the provided string into a Kind
//
// If the string cannot be parsed KindUnknown is returned
func ParseKind(s string) Kind { func ParseKind(s string) Kind {
s = strings.ToLower(s) s = strings.ToLower(s)
switch s { switch s {
@ -34,6 +37,7 @@ func ParseKind(s string) Kind {
return KindUnknown return KindUnknown
} }
// String returns the string representation of the Kind
func (k Kind) String() string { func (k Kind) String() string {
switch k { switch k {
case KindView: case KindView:
@ -47,10 +51,12 @@ func (k Kind) String() string {
return "Unknown" return "Unknown"
} }
// MarshalJSON the Kind to JSON
func (k Kind) MarshalJSON() ([]byte, error) { func (k Kind) MarshalJSON() ([]byte, error) {
return json.Marshal(k.String()) return json.Marshal(k.String())
} }
// UnmarshalJSON the Kind from JSON
func (k *Kind) UnmarshalJSON(b []byte) error { func (k *Kind) UnmarshalJSON(b []byte) error {
var s string var s string
if err := json.Unmarshal(b, &s); err != nil { if err := json.Unmarshal(b, &s); err != nil {
@ -81,6 +87,7 @@ type Usage struct {
Size int64 // provides usage, in bytes, of snapshot Size int64 // provides usage, in bytes, of snapshot
} }
// Add the provided usage to the current usage
func (u *Usage) Add(other Usage) { func (u *Usage) Add(other Usage) {
u.Size += other.Size u.Size += other.Size

View File

@ -15,6 +15,7 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
) )
// WithImageConfig configures the spec to from the configuration of an Image
func WithImageConfig(i Image) SpecOpts { func WithImageConfig(i Image) SpecOpts {
return func(ctx context.Context, client *Client, _ *containers.Container, s *specs.Spec) error { return func(ctx context.Context, client *Client, _ *containers.Container, s *specs.Spec) error {
var ( var (
@ -51,6 +52,8 @@ func WithImageConfig(i Image) SpecOpts {
} }
} }
// WithTTY sets the information on the spec as well as the environment variables for
// using a TTY
func WithTTY(width, height int) SpecOpts { func WithTTY(width, height int) SpecOpts {
return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error { return func(_ context.Context, _ *Client, _ *containers.Container, s *specs.Spec) error {
s.Process.Terminal = true s.Process.Terminal = true
@ -63,6 +66,7 @@ func WithTTY(width, height int) SpecOpts {
} }
} }
// WithResources sets the provided resources on the spec for task updates
func WithResources(resources *specs.WindowsResources) UpdateTaskOpts { func WithResources(resources *specs.WindowsResources) UpdateTaskOpts {
return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error {
r.Resources = resources r.Resources = resources

View File

@ -1,5 +1,8 @@
package sys package sys
// SetOOMScore sets the oom score for the process
//
// Not implemented on Windows
func SetOOMScore(pid, score int) error { func SetOOMScore(pid, score int) error {
return nil return nil
} }

View File

@ -1,19 +0,0 @@
// +build solaris
package sys
import (
"errors"
)
//Solaris TODO
// GetSubreaper returns the subreaper setting for the calling process
func GetSubreaper() (int, error) {
return 0, errors.New("osutils GetSubreaper not implemented on Solaris")
}
// SetSubreaper sets the value i as the subreaper setting for the calling process
func SetSubreaper(i int) error {
return errors.New("osutils SetSubreaper not implemented on Solaris")
}

View File

@ -6,14 +6,17 @@ import (
"syscall" "syscall"
) )
// StatAtime returns the Atim
func StatAtime(st *syscall.Stat_t) syscall.Timespec { func StatAtime(st *syscall.Stat_t) syscall.Timespec {
return st.Atim return st.Atim
} }
// StatCtime returns the Ctim
func StatCtime(st *syscall.Stat_t) syscall.Timespec { func StatCtime(st *syscall.Stat_t) syscall.Timespec {
return st.Ctim return st.Ctim
} }
// StatMtime returns the Mtim
func StatMtime(st *syscall.Stat_t) syscall.Timespec { func StatMtime(st *syscall.Stat_t) syscall.Timespec {
return st.Mtim return st.Mtim
} }

View File

@ -23,7 +23,7 @@ github.com/stretchr/testify v1.1.4
github.com/davecgh/go-spew v1.1.0 github.com/davecgh/go-spew v1.1.0
github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib v1.0.0
github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6 github.com/containerd/fifo fbfb6a11ec671efbe94ad1c12c2e98773f19e1e6
github.com/urfave/cli 8ba6f23b6e36d03666a14bd9421f5e3efcb59aca github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
google.golang.org/grpc v1.3.0 google.golang.org/grpc v1.3.0
github.com/pkg/errors v0.8.0 github.com/pkg/errors v0.8.0