github.com/clusterize-io/tusk@v0.6.3-0.20211001020217-cfe8a8cd0d4a/runner/dependencies.go (about)

     1  package runner
     2  
     3  import (
     4  	"encoding/json"
     5  
     6  	"github.com/clusterize-io/tusk/marshal"
     7  )
     8  
     9  // FindAllOptions returns a list of options relevant for a given config.
    10  func FindAllOptions(t *Task, cfg *Config) ([]*Option, error) {
    11  	names, err := getDependencies(t)
    12  	if err != nil {
    13  		return nil, err
    14  	}
    15  
    16  	candidates := make(map[string]*Option)
    17  	for _, opt := range cfg.Options {
    18  		// Args that share a name with global options take priority
    19  		if _, ok := t.Args.Lookup(opt.Name); ok {
    20  			continue
    21  		}
    22  
    23  		candidates[opt.Name] = opt
    24  	}
    25  
    26  	required := make([]*Option, 0, len(t.Options))
    27  	for _, opt := range t.Options {
    28  		candidates[opt.Name] = opt
    29  		required = append(required, opt)
    30  	}
    31  
    32  	required, err = findRequiredOptionsRecursively(names, candidates, required)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	return required, nil
    38  }
    39  
    40  func findRequiredOptionsRecursively(
    41  	entry []string, candidates map[string]*Option, found []*Option,
    42  ) ([]*Option, error) {
    43  	for _, item := range entry {
    44  		candidate, ok := candidates[item]
    45  		if !ok || optionsContains(found, candidate) {
    46  			continue
    47  		}
    48  
    49  		found = append(found, candidate)
    50  		var dependencies []string
    51  		for _, opt := range found {
    52  			nested, err := getDependencies(opt)
    53  			if err != nil {
    54  				return nil, err
    55  			}
    56  			dependencies = append(dependencies, nested...)
    57  		}
    58  
    59  		var err error
    60  		found, err = findRequiredOptionsRecursively(dependencies, candidates, found)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  	}
    65  
    66  	return found, nil
    67  }
    68  
    69  func optionsContains(items []*Option, item *Option) bool {
    70  	for _, want := range items {
    71  		if item == want {
    72  			return true
    73  		}
    74  	}
    75  
    76  	return false
    77  }
    78  
    79  type dependencyGetter interface {
    80  	Dependencies() []string
    81  }
    82  
    83  func getDependencies(item dependencyGetter) ([]string, error) {
    84  	// TODO: Remove json dependency by implementing stringer interface
    85  	// json is used to print computed fields that should not be yaml parseable
    86  	marshaled, err := json.Marshal(item)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	names := marshal.FindPotentialVariables(marshaled)
    92  	names = append(names, item.Dependencies()...)
    93  
    94  	return names, nil
    95  }