github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/render/config/resolve/dep_graph.go (about)

     1  package resolve
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"text/template"
     8  
     9  	"github.com/pkg/errors"
    10  	"github.com/replicatedhq/libyaml"
    11  	"github.com/replicatedhq/ship/pkg/templates"
    12  )
    13  
    14  type depGraph struct {
    15  	BuilderBuilder *templates.BuilderBuilder
    16  	Dependencies   map[string]map[string]struct{}
    17  }
    18  
    19  //these config functions are used to add their dependencies to the depGraph
    20  func (d *depGraph) funcMap(parent string) template.FuncMap {
    21  	addDepFunc := func(dep string, _ ...string) string {
    22  		d.AddDep(parent, dep)
    23  		return dep
    24  	}
    25  
    26  	return template.FuncMap{
    27  		"ConfigOption":          addDepFunc,
    28  		"ConfigOptionIndex":     addDepFunc,
    29  		"ConfigOptionData":      addDepFunc,
    30  		"ConfigOptionEquals":    addDepFunc,
    31  		"ConfigOptionNotEquals": addDepFunc,
    32  	}
    33  }
    34  
    35  func (d *depGraph) AddNode(source string) {
    36  	if d.Dependencies == nil {
    37  		d.Dependencies = make(map[string]map[string]struct{})
    38  	}
    39  
    40  	if _, ok := d.Dependencies[source]; !ok {
    41  		d.Dependencies[source] = make(map[string]struct{})
    42  	}
    43  }
    44  
    45  func (d *depGraph) AddDep(source, newDependency string) {
    46  	d.AddNode(source)
    47  
    48  	d.Dependencies[source][newDependency] = struct{}{}
    49  }
    50  
    51  func (d *depGraph) ResolveDep(resolvedDependency string) {
    52  	for _, depMap := range d.Dependencies {
    53  		delete(depMap, resolvedDependency)
    54  	}
    55  	delete(d.Dependencies, resolvedDependency)
    56  }
    57  
    58  func (d *depGraph) GetHeadNodes() ([]string, error) {
    59  	headNodes := []string{}
    60  
    61  	for node, deps := range d.Dependencies {
    62  		if len(deps) == 0 {
    63  			headNodes = append(headNodes, node)
    64  		}
    65  	}
    66  
    67  	if len(headNodes) == 0 && len(d.Dependencies) != 0 {
    68  		return headNodes, errors.New("No nodes exist with 0 dependencies")
    69  	}
    70  
    71  	return headNodes, nil
    72  }
    73  
    74  func (d *depGraph) PrintData() string {
    75  	return fmt.Sprintf("deps: %+v", d.Dependencies)
    76  }
    77  
    78  // returns a deep copy of the dep graph
    79  func (d *depGraph) Copy() (depGraph, error) {
    80  	var buf bytes.Buffer
    81  	enc := json.NewEncoder(&buf)
    82  	dec := json.NewDecoder(&buf)
    83  	err := enc.Encode(d.Dependencies)
    84  	if err != nil {
    85  		return depGraph{}, err
    86  	}
    87  	var copy map[string]map[string]struct{}
    88  	err = dec.Decode(&copy)
    89  	if err != nil {
    90  		return depGraph{}, err
    91  	}
    92  
    93  	return depGraph{
    94  		BuilderBuilder: d.BuilderBuilder,
    95  		Dependencies:   copy,
    96  	}, nil
    97  
    98  }
    99  
   100  func (d *depGraph) ParseConfigGroup(configGroups []libyaml.ConfigGroup) error {
   101  	staticCtx := d.BuilderBuilder.NewStaticContext()
   102  	for _, configGroup := range configGroups {
   103  		for _, configItem := range configGroup.Items {
   104  			// add this to the dependency graph
   105  			d.AddNode(configItem.Name)
   106  
   107  			depBuilder := d.BuilderBuilder.NewBuilder(staticCtx)
   108  			depBuilder.Functs = d.funcMap(configItem.Name)
   109  
   110  			// while builder is normally stateless, the functions it uses within this loop are not
   111  			// errors are also discarded as we do not have the full set of template functions available here
   112  			_, _ = depBuilder.String(configItem.Default)
   113  			_, _ = depBuilder.String(configItem.Value)
   114  		}
   115  	}
   116  
   117  	return nil
   118  }