Add field to Container for client-defined data
This field allows a client to store specialized information in the container metadata rather than having to store this itself and keep the data in sync with containerd. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
@@ -50,6 +50,7 @@ var (
|
||||
bucketKeySnapshotKey = []byte("snapshotKey")
|
||||
bucketKeySnapshotter = []byte("snapshotter")
|
||||
bucketKeyTarget = []byte("target")
|
||||
bucketKeyExtensions = []byte("extensions")
|
||||
)
|
||||
|
||||
func getBucket(tx *bolt.Tx, keys ...[]byte) *bolt.Bucket {
|
||||
|
||||
@@ -146,7 +146,7 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai
|
||||
|
||||
if len(fieldpaths) == 0 {
|
||||
// only allow updates to these field on full replace.
|
||||
fieldpaths = []string{"labels", "spec"}
|
||||
fieldpaths = []string{"labels", "spec", "extensions"}
|
||||
|
||||
// Fields that are immutable must cause an error when no field paths
|
||||
// are provided. This allows these fields to become mutable in the
|
||||
@@ -186,6 +186,8 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai
|
||||
updated.Labels = container.Labels
|
||||
case "spec":
|
||||
updated.Spec = container.Spec
|
||||
case "extensions":
|
||||
updated.Extensions = container.Extensions
|
||||
default:
|
||||
return containers.Container{}, errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on %q", path, container.ID)
|
||||
}
|
||||
@@ -288,6 +290,21 @@ func readContainer(container *containers.Container, bkt *bolt.Bucket) error {
|
||||
container.SnapshotKey = string(v)
|
||||
case string(bucketKeySnapshotter):
|
||||
container.Snapshotter = string(v)
|
||||
case string(bucketKeyExtensions):
|
||||
buf := proto.NewBuffer(v)
|
||||
n, err := buf.DecodeVarint()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error reading number of container extensions")
|
||||
}
|
||||
extensions := make([]types.Any, 0, n)
|
||||
for i := 0; i < int(n); i++ {
|
||||
var any types.Any
|
||||
if err := buf.DecodeMessage(&any); err != nil {
|
||||
return errors.Wrap(err, "error decoding container extension")
|
||||
}
|
||||
extensions = append(extensions, any)
|
||||
}
|
||||
container.Extensions = extensions
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -335,6 +352,21 @@ func writeContainer(bkt *bolt.Bucket, container *containers.Container) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if container.Extensions != nil {
|
||||
buf := proto.NewBuffer(nil)
|
||||
if err := buf.EncodeVarint(uint64(len(container.Extensions))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ext := range container.Extensions {
|
||||
if err := buf.EncodeMessage(&ext); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := bkt.Put(bucketKeyExtensions, buf.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if container.Runtime.Options != nil {
|
||||
data, err := proto.Marshal(container.Runtime.Options)
|
||||
if err != nil {
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/containerd/containerd/filters"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/typeurl"
|
||||
"github.com/gogo/protobuf/types"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -420,6 +421,124 @@ func TestContainersCreateUpdateDelete(t *testing.T) {
|
||||
},
|
||||
createerr: errdefs.ErrInvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "UpdateExtensionsFull",
|
||||
original: containers.Container{
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("hello"),
|
||||
},
|
||||
},
|
||||
},
|
||||
input: containers.Container{
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("world"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: containers.Container{
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("world"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "UpdateExtensionsNotInFieldpath",
|
||||
original: containers.Container{
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("hello"),
|
||||
},
|
||||
},
|
||||
},
|
||||
input: containers.Container{
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("world"),
|
||||
},
|
||||
},
|
||||
},
|
||||
fieldpaths: []string{"labels"},
|
||||
expected: containers.Container{
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("hello"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "UpdateExtensionsFieldPath",
|
||||
original: containers.Container{
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("hello"),
|
||||
},
|
||||
},
|
||||
},
|
||||
input: containers.Container{
|
||||
Labels: map[string]string{
|
||||
"foo": "one",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("world"),
|
||||
},
|
||||
},
|
||||
},
|
||||
fieldpaths: []string{"extensions"},
|
||||
expected: containers.Container{
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
},
|
||||
Extensions: []types.Any{
|
||||
{
|
||||
TypeUrl: "test.update.extensions",
|
||||
Value: []byte("world"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
testcase.original.ID = testcase.name
|
||||
|
||||
Reference in New Issue
Block a user