diff --git a/rest-api/api/pkg/api/handler/allocation.go b/rest-api/api/pkg/api/handler/allocation.go index 0c2cf6704b..41ede27f71 100644 --- a/rest-api/api/pkg/api/handler/allocation.go +++ b/rest-api/api/pkg/api/handler/allocation.go @@ -1504,5 +1504,5 @@ func (dah DeleteAllocationHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/deletion_response_test_helper_test.go b/rest-api/api/pkg/api/handler/deletion_response_test_helper_test.go new file mode 100644 index 0000000000..544cf9295f --- /dev/null +++ b/rest-api/api/pkg/api/handler/deletion_response_test_helper_test.go @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package handler + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/NVIDIA/infra-controller/rest-api/api/pkg/api/model" +) + +func assertDeletionAcceptedResponse(t *testing.T, body []byte) { + t.Helper() + + var resp model.APIDeletionAcceptedResponse + require.NoError(t, json.Unmarshal(body, &resp)) + assert.Equal(t, model.DeletionRequestAcceptedMessage, resp.Message) +} diff --git a/rest-api/api/pkg/api/handler/infinibandpartition.go b/rest-api/api/pkg/api/handler/infinibandpartition.go index c3c3da7778..bced691198 100644 --- a/rest-api/api/pkg/api/handler/infinibandpartition.go +++ b/rest-api/api/pkg/api/handler/infinibandpartition.go @@ -1129,6 +1129,6 @@ func (dibph DeleteInfiniBandPartitionHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/instance.go b/rest-api/api/pkg/api/handler/instance.go index c383632448..eb5a7c7191 100644 --- a/rest-api/api/pkg/api/handler/instance.go +++ b/rest-api/api/pkg/api/handler/instance.go @@ -4954,7 +4954,7 @@ func (dih DeleteInstanceHandler) Handle(c echo.Context) error { // Return response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } // GetInstanceStatusDetailsHandler is the API Handler for getting Instance StatusDetail records diff --git a/rest-api/api/pkg/api/handler/instance_test.go b/rest-api/api/pkg/api/handler/instance_test.go index 4b6251ec1d..144f19ba01 100644 --- a/rest-api/api/pkg/api/handler/instance_test.go +++ b/rest-api/api/pkg/api/handler/instance_test.go @@ -9707,7 +9707,7 @@ func TestDeleteInstanceHandler_Handle(t *testing.T) { if tt.args.respCode != http.StatusAccepted { return } - assert.Contains(t, rec.Body.String(), "Deletion request was accepted") + assertDeletionAcceptedResponse(t, rec.Body.Bytes()) // Verify Instance in terminating state insDAO := cdbm.NewInstanceDAO(dbSession) diff --git a/rest-api/api/pkg/api/handler/instancetype.go b/rest-api/api/pkg/api/handler/instancetype.go index 7e37de7584..3d3865499c 100644 --- a/rest-api/api/pkg/api/handler/instancetype.go +++ b/rest-api/api/pkg/api/handler/instancetype.go @@ -1551,5 +1551,5 @@ func (dith DeleteInstanceTypeHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/ipblock.go b/rest-api/api/pkg/api/handler/ipblock.go index 511d7e27e2..4b7d5318a4 100644 --- a/rest-api/api/pkg/api/handler/ipblock.go +++ b/rest-api/api/pkg/api/handler/ipblock.go @@ -1167,5 +1167,5 @@ func (dipbh DeleteIPBlockHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/machine.go b/rest-api/api/pkg/api/handler/machine.go index c6bcd6b298..25493d4626 100644 --- a/rest-api/api/pkg/api/handler/machine.go +++ b/rest-api/api/pkg/api/handler/machine.go @@ -1957,5 +1957,5 @@ func (umh DeleteMachineHandler) Handle(c echo.Context) error { logger.Info().Msg("finishing API handler") - return c.JSON(http.StatusAccepted, nil) + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/machineinstancetype.go b/rest-api/api/pkg/api/handler/machineinstancetype.go index a1e084df3c..fdc236b85e 100644 --- a/rest-api/api/pkg/api/handler/machineinstancetype.go +++ b/rest-api/api/pkg/api/handler/machineinstancetype.go @@ -752,5 +752,5 @@ func (dmith DeleteMachineInstanceTypeHandler) Handle(c echo.Context) error { // Return response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/machinevalidation.go b/rest-api/api/pkg/api/handler/machinevalidation.go index 1e17d9f37c..70b3d2ad73 100644 --- a/rest-api/api/pkg/api/handler/machinevalidation.go +++ b/rest-api/api/pkg/api/handler/machinevalidation.go @@ -1671,5 +1671,5 @@ func (handler DeleteMachineValidationExternalConfigHandler) Handle(c echo.Contex // Create response logger.Info().Msg("finishing API handler") - return c.JSON(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/networksecuritygroup.go b/rest-api/api/pkg/api/handler/networksecuritygroup.go index 368d0eb5ae..98e1e418f0 100644 --- a/rest-api/api/pkg/api/handler/networksecuritygroup.go +++ b/rest-api/api/pkg/api/handler/networksecuritygroup.go @@ -982,7 +982,7 @@ func (dnsgh DeleteNetworkSecurityGroupHandler) Handle(c echo.Context) error { // Return response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } // ~~~~~ Delete Handler ~~~~~ // diff --git a/rest-api/api/pkg/api/handler/nvlinklogicalpartition.go b/rest-api/api/pkg/api/handler/nvlinklogicalpartition.go index 915c279d74..2e61b7bd13 100644 --- a/rest-api/api/pkg/api/handler/nvlinklogicalpartition.go +++ b/rest-api/api/pkg/api/handler/nvlinklogicalpartition.go @@ -1352,5 +1352,5 @@ func (dibph DeleteNVLinkLogicalPartitionHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/operatingsystem.go b/rest-api/api/pkg/api/handler/operatingsystem.go index d166924a09..8951c5fdd2 100644 --- a/rest-api/api/pkg/api/handler/operatingsystem.go +++ b/rest-api/api/pkg/api/handler/operatingsystem.go @@ -1605,6 +1605,6 @@ func (dsh DeleteOperatingSystemHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/site.go b/rest-api/api/pkg/api/handler/site.go index e446ab243d..3dad75fbe8 100644 --- a/rest-api/api/pkg/api/handler/site.go +++ b/rest-api/api/pkg/api/handler/site.go @@ -1144,7 +1144,7 @@ func (dsh DeleteSiteHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } // GetSiteStatusDetailsHandler is the API Handler for getting Site StatusDetail records diff --git a/rest-api/api/pkg/api/handler/sshkey.go b/rest-api/api/pkg/api/handler/sshkey.go index 33af0e7bd5..6d6a504474 100644 --- a/rest-api/api/pkg/api/handler/sshkey.go +++ b/rest-api/api/pkg/api/handler/sshkey.go @@ -1008,5 +1008,5 @@ func (dskh DeleteSSHKeyHandler) Handle(c echo.Context) error { // Return response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/sshkeygroup.go b/rest-api/api/pkg/api/handler/sshkeygroup.go index e9e0b2e129..c794161037 100644 --- a/rest-api/api/pkg/api/handler/sshkeygroup.go +++ b/rest-api/api/pkg/api/handler/sshkeygroup.go @@ -1659,5 +1659,5 @@ func (dskgh DeleteSSHKeyGroupHandler) Handle(c echo.Context) error { // Return response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/subnet.go b/rest-api/api/pkg/api/handler/subnet.go index 05bf63df71..739414b7eb 100644 --- a/rest-api/api/pkg/api/handler/subnet.go +++ b/rest-api/api/pkg/api/handler/subnet.go @@ -1164,5 +1164,5 @@ func (dsh DeleteSubnetHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/tenantaccount.go b/rest-api/api/pkg/api/handler/tenantaccount.go index 7593ae0b6a..1628bafd5c 100644 --- a/rest-api/api/pkg/api/handler/tenantaccount.go +++ b/rest-api/api/pkg/api/handler/tenantaccount.go @@ -866,5 +866,5 @@ func (dtah DeleteTenantAccountHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/vpc.go b/rest-api/api/pkg/api/handler/vpc.go index d574c63643..34813995b9 100644 --- a/rest-api/api/pkg/api/handler/vpc.go +++ b/rest-api/api/pkg/api/handler/vpc.go @@ -1818,5 +1818,5 @@ func (dvh DeleteVPCHandler) Handle(c echo.Context) error { // Return response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/handler/vpc_test.go b/rest-api/api/pkg/api/handler/vpc_test.go index c1f6845fb5..ca07b9f6f5 100644 --- a/rest-api/api/pkg/api/handler/vpc_test.go +++ b/rest-api/api/pkg/api/handler/vpc_test.go @@ -3465,7 +3465,7 @@ func TestDeleteVPCHandler_Handle(t *testing.T) { if tt.args.respCode != http.StatusAccepted { return } - assert.Contains(t, rec.Body.String(), "Deletion request was accepted") + assertDeletionAcceptedResponse(t, rec.Body.Bytes()) // Verify VPC in deleting state vpcDAO := cdbm.NewVpcDAO(dbSession) diff --git a/rest-api/api/pkg/api/handler/vpcprefix.go b/rest-api/api/pkg/api/handler/vpcprefix.go index 7498c6477b..491950b405 100644 --- a/rest-api/api/pkg/api/handler/vpcprefix.go +++ b/rest-api/api/pkg/api/handler/vpcprefix.go @@ -1160,5 +1160,5 @@ func (dsh DeleteVpcPrefixHandler) Handle(c echo.Context) error { // Create response logger.Info().Msg("finishing API handler") - return c.String(http.StatusAccepted, "Deletion request was accepted") + return c.JSON(http.StatusAccepted, model.NewAPIDeletionAcceptedResponse()) } diff --git a/rest-api/api/pkg/api/model/deletion.go b/rest-api/api/pkg/api/model/deletion.go new file mode 100644 index 0000000000..8333ac1d9c --- /dev/null +++ b/rest-api/api/pkg/api/model/deletion.go @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package model + +const ( + // DeletionRequestAcceptedMessage is returned when an asynchronous delete has + // been accepted for processing. + DeletionRequestAcceptedMessage = "Deletion request was accepted" +) + +// APIDeletionAcceptedResponse is the JSON body for accepted async DELETE requests. +type APIDeletionAcceptedResponse struct { + Message string `json:"message"` +} + +// NewAPIDeletionAcceptedResponse returns the JSON body for accepted async deletes. +func NewAPIDeletionAcceptedResponse() APIDeletionAcceptedResponse { + return APIDeletionAcceptedResponse{ + Message: DeletionRequestAcceptedMessage, + } +} diff --git a/rest-api/api/pkg/api/model/deletion_test.go b/rest-api/api/pkg/api/model/deletion_test.go new file mode 100644 index 0000000000..7642341957 --- /dev/null +++ b/rest-api/api/pkg/api/model/deletion_test.go @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package model + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAPIDeletionAcceptedResponse_JSON(t *testing.T) { + t.Parallel() + + payload, err := json.Marshal(NewAPIDeletionAcceptedResponse()) + require.NoError(t, err) + assert.JSONEq(t, `{"message":"`+DeletionRequestAcceptedMessage+`"}`, string(payload)) + + var decoded APIDeletionAcceptedResponse + require.NoError(t, json.Unmarshal(payload, &decoded)) + assert.Equal(t, DeletionRequestAcceptedMessage, decoded.Message) +} diff --git a/rest-api/docs/index.html b/rest-api/docs/index.html index 02c4196f20..56da59eb81 100644 --- a/rest-api/docs/index.html +++ b/rest-api/docs/index.html @@ -1194,13 +1194,13 @@
Name of the Org
ID of the Tenant Account
- {- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}| purgeMachines | boolean Scrub all Machine data associated with this Site to re-pair - |
Deletion request was accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Name of the Org
ID of the Allocation
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Update an existing Allocation
@@ -2608,13 +2608,13 @@Name of the Org
ID of the VPC
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Update an existing VPC
Org must have a Tenant entity. User must have authorization role with TENANT_ADMIN suffix
Name of the Org
ID of the InfiniBand Partition
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Update an existing InfiniBand Partition
@@ -6038,13 +6038,13 @@Name of the NGC Org
ID of the NVLink Logical Partition
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Get all NVLink Interfaces
Org must have a Tenant entity. User must have authorization role with TENANT_ADMIN suffix.
Name of the Org
ID of the Operating System
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Update an Operating System by ID
@@ -6954,13 +6954,13 @@Name of the Org
ID of the Instance Type
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Update an Instance Type by ID.
Org must have an Infrastructure Provider entity that owns the Instance Type. User must have authorization role with PROVIDER_ADMIN suffix.
ID of the Instance Type
Can be ID of the Machine (machineId) or ID of Machine/Instance Type Association (machineAssociationId). Use of machineAssociationId is now deprecated and will no longer be accepted after July 9th, 2026 00:00 UTC.
Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Instance is a Machine provisioned with an Operating System by a Tenant and attached to one or more VPC Prefixes or Subnets.
Should be set to true for Tenants who are performing investigation/repairing the Machine. Otherwise omit or set to false
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "machineHealthIssue":
{- "category": "Network",
- "summary": "Machine has DPU connectivity error",
- "details": "Tenant observed repeated link flaps on the DPU uplink."
}, - "isRepairTenant": false
} {- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "machineHealthIssue":
{- "category": "Network",
- "summary": "Machine has DPU connectivity error",
- "details": "Tenant observed repeated link flaps on the DPU uplink."
}, - "isRepairTenant": false
} {- "message": "Deletion request was accepted"
}Update an Instance by ID
Org must have a Tenant entity. Instance must belong to Tenant. User must have authorization role with TENANT_ADMIN suffix.
Name of the Org
ID of the Machine
-Accepted
Error response when request data cannot be validated
@@ -9552,7 +9552,7 @@Describes an error response for 500 Internal Server Error
{- "source": "nico",
- "message": "Error validating request data",
- "data":
{- "name": "A value is required"
}
} {- "message": "Deletion request was accepted"
}Org must have either an Infrastructure Provider entity or a Tenant entity.
@@ -11990,7 +11990,7 @@Name of the Org
ID of the Network Security Group
-Accepted
Error response when request data cannot be validated
@@ -12006,7 +12006,7 @@Describes an error response for 501 Not Implemented
{- "source": "nico",
- "message": "Error validating request data",
- "data":
{- "name": "A value is required"
}
} {- "message": "Deletion request was accepted"
}IP Block is a contiguous block of IP addresses defined by a prefix and prefix length.
It can be used by the Provider to describe the overlay network of a particular Site. Providers can also use Allocations to delegate portions of these IP Blocks to Tenants.
@@ -12250,7 +12250,7 @@Name of the Org
ID of the IP Block
-Accepted
Error response when request data cannot be validated
@@ -12258,7 +12258,7 @@Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "Error validating request data",
- "data":
{- "name": "A value is required"
}
} {- "message": "Deletion request was accepted"
}Update an existing IP Block
@@ -13028,13 +13028,13 @@Name of the Org
ID of the SSH Key Group
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}Update a specific SSH Key Group.
@@ -13228,13 +13228,13 @@Name of the Org
ID of the SSH Key
-Accepted
Error response when user is not authorized to call an endpoint or retrieve/modify objects
{- "source": "nico",
- "message": "User is not allowed to perform this action",
- "data": null
} {- "message": "Deletion request was accepted"
}