@platforma-sdk/workflow-tengo 2.11.0 → 2.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tengo/lib/constants.lib.tengo +2 -0
- package/dist/tengo/lib/exec.lib.tengo +58 -7
- package/dist/tengo/lib/exec.limits.lib.tengo +149 -0
- package/dist/tengo/lib/exec.runcmd.lib.tengo +5 -2
- package/dist/tengo/lib/feats.lib.tengo +2 -1
- package/dist/tengo/lib/render.lib.tengo +21 -5
- package/dist/tengo/lib/smart.lib.tengo +26 -3
- package/dist/tengo/lib/units.lib.tengo +13 -0
- package/dist/tengo/lib/validation.lib.tengo +1 -1
- package/dist/tengo/lib/workdir.lib.tengo +56 -9
- package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.aggregate.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.map-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
- package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
- package/package.json +3 -3
|
@@ -28,6 +28,7 @@ RTYPE_QUERY_RESULT := { Name: "BQueryResult", Version: "1" }
|
|
|
28
28
|
RTYPE_OBJECT_SPEC := { Name: "BObjectSpec", Version: "1" }
|
|
29
29
|
|
|
30
30
|
RTYPE_WORKDIR_CREATE := { Name: "WorkdirCreate", Version: "1" }
|
|
31
|
+
RTYPE_WORKDIR_CREATE_2 := { Name: "WorkdirCreate", Version: "2" }
|
|
31
32
|
RTYPE_WORKDIR_FILL := { Name: "WorkdirFill", Version: "1" }
|
|
32
33
|
RTYPE_WORKDIR_FILL_RULES := { Name: "WorkdirFill/rules", Version: "1" }
|
|
33
34
|
RTYPE_WORKDIR_SAVE := { Name: "WorkdirSave", Version: "1" }
|
|
@@ -67,6 +68,7 @@ export ll.toStrict({
|
|
|
67
68
|
RTYPE_RENDER_EPH_TEMPLATE : RTYPE_RENDER_EPH_TEMPLATE,
|
|
68
69
|
RTYPE_RENDER_TEMPLATE : RTYPE_RENDER_TEMPLATE,
|
|
69
70
|
RTYPE_WORKDIR_CREATE : RTYPE_WORKDIR_CREATE,
|
|
71
|
+
RTYPE_WORKDIR_CREATE_2 : RTYPE_WORKDIR_CREATE_2,
|
|
70
72
|
RTYPE_WORKDIR_FILL : RTYPE_WORKDIR_FILL,
|
|
71
73
|
RTYPE_WORKDIR_FILL_RULES : RTYPE_WORKDIR_FILL_RULES,
|
|
72
74
|
RTYPE_WORKDIR_SAVE : RTYPE_WORKDIR_SAVE,
|
|
@@ -17,6 +17,7 @@ enum := import("enum")
|
|
|
17
17
|
oop := import("@platforma-sdk/workflow-tengo:oop")
|
|
18
18
|
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
19
19
|
constants := import("@platforma-sdk/workflow-tengo:constants")
|
|
20
|
+
limits := import("@platforma-sdk/workflow-tengo:exec.limits")
|
|
20
21
|
monetization := import("@platforma-sdk/workflow-tengo:exec.monetization")
|
|
21
22
|
execConstants := import("@platforma-sdk/workflow-tengo:exec.constants")
|
|
22
23
|
|
|
@@ -50,7 +51,14 @@ builder := func() {
|
|
|
50
51
|
"either a command with arguments or a software were already set.")
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
//
|
|
55
|
+
// Limits
|
|
56
|
+
//
|
|
53
57
|
queue := _HEAVY_QUEUE
|
|
58
|
+
hasLimits := false
|
|
59
|
+
cpuRequest := undefined
|
|
60
|
+
ramRequest := undefined
|
|
61
|
+
|
|
54
62
|
cache := 0
|
|
55
63
|
nErrorLines := 200
|
|
56
64
|
|
|
@@ -262,6 +270,40 @@ builder := func() {
|
|
|
262
270
|
return self
|
|
263
271
|
},
|
|
264
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Preserve CPUs in environments that plan CPU resources (like PBS or Slurm).
|
|
275
|
+
*
|
|
276
|
+
* @param amount: number - number of cores requested for command.
|
|
277
|
+
*/
|
|
278
|
+
cpu: func(amount) {
|
|
279
|
+
validation.assertType(amount, "number", "exec.builder.cpu: amount of cores must be a number")
|
|
280
|
+
ll.assert(amount > 0, "exec.builder.cpu: amount of cores should be greater than 0")
|
|
281
|
+
hasLimits = true
|
|
282
|
+
cpuRequest = amount
|
|
283
|
+
|
|
284
|
+
return self
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Amount of RAM to request.
|
|
289
|
+
*
|
|
290
|
+
* @param amount: number | string - amount of RAM in bytes or string with size suffix (case-insensitive):
|
|
291
|
+
* K, KB, M, MB, G, GB for base-10 sizes (x1000)
|
|
292
|
+
* Ki, KiB, Mi, MiB, Gi, GiB for base-2 sizes (x1024)
|
|
293
|
+
* when operating with bytes, you may use 'units' package for convenience:
|
|
294
|
+
* 120 * units.GiB
|
|
295
|
+
*
|
|
296
|
+
* @return builder
|
|
297
|
+
*/
|
|
298
|
+
ram: func(amount) {
|
|
299
|
+
validation.assertType(amount, ["or", "number", "string"], "exec.builder.ram: RAM amount should be a number or string")
|
|
300
|
+
ll.assert(is_string(amount) || amount > 0, "exec.builder.ram: amount in bytes should be greater than 0")
|
|
301
|
+
hasLimits = true
|
|
302
|
+
ramRequest = amount
|
|
303
|
+
|
|
304
|
+
return self
|
|
305
|
+
},
|
|
306
|
+
|
|
265
307
|
/**
|
|
266
308
|
* Use cacheMillis, cacheSeconds, cacheMinutes, cacheHours and cacheDays to not make hidden mistakes in cache time ranges.
|
|
267
309
|
* Sets the cache time.
|
|
@@ -684,7 +726,7 @@ builder := func() {
|
|
|
684
726
|
nErrorLines: nErrorLines
|
|
685
727
|
}
|
|
686
728
|
|
|
687
|
-
|
|
729
|
+
pureExecInputs := {
|
|
688
730
|
filesToAdd: smart.createMapResource(filesToAdd),
|
|
689
731
|
filesToWrite: smart.createMapResource(filesToWriteRefs),
|
|
690
732
|
dirsToCreate: slices.fromSet(dirsToCreate),
|
|
@@ -701,30 +743,39 @@ builder := func() {
|
|
|
701
743
|
assetCache: assetCache
|
|
702
744
|
}
|
|
703
745
|
|
|
746
|
+
serviceExecInputs := {}
|
|
747
|
+
if hasLimits && feats.workdirLimits {
|
|
748
|
+
serviceExecInputs.quota = limits.quotaBuilder().
|
|
749
|
+
queue(queue).
|
|
750
|
+
cpu(cpuRequest).
|
|
751
|
+
ram(ramRequest).
|
|
752
|
+
build()
|
|
753
|
+
}
|
|
754
|
+
|
|
704
755
|
// We add monetization only if it's enabled
|
|
705
756
|
// to not break a deduplication of exec's
|
|
706
757
|
// that were created before this feature existed.
|
|
707
758
|
if monetization.shouldRun(mnz) {
|
|
708
|
-
|
|
759
|
+
pureExecInputs.runOptions.monetization = monetization.toJson(mnz)
|
|
709
760
|
}
|
|
710
761
|
|
|
711
762
|
// We add new argsTyped and envsTyped only when they were added
|
|
712
763
|
// to not break a deduplication on old pl backend.
|
|
713
764
|
if typedArgSet || typedEnvSet {
|
|
714
|
-
|
|
715
|
-
|
|
765
|
+
pureExecInputs.runOptions.argsTyped = argsTyped
|
|
766
|
+
pureExecInputs.runOptions.envsTyped = envsTyped
|
|
716
767
|
}
|
|
717
768
|
|
|
718
769
|
if !is_undefined(soft) {
|
|
719
|
-
|
|
720
|
-
|
|
770
|
+
pureExecInputs.softwareInfo = soft.resource
|
|
771
|
+
pureExecInputs.runOptions.software = {
|
|
721
772
|
name: soft.name,
|
|
722
773
|
version: soft.version,
|
|
723
774
|
descriptor: ll.fromStrict(soft.descriptor)
|
|
724
775
|
}
|
|
725
776
|
}
|
|
726
777
|
|
|
727
|
-
tpl := render.create(assets.importTemplate("@platforma-sdk/workflow-tengo:exec.exec"),
|
|
778
|
+
tpl := render.create(assets.importTemplate("@platforma-sdk/workflow-tengo:exec.exec"), pureExecInputs, serviceExecInputs)
|
|
728
779
|
|
|
729
780
|
self := undefined
|
|
730
781
|
self = ll.toStrict(oop.inherit(tpl, {
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A library that creates Quota resource.
|
|
3
|
+
* Shared with 'workdir' package.
|
|
4
|
+
*
|
|
5
|
+
* Was shaped as a separate lib to avoid recursive imports (exec -> workdir -> exec)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
9
|
+
oop := import("@platforma-sdk/workflow-tengo:oop")
|
|
10
|
+
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
11
|
+
|
|
12
|
+
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
13
|
+
json := import("json")
|
|
14
|
+
|
|
15
|
+
_RTYPE_QUOTA := { Name: "Quota", Version: "1" }
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Builder for quota resource.
|
|
19
|
+
*
|
|
20
|
+
* Creates 'Quota' resource used to request specific amount of CPU and RAM in a given queue
|
|
21
|
+
*/
|
|
22
|
+
quotaBuilder := func() {
|
|
23
|
+
self := undefined
|
|
24
|
+
|
|
25
|
+
queue_name := undefined
|
|
26
|
+
cpu_request := undefined
|
|
27
|
+
ram_request := undefined
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
self = ll.toStrict({
|
|
31
|
+
/**
|
|
32
|
+
* Create quota resource for specific execution queue.
|
|
33
|
+
* This is the only method required for call before .build()
|
|
34
|
+
*
|
|
35
|
+
* @param name: string - name of the queue.
|
|
36
|
+
* @return builder
|
|
37
|
+
*/
|
|
38
|
+
queue: func(name) {
|
|
39
|
+
validation.assertType(name, "string", "exec.quota.builder.queue: name of the queue should be a string")
|
|
40
|
+
queue_name = name
|
|
41
|
+
|
|
42
|
+
return self
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Amount of cores to request in quota.
|
|
47
|
+
*
|
|
48
|
+
* @param amount: number - number of cores.
|
|
49
|
+
* @return builder
|
|
50
|
+
*/
|
|
51
|
+
cpu: func(amount) {
|
|
52
|
+
validation.assertType(amount, ["or", "number", "null"], "exec.quota.builder.cpu: CPU amount should be a number")
|
|
53
|
+
ll.assert(is_undefined(amount) || amount > 0, "exec.quota.builder.cpu: amount of cores should be greater than 0")
|
|
54
|
+
cpu_request = amount
|
|
55
|
+
|
|
56
|
+
return self
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Amount of RAM to request.
|
|
61
|
+
*
|
|
62
|
+
* @param amount: number | string - amount of RAM in bytes or string with size suffix (case-insensitive):
|
|
63
|
+
* K, KB, M, MB, G, GB for base-10 sizes (x1000)
|
|
64
|
+
* Ki, KiB, Mi, MiB, Gi, GiB for base-2 sizes (x1024)
|
|
65
|
+
*
|
|
66
|
+
* @return builder
|
|
67
|
+
*/
|
|
68
|
+
ram: func(amount) {
|
|
69
|
+
validation.assertType(amount, ["or", "number", "string", "null"], "exec.quota.builder.ram: RAM amount should be a number or string")
|
|
70
|
+
ll.assert(is_undefined(amount) || is_string(amount) || amount > 0, "exec.quota.builder.ram: amount in bytes should be greater than 0")
|
|
71
|
+
ram_request = amount
|
|
72
|
+
|
|
73
|
+
return self
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create Quota resource.
|
|
78
|
+
*
|
|
79
|
+
* @return smart.resource - reference to the resulting value resource
|
|
80
|
+
*/
|
|
81
|
+
build: func() {
|
|
82
|
+
ll.assert(queue_name != "", "exec.quota.builder: queue name is mandatory and could not be empty string")
|
|
83
|
+
|
|
84
|
+
data := {
|
|
85
|
+
queue: queue_name
|
|
86
|
+
}
|
|
87
|
+
if !is_undefined(cpu_request) {
|
|
88
|
+
data.cpuCores = cpu_request
|
|
89
|
+
}
|
|
90
|
+
if !is_undefined(ram_request) {
|
|
91
|
+
data.memory = ram_request
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return smart.ephemeralBuilder(_RTYPE_QUOTA, json.encode(data)).lockAndBuild()
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
return self
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_RTYPE_STORAGE_SPACE_REQUEST := { Name: "StorageSpaceRequest", Version: "1" }
|
|
102
|
+
_SSR_FIELD_REQUEST := "request" // input
|
|
103
|
+
_SSR_FIELD_ALLOCATION := "allocation" // output
|
|
104
|
+
|
|
105
|
+
storageSpaceRequestBuilder := func() {
|
|
106
|
+
self := undefined
|
|
107
|
+
|
|
108
|
+
quota := undefined
|
|
109
|
+
|
|
110
|
+
self = ll.toStrict({
|
|
111
|
+
/**
|
|
112
|
+
* Request for given resources
|
|
113
|
+
*
|
|
114
|
+
* @param ref: smart.reference - reference to Quota resource to be fulfilled by storage allocator
|
|
115
|
+
* @return spaceRequestBuilder
|
|
116
|
+
*/
|
|
117
|
+
quota: func(ref) {
|
|
118
|
+
validation.assertType(ref, validation.reference, "workdir.limits.quota: quota must be a valid reference to Quota resource")
|
|
119
|
+
quota = ref
|
|
120
|
+
|
|
121
|
+
return self
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Build StorageSpaceRequest resource
|
|
126
|
+
*
|
|
127
|
+
* @return StorageSpaceRequest
|
|
128
|
+
*/
|
|
129
|
+
build: func() {
|
|
130
|
+
ll.assert(smart.isReference(quota), "workdir.limits.build: quota of StorageSpaceRequest must be a valid reference")
|
|
131
|
+
|
|
132
|
+
requestBuilder := smart.ephemeralBuilder(_RTYPE_STORAGE_SPACE_REQUEST)
|
|
133
|
+
requestBuilder.getField(_SSR_FIELD_REQUEST).set(quota)
|
|
134
|
+
|
|
135
|
+
return ll.toStrict(
|
|
136
|
+
oop.inherit(requestBuilder, {
|
|
137
|
+
allocation: requestBuilder.getField(_SSR_FIELD_ALLOCATION)
|
|
138
|
+
})
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
return self
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export ll.toStrict({
|
|
147
|
+
quotaBuilder: quotaBuilder,
|
|
148
|
+
storageSpaceRequestBuilder: storageSpaceRequestBuilder
|
|
149
|
+
})
|
|
@@ -18,6 +18,9 @@ _RTYPE_RUN_COMMAND_ARGS := { Name: "RunCommandArgs", Version: "1" }
|
|
|
18
18
|
_RTYPE_RUN_COMMAND_OPTIONS := { Name: "run-command/options", Version: "1" }
|
|
19
19
|
_RTYPE_RUN_COMMAND_REFS := { Name: "RunCommandRefs", Version: "1" }
|
|
20
20
|
|
|
21
|
+
_FIELD_WORKDIR_IN := "workdirIn"
|
|
22
|
+
_FIELD_WORKDIR_OUT := "workdirOut"
|
|
23
|
+
|
|
21
24
|
/**
|
|
22
25
|
* Creates a simple argument for the command.
|
|
23
26
|
*
|
|
@@ -64,7 +67,7 @@ _createExpressionArg := func(argTpl) {
|
|
|
64
67
|
*/
|
|
65
68
|
builder := func(workdir) {
|
|
66
69
|
res := smart.ephemeralBuilder(_RTYPE_RUN_COMMAND)
|
|
67
|
-
res.getField(
|
|
70
|
+
res.getField(_FIELD_WORKDIR_IN).set(workdir)
|
|
68
71
|
|
|
69
72
|
self := undefined
|
|
70
73
|
|
|
@@ -386,7 +389,7 @@ builder := func(workdir) {
|
|
|
386
389
|
self.getField("args").set(argsRes)
|
|
387
390
|
|
|
388
391
|
res := ll.toStrict(oop.inherit(self.lockAndBuild(), {
|
|
389
|
-
"workdir": self.outputs()
|
|
392
|
+
"workdir": self.outputs()[_FIELD_WORKDIR_OUT],
|
|
390
393
|
"options": options
|
|
391
394
|
}))
|
|
392
395
|
|
|
@@ -20,9 +20,14 @@ _SUBSCRIPTION_FIELD := "subscription"
|
|
|
20
20
|
* @param tpl: template resource (see assets.importTemplate())
|
|
21
21
|
* @param isEph: whether template is ephemeral
|
|
22
22
|
* @param inputs: a map of inputs to be passed to the template
|
|
23
|
+
* @param serviceInputs: (optional) a map of service inputs to be passed to the template.
|
|
24
|
+
* Service inputs are not accounted in dedupliation and recovering.
|
|
25
|
+
* Two resources with the same <inputs> and different <serviceInputs>
|
|
26
|
+
* would be considered as copies of each-other.
|
|
23
27
|
* @return renderer: a smart resource with additional output("name") method
|
|
24
28
|
*/
|
|
25
|
-
createUniversal := func(tpl, isEph, inputs) {
|
|
29
|
+
createUniversal := func(tpl, isEph, inputs, ...opts) {
|
|
30
|
+
serviceInputs := opts[0]
|
|
26
31
|
|
|
27
32
|
// build renderer resource
|
|
28
33
|
rbuilder := undefined
|
|
@@ -45,6 +50,9 @@ createUniversal := func(tpl, isEph, inputs) {
|
|
|
45
50
|
for name, value in inputs {
|
|
46
51
|
inputsMap.add(name, value)
|
|
47
52
|
}
|
|
53
|
+
for name, value in serviceInputs {
|
|
54
|
+
inputsMap.addService(name, value)
|
|
55
|
+
}
|
|
48
56
|
|
|
49
57
|
rbuilder.createInputField(tplutil.INPUTS_MAP_FIELD).set(inputsMap.build())
|
|
50
58
|
// build
|
|
@@ -76,10 +84,14 @@ createUniversal := func(tpl, isEph, inputs) {
|
|
|
76
84
|
*
|
|
77
85
|
* @param tpl: template resource (see assets.importTemplate())
|
|
78
86
|
* @param inputs: a map of inputs to be passed to the template
|
|
87
|
+
* @param serviceInputs: (optional) a map of service inputs to be passed to the template.
|
|
88
|
+
* Service inputs are not accounted in dedupliation and recovering.
|
|
89
|
+
* Two resources with the same <inputs> and different <serviceInputs>
|
|
90
|
+
* would be considered as copies of each-other.
|
|
79
91
|
* @return renderer: a smart resource with additional output("name") method
|
|
80
92
|
*/
|
|
81
|
-
create := func(tpl, inputs) {
|
|
82
|
-
return createUniversal(tpl, false, inputs)
|
|
93
|
+
create := func(tpl, inputs, ...opts) {
|
|
94
|
+
return createUniversal(tpl, false, inputs, opts...)
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
/**
|
|
@@ -87,10 +99,14 @@ create := func(tpl, inputs) {
|
|
|
87
99
|
*
|
|
88
100
|
* @param tpl: template resource (see assets.importTemplate())
|
|
89
101
|
* @param inputs: a map of inputs to be passed to the template
|
|
102
|
+
* @param serviceInputs: (optional) a map of service inputs to be passed to the template.
|
|
103
|
+
* The split into <inputs> and <serviceInputs> has no
|
|
104
|
+
* effect on ephemeral templates, as they do not
|
|
105
|
+
* have deduplication.
|
|
90
106
|
* @return renderer: a smart resource with additional output("name") method
|
|
91
107
|
*/
|
|
92
|
-
createEphemeral := func(tpl, inputs) {
|
|
93
|
-
return createUniversal(tpl, true, inputs)
|
|
108
|
+
createEphemeral := func(tpl, inputs, ...opts) {
|
|
109
|
+
return createUniversal(tpl, true, inputs, opts...)
|
|
94
110
|
}
|
|
95
111
|
|
|
96
112
|
export ll.toStrict({
|
|
@@ -929,7 +929,7 @@ resourceBuilder = func(r) {
|
|
|
929
929
|
* Create new field with given name and field type.
|
|
930
930
|
*
|
|
931
931
|
* @param name field name
|
|
932
|
-
* @param fieldType: field type - input, output, dynamic, service, OTW. Use
|
|
932
|
+
* @param fieldType: field type - input, output, dynamic, service, OTW. Use FTYPE_* constants.
|
|
933
933
|
*/
|
|
934
934
|
createField: func(name, fieldType) {
|
|
935
935
|
assertNotBuilt()
|
|
@@ -938,6 +938,17 @@ resourceBuilder = func(r) {
|
|
|
938
938
|
return field(fid)
|
|
939
939
|
},
|
|
940
940
|
|
|
941
|
+
/**
|
|
942
|
+
* Create service field with given name.
|
|
943
|
+
* Service fields behave very like inputs, but with deduplication disabled.
|
|
944
|
+
* This means, that if you create 2 resources, each having 1 input field with value "A" and the service
|
|
945
|
+
* field with different values, they would be considered the same by backend.
|
|
946
|
+
*/
|
|
947
|
+
createServiceField: func(name) {
|
|
948
|
+
assertNotBuilt()
|
|
949
|
+
return self.createField(name, constants.FTYPE_SERVICE)
|
|
950
|
+
},
|
|
951
|
+
|
|
941
952
|
/**
|
|
942
953
|
* Create new input field with given name.
|
|
943
954
|
*
|
|
@@ -945,7 +956,7 @@ resourceBuilder = func(r) {
|
|
|
945
956
|
*/
|
|
946
957
|
createInputField: func(name) {
|
|
947
958
|
assertNotBuilt()
|
|
948
|
-
return self.createField(name,
|
|
959
|
+
return self.createField(name, constants.FTYPE_INPUT)
|
|
949
960
|
},
|
|
950
961
|
|
|
951
962
|
/**
|
|
@@ -955,7 +966,7 @@ resourceBuilder = func(r) {
|
|
|
955
966
|
*/
|
|
956
967
|
createOutputField: func(name) {
|
|
957
968
|
assertNotBuilt()
|
|
958
|
-
return self.createField(name,
|
|
969
|
+
return self.createField(name, constants.FTYPE_OUTPUT)
|
|
959
970
|
},
|
|
960
971
|
|
|
961
972
|
/**
|
|
@@ -1086,6 +1097,18 @@ _mapBuilder := func(isEph) {
|
|
|
1086
1097
|
return self
|
|
1087
1098
|
},
|
|
1088
1099
|
|
|
1100
|
+
/**
|
|
1101
|
+
* Adds object automatically converting non-reference objects into Json values.
|
|
1102
|
+
* The object is added into service field, meaning the value would not affect deduplication of the map.
|
|
1103
|
+
*
|
|
1104
|
+
* @param key: string - how the field will be named in the map
|
|
1105
|
+
* @param value: any - a reference or a primitive value
|
|
1106
|
+
* @return self: mapBuilder
|
|
1107
|
+
*/
|
|
1108
|
+
addService: func(key, value) {
|
|
1109
|
+
builder.createServiceField(key).setRefOrJson(value)
|
|
1110
|
+
return self
|
|
1111
|
+
},
|
|
1089
1112
|
|
|
1090
1113
|
/**
|
|
1091
1114
|
* Creates a map in the platforma
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
json := import("json")
|
|
6
6
|
|
|
7
7
|
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
8
|
+
feats := import("@platforma-sdk/workflow-tengo:feats")
|
|
9
|
+
|
|
8
10
|
assets := import("@platforma-sdk/workflow-tengo:assets")
|
|
9
11
|
constants := import("@platforma-sdk/workflow-tengo:constants")
|
|
10
12
|
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
@@ -16,9 +18,36 @@ enum := import("enum")
|
|
|
16
18
|
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
17
19
|
render := import("@platforma-sdk/workflow-tengo:render")
|
|
18
20
|
|
|
21
|
+
_WD_CREATE_FIELD_ALLOCATION := "allocation"
|
|
22
|
+
_WD_CREATE_FIELD_WORKDIR := "workdir"
|
|
23
|
+
|
|
24
|
+
_WD_FILL_FIELD_WORKDIR_IN := "workdirIn"
|
|
25
|
+
_WD_FILL_FIELD_WORKDIR_OUT := "workdirOut"
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create empty working directory
|
|
29
|
+
*
|
|
30
|
+
* @return smart.field - field where working directory will appear after creation
|
|
31
|
+
*/
|
|
19
32
|
create := func() {
|
|
20
|
-
|
|
21
|
-
|
|
33
|
+
// working directories without quotas (old-style)
|
|
34
|
+
wdCreate := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_CREATE).lockAndBuild()
|
|
35
|
+
return wdCreate.getField(_WD_CREATE_FIELD_WORKDIR)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create empry working directory for given allocation. This means, the WD would actually be created only
|
|
40
|
+
* when allocation is ready for usage.
|
|
41
|
+
*
|
|
42
|
+
* @param allocation: smart.reference - a reference to storage space allocation.
|
|
43
|
+
* It is usually obtained from StorageSpaceRequest
|
|
44
|
+
* (see exec.limits.storageSpaceRequestBuilder)
|
|
45
|
+
*/
|
|
46
|
+
createV2 := func(allocation) {
|
|
47
|
+
wdCreate := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_CREATE_2).lockAndBuild()
|
|
48
|
+
wdCreate.getField(_WD_CREATE_FIELD_ALLOCATION).set(allocation)
|
|
49
|
+
|
|
50
|
+
return wdCreate.getField(_WD_CREATE_FIELD_WORKDIR)
|
|
22
51
|
}
|
|
23
52
|
|
|
24
53
|
_getFileFillRule := func(filePath, extension) {
|
|
@@ -87,14 +116,12 @@ _getDirFillRule := func(dir) {
|
|
|
87
116
|
/**
|
|
88
117
|
* Fills a workdir with files, values, and directories.
|
|
89
118
|
*
|
|
90
|
-
* @param workdir: reference - a reference to the workdir to fill.
|
|
91
119
|
* @return self: builder - a builder that adds files and directories to the workdir. */
|
|
92
|
-
_fill := func(
|
|
93
|
-
ll.assert(smart.isReference(workdir), "workdir must be a reference to a resource or field")
|
|
94
|
-
|
|
120
|
+
_fill := func() {
|
|
95
121
|
wd := smart.ephemeralBuilder(constants.RTYPE_WORKDIR_FILL)
|
|
96
|
-
wd.getField("workdirIn").set(workdir)
|
|
97
122
|
|
|
123
|
+
/** @type reference - space allocation to be used for WD */
|
|
124
|
+
_allocation := undefined
|
|
98
125
|
blobs := {}
|
|
99
126
|
files := {}
|
|
100
127
|
zipExtractions := {}
|
|
@@ -113,6 +140,16 @@ _fill := func(workdir) {
|
|
|
113
140
|
sets.add(dirs, newDirs...)
|
|
114
141
|
},
|
|
115
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Use given storage allocation for working directory.
|
|
145
|
+
*
|
|
146
|
+
* @param allocation: reference
|
|
147
|
+
*/
|
|
148
|
+
allocation: func(allocation) {
|
|
149
|
+
validation.assertType(allocation, validation.reference, "workdir.builder: allocation should be a valid reference")
|
|
150
|
+
_allocation = allocation
|
|
151
|
+
},
|
|
152
|
+
|
|
116
153
|
/**
|
|
117
154
|
* Adds files from given zip archive into working directory.
|
|
118
155
|
*
|
|
@@ -223,6 +260,15 @@ _fill := func(workdir) {
|
|
|
223
260
|
* @return wd: reference - a reference to the filled workdir.
|
|
224
261
|
*/
|
|
225
262
|
build: func() {
|
|
263
|
+
emptyWorkdir := undefined
|
|
264
|
+
if feats.workdirLimits && smart.isReference(_allocation) {
|
|
265
|
+
emptyWorkdir = createV2(_allocation)
|
|
266
|
+
} else {
|
|
267
|
+
emptyWorkdir = create()
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
wd.getField(_WD_FILL_FIELD_WORKDIR_IN).set(emptyWorkdir)
|
|
271
|
+
|
|
226
272
|
rules := []
|
|
227
273
|
for dir, _ in dirs {
|
|
228
274
|
rules = append(rules, _getDirFillRule(dir))
|
|
@@ -249,7 +295,7 @@ _fill := func(workdir) {
|
|
|
249
295
|
self.getField("dataIn").set(smart.createBinaryMapResource(values))
|
|
250
296
|
self.getField("blobsIn").set(smart.createBlobMapResource(blobs))
|
|
251
297
|
|
|
252
|
-
return self.lockAndBuild().outputs()
|
|
298
|
+
return self.lockAndBuild().outputs()[_WD_FILL_FIELD_WORKDIR_OUT]
|
|
253
299
|
}
|
|
254
300
|
}))
|
|
255
301
|
|
|
@@ -262,7 +308,7 @@ _fill := func(workdir) {
|
|
|
262
308
|
* @return self: builder - a builder that adds files and directories to the workdir.
|
|
263
309
|
*/
|
|
264
310
|
builder := func() {
|
|
265
|
-
return _fill(
|
|
311
|
+
return _fill()
|
|
266
312
|
}
|
|
267
313
|
|
|
268
314
|
/**
|
|
@@ -368,5 +414,6 @@ save := func(workdir) {
|
|
|
368
414
|
export ll.toStrict({
|
|
369
415
|
builder: builder,
|
|
370
416
|
create: create,
|
|
417
|
+
createV2: createV2,
|
|
371
418
|
save: save
|
|
372
419
|
})
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-sdk/workflow-tengo",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.1",
|
|
4
4
|
"description": "Tengo SDK library",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
"./dist/**/*"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@milaboratories/software-pframes-conv": "2.1.
|
|
19
|
+
"@milaboratories/software-pframes-conv": "2.1.12",
|
|
20
20
|
"@platforma-open/milaboratories.software-small-binaries": "^1.15.6"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@platforma-sdk/tengo-builder": "^1.17.
|
|
23
|
+
"@platforma-sdk/tengo-builder": "^1.17.7"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"type-check": "pl-tengo check",
|