github.com/chenbh/concourse/v6@v6.4.2/atc/lidar/scanner.go (about) 1 package lidar 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "runtime/debug" 8 "strconv" 9 "sync" 10 "time" 11 12 "code.cloudfoundry.org/lager" 13 "code.cloudfoundry.org/lager/lagerctx" 14 "github.com/chenbh/concourse/v6/atc/creds" 15 "github.com/chenbh/concourse/v6/atc/db" 16 "github.com/chenbh/concourse/v6/atc/metric" 17 "github.com/chenbh/concourse/v6/tracing" 18 "github.com/pkg/errors" 19 ) 20 21 func NewScanner( 22 logger lager.Logger, 23 checkFactory db.CheckFactory, 24 secrets creds.Secrets, 25 defaultCheckTimeout time.Duration, 26 defaultCheckInterval time.Duration, 27 defaultWithWebhookCheckInterval time.Duration, 28 ) *scanner { 29 return &scanner{ 30 logger: logger, 31 checkFactory: checkFactory, 32 secrets: secrets, 33 defaultCheckTimeout: defaultCheckTimeout, 34 defaultCheckInterval: defaultCheckInterval, 35 defaultWithWebhookCheckInterval: defaultWithWebhookCheckInterval, 36 } 37 } 38 39 type scanner struct { 40 logger lager.Logger 41 42 checkFactory db.CheckFactory 43 secrets creds.Secrets 44 defaultCheckTimeout time.Duration 45 defaultCheckInterval time.Duration 46 defaultWithWebhookCheckInterval time.Duration 47 } 48 49 func (s *scanner) Run(ctx context.Context) error { 50 spanCtx, span := tracing.StartSpan(ctx, "scanner.Run", nil) 51 s.logger.Info("start") 52 defer span.End() 53 defer s.logger.Info("end") 54 55 resources, err := s.checkFactory.Resources() 56 if err != nil { 57 s.logger.Error("failed-to-get-resources", err) 58 return err 59 } 60 61 resourceTypes, err := s.checkFactory.ResourceTypes() 62 if err != nil { 63 s.logger.Error("failed-to-get-resource-types", err) 64 return err 65 } 66 67 waitGroup := new(sync.WaitGroup) 68 resourceTypesChecked := &sync.Map{} 69 70 for _, resource := range resources { 71 waitGroup.Add(1) 72 73 go func(resource db.Resource, resourceTypes db.ResourceTypes) { 74 loggerData := lager.Data{ 75 "resource_id": strconv.Itoa(resource.ID()), 76 "resource_name": resource.Name(), 77 "pipeline_name": resource.PipelineName(), 78 "team_name": resource.TeamName(), 79 } 80 defer func() { 81 if r := recover(); r != nil { 82 err = fmt.Errorf("panic in scanner run %s: %v", loggerData, r) 83 84 fmt.Fprintf(os.Stderr, "%s\n %s\n", err.Error(), string(debug.Stack())) 85 s.logger.Error("panic-in-scanner-run", err) 86 87 s.setCheckError(s.logger, resource, err) 88 } 89 }() 90 defer waitGroup.Done() 91 92 err := s.check(spanCtx, resource, resourceTypes, resourceTypesChecked) 93 s.setCheckError(s.logger, resource, err) 94 95 }(resource, resourceTypes) 96 } 97 98 waitGroup.Wait() 99 100 return s.checkFactory.NotifyChecker() 101 } 102 103 func (s *scanner) check(ctx context.Context, checkable db.Checkable, resourceTypes db.ResourceTypes, resourceTypesChecked *sync.Map) error { 104 105 var err error 106 107 spanCtx, span := tracing.StartSpan(ctx, "scanner.check", tracing.Attrs{ 108 "team": checkable.TeamName(), 109 "pipeline": checkable.PipelineName(), 110 "resource": checkable.Name(), 111 "type": checkable.Type(), 112 "resource_config_scope_id": strconv.Itoa(checkable.ResourceConfigScopeID()), 113 }) 114 defer span.End() 115 116 parentType, found := resourceTypes.Parent(checkable) 117 if found { 118 if _, exists := resourceTypesChecked.LoadOrStore(parentType.ID(), true); !exists { 119 // only create a check for resource type if it has not been checked yet 120 err = s.check(spanCtx, parentType, resourceTypes, resourceTypesChecked) 121 s.setCheckError(s.logger, parentType, err) 122 123 if err != nil { 124 s.logger.Error("failed-to-create-type-check", err) 125 return errors.Wrapf(err, "parent type '%v' error", parentType.Name()) 126 } 127 } 128 } 129 130 interval := s.defaultCheckInterval 131 if checkable.HasWebhook() { 132 interval = s.defaultWithWebhookCheckInterval 133 } 134 if every := checkable.CheckEvery(); every != "" { 135 interval, err = time.ParseDuration(every) 136 if err != nil { 137 s.logger.Error("failed-to-parse-check-every", err) 138 return err 139 } 140 } 141 142 if time.Now().Before(checkable.LastCheckEndTime().Add(interval)) { 143 return nil 144 } 145 146 version := checkable.CurrentPinnedVersion() 147 148 _, created, err := s.checkFactory.TryCreateCheck(lagerctx.NewContext(spanCtx, s.logger), checkable, resourceTypes, version, false) 149 if err != nil { 150 s.logger.Error("failed-to-create-check", err) 151 return err 152 } 153 154 if !created { 155 s.logger.Debug("check-already-exists") 156 } 157 158 metric.ChecksEnqueued.Inc() 159 160 return nil 161 } 162 163 func (s *scanner) setCheckError(logger lager.Logger, checkable db.Checkable, err error) { 164 setErr := checkable.SetCheckSetupError(err) 165 if setErr != nil { 166 logger.Error("failed-to-set-check-error", setErr) 167 } 168 }