github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/commands/utils/precheckrunner/checkrunner.go (about) 1 package precheckrunner 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "github.com/gookit/color" 8 "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" 9 corelog "github.com/jfrog/jfrog-cli-core/v2/utils/log" 10 "github.com/jfrog/jfrog-cli-core/v2/utils/progressbar" 11 "time" 12 13 "github.com/jfrog/jfrog-cli-core/v2/utils/config" 14 "github.com/jfrog/jfrog-client-go/utils/log" 15 ) 16 17 // PreCheck Is interface for a check on an Artifactory server 18 type PreCheck interface { 19 // Name - Describes the check 20 Name() string 21 // Execute the check, return true if passed 22 ExecuteCheck(args RunArguments) (bool, error) 23 } 24 25 // Default struct for small checks 26 type functionPreCheck struct { 27 name string 28 check func(args RunArguments) (bool, error) 29 } 30 31 func (a functionPreCheck) Name() string { 32 return a.name 33 } 34 35 func (a functionPreCheck) ExecuteCheck(args RunArguments) (bool, error) { 36 return a.check(args) 37 } 38 39 // NewCheck - Creates a check base on a given function and name 40 func NewCheck(name string, check func(args RunArguments) (bool, error)) PreCheck { 41 return functionPreCheck{ 42 name: name, 43 check: check, 44 } 45 } 46 47 // PreCheckRunner - Manages and runs the pre-checks 48 type PreCheckRunner struct { 49 // All the checks the runner need to preform 50 checks []PreCheck 51 // The status of the preformed checks on the current run 52 status *RunStatus 53 // Manage all the displayed progress of the run 54 displayBar *RunnerProgressBar 55 } 56 57 // Manage all the displayed progress of the run 58 type RunnerProgressBar struct { 59 manager *progressbar.ProgressBarMng 60 currentDisplay *progressbar.TasksProgressBar 61 } 62 63 // RunStatus - The status of the preformed checks in a run 64 type RunStatus struct { 65 failures uint 66 successes uint 67 currentCheck string 68 startTime time.Time 69 } 70 71 // RunArguments - The arguments of the run that is passed to the checks 72 type RunArguments struct { 73 Context context.Context 74 ServerDetails *config.ServerDetails 75 ProgressMng *progressbar.ProgressBarMng 76 } 77 78 // Creates a new empty runner 79 func NewPreChecksRunner() *PreCheckRunner { 80 runner := &PreCheckRunner{} 81 return runner 82 } 83 84 // Add a check to the runner 85 func (pcr *PreCheckRunner) AddCheck(check PreCheck) { 86 if check == nil { 87 return 88 } 89 pcr.checks = append(pcr.checks, check) 90 } 91 92 // Initialize a progress bar for running pre-checks 93 func (pcr *PreCheckRunner) initProgressBar(status *RunStatus) (runnerProgress *RunnerProgressBar, err error) { 94 mng, shouldDisplay, err := progressbar.NewBarsMng() 95 if !shouldDisplay || err != nil { 96 return 97 } 98 runnerProgress = &RunnerProgressBar{manager: mng} 99 // The current check that is running 100 runnerProgress.currentDisplay = mng.NewStringProgressBar("Running check: ", func() string { 101 return color.Green.Render(status.currentCheck) 102 }) 103 return 104 } 105 106 // Run all the checks and display the process 107 func (pcr *PreCheckRunner) Run(context context.Context, serverDetails *config.ServerDetails) (err error) { 108 log.Info(coreutils.PrintTitle(fmt.Sprintf("Running %d checks.", len(pcr.checks)))) 109 args := RunArguments{Context: context, ServerDetails: serverDetails} 110 pcr.status = &RunStatus{startTime: time.Now()} 111 // Progress display 112 if pcr.displayBar, err = pcr.initProgressBar(pcr.status); err != nil { 113 return 114 } 115 if pcr.displayBar != nil { 116 args.ProgressMng = pcr.displayBar.manager 117 } 118 // Execute checks 119 defer func() { 120 err = errors.Join(err, pcr.cleanup()) 121 }() 122 var checkPassed bool 123 for i, check := range pcr.checks { 124 pcr.prepare(i, check) 125 if checkPassed, err = check.ExecuteCheck(args); err != nil { 126 pcr.finish(check.Name(), false) 127 return 128 } 129 pcr.finish(check.Name(), checkPassed) 130 } 131 return 132 } 133 134 // Update the runner before a check 135 func (pcr *PreCheckRunner) prepare(checkNumber int, check PreCheck) { 136 log.Info(fmt.Sprintf("== Running check (%d) '%s' ======", checkNumber+1, check.Name())) 137 pcr.status.currentCheck = check.Name() 138 } 139 140 // Update the runner after a check 141 func (pcr *PreCheckRunner) finish(checkName string, passed bool) { 142 // Update status 143 checkStatus := "Fail" 144 if passed { 145 checkStatus = "Success" 146 pcr.status.successes++ 147 } else { 148 pcr.status.failures++ 149 } 150 // Update progress 151 log.Info(fmt.Sprintf("Check '%s' is done with status %s", checkName, checkStatus)) 152 } 153 154 // Clean up when the run ends 155 func (pcr *PreCheckRunner) cleanup() (err error) { 156 // Quit progress bar 157 if pcr.displayBar != nil { 158 // Quit text - current check 159 pcr.displayBar.currentDisplay.GetBar().Abort(true) 160 pcr.displayBar.currentDisplay = nil 161 // Wait a refresh rate to make sure all aborts have finished 162 time.Sleep(progressbar.ProgressRefreshRate) 163 // Wait for all go routines to finish before quiting 164 pcr.displayBar.manager.GetBarsWg().Wait() 165 // Close log file 166 if pcr.displayBar.manager.GetLogFile() != nil { 167 if err = corelog.CloseLogFile(pcr.displayBar.manager.GetLogFile()); err != nil { 168 return 169 } 170 } 171 } 172 // Notify on final status of the run 173 if pcr.status.failures == 0 && len(pcr.checks) == int(pcr.status.successes+pcr.status.failures) && err == nil { 174 log.Info(coreutils.PrintTitle(fmt.Sprintf("All the checks passed 🐸 (elapsed time %s).", time.Since(pcr.status.startTime)))) 175 } else { 176 log.Error(coreutils.PrintTitle(fmt.Sprintf("%d/%d checks passed (elapsed time %s), check the log for more information.", 177 pcr.status.successes, 178 pcr.status.successes+pcr.status.failures, 179 time.Since(pcr.status.startTime), 180 ))) 181 } 182 183 return 184 }