
These interfaces allow us to preserve both the checking of error "cause" as well as messages returned from the gRPC API so that the client gets full error reason instead of a default "metadata: not found" in the case of a missing image. Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com>
146 lines
3.0 KiB
Go
146 lines
3.0 KiB
Go
package metadata
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/boltdb/bolt"
|
|
"github.com/containerd/containerd/namespaces"
|
|
)
|
|
|
|
type namespaceStore struct {
|
|
tx *bolt.Tx
|
|
}
|
|
|
|
func NewNamespaceStore(tx *bolt.Tx) namespaces.Store {
|
|
return &namespaceStore{tx: tx}
|
|
}
|
|
|
|
func (s *namespaceStore) Create(ctx context.Context, namespace string, labels map[string]string) error {
|
|
topbkt, err := createBucketIfNotExists(s.tx, bucketKeyVersion)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// provides the already exists error.
|
|
bkt, err := topbkt.CreateBucket([]byte(namespace))
|
|
if err != nil {
|
|
if err == bolt.ErrBucketExists {
|
|
return ErrExists("")
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
lbkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectLabels)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for k, v := range labels {
|
|
if err := lbkt.Put([]byte(k), []byte(v)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *namespaceStore) Labels(ctx context.Context, namespace string) (map[string]string, error) {
|
|
labels := map[string]string{}
|
|
|
|
bkt := getNamespaceLabelsBucket(s.tx, namespace)
|
|
if bkt == nil {
|
|
return labels, nil
|
|
}
|
|
|
|
if err := bkt.ForEach(func(k, v []byte) error {
|
|
labels[string(k)] = string(v)
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return labels, nil
|
|
}
|
|
|
|
func (s *namespaceStore) SetLabel(ctx context.Context, namespace, key, value string) error {
|
|
return withNamespacesLabelsBucket(s.tx, namespace, func(bkt *bolt.Bucket) error {
|
|
if value == "" {
|
|
return bkt.Delete([]byte(key))
|
|
}
|
|
|
|
return bkt.Put([]byte(key), []byte(value))
|
|
})
|
|
|
|
}
|
|
|
|
func (s *namespaceStore) List(ctx context.Context) ([]string, error) {
|
|
bkt := getBucket(s.tx, bucketKeyVersion)
|
|
if bkt == nil {
|
|
return nil, nil // no namespaces!
|
|
}
|
|
|
|
var namespaces []string
|
|
if err := bkt.ForEach(func(k, v []byte) error {
|
|
if v != nil {
|
|
return nil // not a bucket
|
|
}
|
|
|
|
namespaces = append(namespaces, string(k))
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return namespaces, nil
|
|
}
|
|
|
|
func (s *namespaceStore) Delete(ctx context.Context, namespace string) error {
|
|
bkt := getBucket(s.tx, bucketKeyVersion)
|
|
if empty, err := s.namespaceEmpty(ctx, namespace); err != nil {
|
|
return err
|
|
} else if !empty {
|
|
return ErrNotEmpty("")
|
|
}
|
|
|
|
if err := bkt.DeleteBucket([]byte(namespace)); err != nil {
|
|
if err == bolt.ErrBucketNotFound {
|
|
return ErrNotFound("")
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *namespaceStore) namespaceEmpty(ctx context.Context, namespace string) (bool, error) {
|
|
ctx = namespaces.WithNamespace(ctx, namespace)
|
|
|
|
// need to check the various object stores.
|
|
|
|
imageStore := NewImageStore(s.tx)
|
|
images, err := imageStore.List(ctx)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if len(images) > 0 {
|
|
return false, nil
|
|
}
|
|
|
|
containerStore := NewContainerStore(s.tx)
|
|
containers, err := containerStore.List(ctx, "")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if len(containers) > 0 {
|
|
return false, nil
|
|
}
|
|
|
|
// TODO(stevvooe): Need to add check for content store, as well. Still need
|
|
// to make content store namespace aware.
|
|
|
|
return true, nil
|
|
}
|