Move graph logic into package

Graph logic moves into depgraph package and makes internal
fields inaccessible. Completes feedback from @LucasRoesler
from previous PR where the dependency graph was added for 0.9.1

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis (OpenFaaS Ltd) 2020-06-17 14:33:58 +01:00
parent 88f1aa0433
commit 5b92e7793d
6 changed files with 91 additions and 55 deletions

View File

@ -1,37 +0,0 @@
package pkg
import (
"log"
)
func buildInstallOrder(svcs []Service) []string {
graph := Graph{nodes: []*Node{}}
nodeMap := map[string]*Node{}
for _, s := range svcs {
n := &Node{Name: s.Name}
nodeMap[s.Name] = n
graph.nodes = append(graph.nodes, n)
}
for _, s := range svcs {
for _, d := range s.DependsOn {
nodeMap[s.Name].Edges = append(nodeMap[s.Name].Edges, nodeMap[d])
}
}
resolved := &Graph{}
unresolved := &Graph{}
for _, g := range graph.nodes {
resolve(g, resolved, unresolved)
}
log.Printf("Start-up order:\n")
order := []string{}
for _, node := range resolved.nodes {
log.Printf("- %s\n", node.Name)
order = append(order, node.Name)
}
return order
}

View File

@ -1,4 +1,4 @@
package pkg
package depgraph
import "log"
@ -14,6 +14,17 @@ type Graph struct {
nodes []*Node
}
func NewDepgraph() *Graph {
return &Graph{
nodes: []*Node{},
}
}
// Nodes returns the nodes within the graph
func (g *Graph) Nodes() []*Node {
return g.nodes
}
// Contains returns true if the target Node is found
// in its list
func (g *Graph) Contains(target *Node) bool {
@ -47,10 +58,31 @@ func (g *Graph) Remove(target *Node) {
}
}
// resolve finds the order of dependencies for a graph
// of nodes.
// Inspired by algorithm from
// Resolve retruns a list of node names in order of their dependencies.
// A use case may be for determining the correct order to install
// software packages, or to start services.
// Based upon the algorithm described by Ferry Boender in the following article
// https://www.electricmonk.nl/log/2008/08/07/dependency-resolving-algorithm/
func (g *Graph) Resolve() []string {
resolved := &Graph{}
unresolved := &Graph{}
for _, node := range g.nodes {
resolve(node, resolved, unresolved)
}
order := []string{}
for _, node := range resolved.Nodes() {
order = append(order, node.Name)
}
return order
}
// resolve mutates the resolved graph for a given starting
// node. The unresolved graph is used to detect a circular graph
// error and will throw a panic. This can be caught with a resolve
// in a go routine.
func resolve(node *Node, resolved, unresolved *Graph) {
unresolved.Add(node)

View File

@ -1,4 +1,4 @@
package pkg
package depgraph
import "testing"

41
pkg/deployment_order.go Normal file
View File

@ -0,0 +1,41 @@
package pkg
import (
"log"
"github.com/openfaas/faasd/pkg/depgraph"
)
func buildDeploymentOrder(svcs []Service) []string {
graph := buildServiceGraph(svcs)
order := graph.Resolve()
log.Printf("Start-up order:\n")
for _, node := range order {
log.Printf("- %s\n", node)
}
return order
}
func buildServiceGraph(svcs []Service) *depgraph.Graph {
graph := depgraph.NewDepgraph()
nodeMap := map[string]*depgraph.Node{}
for _, s := range svcs {
n := &depgraph.Node{Name: s.Name}
nodeMap[s.Name] = n
graph.Add(n)
}
for _, s := range svcs {
for _, d := range s.DependsOn {
nodeMap[s.Name].Edges = append(nodeMap[s.Name].Edges, nodeMap[d])
}
}
return graph
}

View File

@ -5,7 +5,7 @@ import (
"testing"
)
func Test_buildInstallOrder_ARequiresB(t *testing.T) {
func Test_buildDeploymentOrder_ARequiresB(t *testing.T) {
svcs := []Service{
{
Name: "A",
@ -17,7 +17,7 @@ func Test_buildInstallOrder_ARequiresB(t *testing.T) {
},
}
order := buildInstallOrder(svcs)
order := buildDeploymentOrder(svcs)
if len(order) < len(svcs) {
t.Fatalf("length of order too short: %d", len(order))
@ -30,7 +30,7 @@ func Test_buildInstallOrder_ARequiresB(t *testing.T) {
}
}
func Test_buildInstallOrder_ARequiresBAndC(t *testing.T) {
func Test_buildDeploymentOrder_ARequiresBAndC(t *testing.T) {
svcs := []Service{
{
Name: "A",
@ -46,7 +46,7 @@ func Test_buildInstallOrder_ARequiresBAndC(t *testing.T) {
},
}
order := buildInstallOrder(svcs)
order := buildDeploymentOrder(svcs)
if len(order) < len(svcs) {
t.Fatalf("length of order too short: %d", len(order))
@ -65,7 +65,7 @@ func Test_buildInstallOrder_ARequiresBAndC(t *testing.T) {
}
func Test_buildInstallOrder_ARequiresBRequiresC(t *testing.T) {
func Test_buildDeploymentOrder_ARequiresBRequiresC(t *testing.T) {
svcs := []Service{
{
Name: "A",
@ -81,7 +81,7 @@ func Test_buildInstallOrder_ARequiresBRequiresC(t *testing.T) {
},
}
order := buildInstallOrder(svcs)
order := buildDeploymentOrder(svcs)
if len(order) < len(svcs) {
t.Fatalf("length of order too short: %d", len(order))
@ -104,7 +104,7 @@ func Test_buildInstallOrder_ARequiresBRequiresC(t *testing.T) {
}
}
func Test_buildInstallOrderCircularARequiresBRequiresA(t *testing.T) {
func Test_buildDeploymentOrderCircularARequiresBRequiresA(t *testing.T) {
svcs := []Service{
{
Name: "A",
@ -118,12 +118,12 @@ func Test_buildInstallOrderCircularARequiresBRequiresA(t *testing.T) {
defer func() { recover() }()
buildInstallOrder(svcs)
buildDeploymentOrder(svcs)
t.Fatalf("did not panic as expected")
}
func Test_buildInstallOrderComposeFile(t *testing.T) {
func Test_buildDeploymentOrderComposeFile(t *testing.T) {
// svcs := []Service{}
file, err := LoadComposeFileWithArch("../", "docker-compose.yaml", func() (string, string) {
return "x86_64", "Linux"
@ -145,7 +145,7 @@ func Test_buildInstallOrderComposeFile(t *testing.T) {
}
}
order := buildInstallOrder(svcs)
order := buildDeploymentOrder(svcs)
if len(order) < len(svcs) {
t.Fatalf("length of order too short: %d", len(order))
@ -167,7 +167,7 @@ func Test_buildInstallOrderComposeFile(t *testing.T) {
}
}
func Test_buildInstallOrderOpenFaaS(t *testing.T) {
func Test_buildDeploymentOrderOpenFaaS(t *testing.T) {
svcs := []Service{
{
Name: "queue-worker",
@ -191,7 +191,7 @@ func Test_buildInstallOrderOpenFaaS(t *testing.T) {
},
}
order := buildInstallOrder(svcs)
order := buildDeploymentOrder(svcs)
if len(order) < len(svcs) {
t.Fatalf("length of order too short: %d", len(order))

View File

@ -112,7 +112,7 @@ func (s *Supervisor) Start(svcs []Service) error {
}
}
order := buildInstallOrder(svcs)
order := buildDeploymentOrder(svcs)
for _, key := range order {