github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/scheduler/algorithm/compute.go (about)

     1  package algorithm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/pf-qiu/concourse/v6/atc/db"
     8  	"github.com/pf-qiu/concourse/v6/tracing"
     9  )
    10  
    11  type Resolver interface {
    12  	Resolve(context.Context) (map[string]*versionCandidate, db.ResolutionFailure, error)
    13  	InputConfigs() db.InputConfigs
    14  }
    15  
    16  func New(versionsDB db.VersionsDB) *Algorithm {
    17  	return &Algorithm{
    18  		versionsDB: versionsDB,
    19  	}
    20  }
    21  
    22  type Algorithm struct {
    23  	versionsDB db.VersionsDB
    24  }
    25  
    26  func (a *Algorithm) Compute(
    27  	ctx context.Context,
    28  	job db.Job,
    29  	inputs db.InputConfigs,
    30  ) (db.InputMapping, bool, bool, error) {
    31  	ctx, span := tracing.StartSpan(ctx, "Algorithm.Compute", tracing.Attrs{
    32  		"pipeline": job.PipelineName(),
    33  		"job":      job.Name(),
    34  	})
    35  	defer span.End()
    36  
    37  	resolvers, err := constructResolvers(a.versionsDB, inputs)
    38  	if err != nil {
    39  		return nil, false, false, fmt.Errorf("construct resolvers: %w", err)
    40  	}
    41  
    42  	return a.computeResolvers(ctx, resolvers)
    43  }
    44  
    45  func (a *Algorithm) computeResolvers(
    46  	ctx context.Context,
    47  	resolvers []Resolver,
    48  ) (db.InputMapping, bool, bool, error) {
    49  	finalHasNext := false
    50  	finalResolved := true
    51  	finalMapping := db.InputMapping{}
    52  
    53  	for _, resolver := range resolvers {
    54  		versionCandidates, resolveErr, err := resolver.Resolve(ctx)
    55  		if err != nil {
    56  			return nil, false, false, fmt.Errorf("resolve: %w", err)
    57  		}
    58  
    59  		// determines if the algorithm successfully resolved all inputs depending
    60  		// on if all resolvers did not return a resolve error
    61  		finalResolved = finalResolved && (resolveErr == "")
    62  
    63  		// converts the version candidates into an object that is recognizable by
    64  		// other components. also computes the first occurrence for all satisfiable
    65  		// inputs
    66  		finalMapping, err = a.candidatesToInputMapping(ctx, finalMapping, resolver.InputConfigs(), versionCandidates, resolveErr)
    67  		if err != nil {
    68  			return nil, false, false, fmt.Errorf("candidates to input mapping: %w", err)
    69  		}
    70  
    71  		// if any one of the resolvers has a version candidate that has an unused
    72  		// next every version, the algorithm should return true for being able to
    73  		// be run again
    74  		finalHasNext = finalHasNext || a.finalizeHasNext(versionCandidates)
    75  	}
    76  
    77  	return finalMapping, finalResolved, finalHasNext, nil
    78  }
    79  
    80  func (a *Algorithm) finalizeHasNext(versionCandidates map[string]*versionCandidate) bool {
    81  	hasNextCombined := false
    82  	for _, candidate := range versionCandidates {
    83  		hasNextCombined = hasNextCombined || candidate.HasNextEveryVersion
    84  	}
    85  
    86  	return hasNextCombined
    87  }
    88  
    89  func (a *Algorithm) candidatesToInputMapping(ctx context.Context, mapping db.InputMapping, inputConfigs db.InputConfigs, candidates map[string]*versionCandidate, resolveErr db.ResolutionFailure) (db.InputMapping, error) {
    90  	for _, input := range inputConfigs {
    91  		if resolveErr != "" {
    92  			mapping[input.Name] = db.InputResult{
    93  				ResolveError: resolveErr,
    94  			}
    95  		} else {
    96  			firstOcc, err := a.versionsDB.IsFirstOccurrence(ctx, input.JobID, input.Name, candidates[input.Name].Version, input.ResourceID)
    97  			if err != nil {
    98  				return nil, err
    99  			}
   100  
   101  			mapping[input.Name] = db.InputResult{
   102  				Input: &db.AlgorithmInput{
   103  					AlgorithmVersion: db.AlgorithmVersion{
   104  						ResourceID: input.ResourceID,
   105  						Version:    candidates[input.Name].Version,
   106  					},
   107  					FirstOccurrence: firstOcc,
   108  				},
   109  				PassedBuildIDs: candidates[input.Name].SourceBuildIds,
   110  			}
   111  		}
   112  	}
   113  
   114  	return mapping, nil
   115  }