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  }