Skip to content
This repository was archived by the owner on Jun 2, 2026. It is now read-only.

Commit 728c5b1

Browse files
committed
Handled the pending interaface check and updated spec
1 parent 3470ab2 commit 728c5b1

24 files changed

Lines changed: 359 additions & 84 deletions

api/pkg/api/handler/instance.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"net/http"
2626
"slices"
2727
"strings"
28+
"time"
2829

2930
goset "github.com/deckarep/golang-set/v2"
3031
"github.com/labstack/echo/v4"
@@ -3126,7 +3127,13 @@ func (uih UpdateInstanceHandler) Handle(c echo.Context) error {
31263127
case cdbm.NVLinkInterfaceStatusReady:
31273128
existingReadyNvlIfcsCount++
31283129
case cdbm.NVLinkInterfaceStatusPending:
3129-
existingPendingNvlIfcsCount++
3130+
// Only count pending interfaces whose updated timestamp is older than the grace window;
3131+
// recently-updated pending rows are excluded so in-flight updates are not treated as a no-op match.
3132+
lastUpdatedAt := existingNvlIfcs[i].Updated
3133+
if lastUpdatedAt.Before(time.Now().Add(-90 * time.Second)) {
3134+
// 90 seconds is the grace period for pending interfaces to be considered unchanged
3135+
existingPendingNvlIfcsCount++
3136+
}
31303137
case cdbm.NVLinkInterfaceStatusDeleting:
31313138
existingDeletingNvlIfcsCount++
31323139
default:

api/pkg/api/handler/instance_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"reflect"
2929
"strings"
3030
"testing"
31+
"time"
3132

3233
"github.com/NVIDIA/infra-controller-rest/api/internal/config"
3334
"github.com/NVIDIA/infra-controller-rest/api/pkg/api/handler/util/common"
@@ -3943,6 +3944,31 @@ func TestUpdateInstanceHandler_Handle(t *testing.T) {
39433944
instnvlifc4 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13.ID, nvllp2.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 3, cdbm.NVLinkInterfaceStatusReady)
39443945
assert.NotNil(t, instnvlifc4)
39453946

3947+
// Instances with four Pending NVLink interfaces — isolate stale vs fresh `updated` timestamp behavior across subtests.
3948+
inst13PendingStale := testInstanceBuildInstance(t, dbSession, "test-instance-nvlink-pending-stale", tn1.ID, ip.ID, st3.ID, &ist4.ID, vpc4.ID, cdb.GetStrPtr(mc5.ID), &os2.ID, nil, cdbm.InstanceStatusReady)
3949+
assert.NotNil(t, inst13PendingStale)
3950+
3951+
inst13psNvl1 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13PendingStale.ID, nvllp1.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 0, cdbm.NVLinkInterfaceStatusPending)
3952+
assert.NotNil(t, inst13psNvl1)
3953+
inst13psNvl2 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13PendingStale.ID, nvllp1.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 1, cdbm.NVLinkInterfaceStatusPending)
3954+
assert.NotNil(t, inst13psNvl2)
3955+
inst13psNvl3 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13PendingStale.ID, nvllp2.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 2, cdbm.NVLinkInterfaceStatusPending)
3956+
assert.NotNil(t, inst13psNvl3)
3957+
inst13psNvl4 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13PendingStale.ID, nvllp2.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 3, cdbm.NVLinkInterfaceStatusPending)
3958+
assert.NotNil(t, inst13psNvl4)
3959+
3960+
inst13PendingFresh := testInstanceBuildInstance(t, dbSession, "test-instance-nvlink-pending-fresh", tn1.ID, ip.ID, st3.ID, &ist4.ID, vpc4.ID, cdb.GetStrPtr(mc5.ID), &os2.ID, nil, cdbm.InstanceStatusReady)
3961+
assert.NotNil(t, inst13PendingFresh)
3962+
3963+
inst13pfNvl1 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13PendingFresh.ID, nvllp1.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 0, cdbm.NVLinkInterfaceStatusPending)
3964+
assert.NotNil(t, inst13pfNvl1)
3965+
inst13pfNvl2 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13PendingFresh.ID, nvllp1.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 1, cdbm.NVLinkInterfaceStatusPending)
3966+
assert.NotNil(t, inst13pfNvl2)
3967+
inst13pfNvl3 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13PendingFresh.ID, nvllp2.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 2, cdbm.NVLinkInterfaceStatusPending)
3968+
assert.NotNil(t, inst13pfNvl3)
3969+
inst13pfNvl4 := testInstanceBuildInstanceNVLinkInterface(t, dbSession, st3.ID, inst13PendingFresh.ID, nvllp2.ID, cdb.GetUUIDPtr(uuid.New()), cdb.GetStrPtr("NVIDIA GB200"), 3, cdbm.NVLinkInterfaceStatusPending)
3970+
assert.NotNil(t, inst13pfNvl4)
3971+
39463972
mc6 := testInstanceBuildMachine(t, dbSession, ip.ID, st2.ID, cdb.GetBoolPtr(false), nil)
39473973
assert.NotNil(t, mc6)
39483974

@@ -4140,6 +4166,8 @@ func TestUpdateInstanceHandler_Handle(t *testing.T) {
41404166
expectedPropagationStatus *string
41414167
// When true, only assert len(siteReq.Config.Nvlink.GpuConfigs) matches the request (e.g. NVLink no-op where workflow uses DB order).
41424168
nvLinkGpuConfigsVerifyCountOnly bool
4169+
// Optional hook after building the echo context and before Handle (e.g. adjust DB timestamps for time-sensitive branches).
4170+
beforeHandle func(t *testing.T)
41434171
}
41444172

41454173
tests := []struct {
@@ -5689,6 +5717,77 @@ func TestUpdateInstanceHandler_Handle(t *testing.T) {
56895717
verifySiteControllerRequest: true,
56905718
verifyChildSpanner: true,
56915719
},
5720+
{
5721+
name: "test Instance update API endpoint success when NVLink interfaces unchanged — pending rows older than grace window count toward multiset no-op",
5722+
fields: fields{
5723+
dbSession: dbSession,
5724+
tc: tc,
5725+
scp: scp,
5726+
cfg: cfg,
5727+
},
5728+
args: args{
5729+
reqData: &model.APIInstanceUpdateRequest{
5730+
IpxeScript: os2.IpxeScript,
5731+
NVLinkInterfaces: []model.APINVLinkInterfaceCreateOrUpdateRequest{
5732+
{NVLinkLogicalPartitionID: nvllp2.ID.String(), DeviceInstance: 3},
5733+
{NVLinkLogicalPartitionID: nvllp1.ID.String(), DeviceInstance: 0},
5734+
{NVLinkLogicalPartitionID: nvllp2.ID.String(), DeviceInstance: 2},
5735+
{NVLinkLogicalPartitionID: nvllp1.ID.String(), DeviceInstance: 1},
5736+
},
5737+
},
5738+
reqInstance: inst13PendingStale.ID.String(),
5739+
reqOrg: tnOrg1,
5740+
reqUser: tnu1,
5741+
respCode: http.StatusOK,
5742+
nvLinkGpuConfigsVerifyCountOnly: true,
5743+
beforeHandle: func(t *testing.T) {
5744+
stale := time.Now().UTC().Add(-2 * time.Minute)
5745+
_, err := dbSession.DB.Exec(
5746+
"UPDATE nvlink_interface SET updated = ? WHERE instance_id = ?",
5747+
stale,
5748+
inst13PendingStale.ID,
5749+
)
5750+
require.NoError(t, err)
5751+
},
5752+
},
5753+
wantErr: false,
5754+
verifySiteControllerRequest: true,
5755+
verifyChildSpanner: true,
5756+
},
5757+
{
5758+
name: "test Instance update API endpoint success when NVLink pending rows are recent — excluded from unchanged match, interfaces replaced",
5759+
fields: fields{
5760+
dbSession: dbSession,
5761+
tc: tc,
5762+
scp: scp,
5763+
cfg: cfg,
5764+
},
5765+
args: args{
5766+
reqData: &model.APIInstanceUpdateRequest{
5767+
IpxeScript: os2.IpxeScript,
5768+
NVLinkInterfaces: []model.APINVLinkInterfaceCreateOrUpdateRequest{
5769+
{NVLinkLogicalPartitionID: nvllp2.ID.String(), DeviceInstance: 3},
5770+
{NVLinkLogicalPartitionID: nvllp1.ID.String(), DeviceInstance: 0},
5771+
{NVLinkLogicalPartitionID: nvllp2.ID.String(), DeviceInstance: 2},
5772+
{NVLinkLogicalPartitionID: nvllp1.ID.String(), DeviceInstance: 1},
5773+
},
5774+
},
5775+
reqInstance: inst13PendingFresh.ID.String(),
5776+
reqOrg: tnOrg1,
5777+
reqUser: tnu1,
5778+
respCode: http.StatusOK,
5779+
respNoOfNVLinkInterfaces: cdb.GetIntPtr(4),
5780+
nvlinkInterfacesToDelete: []cdbm.NVLinkInterface{
5781+
*inst13pfNvl1,
5782+
*inst13pfNvl2,
5783+
*inst13pfNvl3,
5784+
*inst13pfNvl4,
5785+
},
5786+
},
5787+
wantErr: false,
5788+
verifySiteControllerRequest: true,
5789+
verifyChildSpanner: true,
5790+
},
56925791
{
56935792
name: "test Instance update API endpoint success with NVLink interface subset replacing full set",
56945793
fields: fields{
@@ -5842,6 +5941,10 @@ func TestUpdateInstanceHandler_Handle(t *testing.T) {
58425941
ctx = context.WithValue(ctx, otelecho.TracerKey, tracer)
58435942
ec.SetRequest(ec.Request().WithContext(ctx))
58445943

5944+
if tt.args.beforeHandle != nil {
5945+
tt.args.beforeHandle(t)
5946+
}
5947+
58455948
if err := uih.Handle(ec); (err != nil) != tt.wantErr {
58465949
t.Errorf("UpdateInstanceHandler.Handle() error = %v, wantErr %v", err, tt.wantErr)
58475950
}

openapi/spec.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15355,7 +15355,7 @@ components:
1535515355
type: array
1535615356
description: Define Interfaces to associate Instance GPUs with NVLink Logical Partitions. A subset of GPUs may be specified (it is not required to include all GPUs). Each item references one GPU index (`deviceInstance`) and one NVLink Logical Partition. Different interfaces may reference different NVLink Logical Partitions.
1535715357
items:
15358-
$ref: '#/components/schemas/NVLinkInterfaceCreateRequest'
15358+
$ref: '#/components/schemas/NVLinkInterfaceCreateOrUpdateRequest'
1535915359
sshKeyGroupIds:
1536015360
type: array
1536115361
description: Specify list of SSH Key Group IDs that will provide Serial over LAN access
@@ -15502,7 +15502,7 @@ components:
1550215502
type: array
1550315503
description: NVLink interface configuration shared across all instances. A subset of GPUs may be specified. Each item references one GPU index (`deviceInstance`) and one NVLink Logical Partition. Different interfaces may reference different NVLink Logical Partitions.
1550415504
items:
15505-
$ref: '#/components/schemas/NVLinkInterfaceCreateRequest'
15505+
$ref: '#/components/schemas/NVLinkInterfaceCreateOrUpdateRequest'
1550615506
sshKeyGroupIds:
1550715507
type: array
1550815508
description: SSH Key Group IDs that will provide Serial over LAN access to all instances
@@ -15674,9 +15674,9 @@ components:
1567415674
$ref: '#/components/schemas/InfiniBandInterfaceCreateRequest'
1567515675
nvLinkInterfaces:
1567615676
type: array
15677-
description: Update NVLink Interfaces of the Instance. A subset of GPUs may be specified. Each item references one GPU index (`deviceInstance`) and one NVLink Logical Partition. Different interfaces may reference different NVLink Logical Partitions.
15677+
description: Update NVLink Interfaces of the Instance. A subset of GPUs may be specified. Each item binds one GPU index (`deviceInstance`) to one NVLink Logical Partition; items may reference different partitions. Entries present in the request are updated or left intact when they already match; entries omitted from the request are marked for deletion.
1567815678
items:
15679-
$ref: '#/components/schemas/NVLinkInterfaceCreateRequest'
15679+
$ref: '#/components/schemas/NVLinkInterfaceCreateOrUpdateRequest'
1568015680
dpuExtensionServiceDeployments:
1568115681
type: array
1568215682
description: Updated set of DPU Extension Services to deploy to the DPUs of this Instance
@@ -16059,8 +16059,8 @@ components:
1605916059
- Ready
1606016060
- Deleting
1606116061
- Error
16062-
NVLinkInterfaceCreateRequest:
16063-
title: NVLinkInterfaceCreateRequest
16062+
NVLinkInterfaceCreateOrUpdateRequest:
16063+
title: NVLinkInterfaceCreateOrUpdateRequest
1606416064
type: object
1606516065
examples:
1606616066
- nvLinklogicalPartitionId: 505e07bc-86bf-489a-807d-92dc8b80e6ff

sdk/standard/client.go

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/standard/model_batch_instance_create_request.go

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/standard/model_expected_machine.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/standard/model_expected_machine_create_request.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/standard/model_expected_machine_update_request.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/standard/model_expected_power_shelf.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/standard/model_expected_power_shelf_create_request.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)