fixes nil config flakes for spec and runtimeoptions

Signed-off-by: Mike Brown <brownwm@us.ibm.com>
This commit is contained in:
Mike Brown committed 2026-10-16 12:49:03 -05:00
1 parent b90f717873
commit e2ba25eb48
4 files changed
+91 -3

No files matched your search

+6 -1
View File
@@ -383,7 +383,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 {
+38
View File
@@ -721,3 +721,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)
}
}
+10 -2
View File
@@ -371,10 +371,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 {
@@ -382,10 +387,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 {
+37
View File
@@ -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)
}
}