11package worker
22
33import (
4+ "fmt"
45 "sort"
56 "strings"
67 "time"
2223 ErrTaskTypeAssertion = errors .New ("error asserting Task type" )
2324 errTaskQueryInventory = errors .New ("error in task query inventory for installed firmware" )
2425 errTaskPlanActions = errors .New ("error in task action planning" )
25- errTaskPlanValidate = errors .New ("error in task plan validation" )
2626)
2727
2828// taskHandler implements the taskTransitionHandler methods
@@ -44,10 +44,7 @@ func (h *taskHandler) Query(t sw.StateSwitch, args sw.TransitionArgs) error {
4444 return errors .Wrap (errTaskQueryInventory , ErrTaskTypeAssertion .Error ())
4545 }
4646
47- // asset has component inventory
48- if len (tctx .Asset .Components ) > 0 {
49- return nil
50- }
47+ tctx .Logger .Debug ("run query step" )
5148
5249 // attempt to fetch component inventory from the device
5350 components , err := h .queryFromDevice (tctx )
@@ -76,6 +73,8 @@ func (h *taskHandler) Plan(t sw.StateSwitch, args sw.TransitionArgs) error {
7673 return errors .Wrap (ErrSaveTask , ErrTaskTypeAssertion .Error ())
7774 }
7875
76+ tctx .Logger .Debug ("create the plan" )
77+
7978 switch task .FirmwarePlanMethod {
8079 case model .FromFirmwareSet :
8180 return h .planFromFirmwareSet (tctx , task )
@@ -86,30 +85,6 @@ func (h *taskHandler) Plan(t sw.StateSwitch, args sw.TransitionArgs) error {
8685 }
8786}
8887
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-
11388func (h * taskHandler ) registerActionMetrics (startTS time.Time , action * model.Action , state string ) {
11489 metrics .ActionRuntimeSummary .With (
11590 prometheus.Labels {
@@ -131,8 +106,13 @@ func (h *taskHandler) Run(t sw.StateSwitch, args sw.TransitionArgs) error {
131106 return errors .Wrap (ErrSaveTask , ErrTaskTypeAssertion .Error ())
132107 }
133108
109+ tctx .Logger .Debug ("running the plan" )
110+
134111 // each actionSM (state machine) corresponds to a firmware to be installed
135112 for _ , actionSM := range tctx .ActionStateMachines {
113+ tctx .Logger .WithFields (logrus.Fields {
114+ "statemachineID" : actionSM .ActionID (),
115+ }).Debug ("state machine start" )
136116 startTS := time .Now ()
137117
138118 // fetch action attributes from task
@@ -160,8 +140,12 @@ func (h *taskHandler) Run(t sw.StateSwitch, args sw.TransitionArgs) error {
160140 }
161141
162142 h .registerActionMetrics (startTS , action , string (cptypes .Succeeded ))
143+ tctx .Logger .WithFields (logrus.Fields {
144+ "statemachineID" : actionSM .ActionID (),
145+ }).Debug ("state machine end" )
163146 }
164147
148+ tctx .Logger .Debug ("plan finished" )
165149 return nil
166150}
167151
@@ -214,7 +198,8 @@ func (h *taskHandler) planFromFirmwareSet(tctx *sm.HandlerContext, task *model.T
214198 }
215199
216200 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" )
218203 }
219204
220205 // plan actions based and update task action list
@@ -266,23 +251,34 @@ func (h *taskHandler) queryFromDevice(tctx *sm.HandlerContext) (model.Components
266251// planInstall sets up the firmware install plan
267252//
268253// 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 ) {
270255 actionMachines := make (sm.ActionStateMachines , 0 )
271256 actions := make (model.Actions , 0 )
272257
273258 // final is set to true in the final action
274259 var final bool
275260
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+ }
277276
277+ sortFirmwareByInstallOrder (toInstall )
278278 // each firmware applicable results in an ActionPlan and an Action
279- for idx , firmware := range firmwares {
279+ for idx , firmware := range toInstall {
280280 // 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 )
286282
287283 // generate an action ID
288284 actionID := sm .ActionID (task .ID .String (), firmware .Component , idx )
@@ -337,3 +333,41 @@ func sortFirmwareByInstallOrder(firmwares []*model.Firmware) {
337333 return model .FirmwareInstallOrder [slugi ] < model .FirmwareInstallOrder [slugj ]
338334 })
339335}
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