Merge pull request #538 from AkihiroSuda/vendor-containerd-20180724
vendor: update containerddocker-18.09
commit
1b8651bbe8
|
@ -25,8 +25,8 @@ import (
|
|||
|
||||
const (
|
||||
dgstFileData0 = digest.Digest("sha256:cd8e75bca50f2d695f220d0cb0997d8ead387e4f926e8669a92d7f104cc9885b")
|
||||
dgstDirD0 = digest.Digest("sha256:311457c20a9b6bfc7b368282be86a0c98b7be882a268967605559c9b5acd7068")
|
||||
dgstDirD0Modified = digest.Digest("sha256:a0da3975efcd81ddec35ba1481f7b57a46af1c1e42a14b6024323d3fe2e7b2d8")
|
||||
dgstDirD0 = digest.Digest("sha256:d47454417d2c554067fbefe5f5719edc49f3cfe969c36b62e34a187a4da0cc9a")
|
||||
dgstDirD0Modified = digest.Digest("sha256:555ffa3028630d97ba37832b749eda85ab676fd64ffb629fbf0f4ec8c1e3bff1")
|
||||
)
|
||||
|
||||
func TestChecksumBasicFile(t *testing.T) {
|
||||
|
@ -96,7 +96,7 @@ func TestChecksumBasicFile(t *testing.T) {
|
|||
dgst, err = cc.Checksum(context.TODO(), ref, "/")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, digest.Digest("sha256:f57ab28e15b8dadb573ef097f2f99967f3acc4c44accc4888f4df510f9e9d2de"), dgst)
|
||||
assert.Equal(t, digest.Digest("sha256:427c9cf9ae98c0f81fb57a3076b965c7c149b6b0a85625ad4e884236649a42c6"), dgst)
|
||||
|
||||
dgst, err = cc.Checksum(context.TODO(), ref, "d0")
|
||||
assert.NoError(t, err)
|
||||
|
@ -336,7 +336,7 @@ func TestChecksumUnorderedFiles(t *testing.T) {
|
|||
dgst, err := cc.Checksum(context.TODO(), ref, "d0")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, dgst, digest.Digest("sha256:67bed5f4c5ec9cd367b89962f6b1836740e1694e35a127fa4af58b0c339a7b7b"))
|
||||
assert.Equal(t, dgst, digest.Digest("sha256:14276c302c940a80f82ca5477bf766c98a24702d6a9948ee71bb277cdad3ae05"))
|
||||
|
||||
// check regression from earier version that didn't track some files
|
||||
ch = []string{
|
||||
|
@ -498,7 +498,7 @@ func parseChange(str string) *change {
|
|||
st.Mode |= 0644
|
||||
case "dir":
|
||||
st.Mode |= uint32(os.ModeDir)
|
||||
st.Mode |= 0700
|
||||
st.Mode |= 0755
|
||||
case "symlink":
|
||||
if len(f) < 4 {
|
||||
panic(errStr)
|
||||
|
@ -554,7 +554,9 @@ func writeChanges(p string, inp []*change) error {
|
|||
return errors.Errorf("invalid non-stat change %s", p)
|
||||
}
|
||||
if c.fi.IsDir() {
|
||||
if err := os.Mkdir(p, 0700); err != nil {
|
||||
// The snapshot root ('/') is always created with 0755.
|
||||
// We use the same permission mode here.
|
||||
if err := os.Mkdir(p, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if c.fi.Mode()&os.ModeSymlink != 0 {
|
||||
|
|
|
@ -6,7 +6,7 @@ github.com/davecgh/go-spew v1.1.0
|
|||
github.com/pmezard/go-difflib v1.0.0
|
||||
golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2
|
||||
|
||||
github.com/containerd/containerd fb1084d9cc2b97aa679bf323158ac60d6e2ac6a8
|
||||
github.com/containerd/containerd a88b6319614de846458750ff882723479ca7b1a1
|
||||
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
|
||||
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
|
||||
github.com/sirupsen/logrus v1.0.0
|
||||
|
|
|
@ -366,10 +366,11 @@ func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header
|
|||
}
|
||||
|
||||
case tar.TypeLink:
|
||||
targetPath, err := fs.RootPath(extractDir, hdr.Linkname)
|
||||
targetPath, err := hardlinkRootPath(extractDir, hdr.Linkname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Link(targetPath, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -648,3 +649,27 @@ func copyBuffered(ctx context.Context, dst io.Writer, src io.Reader) (written in
|
|||
return written, err
|
||||
|
||||
}
|
||||
|
||||
// hardlinkRootPath returns target linkname, evaluating and bounding any
|
||||
// symlink to the parent directory.
|
||||
//
|
||||
// NOTE: Allow hardlink to the softlink, not the real one. For example,
|
||||
//
|
||||
// touch /tmp/zzz
|
||||
// ln -s /tmp/zzz /tmp/xxx
|
||||
// ln /tmp/xxx /tmp/yyy
|
||||
//
|
||||
// /tmp/yyy should be softlink which be same of /tmp/xxx, not /tmp/zzz.
|
||||
func hardlinkRootPath(root, linkname string) (string, error) {
|
||||
ppath, base := filepath.Split(linkname)
|
||||
ppath, err := fs.RootPath(root, ppath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
targetPath := filepath.Join(ppath, base)
|
||||
if !strings.HasPrefix(targetPath, root) {
|
||||
targetPath = root
|
||||
}
|
||||
return targetPath, nil
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ import (
|
|||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/events"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/leases"
|
||||
leasesproxy "github.com/containerd/containerd/leases/proxy"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/pkg/dialer"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
|
@ -79,8 +81,12 @@ func New(address string, opts ...ClientOpt) (*Client, error) {
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
rt := fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS)
|
||||
if copts.defaultRuntime != "" {
|
||||
rt = copts.defaultRuntime
|
||||
}
|
||||
c := &Client{
|
||||
runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS),
|
||||
runtime: rt,
|
||||
}
|
||||
if copts.services != nil {
|
||||
c.services = *copts.services
|
||||
|
@ -508,11 +514,11 @@ func (c *Client) IntrospectionService() introspectionapi.IntrospectionClient {
|
|||
}
|
||||
|
||||
// LeasesService returns the underlying Leases Client
|
||||
func (c *Client) LeasesService() leasesapi.LeasesClient {
|
||||
func (c *Client) LeasesService() leases.Manager {
|
||||
if c.leasesService != nil {
|
||||
return c.leasesService
|
||||
}
|
||||
return leasesapi.NewLeasesClient(c.conn)
|
||||
return leasesproxy.NewLeaseManager(leasesapi.NewLeasesClient(c.conn))
|
||||
}
|
||||
|
||||
// HealthService returns the underlying GRPC HealthClient
|
||||
|
|
|
@ -23,9 +23,10 @@ import (
|
|||
)
|
||||
|
||||
type clientOpts struct {
|
||||
defaultns string
|
||||
services *services
|
||||
dialOptions []grpc.DialOption
|
||||
defaultns string
|
||||
defaultRuntime string
|
||||
services *services
|
||||
dialOptions []grpc.DialOption
|
||||
}
|
||||
|
||||
// ClientOpt allows callers to set options on the containerd client
|
||||
|
@ -42,6 +43,14 @@ func WithDefaultNamespace(ns string) ClientOpt {
|
|||
}
|
||||
}
|
||||
|
||||
// WithDefaultRuntime sets the default runtime on the client
|
||||
func WithDefaultRuntime(rt string) ClientOpt {
|
||||
return func(c *clientOpts) error {
|
||||
c.defaultRuntime = rt
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDialOpts allows grpc.DialOptions to be set on the connection
|
||||
func WithDialOpts(opts []grpc.DialOption) ClientOpt {
|
||||
return func(c *clientOpts) error {
|
||||
|
|
|
@ -20,89 +20,27 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
leasesapi "github.com/containerd/containerd/api/services/leases/v1"
|
||||
"github.com/containerd/containerd/leases"
|
||||
)
|
||||
|
||||
// Lease is used to hold a reference to active resources which have not been
|
||||
// referenced by a root resource. This is useful for preventing garbage
|
||||
// collection of resources while they are actively being updated.
|
||||
type Lease struct {
|
||||
id string
|
||||
createdAt time.Time
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
// CreateLease creates a new lease
|
||||
func (c *Client) CreateLease(ctx context.Context) (Lease, error) {
|
||||
lapi := c.LeasesService()
|
||||
resp, err := lapi.Create(ctx, &leasesapi.CreateRequest{})
|
||||
if err != nil {
|
||||
return Lease{}, err
|
||||
}
|
||||
|
||||
return Lease{
|
||||
id: resp.Lease.ID,
|
||||
client: c,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListLeases lists active leases
|
||||
func (c *Client) ListLeases(ctx context.Context) ([]Lease, error) {
|
||||
lapi := c.LeasesService()
|
||||
resp, err := lapi.List(ctx, &leasesapi.ListRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
leases := make([]Lease, len(resp.Leases))
|
||||
for i := range resp.Leases {
|
||||
leases[i] = Lease{
|
||||
id: resp.Leases[i].ID,
|
||||
createdAt: resp.Leases[i].CreatedAt,
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
return leases, nil
|
||||
}
|
||||
|
||||
// WithLease attaches a lease on the context
|
||||
func (c *Client) WithLease(ctx context.Context) (context.Context, func(context.Context) error, error) {
|
||||
_, ok := leases.Lease(ctx)
|
||||
_, ok := leases.FromContext(ctx)
|
||||
if ok {
|
||||
return ctx, func(context.Context) error {
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
l, err := c.CreateLease(ctx)
|
||||
ls := c.LeasesService()
|
||||
|
||||
l, err := ls.Create(ctx, leases.WithRandomID(), leases.WithExpiration(24*3600*time.Second))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ctx = leases.WithLease(ctx, l.ID())
|
||||
ctx = leases.WithLease(ctx, l.ID)
|
||||
return ctx, func(ctx context.Context) error {
|
||||
return l.Delete(ctx)
|
||||
return ls.Delete(ctx, l)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ID returns the lease ID
|
||||
func (l Lease) ID() string {
|
||||
return l.id
|
||||
}
|
||||
|
||||
// CreatedAt returns the time at which the lease was created
|
||||
func (l Lease) CreatedAt() time.Time {
|
||||
return l.createdAt
|
||||
}
|
||||
|
||||
// Delete deletes the lease, removing the reference to all resources created
|
||||
// during the lease.
|
||||
func (l Lease) Delete(ctx context.Context) error {
|
||||
lapi := l.client.LeasesService()
|
||||
_, err := lapi.Delete(ctx, &leasesapi.DeleteRequest{
|
||||
ID: l.id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -29,8 +29,8 @@ func WithLease(ctx context.Context, lid string) context.Context {
|
|||
return withGRPCLeaseHeader(ctx, lid)
|
||||
}
|
||||
|
||||
// Lease returns the lease from the context.
|
||||
func Lease(ctx context.Context) (string, bool) {
|
||||
// FromContext returns the lease from the context.
|
||||
func FromContext(ctx context.Context) (string, bool) {
|
||||
lid, ok := ctx.Value(leaseKey{}).(string)
|
||||
if !ok {
|
||||
return fromGRPCHeader(ctx)
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package leases
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WithRandomID sets the lease ID to a random unique value
|
||||
func WithRandomID() Opt {
|
||||
return func(l *Lease) error {
|
||||
t := time.Now()
|
||||
var b [3]byte
|
||||
rand.Read(b[:])
|
||||
l.ID = fmt.Sprintf("%d-%s", t.Nanosecond(), base64.URLEncoding.EncodeToString(b[:]))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithID sets the ID for the lease
|
||||
func WithID(id string) Opt {
|
||||
return func(l *Lease) error {
|
||||
l.ID = id
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package leases
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Opt is used to set options on a lease
|
||||
type Opt func(*Lease) error
|
||||
|
||||
// Manager is used to create, list, and remove leases
|
||||
type Manager interface {
|
||||
Create(context.Context, ...Opt) (Lease, error)
|
||||
Delete(context.Context, Lease) error
|
||||
List(context.Context, ...string) ([]Lease, error)
|
||||
}
|
||||
|
||||
// Lease retains resources to prevent cleanup before
|
||||
// the resources can be fully referenced.
|
||||
type Lease struct {
|
||||
ID string
|
||||
CreatedAt time.Time
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// WithLabels sets labels on a lease
|
||||
func WithLabels(labels map[string]string) Opt {
|
||||
return func(l *Lease) error {
|
||||
l.Labels = labels
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithExpiration sets an expiration on the lease
|
||||
func WithExpiration(d time.Duration) Opt {
|
||||
return func(l *Lease) error {
|
||||
if l.Labels == nil {
|
||||
l.Labels = map[string]string{}
|
||||
}
|
||||
l.Labels["containerd.io/gc.expire"] = time.Now().Add(d).Format(time.RFC3339)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
leasesapi "github.com/containerd/containerd/api/services/leases/v1"
|
||||
"github.com/containerd/containerd/leases"
|
||||
)
|
||||
|
||||
type proxyManager struct {
|
||||
client leasesapi.LeasesClient
|
||||
}
|
||||
|
||||
// NewLeaseManager returns a lease manager which communicates
|
||||
// through a grpc lease service.
|
||||
func NewLeaseManager(client leasesapi.LeasesClient) leases.Manager {
|
||||
return &proxyManager{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *proxyManager) Create(ctx context.Context, opts ...leases.Opt) (leases.Lease, error) {
|
||||
l := leases.Lease{}
|
||||
for _, opt := range opts {
|
||||
if err := opt(&l); err != nil {
|
||||
return leases.Lease{}, err
|
||||
}
|
||||
}
|
||||
resp, err := pm.client.Create(ctx, &leasesapi.CreateRequest{
|
||||
ID: l.ID,
|
||||
Labels: l.Labels,
|
||||
})
|
||||
if err != nil {
|
||||
return leases.Lease{}, err
|
||||
}
|
||||
|
||||
return leases.Lease{
|
||||
ID: resp.Lease.ID,
|
||||
CreatedAt: resp.Lease.CreatedAt,
|
||||
Labels: resp.Lease.Labels,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pm *proxyManager) Delete(ctx context.Context, l leases.Lease) error {
|
||||
_, err := pm.client.Delete(ctx, &leasesapi.DeleteRequest{
|
||||
ID: l.ID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (pm *proxyManager) List(ctx context.Context, filters ...string) ([]leases.Lease, error) {
|
||||
resp, err := pm.client.List(ctx, &leasesapi.ListRequest{
|
||||
Filters: filters,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := make([]leases.Lease, len(resp.Leases))
|
||||
for i := range resp.Leases {
|
||||
l[i] = leases.Lease{
|
||||
ID: resp.Leases[i].ID,
|
||||
CreatedAt: resp.Leases[i].CreatedAt,
|
||||
Labels: resp.Leases[i].Labels,
|
||||
}
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/filters"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/leases"
|
||||
)
|
||||
|
||||
func adaptImage(o interface{}) filters.Adaptor {
|
||||
|
@ -119,6 +120,23 @@ func adaptContentStatus(status content.Status) filters.Adaptor {
|
|||
})
|
||||
}
|
||||
|
||||
func adaptLease(lease leases.Lease) filters.Adaptor {
|
||||
return filters.AdapterFunc(func(fieldpath []string) (string, bool) {
|
||||
if len(fieldpath) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
switch fieldpath[0] {
|
||||
case "id":
|
||||
return lease.ID, len(lease.ID) > 0
|
||||
case "labels":
|
||||
return checkMap(fieldpath[1:], lease.Labels)
|
||||
}
|
||||
|
||||
return "", false
|
||||
})
|
||||
}
|
||||
|
||||
func checkMap(fieldpath []string, m map[string]string) (string, bool) {
|
||||
if len(m) == 0 {
|
||||
return "", false
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/containerd/containerd/gc"
|
||||
|
@ -39,12 +40,15 @@ const (
|
|||
ResourceContainer
|
||||
// ResourceTask specifies a task resource
|
||||
ResourceTask
|
||||
// ResourceLease specifies a lease
|
||||
ResourceLease
|
||||
)
|
||||
|
||||
var (
|
||||
labelGCRoot = []byte("containerd.io/gc.root")
|
||||
labelGCSnapRef = []byte("containerd.io/gc.ref.snapshot.")
|
||||
labelGCContentRef = []byte("containerd.io/gc.ref.content")
|
||||
labelGCExpire = []byte("containerd.io/gc.expire")
|
||||
)
|
||||
|
||||
func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
|
||||
|
@ -53,6 +57,8 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
expThreshold := time.Now()
|
||||
|
||||
// iterate through each namespace
|
||||
v1c := v1bkt.Cursor()
|
||||
|
||||
|
@ -71,6 +77,30 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
|
|||
}
|
||||
libkt := lbkt.Bucket(k)
|
||||
|
||||
if lblbkt := libkt.Bucket(bucketKeyObjectLabels); lblbkt != nil {
|
||||
if expV := lblbkt.Get(labelGCExpire); expV != nil {
|
||||
exp, err := time.Parse(time.RFC3339, string(expV))
|
||||
if err != nil {
|
||||
// label not used, log and continue to use lease
|
||||
log.G(ctx).WithError(err).WithField("lease", string(k)).Infof("ignoring invalid expiration value %q", string(expV))
|
||||
} else if expThreshold.After(exp) {
|
||||
// lease has expired, skip
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case nc <- gcnode(ResourceLease, ns, string(k)):
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
// Emit content and snapshots as roots instead of implementing
|
||||
// in references. Since leases cannot be referenced there is
|
||||
// no need to allow the lookup to be recursive, handling here
|
||||
// therefore reduces the number of database seeks.
|
||||
|
||||
cbkt := libkt.Bucket(bucketKeyObjectContent)
|
||||
if cbkt != nil {
|
||||
if err := cbkt.ForEach(func(k, v []byte) error {
|
||||
|
@ -261,6 +291,18 @@ func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc
|
|||
nbkt := v1bkt.Bucket(k)
|
||||
ns := string(k)
|
||||
|
||||
lbkt := nbkt.Bucket(bucketKeyObjectLeases)
|
||||
if lbkt != nil {
|
||||
if err := lbkt.ForEach(func(k, v []byte) error {
|
||||
if v != nil {
|
||||
return nil
|
||||
}
|
||||
return fn(ctx, gcnode(ResourceLease, ns, string(k)))
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sbkt := nbkt.Bucket(bucketKeyObjectSnapshots)
|
||||
if sbkt != nil {
|
||||
if err := sbkt.ForEach(func(sk, sv []byte) error {
|
||||
|
@ -334,6 +376,11 @@ func remove(ctx context.Context, tx *bolt.Tx, node gc.Node) error {
|
|||
return ssbkt.DeleteBucket([]byte(parts[1]))
|
||||
}
|
||||
}
|
||||
case ResourceLease:
|
||||
lbkt := nsbkt.Bucket(bucketKeyObjectLeases)
|
||||
if lbkt != nil {
|
||||
return lbkt.DeleteBucket([]byte(node.Key))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/filters"
|
||||
"github.com/containerd/containerd/leases"
|
||||
"github.com/containerd/containerd/metadata/boltutil"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
|
@ -29,17 +30,6 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Lease retains resources to prevent garbage collection before
|
||||
// the resources can be fully referenced.
|
||||
type Lease struct {
|
||||
ID string
|
||||
CreatedAt time.Time
|
||||
Labels map[string]string
|
||||
|
||||
Content []string
|
||||
Snapshots map[string][]string
|
||||
}
|
||||
|
||||
// LeaseManager manages the create/delete lifecyle of leases
|
||||
// and also returns existing leases
|
||||
type LeaseManager struct {
|
||||
|
@ -55,49 +45,56 @@ func NewLeaseManager(tx *bolt.Tx) *LeaseManager {
|
|||
}
|
||||
|
||||
// Create creates a new lease using the provided lease
|
||||
func (lm *LeaseManager) Create(ctx context.Context, lid string, labels map[string]string) (Lease, error) {
|
||||
func (lm *LeaseManager) Create(ctx context.Context, opts ...leases.Opt) (leases.Lease, error) {
|
||||
var l leases.Lease
|
||||
for _, opt := range opts {
|
||||
if err := opt(&l); err != nil {
|
||||
return leases.Lease{}, err
|
||||
}
|
||||
}
|
||||
if l.ID == "" {
|
||||
return leases.Lease{}, errors.New("lease id must be provided")
|
||||
}
|
||||
|
||||
namespace, err := namespaces.NamespaceRequired(ctx)
|
||||
if err != nil {
|
||||
return Lease{}, err
|
||||
return leases.Lease{}, err
|
||||
}
|
||||
|
||||
topbkt, err := createBucketIfNotExists(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
|
||||
if err != nil {
|
||||
return Lease{}, err
|
||||
return leases.Lease{}, err
|
||||
}
|
||||
|
||||
txbkt, err := topbkt.CreateBucket([]byte(lid))
|
||||
txbkt, err := topbkt.CreateBucket([]byte(l.ID))
|
||||
if err != nil {
|
||||
if err == bolt.ErrBucketExists {
|
||||
err = errdefs.ErrAlreadyExists
|
||||
}
|
||||
return Lease{}, errors.Wrapf(err, "lease %q", lid)
|
||||
return leases.Lease{}, errors.Wrapf(err, "lease %q", l.ID)
|
||||
}
|
||||
|
||||
t := time.Now().UTC()
|
||||
createdAt, err := t.MarshalBinary()
|
||||
if err != nil {
|
||||
return Lease{}, err
|
||||
return leases.Lease{}, err
|
||||
}
|
||||
if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil {
|
||||
return Lease{}, err
|
||||
return leases.Lease{}, err
|
||||
}
|
||||
|
||||
if labels != nil {
|
||||
if err := boltutil.WriteLabels(txbkt, labels); err != nil {
|
||||
return Lease{}, err
|
||||
if l.Labels != nil {
|
||||
if err := boltutil.WriteLabels(txbkt, l.Labels); err != nil {
|
||||
return leases.Lease{}, err
|
||||
}
|
||||
}
|
||||
l.CreatedAt = t
|
||||
|
||||
return Lease{
|
||||
ID: lid,
|
||||
CreatedAt: t,
|
||||
Labels: labels,
|
||||
}, nil
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Delete delets the lease with the provided lease ID
|
||||
func (lm *LeaseManager) Delete(ctx context.Context, lid string) error {
|
||||
func (lm *LeaseManager) Delete(ctx context.Context, lease leases.Lease) error {
|
||||
namespace, err := namespaces.NamespaceRequired(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -107,24 +104,29 @@ func (lm *LeaseManager) Delete(ctx context.Context, lid string) error {
|
|||
if topbkt == nil {
|
||||
return nil
|
||||
}
|
||||
if err := topbkt.DeleteBucket([]byte(lid)); err != nil && err != bolt.ErrBucketNotFound {
|
||||
if err := topbkt.DeleteBucket([]byte(lease.ID)); err != nil && err != bolt.ErrBucketNotFound {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List lists all active leases
|
||||
func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter ...string) ([]Lease, error) {
|
||||
func (lm *LeaseManager) List(ctx context.Context, fs ...string) ([]leases.Lease, error) {
|
||||
namespace, err := namespaces.NamespaceRequired(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var leases []Lease
|
||||
filter, err := filters.ParseAll(fs...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
var ll []leases.Lease
|
||||
|
||||
topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
|
||||
if topbkt == nil {
|
||||
return leases, nil
|
||||
return ll, nil
|
||||
}
|
||||
|
||||
if err := topbkt.ForEach(func(k, v []byte) error {
|
||||
|
@ -133,7 +135,7 @@ func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter
|
|||
}
|
||||
txbkt := topbkt.Bucket(k)
|
||||
|
||||
l := Lease{
|
||||
l := leases.Lease{
|
||||
ID: string(k),
|
||||
}
|
||||
|
||||
|
@ -150,21 +152,20 @@ func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter
|
|||
}
|
||||
l.Labels = labels
|
||||
|
||||
// TODO: Read Snapshots
|
||||
// TODO: Read Content
|
||||
|
||||
leases = append(leases, l)
|
||||
if filter.Match(adaptLease(l)) {
|
||||
ll = append(ll, l)
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return leases, nil
|
||||
return ll, nil
|
||||
}
|
||||
|
||||
func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
|
||||
lid, ok := leases.Lease(ctx)
|
||||
lid, ok := leases.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -193,7 +194,7 @@ func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string)
|
|||
}
|
||||
|
||||
func removeSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
|
||||
lid, ok := leases.Lease(ctx)
|
||||
lid, ok := leases.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -213,7 +214,7 @@ func removeSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key stri
|
|||
}
|
||||
|
||||
func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error {
|
||||
lid, ok := leases.Lease(ctx)
|
||||
lid, ok := leases.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -237,7 +238,7 @@ func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error
|
|||
}
|
||||
|
||||
func removeContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error {
|
||||
lid, ok := leases.Lease(ctx)
|
||||
lid, ok := leases.FromContext(ctx)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ const (
|
|||
InternalPlugin Type = "io.containerd.internal.v1"
|
||||
// RuntimePlugin implements a runtime
|
||||
RuntimePlugin Type = "io.containerd.runtime.v1"
|
||||
// RuntimePluginV2 implements a runtime v2
|
||||
RuntimePluginV2 Type = "io.containerd.runtime.v2"
|
||||
// ServicePlugin implements a internal service
|
||||
ServicePlugin Type = "io.containerd.service.v1"
|
||||
// GRPCPlugin implements a grpc service
|
||||
|
|
|
@ -20,12 +20,12 @@ import (
|
|||
containersapi "github.com/containerd/containerd/api/services/containers/v1"
|
||||
"github.com/containerd/containerd/api/services/diff/v1"
|
||||
imagesapi "github.com/containerd/containerd/api/services/images/v1"
|
||||
"github.com/containerd/containerd/api/services/leases/v1"
|
||||
namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1"
|
||||
"github.com/containerd/containerd/api/services/tasks/v1"
|
||||
"github.com/containerd/containerd/containers"
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/leases"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
)
|
||||
|
@ -39,7 +39,7 @@ type services struct {
|
|||
taskService tasks.TasksClient
|
||||
diffService DiffService
|
||||
eventService EventService
|
||||
leasesService leases.LeasesClient
|
||||
leasesService leases.Manager
|
||||
}
|
||||
|
||||
// ServicesOpt allows callers to set options on the services
|
||||
|
@ -105,7 +105,7 @@ func WithNamespaceService(namespaceService namespacesapi.NamespacesClient) Servi
|
|||
}
|
||||
|
||||
// WithLeasesService sets the lease service.
|
||||
func WithLeasesService(leasesService leases.LeasesClient) ServicesOpt {
|
||||
func WithLeasesService(leasesService leases.Manager) ServicesOpt {
|
||||
return func(s *services) {
|
||||
s.leasesService = leasesService
|
||||
}
|
||||
|
|
|
@ -251,6 +251,9 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
|
|||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
if err := os.Chmod(td, 0755); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to chmod %s to 0755", td)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if td != "" {
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
package sys
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Exit is the wait4 information from an exited process
|
||||
type Exit struct {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright The containerd Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package sys
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// If arg2 is nonzero, set the "child subreaper" attribute of the
|
||||
// calling process; if arg2 is zero, unset the attribute. When a
|
||||
// process is marked as a child subreaper, all of the children
|
||||
// that it creates, and their descendants, will be marked as
|
||||
// having a subreaper. In effect, a subreaper fulfills the role
|
||||
// of init(1) for its descendant processes. Upon termination of
|
||||
// a process that is orphaned (i.e., its immediate parent has
|
||||
// already terminated) and marked as having a subreaper, the
|
||||
// nearest still living ancestor subreaper will receive a SIGCHLD
|
||||
// signal and be able to wait(2) on the process to discover its
|
||||
// termination status.
|
||||
const setChildSubreaper = 36
|
||||
|
||||
// SetSubreaper sets the value i as the subreaper setting for the calling process
|
||||
func SetSubreaper(i int) error {
|
||||
return unix.Prctl(setChildSubreaper, uintptr(i), 0, 0, 0)
|
||||
}
|
||||
|
||||
// GetSubreaper returns the subreaper setting for the calling process
|
||||
func GetSubreaper() (int, error) {
|
||||
var i uintptr
|
||||
|
||||
if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return int(i), nil
|
||||
}
|
|
@ -40,6 +40,7 @@ import (
|
|||
"github.com/containerd/typeurl"
|
||||
google_protobuf "github.com/gogo/protobuf/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
is "github.com/opencontainers/image-spec/specs-go"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -424,6 +425,9 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag
|
|||
return nil, err
|
||||
}
|
||||
index := v1.Index{
|
||||
Versioned: is.Versioned{
|
||||
SchemaVersion: 2,
|
||||
},
|
||||
Annotations: make(map[string]string),
|
||||
}
|
||||
if err := t.checkpointTask(ctx, &index, request); err != nil {
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
)
|
||||
|
||||
// NewTaskOpts allows the caller to set options on a new task
|
||||
|
@ -36,14 +35,6 @@ func WithRootFS(mounts []mount.Mount) NewTaskOpts {
|
|||
}
|
||||
}
|
||||
|
||||
// WithExit causes the task to exit after a successful checkpoint
|
||||
func WithExit(r *CheckpointTaskInfo) error {
|
||||
r.Options = &runctypes.CheckpointOptions{
|
||||
Exit: true,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithCheckpointName sets the image name for the checkpoint
|
||||
func WithCheckpointName(name string) CheckpointTaskOpts {
|
||||
return func(r *CheckpointTaskInfo) error {
|
||||
|
|
|
@ -43,7 +43,7 @@ gotest.tools v2.1.0
|
|||
github.com/google/go-cmp v0.1.0
|
||||
|
||||
# cri dependencies
|
||||
github.com/containerd/cri v1.11.0
|
||||
github.com/containerd/cri 661f3b0377db409fe0e5677115f02ce7b89fd17d https://github.com/dmcgowan/cri-containerd
|
||||
github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534
|
||||
github.com/blang/semver v3.1.0
|
||||
github.com/containernetworking/cni v0.6.0
|
||||
|
|
Loading…
Reference in New Issue