Skip to content
Merged
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
121 changes: 111 additions & 10 deletions rest-api/flow/internal/converter/protobuf/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -1046,27 +1046,46 @@ func TargetSpecFrom(ts *pb.OperationTargetSpec) (operation.TargetSpec, error) {
}
spec.Components = append(spec.Components, ct)
}
case *pb.OperationTargetSpec_NvlDomains:
if len(targets.NvlDomains.GetTargets()) == 0 {
return operation.TargetSpec{}, fmt.Errorf(
"nvl_domains.targets must have at least one entry",
)
}
for _, pbDomain := range targets.NvlDomains.GetTargets() {
dt, err := NVLDomainTargetFrom(pbDomain)
if err != nil {
return operation.TargetSpec{}, fmt.Errorf(
"convert NVLink domain target: %w", err,
)
}
spec.NVLDomains = append(spec.NVLDomains, dt)
}
default:
return operation.TargetSpec{}, fmt.Errorf(
"target_spec must have either racks or components set",
"target_spec must have one of racks, nvl_domains, or components set",
)
}

return spec, nil
}

// TargetSpecTo converts an internal operation.TargetSpec to its proto form.
// It returns an error when both or neither of Racks and Components are populated,
// matching the mutual-exclusion rule enforced by TargetSpecFrom on the inbound path.
// It returns an error unless exactly one target kind is populated, matching the
// mutual-exclusion rule enforced by TargetSpecFrom on the inbound path.
func TargetSpecTo(ts operation.TargetSpec) (*pb.OperationTargetSpec, error) {
hasRacks := len(ts.Racks) > 0
hasNVLDomains := len(ts.NVLDomains) > 0
hasComponents := len(ts.Components) > 0

if hasRacks && hasComponents {
return nil, fmt.Errorf("target_spec cannot have both racks and components set")
targetKinds := 0
for _, present := range []bool{hasRacks, hasNVLDomains, hasComponents} {
if present {
targetKinds++
}
}
if !hasRacks && !hasComponents {
return nil, fmt.Errorf("target_spec must have either racks or components set")
if targetKinds != 1 {
return nil, fmt.Errorf("target_spec must have exactly one of racks, nvl_domains, or components set")
}

// Rack targets, converted to proto RackTargets.
Expand All @@ -1087,12 +1106,13 @@ func TargetSpecTo(ts operation.TargetSpec) (*pb.OperationTargetSpec, error) {
}

for _, ct := range r.ComponentTypes {
if ct == devicetypes.ComponentTypeUnknown {
protoType := ComponentTypeTo(ct)
if protoType == pb.ComponentType_COMPONENT_TYPE_UNKNOWN {
return nil, fmt.Errorf(
"invalid rack target: unknown component type filter",
)
}
rt.ComponentTypes = append(rt.ComponentTypes, ComponentTypeTo(ct))
rt.ComponentTypes = append(rt.ComponentTypes, protoType)
}

racks = append(racks, rt)
Expand All @@ -1107,6 +1127,47 @@ func TargetSpecTo(ts operation.TargetSpec) (*pb.OperationTargetSpec, error) {
}, nil
}

if hasNVLDomains {
domains := make([]*pb.NVLDomainTarget, 0, len(ts.NVLDomains))
for _, domain := range ts.NVLDomains {
target := &pb.NVLDomainTarget{}
if domain.Identifier.ID != uuid.Nil {
target.Identifier = &pb.NVLDomainTarget_Id{
Id: UUIDTo(domain.Identifier.ID),
}
} else if domain.Identifier.Name != "" {
target.Identifier = &pb.NVLDomainTarget_Name{
Name: domain.Identifier.Name,
}
} else {
return nil, fmt.Errorf("invalid NVLink domain target: neither id nor name is set")
}

for _, componentType := range domain.ComponentTypes {
protoType := ComponentTypeTo(componentType)
if protoType == pb.ComponentType_COMPONENT_TYPE_UNKNOWN {
return nil, fmt.Errorf(
"invalid NVLink domain target: unknown component type filter",
)
}
target.ComponentTypes = append(
target.ComponentTypes,
protoType,
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

domains = append(domains, target)
}

return &pb.OperationTargetSpec{
Targets: &pb.OperationTargetSpec_NvlDomains{
NvlDomains: &pb.NVLDomainTargets{
Targets: domains,
},
},
}, nil
}

// Component targets, converted to proto ComponentTargets.
comps := make([]*pb.ComponentTarget, 0, len(ts.Components))
for _, c := range ts.Components {
Expand Down Expand Up @@ -1138,6 +1199,46 @@ func TargetSpecTo(ts operation.TargetSpec) (*pb.OperationTargetSpec, error) {
}, nil
}

// NVLDomainTargetFrom converts a proto NVLink domain target to an internal target.
func NVLDomainTargetFrom(dt *pb.NVLDomainTarget) (operation.NVLDomainTarget, error) {
if dt == nil {
return operation.NVLDomainTarget{}, fmt.Errorf("NVLink domain target is nil")
}

var target operation.NVLDomainTarget
switch id := dt.GetIdentifier().(type) {
case *pb.NVLDomainTarget_Id:
parsed, err := uuid.Parse(id.Id.GetId())
if err != nil {
return operation.NVLDomainTarget{}, fmt.Errorf(
"invalid NVLink domain id %q: %w", id.Id.GetId(), err,
)
}
target.Identifier.ID = parsed
case *pb.NVLDomainTarget_Name:
if id.Name == "" {
return operation.NVLDomainTarget{}, fmt.Errorf("NVLink domain target name must not be empty")
}
target.Identifier.Name = id.Name
default:
return operation.NVLDomainTarget{}, fmt.Errorf(
"NVLink domain target must have either id or name set",
)
}

for _, pbType := range dt.GetComponentTypes() {
componentType := ComponentTypeFrom(pbType)
if componentType == devicetypes.ComponentTypeUnknown {
return operation.NVLDomainTarget{}, fmt.Errorf(
"unknown component type %v in NVLink domain target filter", pbType,
)
}
target.ComponentTypes = append(target.ComponentTypes, componentType)
}

return target, nil
}

// RackTargetFrom converts a proto RackTarget to an internal operation.RackTarget.
func RackTargetFrom(rt *pb.RackTarget) (operation.RackTarget, error) {
if rt == nil {
Expand Down Expand Up @@ -1212,7 +1313,7 @@ func ComponentTargetFrom(ct *pb.ComponentTarget) (operation.ComponentTarget, err
// ScheduledOperationFrom converts a proto ScheduledOperation oneof to the
// internal Operation, TargetSpec, and request-level scheduling options. All
// values are always valid together: the Operation carries the task-type and
// parameters, the TargetSpec identifies the racks or components the task will
// parameters, the TargetSpec identifies the racks, NVLink domains, or components the task will
// run against, and the returned QueueOptions / rule UUID carry the caller's
// conflict-handling and rule-override preferences for use at fire time.
func ScheduledOperationFrom(
Expand Down
153 changes: 149 additions & 4 deletions rest-api/flow/internal/converter/protobuf/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,72 @@ func TestComponentTargetFrom(t *testing.T) {
}
}

func TestNVLDomainTargetFrom(t *testing.T) {
domainID := uuid.New()
testCases := map[string]struct {
input *pb.NVLDomainTarget
want operation.NVLDomainTarget
wantErr string
}{
"nil input": {
wantErr: "NVLink domain target is nil",
},
"no identifier": {
input: &pb.NVLDomainTarget{},
wantErr: "must have either id or name set",
},
"ID with filter": {
input: &pb.NVLDomainTarget{
Identifier: &pb.NVLDomainTarget_Id{Id: &pb.UUID{Id: domainID.String()}},
ComponentTypes: []pb.ComponentType{
pb.ComponentType_COMPONENT_TYPE_COMPUTE,
},
},
want: operation.NVLDomainTarget{
Identifier: identifier.Identifier{ID: domainID},
ComponentTypes: []devicetypes.ComponentType{
devicetypes.ComponentTypeCompute,
},
},
},
"name": {
input: &pb.NVLDomainTarget{
Identifier: &pb.NVLDomainTarget_Name{Name: "domain-1"},
},
want: operation.NVLDomainTarget{
Identifier: identifier.Identifier{Name: "domain-1"},
},
},
"invalid ID": {
input: &pb.NVLDomainTarget{
Identifier: &pb.NVLDomainTarget_Id{Id: &pb.UUID{Id: "invalid"}},
},
wantErr: "invalid NVLink domain id",
},
"unknown component type": {
input: &pb.NVLDomainTarget{
Identifier: &pb.NVLDomainTarget_Name{Name: "domain-1"},
ComponentTypes: []pb.ComponentType{
pb.ComponentType_COMPONENT_TYPE_UNKNOWN,
},
},
wantErr: "unknown component type",
},
}

for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
got, err := NVLDomainTargetFrom(testCase.input)
if testCase.wantErr != "" {
assert.ErrorContains(t, err, testCase.wantErr)
return
}
assert.NoError(t, err)
assert.Equal(t, testCase.want, got)
})
}
}

func TestTargetSpecTo(t *testing.T) {
rackID := uuid.New()
compID := uuid.New()
Expand All @@ -925,16 +991,16 @@ func TestTargetSpecTo(t *testing.T) {
input operation.TargetSpec
wantErr string
}{
"both racks and components set": {
"multiple target kinds set": {
input: operation.TargetSpec{
Racks: []operation.RackTarget{{Identifier: identifier.Identifier{Name: "rack-1"}}},
Components: []operation.ComponentTarget{{UUID: compID}},
},
wantErr: "cannot have both racks and components",
wantErr: "must have exactly one of racks, nvl_domains, or components",
},
"neither racks nor components set": {
"no target kind set": {
input: operation.TargetSpec{},
wantErr: "must have either racks or components",
wantErr: "must have exactly one of racks, nvl_domains, or components",
},
"rack target by name": {
input: operation.TargetSpec{
Expand All @@ -955,6 +1021,31 @@ func TestTargetSpecTo(t *testing.T) {
Components: []operation.ComponentTarget{{UUID: compID}},
},
},
"NVLink domain target by UUID": {
input: operation.TargetSpec{
NVLDomains: []operation.NVLDomainTarget{
{
Identifier: identifier.Identifier{ID: rackID},
ComponentTypes: []devicetypes.ComponentType{
devicetypes.ComponentTypeCompute,
},
},
},
},
},
"NVLink domain target with unmapped component type": {
input: operation.TargetSpec{
NVLDomains: []operation.NVLDomainTarget{
{
Identifier: identifier.Identifier{ID: rackID},
ComponentTypes: []devicetypes.ComponentType{
devicetypes.ComponentType(999),
},
},
},
},
wantErr: "unknown component type filter",
},
"component target with no UUID and no external": {
input: operation.TargetSpec{
Components: []operation.ComponentTarget{{}},
Expand Down Expand Up @@ -985,6 +1076,60 @@ func TestTargetSpecTo(t *testing.T) {
}
}

func TestTargetSpecFromNVLDomains(t *testing.T) {
domainID := uuid.New()
testCases := map[string]struct {
input *pb.OperationTargetSpec
want operation.TargetSpec
wantErr string
}{
"empty targets": {
input: &pb.OperationTargetSpec{
Targets: &pb.OperationTargetSpec_NvlDomains{
NvlDomains: &pb.NVLDomainTargets{},
},
},
wantErr: "nvl_domains.targets must have at least one entry",
},
"ID and name targets": {
input: &pb.OperationTargetSpec{
Targets: &pb.OperationTargetSpec_NvlDomains{
NvlDomains: &pb.NVLDomainTargets{
Targets: []*pb.NVLDomainTarget{
{
Identifier: &pb.NVLDomainTarget_Id{
Id: &pb.UUID{Id: domainID.String()},
},
},
{
Identifier: &pb.NVLDomainTarget_Name{Name: "domain-2"},
},
},
},
},
},
want: operation.TargetSpec{
NVLDomains: []operation.NVLDomainTarget{
{Identifier: identifier.Identifier{ID: domainID}},
{Identifier: identifier.Identifier{Name: "domain-2"}},
},
},
},
}

for name, testCase := range testCases {
t.Run(name, func(t *testing.T) {
got, err := TargetSpecFrom(testCase.input)
if testCase.wantErr != "" {
assert.ErrorContains(t, err, testCase.wantErr)
return
}
assert.NoError(t, err)
assert.Equal(t, testCase.want, got)
})
}
}

func TestScheduledOperationFrom(t *testing.T) {
rackTargetProto := &pb.OperationTargetSpec{
Targets: &pb.OperationTargetSpec_Racks{
Expand Down
Loading
Loading