libs/skylab/request: add facility for specifing SchedulableLabels
BUG=974004
TEST=unit tests updated
Change-Id: I08727775982940a18f9ed6c7a9f9c4eb456e7bdc
Reviewed-on: https://chromium-review.googlesource.com/c/infra/infra/+/1659235
Reviewed-by: Prathmesh Prabhu <pprabhu@chromium.org>
Commit-Queue: Aviv Keshet <akeshet@chromium.org>
Cr-Commit-Position: refs/heads/master@{#23693}
diff --git a/go/src/infra/libs/skylab/request/request.go b/go/src/infra/libs/skylab/request/request.go
index 5f2037c..3f373b2 100644
--- a/go/src/infra/libs/skylab/request/request.go
+++ b/go/src/infra/libs/skylab/request/request.go
@@ -14,23 +14,41 @@
"go.chromium.org/luci/common/data/strpair"
"go.chromium.org/luci/common/errors"
+ "infra/libs/skylab/inventory"
+ swarming_inventory "infra/libs/skylab/inventory/swarming"
"infra/libs/skylab/worker"
)
// Args defines the set of arguments for creating a request.
type Args struct {
- Cmd worker.Command
- Tags []string
+ // Cmd specifies the payload command to run for the request.
+ Cmd worker.Command
+ // Tags specifies swarming tags to apply to the request.
+ Tags []string
+ // ProvisionableDimensions specifies the provisionable dimensions in raw
+ // string form; e.g. {"provisionable-cros-version:foo-cq-R75-1.2.3.4"}
ProvisionableDimensions []string
- Dimensions []string
- TimeoutMins int
- Priority int64
- ParentTaskID string
+ // Dimensions specifies swarming dimensions in raw string form.
+ //
+ // It is preferable to specify dimensions via the SchedulableLabels
+ // argument. This argument should only be used for user-supplied freeform
+ // dimensions; e.g. {"label-power:battery"}
+ //
+ // TODO(akeshet): This feature is needed to support `skylab create-test`
+ // which allows arbitrary user-specified dimensions. If and when that
+ // feature is dropped, then this feature can be dropped as well.
+ Dimensions []string
+ // SchedulableLabels specifies schedulable label requirements that will
+ // be translated to dimensions.
+ SchedulableLabels inventory.SchedulableLabels
+ TimeoutMins int
+ Priority int64
+ ParentTaskID string
}
// New creates a new swarming request for the given worker command and parameters.
func New(args Args) (*swarming.SwarmingRpcsNewTaskRequest, error) {
- slices, err := getSlices(args.Cmd, args.ProvisionableDimensions, args.Dimensions, args.TimeoutMins)
+ slices, err := getSlices(args.Cmd, args.ProvisionableDimensions, args.Dimensions, args.SchedulableLabels, args.TimeoutMins)
if err != nil {
return nil, errors.Annotate(err, "create request").Err()
}
@@ -46,14 +64,18 @@
}
// getSlices generates and returns the set of swarming task slices for the given test task.
-func getSlices(cmd worker.Command, provisionableDimensions []string, dimensions []string, timeoutMins int) ([]*swarming.SwarmingRpcsTaskSlice, error) {
+func getSlices(cmd worker.Command, provisionableDimensions []string, dimensions []string, inv inventory.SchedulableLabels, timeoutMins int) ([]*swarming.SwarmingRpcsTaskSlice, error) {
slices := make([]*swarming.SwarmingRpcsTaskSlice, 1, 2)
- basePairs, err := toPairs(dimensions)
+ rawPairs, err := stringToPairs(dimensions)
if err != nil {
return nil, errors.Annotate(err, "create slices").Err()
}
- provisionablePairs, err := toPairs(provisionableDimensions)
+
+ inventoryPairs := schedulableLabelsToPairs(inv)
+ basePairs := append(inventoryPairs, rawPairs...)
+
+ provisionablePairs, err := stringToPairs(provisionableDimensions)
if err != nil {
return nil, errors.Annotate(err, "create slices").Err()
}
@@ -105,9 +127,9 @@
return labels
}
-// toPairs converts a slice of strings in foo:bar form to a slice of swarming
+// stringToPairs converts a slice of strings in foo:bar form to a slice of swarming
// rpc string pairs.
-func toPairs(dimensions []string) ([]*swarming.SwarmingRpcsStringPair, error) {
+func stringToPairs(dimensions []string) ([]*swarming.SwarmingRpcsStringPair, error) {
pairs := make([]*swarming.SwarmingRpcsStringPair, len(dimensions))
for i, d := range dimensions {
k, v := strpair.Parse(d)
@@ -118,3 +140,14 @@
}
return pairs, nil
}
+
+func schedulableLabelsToPairs(inv inventory.SchedulableLabels) []*swarming.SwarmingRpcsStringPair {
+ dimensions := swarming_inventory.Convert(&inv)
+ pairs := make([]*swarming.SwarmingRpcsStringPair, 0, len(dimensions))
+ for key, values := range dimensions {
+ for _, value := range values {
+ pairs = append(pairs, &swarming.SwarmingRpcsStringPair{Key: key, Value: value})
+ }
+ }
+ return pairs
+}
diff --git a/go/src/infra/libs/skylab/request/request_test.go b/go/src/infra/libs/skylab/request/request_test.go
index 5d259ff..87f9d5c 100644
--- a/go/src/infra/libs/skylab/request/request_test.go
+++ b/go/src/infra/libs/skylab/request/request_test.go
@@ -10,14 +10,17 @@
. "github.com/smartystreets/goconvey/convey"
+ "infra/libs/skylab/inventory"
"infra/libs/skylab/request"
)
func TestProvisionableDimensions(t *testing.T) {
- Convey("Given request arguments that specify provisionable and regular dimenisons", t, func() {
+ Convey("Given request arguments that specify provisionable and regular dimenisons and inventory labels", t, func() {
+ model := "foo-model"
args := request.Args{
Dimensions: []string{"k1:v1"},
ProvisionableDimensions: []string{"k2:v2", "k3:v3"},
+ SchedulableLabels: inventory.SchedulableLabels{Model: &model},
}
Convey("when a request is formed", func() {
req, err := request.New(args)
@@ -30,22 +33,28 @@
// Second slice (fallback) requires only non-provisionable dimensions.
s0 := req.TaskSlices[0]
s1 := req.TaskSlices[1]
- So(s0.Properties.Dimensions, ShouldHaveLength, 3)
- So(s1.Properties.Dimensions, ShouldHaveLength, 1)
+ So(s0.Properties.Dimensions, ShouldHaveLength, 4)
+ So(s1.Properties.Dimensions, ShouldHaveLength, 2)
d00 := s0.Properties.Dimensions[0]
d01 := s0.Properties.Dimensions[1]
d02 := s0.Properties.Dimensions[2]
- So(d00.Key, ShouldEqual, "k1")
- So(d00.Value, ShouldEqual, "v1")
- So(d01.Key, ShouldEqual, "k2")
- So(d01.Value, ShouldEqual, "v2")
- So(d02.Key, ShouldEqual, "k3")
- So(d02.Value, ShouldEqual, "v3")
+ d03 := s0.Properties.Dimensions[3]
+ So(d00.Key, ShouldEqual, "label-model")
+ So(d00.Value, ShouldEqual, "foo-model")
+ So(d01.Key, ShouldEqual, "k1")
+ So(d01.Value, ShouldEqual, "v1")
+ So(d02.Key, ShouldEqual, "k2")
+ So(d02.Value, ShouldEqual, "v2")
+ So(d03.Key, ShouldEqual, "k3")
+ So(d03.Value, ShouldEqual, "v3")
d10 := s1.Properties.Dimensions[0]
- So(d10.Key, ShouldEqual, "k1")
- So(d10.Value, ShouldEqual, "v1")
+ d11 := s1.Properties.Dimensions[1]
+ So(d10.Key, ShouldEqual, "label-model")
+ So(d10.Value, ShouldEqual, "foo-model")
+ So(d11.Key, ShouldEqual, "k1")
+ So(d11.Value, ShouldEqual, "v1")
// First slice command doesn't include provisioning.
// Second slice (fallback) does.