Make the tense and casing consistent. Add useful log messages in image service. Signed-off-by: Derek McGowan <derek@mcgstyle.net>
		
			
				
	
	
		
			390 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			390 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package metadata
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/boltdb/bolt"
 | 
						|
	"github.com/containerd/containerd/gc"
 | 
						|
	"github.com/containerd/containerd/log"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	// ResourceUnknown specifies an unknown resource
 | 
						|
	ResourceUnknown gc.ResourceType = iota
 | 
						|
	// ResourceContent specifies a content resource
 | 
						|
	ResourceContent
 | 
						|
	// ResourceSnapshot specifies a snapshot resource
 | 
						|
	ResourceSnapshot
 | 
						|
	// ResourceContainer specifies a container resource
 | 
						|
	ResourceContainer
 | 
						|
	// ResourceTask specifies a task resource
 | 
						|
	ResourceTask
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	labelGCRoot       = []byte("containerd.io/gc.root")
 | 
						|
	labelGCSnapRef    = []byte("containerd.io/gc.ref.snapshot.")
 | 
						|
	labelGCContentRef = []byte("containerd.io/gc.ref.content")
 | 
						|
)
 | 
						|
 | 
						|
func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
 | 
						|
	v1bkt := tx.Bucket(bucketKeyVersion)
 | 
						|
	if v1bkt == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// iterate through each namespace
 | 
						|
	v1c := v1bkt.Cursor()
 | 
						|
 | 
						|
	for k, v := v1c.First(); k != nil; k, v = v1c.Next() {
 | 
						|
		if v != nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		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
 | 
						|
				}
 | 
						|
				libkt := lbkt.Bucket(k)
 | 
						|
 | 
						|
				cbkt := libkt.Bucket(bucketKeyObjectContent)
 | 
						|
				if cbkt != nil {
 | 
						|
					if err := cbkt.ForEach(func(k, v []byte) error {
 | 
						|
						select {
 | 
						|
						case nc <- gcnode(ResourceContent, ns, string(k)):
 | 
						|
						case <-ctx.Done():
 | 
						|
							return ctx.Err()
 | 
						|
						}
 | 
						|
						return nil
 | 
						|
					}); err != nil {
 | 
						|
						return err
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				sbkt := libkt.Bucket(bucketKeyObjectSnapshots)
 | 
						|
				if sbkt != nil {
 | 
						|
					if err := sbkt.ForEach(func(sk, sv []byte) error {
 | 
						|
						if sv != nil {
 | 
						|
							return nil
 | 
						|
						}
 | 
						|
						snbkt := sbkt.Bucket(sk)
 | 
						|
 | 
						|
						return snbkt.ForEach(func(k, v []byte) error {
 | 
						|
							select {
 | 
						|
							case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)):
 | 
						|
							case <-ctx.Done():
 | 
						|
								return ctx.Err()
 | 
						|
							}
 | 
						|
							return nil
 | 
						|
						})
 | 
						|
					}); err != nil {
 | 
						|
						return err
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				return nil
 | 
						|
			}); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		ibkt := nbkt.Bucket(bucketKeyObjectImages)
 | 
						|
		if ibkt != nil {
 | 
						|
			if err := ibkt.ForEach(func(k, v []byte) error {
 | 
						|
				if v != nil {
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
 | 
						|
				target := ibkt.Bucket(k).Bucket(bucketKeyTarget)
 | 
						|
				if target != nil {
 | 
						|
					contentKey := string(target.Get(bucketKeyDigest))
 | 
						|
					select {
 | 
						|
					case nc <- gcnode(ResourceContent, ns, contentKey):
 | 
						|
					case <-ctx.Done():
 | 
						|
						return ctx.Err()
 | 
						|
					}
 | 
						|
				}
 | 
						|
				return sendSnapshotRefs(ns, ibkt.Bucket(k), func(n gc.Node) {
 | 
						|
					select {
 | 
						|
					case nc <- n:
 | 
						|
					case <-ctx.Done():
 | 
						|
					}
 | 
						|
				})
 | 
						|
			}); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		cbkt := nbkt.Bucket(bucketKeyObjectContent)
 | 
						|
		if cbkt != nil {
 | 
						|
			cbkt = cbkt.Bucket(bucketKeyObjectBlob)
 | 
						|
		}
 | 
						|
		if cbkt != nil {
 | 
						|
			if err := cbkt.ForEach(func(k, v []byte) error {
 | 
						|
				if v != nil {
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				return sendRootRef(ctx, nc, gcnode(ResourceContent, ns, string(k)), cbkt.Bucket(k))
 | 
						|
			}); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		cbkt = nbkt.Bucket(bucketKeyObjectContainers)
 | 
						|
		if cbkt != nil {
 | 
						|
			if err := cbkt.ForEach(func(k, v []byte) error {
 | 
						|
				if v != nil {
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				snapshotter := string(cbkt.Bucket(k).Get(bucketKeySnapshotter))
 | 
						|
				if snapshotter != "" {
 | 
						|
					ss := string(cbkt.Bucket(k).Get(bucketKeySnapshotKey))
 | 
						|
					select {
 | 
						|
					case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, ss)):
 | 
						|
					case <-ctx.Done():
 | 
						|
						return ctx.Err()
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				// TODO: Send additional snapshot refs through labels
 | 
						|
				return sendSnapshotRefs(ns, cbkt.Bucket(k), func(n gc.Node) {
 | 
						|
					select {
 | 
						|
					case nc <- n:
 | 
						|
					case <-ctx.Done():
 | 
						|
					}
 | 
						|
				})
 | 
						|
			}); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		sbkt := nbkt.Bucket(bucketKeyObjectSnapshots)
 | 
						|
		if sbkt != nil {
 | 
						|
			if err := sbkt.ForEach(func(sk, sv []byte) error {
 | 
						|
				if sv != nil {
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				snbkt := sbkt.Bucket(sk)
 | 
						|
 | 
						|
				return snbkt.ForEach(func(k, v []byte) error {
 | 
						|
					if v != nil {
 | 
						|
						return nil
 | 
						|
					}
 | 
						|
 | 
						|
					return sendRootRef(ctx, nc, gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)), snbkt.Bucket(k))
 | 
						|
				})
 | 
						|
			}); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)) error {
 | 
						|
	if node.Type == ResourceContent {
 | 
						|
		bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectContent, bucketKeyObjectBlob, []byte(node.Key))
 | 
						|
		if bkt == nil {
 | 
						|
			// Node may be created from dead edge
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		if err := sendSnapshotRefs(node.Namespace, bkt, fn); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		return sendContentRefs(node.Namespace, bkt, fn)
 | 
						|
	} else if node.Type == ResourceSnapshot {
 | 
						|
		parts := strings.SplitN(node.Key, "/", 2)
 | 
						|
		if len(parts) != 2 {
 | 
						|
			return errors.Errorf("invalid snapshot gc key %s", node.Key)
 | 
						|
		}
 | 
						|
		ss := parts[0]
 | 
						|
		name := parts[1]
 | 
						|
 | 
						|
		bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectSnapshots, []byte(ss), []byte(name))
 | 
						|
		if bkt == nil {
 | 
						|
			getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectSnapshots).ForEach(func(k, v []byte) error {
 | 
						|
				return nil
 | 
						|
			})
 | 
						|
 | 
						|
			// Node may be created from dead edge
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		if pv := bkt.Get(bucketKeyParent); len(pv) > 0 {
 | 
						|
			fn(gcnode(ResourceSnapshot, node.Namespace, fmt.Sprintf("%s/%s", ss, pv)))
 | 
						|
		}
 | 
						|
 | 
						|
		return sendSnapshotRefs(node.Namespace, bkt, fn)
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc.Node) error) error {
 | 
						|
	v1bkt := tx.Bucket(bucketKeyVersion)
 | 
						|
	if v1bkt == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// iterate through each namespace
 | 
						|
	v1c := v1bkt.Cursor()
 | 
						|
 | 
						|
	for k, v := v1c.First(); k != nil; k, v = v1c.Next() {
 | 
						|
		if v != nil {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		nbkt := v1bkt.Bucket(k)
 | 
						|
		ns := string(k)
 | 
						|
 | 
						|
		sbkt := nbkt.Bucket(bucketKeyObjectSnapshots)
 | 
						|
		if sbkt != nil {
 | 
						|
			if err := sbkt.ForEach(func(sk, sv []byte) error {
 | 
						|
				if sv != nil {
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				snbkt := sbkt.Bucket(sk)
 | 
						|
				return snbkt.ForEach(func(k, v []byte) error {
 | 
						|
					if v != nil {
 | 
						|
						return nil
 | 
						|
					}
 | 
						|
					node := gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k))
 | 
						|
					return fn(ctx, node)
 | 
						|
				})
 | 
						|
			}); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		cbkt := nbkt.Bucket(bucketKeyObjectContent)
 | 
						|
		if cbkt != nil {
 | 
						|
			cbkt = cbkt.Bucket(bucketKeyObjectBlob)
 | 
						|
		}
 | 
						|
		if cbkt != nil {
 | 
						|
			if err := cbkt.ForEach(func(k, v []byte) error {
 | 
						|
				if v != nil {
 | 
						|
					return nil
 | 
						|
				}
 | 
						|
				node := gcnode(ResourceContent, ns, string(k))
 | 
						|
				return fn(ctx, node)
 | 
						|
			}); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func remove(ctx context.Context, tx *bolt.Tx, node gc.Node) error {
 | 
						|
	v1bkt := tx.Bucket(bucketKeyVersion)
 | 
						|
	if v1bkt == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	nsbkt := v1bkt.Bucket([]byte(node.Namespace))
 | 
						|
	if nsbkt == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	switch node.Type {
 | 
						|
	case ResourceContent:
 | 
						|
		cbkt := nsbkt.Bucket(bucketKeyObjectContent)
 | 
						|
		if cbkt != nil {
 | 
						|
			cbkt = cbkt.Bucket(bucketKeyObjectBlob)
 | 
						|
		}
 | 
						|
		if cbkt != nil {
 | 
						|
			log.G(ctx).WithField("key", node.Key).Debug("remove content")
 | 
						|
			return cbkt.DeleteBucket([]byte(node.Key))
 | 
						|
		}
 | 
						|
	case ResourceSnapshot:
 | 
						|
		sbkt := nsbkt.Bucket(bucketKeyObjectSnapshots)
 | 
						|
		if sbkt != nil {
 | 
						|
			parts := strings.SplitN(node.Key, "/", 2)
 | 
						|
			if len(parts) != 2 {
 | 
						|
				return errors.Errorf("invalid snapshot gc key %s", node.Key)
 | 
						|
			}
 | 
						|
			ssbkt := sbkt.Bucket([]byte(parts[0]))
 | 
						|
			if ssbkt != nil {
 | 
						|
				log.G(ctx).WithField("key", parts[1]).WithField("snapshotter", parts[0]).Debug("remove snapshot")
 | 
						|
				return ssbkt.DeleteBucket([]byte(parts[1]))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// sendSnapshotRefs sends all snapshot references referred to by the labels in the bkt
 | 
						|
func sendSnapshotRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error {
 | 
						|
	lbkt := bkt.Bucket(bucketKeyObjectLabels)
 | 
						|
	if lbkt != nil {
 | 
						|
		lc := lbkt.Cursor()
 | 
						|
 | 
						|
		for k, v := lc.Seek(labelGCSnapRef); k != nil && strings.HasPrefix(string(k), string(labelGCSnapRef)); k, v = lc.Next() {
 | 
						|
			snapshotter := string(k[len(labelGCSnapRef):])
 | 
						|
			fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, v)))
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// sendContentRefs sends all content references referred to by the labels in the bkt
 | 
						|
func sendContentRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error {
 | 
						|
	lbkt := bkt.Bucket(bucketKeyObjectLabels)
 | 
						|
	if lbkt != nil {
 | 
						|
		lc := lbkt.Cursor()
 | 
						|
 | 
						|
		labelRef := string(labelGCContentRef)
 | 
						|
		for k, v := lc.Seek(labelGCContentRef); k != nil && strings.HasPrefix(string(k), labelRef); k, v = lc.Next() {
 | 
						|
			if ks := string(k); ks != labelRef {
 | 
						|
				// Allow reference naming, ignore names
 | 
						|
				if ks[len(labelRef)] != '.' {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			fn(gcnode(ResourceContent, ns, string(v)))
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func isRootRef(bkt *bolt.Bucket) bool {
 | 
						|
	lbkt := bkt.Bucket(bucketKeyObjectLabels)
 | 
						|
	if lbkt != nil {
 | 
						|
		rv := lbkt.Get(labelGCRoot)
 | 
						|
		if rv != nil {
 | 
						|
			// TODO: interpret rv as a timestamp and skip if expired
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func sendRootRef(ctx context.Context, nc chan<- gc.Node, n gc.Node, bkt *bolt.Bucket) error {
 | 
						|
	if isRootRef(bkt) {
 | 
						|
		select {
 | 
						|
		case nc <- n:
 | 
						|
		case <-ctx.Done():
 | 
						|
			return ctx.Err()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func gcnode(t gc.ResourceType, ns, key string) gc.Node {
 | 
						|
	return gc.Node{
 | 
						|
		Type:      t,
 | 
						|
		Namespace: ns,
 | 
						|
		Key:       key,
 | 
						|
	}
 | 
						|
}
 |