1
1
package worker
2
2
3
3
import (
4
+ "fmt"
4
5
"sort"
5
6
"strings"
6
7
"time"
22
23
ErrTaskTypeAssertion = errors .New ("error asserting Task type" )
23
24
errTaskQueryInventory = errors .New ("error in task query inventory for installed firmware" )
24
25
errTaskPlanActions = errors .New ("error in task action planning" )
25
- errTaskPlanValidate = errors .New ("error in task plan validation" )
26
26
)
27
27
28
28
// taskHandler implements the taskTransitionHandler methods
@@ -44,10 +44,7 @@ func (h *taskHandler) Query(t sw.StateSwitch, args sw.TransitionArgs) error {
44
44
return errors .Wrap (errTaskQueryInventory , ErrTaskTypeAssertion .Error ())
45
45
}
46
46
47
- // asset has component inventory
48
- if len (tctx .Asset .Components ) > 0 {
49
- return nil
50
- }
47
+ tctx .Logger .Debug ("run query step" )
51
48
52
49
// attempt to fetch component inventory from the device
53
50
components , err := h .queryFromDevice (tctx )
@@ -76,6 +73,8 @@ func (h *taskHandler) Plan(t sw.StateSwitch, args sw.TransitionArgs) error {
76
73
return errors .Wrap (ErrSaveTask , ErrTaskTypeAssertion .Error ())
77
74
}
78
75
76
+ tctx .Logger .Debug ("create the plan" )
77
+
79
78
switch task .FirmwarePlanMethod {
80
79
case model .FromFirmwareSet :
81
80
return h .planFromFirmwareSet (tctx , task )
@@ -86,30 +85,6 @@ func (h *taskHandler) Plan(t sw.StateSwitch, args sw.TransitionArgs) error {
86
85
}
87
86
}
88
87
89
- func (h * taskHandler ) ValidatePlan (t sw.StateSwitch , args sw.TransitionArgs ) (bool , error ) {
90
- tctx , ok := args .(* sm.HandlerContext )
91
- if ! ok {
92
- return false , sm .ErrInvalidtaskHandlerContext
93
- }
94
-
95
- task , ok := t .(* model.Task )
96
- if ! ok {
97
- return false , errors .Wrap (ErrSaveTask , ErrTaskTypeAssertion .Error ())
98
- }
99
-
100
- // validate task has action plans listed
101
- if len (task .ActionsPlanned ) == 0 {
102
- return false , errors .Wrap (errTaskPlanValidate , "no task actions planned" )
103
- }
104
-
105
- // validate task context has action statemachines for execution
106
- if len (tctx .ActionStateMachines ) == 0 {
107
- return false , errors .Wrap (errTaskPlanValidate , "task action plan empty" )
108
- }
109
-
110
- return true , nil
111
- }
112
-
113
88
func (h * taskHandler ) registerActionMetrics (startTS time.Time , action * model.Action , state string ) {
114
89
metrics .ActionRuntimeSummary .With (
115
90
prometheus.Labels {
@@ -131,8 +106,13 @@ func (h *taskHandler) Run(t sw.StateSwitch, args sw.TransitionArgs) error {
131
106
return errors .Wrap (ErrSaveTask , ErrTaskTypeAssertion .Error ())
132
107
}
133
108
109
+ tctx .Logger .Debug ("running the plan" )
110
+
134
111
// each actionSM (state machine) corresponds to a firmware to be installed
135
112
for _ , actionSM := range tctx .ActionStateMachines {
113
+ tctx .Logger .WithFields (logrus.Fields {
114
+ "statemachineID" : actionSM .ActionID (),
115
+ }).Debug ("state machine start" )
136
116
startTS := time .Now ()
137
117
138
118
// fetch action attributes from task
@@ -160,8 +140,12 @@ func (h *taskHandler) Run(t sw.StateSwitch, args sw.TransitionArgs) error {
160
140
}
161
141
162
142
h .registerActionMetrics (startTS , action , string (cptypes .Succeeded ))
143
+ tctx .Logger .WithFields (logrus.Fields {
144
+ "statemachineID" : actionSM .ActionID (),
145
+ }).Debug ("state machine end" )
163
146
}
164
147
148
+ tctx .Logger .Debug ("plan finished" )
165
149
return nil
166
150
}
167
151
@@ -214,7 +198,8 @@ func (h *taskHandler) planFromFirmwareSet(tctx *sm.HandlerContext, task *model.T
214
198
}
215
199
216
200
if len (applicable ) == 0 {
217
- return errors .Wrap (errTaskPlanActions , "planFromFirmwareSet(): no applicable firmware identified" )
201
+ // XXX: why not just short-circuit success here on the GIGO theory?
202
+ return errors .Wrap (errTaskPlanActions , "planFromFirmwareSet(): firmware set lacks any members" )
218
203
}
219
204
220
205
// plan actions based and update task action list
@@ -266,23 +251,34 @@ func (h *taskHandler) queryFromDevice(tctx *sm.HandlerContext) (model.Components
266
251
// planInstall sets up the firmware install plan
267
252
//
268
253
// This returns a list of actions to added to the task and a list of action state machines for those actions.
269
- func (h * taskHandler ) planInstall (_ * sm.HandlerContext , task * model.Task , firmwares []* model.Firmware ) (sm.ActionStateMachines , model.Actions , error ) {
254
+ func (h * taskHandler ) planInstall (hCtx * sm.HandlerContext , task * model.Task , firmwares []* model.Firmware ) (sm.ActionStateMachines , model.Actions , error ) {
270
255
actionMachines := make (sm.ActionStateMachines , 0 )
271
256
actions := make (model.Actions , 0 )
272
257
273
258
// final is set to true in the final action
274
259
var final bool
275
260
276
- sortFirmwareByInstallOrder (firmwares )
261
+ hCtx .Logger .WithFields (logrus.Fields {
262
+ "condition.id" : task .ID ,
263
+ "requested.firmware.count" : fmt .Sprintf ("%d" , len (firmwares )),
264
+ }).Info ("checking against current inventory" )
265
+
266
+ toInstall := firmwares
267
+
268
+ if ! task .Parameters .ForceInstall {
269
+ toInstall = removeFirmwareAlreadyAtDesiredVersion (hCtx , firmwares )
270
+ }
271
+
272
+ if len (toInstall ) == 0 {
273
+ hCtx .Logger .Info ("no action required for this task" )
274
+ return actionMachines , actions , nil
275
+ }
277
276
277
+ sortFirmwareByInstallOrder (toInstall )
278
278
// each firmware applicable results in an ActionPlan and an Action
279
- for idx , firmware := range firmwares {
279
+ for idx , firmware := range toInstall {
280
280
// set final bool when its the last firmware in the slice
281
- if len (firmwares ) > 1 {
282
- final = (idx == len (firmwares )- 1 )
283
- } else {
284
- final = true
285
- }
281
+ final = (idx == len (toInstall )- 1 )
286
282
287
283
// generate an action ID
288
284
actionID := sm .ActionID (task .ID .String (), firmware .Component , idx )
@@ -337,3 +333,41 @@ func sortFirmwareByInstallOrder(firmwares []*model.Firmware) {
337
333
return model .FirmwareInstallOrder [slugi ] < model .FirmwareInstallOrder [slugj ]
338
334
})
339
335
}
336
+
337
+ func removeFirmwareAlreadyAtDesiredVersion (hCtx * sm.HandlerContext , fws []* model.Firmware ) []* model.Firmware {
338
+ var toInstall []* model.Firmware
339
+
340
+ // only iterate the inventory once
341
+ invMap := make (map [string ]string )
342
+ for _ , cmp := range hCtx .Asset .Components {
343
+ invMap [strings .ToLower (cmp .Slug )] = cmp .FirmwareInstalled
344
+ }
345
+
346
+ // XXX: this will drop firmware for components that are specified in
347
+ // the firmware set but not in the inventory. This is consistent with the
348
+ // desire of users to not require a force or a re-run to accomplish an
349
+ // attainable goal.
350
+ for _ , fw := range fws {
351
+ currentVersion , ok := invMap [strings .ToLower (fw .Component )]
352
+
353
+ switch {
354
+ case ! ok :
355
+ hCtx .Logger .WithFields (logrus.Fields {
356
+ "component" : fw .Component ,
357
+ }).Warn ("inventory missing component" )
358
+ case strings .EqualFold (currentVersion , fw .Version ):
359
+ hCtx .Logger .WithFields (logrus.Fields {
360
+ "component" : fw .Component ,
361
+ "version" : fw .Version ,
362
+ }).Debug ("inventory firmware version matches set" )
363
+ default :
364
+ hCtx .Logger .WithFields (logrus.Fields {
365
+ "component" : fw .Component ,
366
+ "installed.version" : currentVersion ,
367
+ "mandated.version" : fw .Version ,
368
+ }).Debug ("firmware queued for install" )
369
+ toInstall = append (toInstall , fw )
370
+ }
371
+ }
372
+ return toInstall
373
+ }
0 commit comments