The primary feature we get with this PR is support for filters and labels on the image metadata store. In the process of doing this, the conventions for the API have been converged between containers and images, providing a model for other services. With images, `Put` (renamed to `Update` briefly) has been split into a `Create` and `Update`, allowing one to control the behavior around these operations. `Update` now includes support for masking fields at the datastore-level across both the containers and image service. Filters are now just string values to interpreted directly within the data store. This should allow for some interesting future use cases in which the datastore might use the syntax for more efficient query paths. The containers service has been updated to follow these conventions as closely as possible. Signed-off-by: Stephen J Day <stephen.day@docker.com>
142 lines
4.4 KiB
Go
142 lines
4.4 KiB
Go
package metadata
|
|
|
|
import (
|
|
"github.com/boltdb/bolt"
|
|
)
|
|
|
|
// The layout where a "/" delineates a bucket is desribed in the following
|
|
// section. Please try to follow this as closely as possible when adding
|
|
// functionality. We can bolster this with helpers and more structure if that
|
|
// becomes an issue.
|
|
//
|
|
// Generically, we try to do the following:
|
|
//
|
|
// <version>/<namespace>/<object>/<key> -> <field>
|
|
//
|
|
// version: Currently, this is "v1". Additions can be made to v1 in a backwards
|
|
// compatible way. If the layout changes, a new version must be made, along
|
|
// with a migration.
|
|
//
|
|
// namespace: the namespace to which this object belongs.
|
|
//
|
|
// object: defines which object set is stored in the bucket. There are two
|
|
// special objects, "labels" and "indexes". The "labels" bucket stores the
|
|
// labels for the parent namespace. The "indexes" object is reserved for
|
|
// indexing objects, if we require in the future.
|
|
//
|
|
// key: object-specific key identifying the storage bucket for the objects
|
|
// contents.
|
|
var (
|
|
bucketKeyVersion = []byte("v1")
|
|
bucketKeyObjectLabels = []byte("labels") // stores the labels for a namespace.
|
|
bucketKeyObjectIndexes = []byte("indexes") // reserved
|
|
bucketKeyObjectImages = []byte("images") // stores image objects
|
|
bucketKeyObjectContainers = []byte("containers") // stores container objects
|
|
bucketKeyObjectSnapshots = []byte("snapshots") // stores snapshot references
|
|
|
|
bucketKeyDigest = []byte("digest")
|
|
bucketKeyMediaType = []byte("mediatype")
|
|
bucketKeySize = []byte("size")
|
|
bucketKeyLabels = []byte("labels")
|
|
bucketKeyImage = []byte("image")
|
|
bucketKeyRuntime = []byte("runtime")
|
|
bucketKeyName = []byte("name")
|
|
bucketKeyOptions = []byte("options")
|
|
bucketKeySpec = []byte("spec")
|
|
bucketKeyRootFS = []byte("rootfs")
|
|
bucketKeyTarget = []byte("target")
|
|
bucketKeyCreatedAt = []byte("createdat")
|
|
bucketKeyUpdatedAt = []byte("updatedat")
|
|
)
|
|
|
|
func getBucket(tx *bolt.Tx, keys ...[]byte) *bolt.Bucket {
|
|
bkt := tx.Bucket(keys[0])
|
|
|
|
for _, key := range keys[1:] {
|
|
if bkt == nil {
|
|
break
|
|
}
|
|
bkt = bkt.Bucket(key)
|
|
}
|
|
|
|
return bkt
|
|
}
|
|
|
|
func createBucketIfNotExists(tx *bolt.Tx, keys ...[]byte) (*bolt.Bucket, error) {
|
|
bkt, err := tx.CreateBucketIfNotExists(keys[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, key := range keys[1:] {
|
|
bkt, err = bkt.CreateBucketIfNotExists(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return bkt, nil
|
|
}
|
|
|
|
func namespaceLabelsBucketPath(namespace string) [][]byte {
|
|
return [][]byte{bucketKeyVersion, []byte(namespace), bucketKeyObjectLabels}
|
|
}
|
|
|
|
func withNamespacesLabelsBucket(tx *bolt.Tx, namespace string, fn func(bkt *bolt.Bucket) error) error {
|
|
bkt, err := createBucketIfNotExists(tx, namespaceLabelsBucketPath(namespace)...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return fn(bkt)
|
|
}
|
|
|
|
func getNamespaceLabelsBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {
|
|
return getBucket(tx, namespaceLabelsBucketPath(namespace)...)
|
|
}
|
|
|
|
func imagesBucketPath(namespace string) [][]byte {
|
|
return [][]byte{bucketKeyVersion, []byte(namespace), bucketKeyObjectImages}
|
|
}
|
|
|
|
func withImagesBucket(tx *bolt.Tx, namespace string, fn func(bkt *bolt.Bucket) error) error {
|
|
bkt, err := createBucketIfNotExists(tx, imagesBucketPath(namespace)...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return fn(bkt)
|
|
}
|
|
|
|
func getImagesBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {
|
|
return getBucket(tx, imagesBucketPath(namespace)...)
|
|
}
|
|
|
|
func createContainersBucket(tx *bolt.Tx, namespace string) (*bolt.Bucket, error) {
|
|
bkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectContainers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bkt, nil
|
|
}
|
|
|
|
func getContainersBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {
|
|
return getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectContainers)
|
|
}
|
|
|
|
func getContainerBucket(tx *bolt.Tx, namespace, id string) *bolt.Bucket {
|
|
return getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectContainers, []byte(id))
|
|
}
|
|
|
|
func createSnapshotterBucket(tx *bolt.Tx, namespace, snapshotter string) (*bolt.Bucket, error) {
|
|
bkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectSnapshots, []byte(snapshotter))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bkt, nil
|
|
}
|
|
|
|
func getSnapshotterBucket(tx *bolt.Tx, namespace, snapshotter string) *bolt.Bucket {
|
|
return getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectSnapshots, []byte(snapshotter))
|
|
}
|