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  }