Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions rest-api/api/pkg/api/handler/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"go.opentelemetry.io/otel/attribute"
temporalClient "go.temporal.io/sdk/client"
tsdkConverter "go.temporal.io/sdk/converter"
tp "go.temporal.io/sdk/temporal"

"github.com/google/uuid"
Expand Down Expand Up @@ -2123,8 +2124,15 @@ func (gadmh GetAllDpuMachineHandler) Handle(c echo.Context) error {
logger.Info().Str("Workflow ID", wid).Msg("executed synchronous GetDpuMachines workflow")

// Block until the workflow has completed and returned success/error.
var controllerDpuMachines []*cwssaws.DpuMachine
var controllerDpuMachines cwssaws.DpuMachineList
wferr := we.Get(wfCtx, &controllerDpuMachines)
if errors.Is(wferr, tsdkConverter.ErrUnableToDecode) {
var legacyDpuMachines []*cwssaws.DpuMachine
wferr = we.Get(wfCtx, &legacyDpuMachines)
if wferr == nil {
controllerDpuMachines.Machines = legacyDpuMachines
}
}
if wferr != nil {
var timeoutErr *tp.TimeoutError
if errors.As(wferr, &timeoutErr) || wferr == context.DeadlineExceeded || wfCtx.Err() != nil {
Expand All @@ -2136,7 +2144,7 @@ func (gadmh GetAllDpuMachineHandler) Handle(c echo.Context) error {
return cutil.NewAPIErrorResponse(c, code, fmt.Sprintf("Failed to retrieve Machine DPU information from Site: %s", uwerr), nil)
}

apiDpuMachines = model.NewAPIDpuMachines(controllerDpuMachines, model.APIDpuMachineProtoContext{
apiDpuMachines = model.NewAPIDpuMachines(controllerDpuMachines.GetMachines(), model.APIDpuMachineProtoContext{
HostMachineID: mID,
SiteID: site.ID,
InfrastructureProviderID: site.InfrastructureProviderID,
Expand Down
60 changes: 54 additions & 6 deletions rest-api/api/pkg/api/handler/machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (

"go.temporal.io/api/enums/v1"
temporalClient "go.temporal.io/sdk/client"
tsdkConverter "go.temporal.io/sdk/converter"
tmocks "go.temporal.io/sdk/mocks"
tp "go.temporal.io/sdk/temporal"

Expand Down Expand Up @@ -3227,6 +3228,21 @@ func TestMachineHandler_Delete(t *testing.T) {
}
}

func TestGetDpuMachinesLegacyTemporalPayload(t *testing.T) {
legacyDpuMachines := []*cwssaws.DpuMachine{{Machine: &cwssaws.Machine{Id: &cwssaws.MachineId{Id: uuid.NewString()}}}}
dataConverter := tsdkConverter.GetDefaultDataConverter()
payloads, err := dataConverter.ToPayloads(legacyDpuMachines)
require.NoError(t, err)

var currentResult cwssaws.DpuMachineList
err = dataConverter.FromPayloads(payloads, &currentResult)
require.ErrorIs(t, err, tsdkConverter.ErrUnableToDecode)

var decodedLegacyResult []*cwssaws.DpuMachine
require.NoError(t, dataConverter.FromPayloads(payloads, &decodedLegacyResult))
require.Equal(t, legacyDpuMachines, decodedLegacyResult)
}

func TestMachineHandler_GetDpuMachines(t *testing.T) {
ctx := context.Background()
dbSession := testMachineInitDB(t)
Expand Down Expand Up @@ -3317,7 +3333,7 @@ func TestMachineHandler_GetDpuMachines(t *testing.T) {
cfg := common.GetTestConfig()

// Mock Temporal: success path returns two DPU machines for the workflow.
dpuMachineList := []*cwssaws.DpuMachine{
dpuMachineList := &cwssaws.DpuMachineList{Machines: []*cwssaws.DpuMachine{
{
Machine: &cwssaws.Machine{
Id: &cwssaws.MachineId{Id: dpu1.ID},
Expand Down Expand Up @@ -3356,27 +3372,44 @@ func TestMachineHandler_GetDpuMachines(t *testing.T) {
DatacenterAsn: 65000,
},
},
}
}}

wrun := &tmocks.WorkflowRun{}
wrun.On("GetID").Return("test-workflow-id-dpu")
wrun.Mock.On("Get", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
result := args.Get(1).(*[]*cwssaws.DpuMachine)
*result = dpuMachineList
result := args.Get(1).(*cwssaws.DpuMachineList)
result.Machines = dpuMachineList.Machines
}).Return(nil)

tsc := &tmocks.Client{}
tsc.Mock.On("ExecuteWorkflow", mock.Anything, mock.AnythingOfType("internal.StartWorkflowOptions"), "GetDpuMachines", mock.Anything).Return(wrun, nil)

wrunLegacy := &tmocks.WorkflowRun{}
wrunLegacy.On("GetID").Return("test-workflow-id-dpu-legacy")
wrunLegacy.Mock.On("Get", mock.Anything, mock.MatchedBy(func(result any) bool {
_, ok := result.(*cwssaws.DpuMachineList)
return ok
})).Return(fmt.Errorf("payload item 0: %w: cannot unmarshal array into DpuMachineList", tsdkConverter.ErrUnableToDecode)).Once()
wrunLegacy.Mock.On("Get", mock.Anything, mock.MatchedBy(func(result any) bool {
_, ok := result.(*[]*cwssaws.DpuMachine)
return ok
})).Run(func(args mock.Arguments) {
result := args.Get(1).(*[]*cwssaws.DpuMachine)
*result = dpuMachineList.Machines
}).Return(nil).Once()

tscLegacy := &tmocks.Client{}
tscLegacy.Mock.On("ExecuteWorkflow", mock.Anything, mock.AnythingOfType("internal.StartWorkflowOptions"), "GetDpuMachines", mock.Anything).Return(wrunLegacy, nil)

wrunErr := &tmocks.WorkflowRun{}
wrunErr.On("GetID").Return("test-workflow-error-id")
wrunErr.Mock.On("Get", mock.Anything, mock.Anything).Return(fmt.Errorf("workflow failed"))
wrunErr.Mock.On("Get", mock.Anything, mock.Anything).Return(fmt.Errorf("workflow failed")).Once()
tscErr := &tmocks.Client{}
tscErr.Mock.On("ExecuteWorkflow", mock.Anything, mock.AnythingOfType("internal.StartWorkflowOptions"), "GetDpuMachines", mock.Anything).Return(wrunErr, nil)

wrunTimeout := &tmocks.WorkflowRun{}
wrunTimeout.On("GetID").Return("test-workflow-timeout-id")
wrunTimeout.Mock.On("Get", mock.Anything, mock.Anything).Return(tp.NewTimeoutError(enums.TIMEOUT_TYPE_UNSPECIFIED, nil, nil))
wrunTimeout.Mock.On("Get", mock.Anything, mock.Anything).Return(tp.NewTimeoutError(enums.TIMEOUT_TYPE_UNSPECIFIED, nil, nil)).Once()
tscTimeout := &tmocks.Client{}
tscTimeout.Mock.On("ExecuteWorkflow", mock.Anything, mock.AnythingOfType("internal.StartWorkflowOptions"), "GetDpuMachines", mock.Anything).Return(wrunTimeout, nil)
tscTimeout.Mock.On("TerminateWorkflow", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
Expand All @@ -3390,6 +3423,9 @@ func TestMachineHandler_GetDpuMachines(t *testing.T) {
scpErr := sc.NewClientPool(tcfg)
scpErr.IDClientMap[site.ID.String()] = tscErr

scpLegacy := sc.NewClientPool(tcfg)
scpLegacy.IDClientMap[site.ID.String()] = tscLegacy

scpTimeout := sc.NewClientPool(tcfg)
scpTimeout.IDClientMap[site.ID.String()] = tscTimeout

Expand Down Expand Up @@ -3481,6 +3517,16 @@ func TestMachineHandler_GetDpuMachines(t *testing.T) {
expectedDpuCount: 2,
verifyChildSpanner: true,
},
{
name: "provider admin: legacy workflow result remains supported during rollout",
reqOrgName: ipOrg1,
user: ipu,
mID: mWithDpu.ID,
scp: scpLegacy,
expectedStatus: http.StatusOK,
expectedDpuCount: 2,
verifyChildSpanner: true,
},
{
name: "privileged tenant with account on provider can access",
reqOrgName: tnOrgPriv,
Expand Down Expand Up @@ -3555,6 +3601,8 @@ func TestMachineHandler_GetDpuMachines(t *testing.T) {
// regressions in timeout cleanup are caught instead of passing silently.
tsc.AssertExpectations(t)
wrun.AssertExpectations(t)
tscLegacy.AssertExpectations(t)
wrunLegacy.AssertExpectations(t)
tscErr.AssertExpectations(t)
wrunErr.AssertExpectations(t)
tscTimeout.AssertExpectations(t)
Expand Down
6 changes: 4 additions & 2 deletions rest-api/api/pkg/api/model/dpumachine.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
cwssaws "github.com/NVIDIA/infra-controller/rest-api/workflow-schema/schema/site-agent/workflows/v1"
)

// APIDpuNetworkConfig represents the complete network configuration for a DPU
// APIDpuNetworkConfig represents the network configuration fields exposed by the REST API for a DPU.
// Internal-only and sensitive Core fields are omitted; this is not the complete Core configuration.
type APIDpuNetworkConfig struct {
// Asn is the Autonomous System Number for BGP routing
Asn int `json:"asn"`
Expand Down Expand Up @@ -440,7 +441,8 @@ type APIDpuMachine struct {
Labels map[string]string `json:"labels"`
// State is the lifecycle state of the DPU as reported by NICo Core
State string `json:"state"`
// DpuNetworkConfig is the complete network configuration sent to the DPU agent
// DpuNetworkConfig contains the network configuration fields exposed by the REST API for the DPU.
// Internal-only and sensitive Core fields are omitted; the REST response retains its public JSON shape.
DpuNetworkConfig APIDpuNetworkConfig `json:"dpuNetworkConfig"`
// LastRebooted is the last reboot timestamp reported by NICo Core
LastRebooted *time.Time `json:"lastRebooted"`
Expand Down
Loading
Loading