github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/scout/cmd/maintenance.go (about) 1 // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package scoutcmd 6 7 import ( 8 "context" 9 "fmt" 10 "os" 11 "strings" 12 "sync" 13 14 "github.com/choria-io/go-choria/inter" 15 iu "github.com/choria-io/go-choria/internal/util" 16 "github.com/sirupsen/logrus" 17 18 "github.com/choria-io/go-choria/client/discovery" 19 "github.com/choria-io/go-choria/client/scoutclient" 20 scoutagent "github.com/choria-io/go-choria/scout/agent/scout" 21 ) 22 23 type MaintenanceCommand struct { 24 fw inter.Framework 25 sopt *discovery.StandardOptions 26 checks []string 27 json bool 28 verbose bool 29 colorize bool 30 log *logrus.Entry 31 } 32 33 func NewMaintenanceCommand(sopt *discovery.StandardOptions, fw inter.Framework, checks []string, json bool, verbose bool, colorize bool, log *logrus.Entry) (*MaintenanceCommand, error) { 34 return &MaintenanceCommand{ 35 fw: fw, 36 sopt: sopt, 37 checks: checks, 38 json: json, 39 log: log, 40 verbose: verbose, 41 colorize: colorize, 42 }, nil 43 } 44 45 func (t *MaintenanceCommand) Run(ctx context.Context, wg *sync.WaitGroup) error { 46 defer wg.Done() 47 48 sc, err := scoutClient(t.fw, t.sopt, t.log) 49 if err != nil { 50 return err 51 } 52 53 var checks = make([]any, len(t.checks)) 54 for i, c := range t.checks { 55 checks[i] = c 56 } 57 58 result, err := sc.Maintenance().Checks(checks).Do(ctx) 59 if err != nil { 60 return err 61 } 62 63 if t.json { 64 return result.RenderResults(os.Stdout, scoutclient.JSONFormat, scoutclient.DisplayDDL, t.verbose, false, t.colorize, t.log) 65 } 66 67 if result.Stats().ResponsesCount() == 0 { 68 return fmt.Errorf("no responses received") 69 } 70 71 mu := sync.Mutex{} 72 triggered := 0 73 shown := 0 74 table := iu.NewUTF8TableWithTitle("Scout check maintenance", "Name", "Triggered", "Skipped", "Failed", "Message") 75 76 result.EachOutput(func(r *scoutclient.MaintenanceOutput) { 77 tr := &scoutagent.TriggerReply{} 78 err = r.ParseMaintenanceOutput(tr) 79 if err != nil { 80 t.log.Errorf("Could not parse output from %s: %s", r.ResultDetails().Sender(), err) 81 return 82 } 83 84 mu.Lock() 85 defer mu.Unlock() 86 87 triggered += len(tr.TransitionedChecks) 88 89 if !t.verbose && r.ResultDetails().OK() && len(tr.FailedChecks) == 0 { 90 return 91 } 92 93 shown++ 94 95 table.AddRow(r.ResultDetails().Sender(), strings.Join(tr.TransitionedChecks, ", "), strings.Join(tr.SkippedChecks, ", "), strings.Join(tr.FailedChecks, ", "), r.ResultDetails().StatusMessage()) 96 }) 97 98 if shown == 0 { 99 fmt.Printf("Placed %d checks into maintenance mode on %d nodes\n", triggered, result.Stats().ResponsesCount()) 100 fmt.Println() 101 } else { 102 fmt.Println(table.Render()) 103 } 104 105 return result.RenderResults(os.Stdout, scoutclient.TXTFooter, scoutclient.DisplayDDL, t.verbose, false, t.colorize, t.log) 106 }