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
41 changes: 41 additions & 0 deletions protocol/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,47 @@ type PlacementConstraints struct {
StorageGuidance StorageGuidance `json:"storage_guidance,omitzero"`
}

type PlacementCapabilities struct {
DiskBytes int64 `json:"disk_bytes,omitempty"`
MemoryBytes int64 `json:"memory_bytes,omitempty"`
BandwidthMbps int64 `json:"bandwidth_mbps,omitempty"`
IngressCapable bool `json:"ingress_capable,omitempty"`
CapabilityTags []string `json:"capability_tags,omitempty"`
}

type PlacementNetworkPolicy struct {
AllowIngress bool `json:"allow_ingress,omitempty"`
}

func PlacementConstraintsSatisfiedBy(c PlacementConstraints, caps PlacementCapabilities, task PlacementNetworkPolicy) error {
if c.IsZero() {
return nil
}
var errs []error
if c.MinDiskBytes > 0 && caps.DiskBytes < c.MinDiskBytes {
errs = append(errs, fmt.Errorf("worker disk_bytes %d below placement min_disk_bytes %d", caps.DiskBytes, c.MinDiskBytes))
}
if c.MinMemoryBytes > 0 && caps.MemoryBytes < c.MinMemoryBytes {
errs = append(errs, fmt.Errorf("worker memory_bytes %d below placement min_memory_bytes %d", caps.MemoryBytes, c.MinMemoryBytes))
}
if c.MinBandwidthMbps > 0 && caps.BandwidthMbps < c.MinBandwidthMbps {
errs = append(errs, fmt.Errorf("worker bandwidth_mbps %d below placement min_bandwidth_mbps %d", caps.BandwidthMbps, c.MinBandwidthMbps))
}
if c.RequiresIngress && (!caps.IngressCapable || !task.AllowIngress) {
errs = append(errs, errors.New("worker/task does not satisfy placement ingress requirement"))
}
capabilityTags := map[string]struct{}{}
for _, tag := range caps.CapabilityTags {
capabilityTags[tag] = struct{}{}
}
for _, tag := range c.RequiredCapabilities {
if _, ok := capabilityTags[tag]; !ok {
errs = append(errs, fmt.Errorf("worker missing placement required capability %q", tag))
}
}
return errors.Join(errs...)
}

func (c PlacementConstraints) Validate(required bool, target SettlementTarget) error {
if c.IsZero() {
if required {
Expand Down
59 changes: 59 additions & 0 deletions protocol/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,65 @@ func TestRuntimeAdapterContractRequiresServiceWorkloadForServiceAdapters(t *test
}
}

func TestPlacementConstraintsSatisfiedByEvaluatesWorkerAndTaskInputs(t *testing.T) {
constraints := protocol.PlacementConstraints{
MinDiskBytes: 100,
MinMemoryBytes: 200,
MinBandwidthMbps: 300,
RequiresIngress: true,
RequiredCapabilities: []string{"gpu", "tee"},
}

err := protocol.PlacementConstraintsSatisfiedBy(
constraints,
protocol.PlacementCapabilities{
DiskBytes: 50,
MemoryBytes: 150,
BandwidthMbps: 250,
CapabilityTags: []string{
"gpu",
},
},
protocol.PlacementNetworkPolicy{},
)
if err == nil {
t.Fatal("expected insufficient placement inputs to fail")
}
for _, want := range []string{
"disk_bytes",
"memory_bytes",
"bandwidth_mbps",
"ingress",
`required capability "tee"`,
} {
if !strings.Contains(err.Error(), want) {
t.Fatalf("expected placement error to contain %q, got %v", want, err)
}
}

if err := protocol.PlacementConstraintsSatisfiedBy(
constraints,
protocol.PlacementCapabilities{
DiskBytes: 100,
MemoryBytes: 200,
BandwidthMbps: 300,
IngressCapable: true,
CapabilityTags: []string{"gpu", "tee"},
},
protocol.PlacementNetworkPolicy{AllowIngress: true},
); err != nil {
t.Fatalf("expected placement inputs to satisfy constraints: %v", err)
}

if err := protocol.PlacementConstraintsSatisfiedBy(
protocol.PlacementConstraints{},
protocol.PlacementCapabilities{},
protocol.PlacementNetworkPolicy{},
); err != nil {
t.Fatalf("zero constraints should be satisfied: %v", err)
}
}

func TestRuntimeDescriptorFallsBackToProviderNameAndDevVersion(t *testing.T) {
ref := (protocol.RuntimeDescriptor{}).ExecutorRef("command")

Expand Down
Loading