@@ -52,6 +52,9 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
52
52
// Initialize the job fields (sets defaults and any necessary init work).
53
53
args .Job .Canonicalize ()
54
54
55
+ // Add implicit constraints
56
+ setImplicitConstraints (args .Job )
57
+
55
58
// Validate the job.
56
59
if err := validateJob (args .Job ); err != nil {
57
60
return err
@@ -115,28 +118,6 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
115
118
}
116
119
}
117
120
}
118
-
119
- // Add implicit constraints that the task groups are run on a Node with
120
- // Vault
121
- for _ , tg := range args .Job .TaskGroups {
122
- _ , ok := policies [tg .Name ]
123
- if ! ok {
124
- // Not requesting Vault
125
- continue
126
- }
127
-
128
- found := false
129
- for _ , c := range tg .Constraints {
130
- if c .Equal (vaultConstraint ) {
131
- found = true
132
- break
133
- }
134
- }
135
-
136
- if ! found {
137
- tg .Constraints = append (tg .Constraints , vaultConstraint )
138
- }
139
- }
140
121
}
141
122
142
123
// Clear the Vault token
@@ -188,6 +169,77 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis
188
169
return nil
189
170
}
190
171
172
+ // setImplicitConstraints adds implicit constraints to the job based on the
173
+ // features it is requesting.
174
+ func setImplicitConstraints (j * structs.Job ) {
175
+ // Get the required Vault Policies
176
+ policies := j .VaultPolicies ()
177
+
178
+ // Get the required signals
179
+ signals := j .RequiredSignals ()
180
+
181
+ // Hot path
182
+ if len (signals ) == 0 && len (policies ) == 0 {
183
+ return
184
+ }
185
+
186
+ // Add Vault constraints
187
+ for _ , tg := range j .TaskGroups {
188
+ _ , ok := policies [tg .Name ]
189
+ if ! ok {
190
+ // Not requesting Vault
191
+ continue
192
+ }
193
+
194
+ found := false
195
+ for _ , c := range tg .Constraints {
196
+ if c .Equal (vaultConstraint ) {
197
+ found = true
198
+ break
199
+ }
200
+ }
201
+
202
+ if ! found {
203
+ tg .Constraints = append (tg .Constraints , vaultConstraint )
204
+ }
205
+ }
206
+
207
+ // Add signal constraints
208
+ for _ , tg := range j .TaskGroups {
209
+ tgSignals , ok := signals [tg .Name ]
210
+ if ! ok {
211
+ // Not requesting Vault
212
+ continue
213
+ }
214
+
215
+ // Flatten the signals
216
+ required := structs .MapStringStringSliceValueSet (tgSignals )
217
+ sigConstraint := getSignalConstraint (required )
218
+
219
+ found := false
220
+ for _ , c := range tg .Constraints {
221
+ if c .Equal (sigConstraint ) {
222
+ found = true
223
+ break
224
+ }
225
+ }
226
+
227
+ if ! found {
228
+ tg .Constraints = append (tg .Constraints , sigConstraint )
229
+ }
230
+ }
231
+ }
232
+
233
+ // getSignalConstraint builds a suitable constraint based on the required
234
+ // signals
235
+ func getSignalConstraint (signals []string ) * structs.Constraint {
236
+ return & structs.Constraint {
237
+ Operand : structs .ConstraintSetContains ,
238
+ LTarget : "${attr.os.signals}" ,
239
+ RTarget : strings .Join (signals , "," ),
240
+ }
241
+ }
242
+
191
243
// Summary retreives the summary of a job
192
244
func (j * Job ) Summary (args * structs.JobSummaryRequest ,
193
245
reply * structs.JobSummaryResponse ) error {
@@ -556,6 +608,9 @@ func (j *Job) Plan(args *structs.JobPlanRequest, reply *structs.JobPlanResponse)
556
608
// Initialize the job fields (sets defaults and any necessary init work).
557
609
args .Job .Canonicalize ()
558
610
611
+ // Add implicit constraints
612
+ setImplicitConstraints (args .Job )
613
+
559
614
// Validate the job.
560
615
if err := validateJob (args .Job ); err != nil {
561
616
return err
@@ -656,8 +711,14 @@ func validateJob(job *structs.Job) error {
656
711
multierror .Append (validationErrors , err )
657
712
}
658
713
714
+ // Get the signals required
715
+ signals := job .RequiredSignals ()
716
+
659
717
// Validate the driver configurations.
660
718
for _ , tg := range job .TaskGroups {
719
+ // Get the signals for the task group
720
+ tgSignals , tgOk := signals [tg .Name ]
721
+
661
722
for _ , task := range tg .Tasks {
662
723
d , err := driver .NewDriver (
663
724
task .Driver ,
@@ -673,6 +734,21 @@ func validateJob(job *structs.Job) error {
673
734
formatted := fmt .Errorf ("group %q -> task %q -> config: %v" , tg .Name , task .Name , err )
674
735
multierror .Append (validationErrors , formatted )
675
736
}
737
+
738
+ // The task group didn't have any task that required signals
739
+ if ! tgOk {
740
+ continue
741
+ }
742
+
743
+ // This task requires signals. Ensure the driver is capable
744
+ if required , ok := tgSignals [task .Name ]; ok {
745
+ abilities := d .Abilities ()
746
+ if ! abilities .SendSignals {
747
+ formatted := fmt .Errorf ("group %q -> task %q: driver %q doesn't support sending signals. Requested signals are %v" ,
748
+ tg .Name , task .Name , task .Driver , strings .Join (required , ", " ))
749
+ multierror .Append (validationErrors , formatted )
750
+ }
751
+ }
676
752
}
677
753
}
678
754
0 commit comments