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(©) 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 }