From 27daa745e4a770c112177e90a90b86f2044bf32e Mon Sep 17 00:00:00 2001 From: James Rasell Date: Tue, 19 Aug 2025 13:51:12 +0100 Subject: [PATCH] namespace: Avoid potential panic when logging namespace name. (#26565) When the namespace was not found in state, indicated by a nil object, we were using the name field of the nil object for the return error. This code path does not currently get triggered as the call flow ensures the namespace will always be found within state. Making this change makes sure we do not hit this panic in the future. --- nomad/namespace_endpoint.go | 2 +- nomad/namespace_endpoint_test.go | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/nomad/namespace_endpoint.go b/nomad/namespace_endpoint.go index b9812dac5..273be4dc7 100644 --- a/nomad/namespace_endpoint.go +++ b/nomad/namespace_endpoint.go @@ -305,7 +305,7 @@ func (n *Namespace) namespaceNoAssociatedVarsLocally(namespace string, snap *sta func (n *Namespace) namespaceNoAssociatedQuotasLocally(namespace string, snap *state.StateSnapshot) (bool, error) { ns, _ := snap.NamespaceByName(nil, namespace) if ns == nil { - return false, fmt.Errorf("namespace %s does not exist", ns.Name) + return false, fmt.Errorf("namespace %s does not exist", namespace) } if ns.Quota != "" { return false, nil diff --git a/nomad/namespace_endpoint_test.go b/nomad/namespace_endpoint_test.go index 6a0583cc6..551c85515 100644 --- a/nomad/namespace_endpoint_test.go +++ b/nomad/namespace_endpoint_test.go @@ -696,6 +696,42 @@ func TestNamespaceEndpoint_DeleteNamespaces_Default(t *testing.T) { assert.NotNil(msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)) } +func TestNamespace_namespaceNoAssociatedQuotasLocally(t *testing.T) { + ci.Parallel(t) + + testServer, testServerCleanupFn := TestServer(t, nil) + t.Cleanup(testServerCleanupFn) + + namespaceEndpoint := &Namespace{ + srv: testServer, + } + + t.Run("namespace not found", func(t *testing.T) { + + stateSnapshot, err := testServer.State().Snapshot() + must.NoError(t, err) + + res, err := namespaceEndpoint.namespaceNoAssociatedQuotasLocally("not-found", stateSnapshot) + must.ErrorContains(t, err, "namespace not-found does not exist") + assert.False(t, res) + }) + + t.Run("namespace without quota", func(t *testing.T) { + + mockNamespace := mock.Namespace() + mockNamespace.Quota = "" + + testServer.State().UpsertNamespaces(testServer.raft.LastIndex(), []*structs.Namespace{mockNamespace}) + + stateSnapshot, err := testServer.State().Snapshot() + must.NoError(t, err) + + res, err := namespaceEndpoint.namespaceNoAssociatedQuotasLocally(mockNamespace.Name, stateSnapshot) + must.NoError(t, err) + assert.True(t, res) + }) +} + func TestNamespaceEndpoint_UpsertNamespaces(t *testing.T) { ci.Parallel(t) assert := assert.New(t)