github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/worker/fetcher.go (about) 1 package worker 2 3 import ( 4 "context" 5 "errors" 6 "time" 7 8 "code.cloudfoundry.org/clock" 9 "code.cloudfoundry.org/lager" 10 "github.com/pf-qiu/concourse/v6/atc/db" 11 "github.com/pf-qiu/concourse/v6/atc/db/lock" 12 "github.com/pf-qiu/concourse/v6/atc/resource" 13 "github.com/pf-qiu/concourse/v6/atc/runtime" 14 ) 15 16 const GetResourceLockInterval = 5 * time.Second 17 18 var ErrFailedToGetLock = errors.New("failed to get lock") 19 20 //go:generate counterfeiter . Fetcher 21 22 type Fetcher interface { 23 Fetch( 24 ctx context.Context, 25 logger lager.Logger, 26 containerMetadata db.ContainerMetadata, 27 gardenWorker Worker, 28 containerSpec ContainerSpec, 29 processSpec runtime.ProcessSpec, 30 resource resource.Resource, 31 owner db.ContainerOwner, 32 cache db.UsedResourceCache, 33 lockName string, 34 ) (GetResult, Volume, error) 35 } 36 37 func NewFetcher( 38 clock clock.Clock, 39 lockFactory lock.LockFactory, 40 fetchSourceFactory FetchSourceFactory, 41 ) Fetcher { 42 return &fetcher{ 43 clock: clock, 44 lockFactory: lockFactory, 45 fetchSourceFactory: fetchSourceFactory, 46 } 47 } 48 49 type fetcher struct { 50 clock clock.Clock 51 lockFactory lock.LockFactory 52 fetchSourceFactory FetchSourceFactory 53 } 54 55 func (f *fetcher) Fetch( 56 ctx context.Context, 57 logger lager.Logger, 58 containerMetadata db.ContainerMetadata, 59 gardenWorker Worker, 60 containerSpec ContainerSpec, 61 processSpec runtime.ProcessSpec, 62 resource resource.Resource, 63 owner db.ContainerOwner, 64 cache db.UsedResourceCache, 65 lockName string, 66 ) (GetResult, Volume, error) { 67 result := GetResult{} 68 var volume Volume 69 // TODO: resource_instance_fetch_source.go already knows which volume to use for the resource output, can this be consolidated 70 containerSpec.Outputs = map[string]string{ 71 "resource": processSpec.Args[0], 72 } 73 74 // TODO: just pass in imageFetcherSpec not its contents 75 // this might be a bad idea, don't want the fetchsource to know about images? 76 fetchSource := f.fetchSourceFactory.NewFetchSource( 77 logger, 78 gardenWorker, 79 owner, 80 cache, 81 resource, 82 containerSpec, 83 processSpec, 84 containerMetadata, 85 ) 86 87 ticker := f.clock.NewTicker(GetResourceLockInterval) 88 defer ticker.Stop() 89 90 result, volume, err := f.fetchUnderLock( 91 ctx, 92 logger, 93 fetchSource, 94 cache, 95 lockName, 96 ) 97 if err == nil || err != ErrFailedToGetLock { 98 return result, volume, err 99 } 100 101 for { 102 select { 103 case <-ticker.C(): 104 result, volume, err = f.fetchUnderLock( 105 ctx, 106 logger, 107 fetchSource, 108 cache, 109 lockName, 110 ) 111 if err != nil { 112 if err == ErrFailedToGetLock { 113 break 114 } 115 return result, nil, err 116 } 117 118 return result, volume, nil 119 120 case <-ctx.Done(): 121 return GetResult{}, nil, ctx.Err() 122 } 123 } 124 } 125 126 func (f *fetcher) fetchUnderLock( 127 ctx context.Context, 128 logger lager.Logger, 129 source FetchSource, 130 cache db.UsedResourceCache, 131 lockName string, 132 ) (GetResult, Volume, error) { 133 result := GetResult{} 134 findResult, volume, found, err := source.Find() 135 if err != nil { 136 return result, nil, err 137 } 138 139 if found { 140 return findResult, volume, nil 141 } 142 143 lockLogger := logger.Session("lock-task", lager.Data{"lock-name": lockName}) 144 145 lock, acquired, err := f.lockFactory.Acquire(lockLogger, lock.NewTaskLockID(lockName)) 146 if err != nil { 147 lockLogger.Error("failed-to-get-lock", err) 148 return result, nil, ErrFailedToGetLock 149 } 150 151 if !acquired { 152 lockLogger.Debug("did-not-get-lock") 153 return result, nil, ErrFailedToGetLock 154 } 155 156 defer lock.Release() 157 158 return source.Create(ctx) 159 }