mirror of
https://github.com/containerd/containerd.git
synced 2026-11-06 05:17:02 +00:00
Merge pull request #14403 from k8s-infra-cherrypick-robot/cherry-pick-14399-to-release/2.2
[release/2.2] fixes nil config flakes for spec and runtimeoptions
This commit is contained in:
4 files changed
+91
-3
No files matched your search
@@ -369,7 +369,12 @@ func readContainer(container *containers.Container, bkt *bolt.Bucket) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container.Runtime.Options = o
|
||||
// Avoid assigning a typed-nil (*anypb.Any)(nil) to the typeurl.Any
|
||||
// interface, which would produce a non-nil interface value that
|
||||
// fails type-URL lookup.
|
||||
if o != nil {
|
||||
container.Runtime.Options = o
|
||||
}
|
||||
case string(bucketKeySpec):
|
||||
var spec types.Any
|
||||
if err := proto.Unmarshal(v, &spec); err != nil {
|
||||
|
||||
@@ -722,3 +722,41 @@ func testEnv(t *testing.T) (context.Context, *bolt.DB) {
|
||||
|
||||
return ctx, db
|
||||
}
|
||||
|
||||
// TestContainerNilRuntimeOptionsRoundTrip verifies that a container stored
|
||||
// without Runtime.Options round-trips back with a genuinely nil interface
|
||||
// value, not a typed-nil *types.Any wrapped in a typeurl.Any interface.
|
||||
//
|
||||
// checkContainersEqual uses compareNil, which treats a typed-nil pointer
|
||||
// wrapped in an interface as equal to a true nil interface and therefore would
|
||||
// not catch this regression. A non-nil interface with a nil underlying pointer
|
||||
// causes type-URL lookup failures at runtime.
|
||||
func TestContainerNilRuntimeOptionsRoundTrip(t *testing.T) {
|
||||
ctx, db := testEnv(t)
|
||||
store := NewContainerStore(NewDB(db, nil, nil))
|
||||
|
||||
spec := &specs.Spec{}
|
||||
encoded, err := typeurl.MarshalAnyToProto(spec)
|
||||
require.NoError(t, err)
|
||||
|
||||
in := containers.Container{
|
||||
ID: "nil-runtime-opts",
|
||||
Spec: encoded,
|
||||
Runtime: containers.RuntimeInfo{
|
||||
Name: "testruntime",
|
||||
// Options intentionally left nil.
|
||||
},
|
||||
}
|
||||
|
||||
_, err = store.Create(ctx, in)
|
||||
require.NoError(t, err)
|
||||
|
||||
out, err := store.Get(ctx, "nil-runtime-opts")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Use direct interface comparison — not compareNil — to distinguish a
|
||||
// true nil interface from a typed-nil pointer wrapped in an interface.
|
||||
if out.Runtime.Options != nil {
|
||||
t.Errorf("Runtime.Options: want nil interface, got %T(%v)", out.Runtime.Options, out.Runtime.Options)
|
||||
}
|
||||
}
|
||||
@@ -365,10 +365,15 @@ func (s *sandboxStore) read(parent *bbolt.Bucket, id []byte) (api.Sandbox, error
|
||||
return api.Sandbox{}, err
|
||||
}
|
||||
|
||||
inst.Spec, err = boltutil.ReadAny(bucket, bucketKeySpec)
|
||||
spec, err := boltutil.ReadAny(bucket, bucketKeySpec)
|
||||
if err != nil {
|
||||
return api.Sandbox{}, err
|
||||
}
|
||||
// Avoid assigning a typed-nil (*anypb.Any)(nil) to the typeurl.Any interface,
|
||||
// which would produce a non-nil interface value that fails type-URL lookup.
|
||||
if spec != nil {
|
||||
inst.Spec = spec
|
||||
}
|
||||
|
||||
runtimeBucket := bucket.Bucket(bucketKeyRuntime)
|
||||
if runtimeBucket == nil {
|
||||
@@ -376,10 +381,13 @@ func (s *sandboxStore) read(parent *bbolt.Bucket, id []byte) (api.Sandbox, error
|
||||
}
|
||||
|
||||
inst.Runtime.Name = string(runtimeBucket.Get(bucketKeyName))
|
||||
inst.Runtime.Options, err = boltutil.ReadAny(runtimeBucket, bucketKeyOptions)
|
||||
runtimeOpts, err := boltutil.ReadAny(runtimeBucket, bucketKeyOptions)
|
||||
if err != nil {
|
||||
return api.Sandbox{}, err
|
||||
}
|
||||
if runtimeOpts != nil {
|
||||
inst.Runtime.Options = runtimeOpts
|
||||
}
|
||||
|
||||
inst.Extensions, err = boltutil.ReadExtensions(bucket)
|
||||
if err != nil {
|
||||
|
||||
@@ -264,3 +264,40 @@ func assertEqualInstances(t *testing.T, x, y api.Sandbox) {
|
||||
t.Fatalf("x and y are different: %s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSandboxNilInterfaceRoundTrip verifies that a sandbox stored without a
|
||||
// Spec or Runtime.Options round-trips back with genuinely nil interface values.
|
||||
//
|
||||
// compareNil (used by assertEqualInstances) treats a typed-nil pointer wrapped
|
||||
// in an interface as equal to a true nil interface, so it would not catch a
|
||||
// regression where ReadAny returns (*types.Any)(nil) and that typed-nil is
|
||||
// stored directly in the typeurl.Any field — producing a non-nil interface
|
||||
// that fails type-URL lookup at runtime.
|
||||
func TestSandboxNilInterfaceRoundTrip(t *testing.T) {
|
||||
ctx, db := testDB(t)
|
||||
store := NewSandboxStore(db)
|
||||
|
||||
in := api.Sandbox{
|
||||
ID: "nil-fields",
|
||||
Runtime: api.RuntimeOpts{Name: "test"},
|
||||
// Spec and Runtime.Options are intentionally left nil.
|
||||
}
|
||||
|
||||
if _, err := store.Create(ctx, in); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out, err := store.Get(ctx, "nil-fields")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Use direct interface comparison — not compareNil — to distinguish a
|
||||
// true nil interface from a typed-nil pointer wrapped in an interface.
|
||||
if out.Spec != nil {
|
||||
t.Errorf("Spec: want nil interface, got %T(%v)", out.Spec, out.Spec)
|
||||
}
|
||||
if out.Runtime.Options != nil {
|
||||
t.Errorf("Runtime.Options: want nil interface, got %T(%v)", out.Runtime.Options, out.Runtime.Options)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user