github.com/chenbh/concourse/v6@v6.4.2/atc/lidar/checker.go (about)

     1  package lidar
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"runtime/debug"
     8  	"strconv"
     9  	"sync"
    10  
    11  	"code.cloudfoundry.org/lager"
    12  	"code.cloudfoundry.org/lager/lagerctx"
    13  	"github.com/chenbh/concourse/v6/atc/db"
    14  	"github.com/chenbh/concourse/v6/atc/engine"
    15  	"github.com/chenbh/concourse/v6/atc/metric"
    16  	"github.com/chenbh/concourse/v6/tracing"
    17  )
    18  
    19  //go:generate counterfeiter . RateCalculator
    20  
    21  type RateCalculator interface {
    22  	RateLimiter() (Limiter, error)
    23  }
    24  
    25  func NewChecker(
    26  	logger lager.Logger,
    27  	checkFactory db.CheckFactory,
    28  	engine engine.Engine,
    29  	checkRateCalculator RateCalculator,
    30  ) *checker {
    31  	return &checker{
    32  		logger:              logger,
    33  		checkFactory:        checkFactory,
    34  		engine:              engine,
    35  		running:             &sync.Map{},
    36  		checkRateCalculator: checkRateCalculator,
    37  	}
    38  }
    39  
    40  type checker struct {
    41  	logger lager.Logger
    42  
    43  	checkFactory        db.CheckFactory
    44  	engine              engine.Engine
    45  	checkRateCalculator RateCalculator
    46  
    47  	running *sync.Map
    48  }
    49  
    50  func (c *checker) Run(ctx context.Context) error {
    51  	c.logger.Info("start")
    52  	defer c.logger.Info("end")
    53  
    54  	checks, err := c.checkFactory.StartedChecks()
    55  	if err != nil {
    56  		c.logger.Error("failed-to-fetch-resource-checks", err)
    57  		return err
    58  	}
    59  
    60  	metric.ChecksQueueSize.Set(int64(len(checks)))
    61  
    62  	if len(checks) == 0 {
    63  		return nil
    64  	}
    65  
    66  	limiter, err := c.checkRateCalculator.RateLimiter()
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	for _, ck := range checks {
    72  		if _, exists := c.running.LoadOrStore(ck.ID(), true); !exists {
    73  			if !ck.ManuallyTriggered() {
    74  				err := limiter.Wait(ctx)
    75  				if err != nil {
    76  					c.logger.Error("failed-to-wait-for-limiter", err)
    77  					continue
    78  				}
    79  			}
    80  
    81  			go func(check db.Check) {
    82  				loggerData := lager.Data{
    83  					"check_id": strconv.Itoa(check.ID()),
    84  				}
    85  				defer func() {
    86  					if r := recover(); r != nil {
    87  						err = fmt.Errorf("panic in checker check run %s: %v", loggerData, r)
    88  
    89  						fmt.Fprintf(os.Stderr, "%s\n %s\n", err.Error(), string(debug.Stack()))
    90  						c.logger.Error("panic-in-checker-check-run", err)
    91  
    92  						check.FinishWithError(err)
    93  					}
    94  				}()
    95  
    96  				spanCtx, span := tracing.StartSpanFollowing(
    97  					ctx,
    98  					check,
    99  					"checker.Run",
   100  					tracing.Attrs{
   101  						"team":                     check.TeamName(),
   102  						"pipeline":                 check.PipelineName(),
   103  						"check_id":                 strconv.Itoa(check.ID()),
   104  						"resource_config_scope_id": strconv.Itoa(check.ResourceConfigScopeID()),
   105  					},
   106  				)
   107  				defer span.End()
   108  				defer c.running.Delete(check.ID())
   109  
   110  				c.engine.NewCheck(check).Run(
   111  					lagerctx.NewContext(
   112  						spanCtx,
   113  						c.logger.WithData(loggerData),
   114  					),
   115  				)
   116  			}(ck)
   117  		}
   118  	}
   119  
   120  	return nil
   121  }