From 22c037982cd13e31a5a07cc9978ede6d9763d1a1 Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Thu, 30 Oct 2025 22:48:32 +0000 Subject: [PATCH] API compat: replace nil values when adding fields Signed-off-by: Rob Murray --- daemon/internal/compat/compat.go | 2 +- daemon/internal/compat/compat_test.go | 45 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/daemon/internal/compat/compat.go b/daemon/internal/compat/compat.go index df9f455672..d80f14afe5 100644 --- a/daemon/internal/compat/compat.go +++ b/daemon/internal/compat/compat.go @@ -46,7 +46,7 @@ func appendFields(src, dst map[string]any) { continue } } - if _, ok := dst[k]; !ok { + if existing, ok := dst[k]; !ok || existing == nil { dst[k] = v } } diff --git a/daemon/internal/compat/compat_test.go b/daemon/internal/compat/compat_test.go index 449e9f27f1..18a025a59e 100644 --- a/daemon/internal/compat/compat_test.go +++ b/daemon/internal/compat/compat_test.go @@ -7,6 +7,8 @@ import ( "testing" "github.com/moby/moby/v2/daemon/internal/compat" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) type Info struct { @@ -80,6 +82,49 @@ func TestWrap(t *testing.T) { } } +func TestWrapNilPtrField(t *testing.T) { + type bStruct struct { + StringField string `json:"stringfield"` + } + type aStruct struct { + IntField *int `json:"intfield"` + StructField *bStruct `json:"structfield"` + } + info := &aStruct{} + + tests := []struct { + name string + options []compat.Option + expected string + }{ + { + name: "none", + expected: `{"intfield":null,"structfield":null}`, + }, + { + name: "replace nil int", + options: []compat.Option{compat.WithExtraFields(map[string]any{"intfield": 42})}, + expected: `{"intfield":42,"structfield":null}`, + }, + { + name: "replace nil struct", + options: []compat.Option{compat.WithExtraFields(map[string]any{ + "structfield": map[string]any{"stringfield": "hello"}, + })}, + expected: `{"intfield":null,"structfield":{"stringfield":"hello"}}`, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + resp := compat.Wrap(info, tc.options...) + data, err := json.Marshal(resp) + if assert.Check(t, err) { + assert.Check(t, is.Equal(string(data), tc.expected)) + } + }) + } +} + func TestNestedCompat(t *testing.T) { info := &Info{ Name: "daemon",