mirror of
https://github.com/docker/compose.git
synced 2025-07-25 22:54:54 +02:00
Detect cycles
Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
This commit is contained in:
parent
1f43b83409
commit
5cf5410bc8
@ -21,6 +21,7 @@ package local
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
@ -36,6 +37,10 @@ const (
|
|||||||
|
|
||||||
func inDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, types.ServiceConfig) error) error {
|
func inDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, types.ServiceConfig) error) error {
|
||||||
g := NewGraph(project.Services)
|
g := NewGraph(project.Services)
|
||||||
|
if b, err := g.HasCycles(); b {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
leaves := g.Leaves()
|
leaves := g.Leaves()
|
||||||
|
|
||||||
eg, _ := errgroup.WithContext(ctx)
|
eg, _ := errgroup.WithContext(ctx)
|
||||||
@ -50,7 +55,7 @@ func inDependencyOrder(ctx context.Context, project *types.Project, fn func(cont
|
|||||||
func run(ctx context.Context, graph *Graph, eg *errgroup.Group, nodes []*Vertex, fn func(context.Context, types.ServiceConfig) error) error {
|
func run(ctx context.Context, graph *Graph, eg *errgroup.Group, nodes []*Vertex, fn func(context.Context, types.ServiceConfig) error) error {
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
n := node
|
n := node
|
||||||
// Don't start this service yet if all of their children have
|
// Don't start this service yet if all of its children have
|
||||||
// not been started yet.
|
// not been started yet.
|
||||||
if len(graph.FilterChildren(n.Service.Name, ServiceStopped)) != 0 {
|
if len(graph.FilterChildren(n.Service.Name, ServiceStopped)) != 0 {
|
||||||
continue
|
continue
|
||||||
@ -152,8 +157,6 @@ func (g *Graph) AddEdge(source string, destination string) error {
|
|||||||
sourceVertex.Children[destination] = destinationVertex
|
sourceVertex.Children[destination] = destinationVertex
|
||||||
destinationVertex.Parents[source] = sourceVertex
|
destinationVertex.Parents[source] = sourceVertex
|
||||||
|
|
||||||
g.Vertices[source] = sourceVertex
|
|
||||||
g.Vertices[destination] = destinationVertex
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,3 +195,55 @@ func (g *Graph) FilterChildren(key string, status ServiceStatus) []*Vertex {
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Graph) HasCycles() (bool, error) {
|
||||||
|
discovered := []string{}
|
||||||
|
finished := []string{}
|
||||||
|
|
||||||
|
for _, vertex := range g.Vertices {
|
||||||
|
path := []string{
|
||||||
|
vertex.Key,
|
||||||
|
}
|
||||||
|
if !contains(discovered, vertex.Key) && !contains(finished, vertex.Key) {
|
||||||
|
var err error
|
||||||
|
discovered, finished, err = g.visit(vertex.Key, path, discovered, finished)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Graph) visit(key string, path []string, discovered []string, finished []string) ([]string, []string, error) {
|
||||||
|
discovered = append(discovered, key)
|
||||||
|
|
||||||
|
for _, v := range g.Vertices[key].Children {
|
||||||
|
path := append(path, v.Key)
|
||||||
|
if contains(discovered, v.Key) {
|
||||||
|
return nil, nil, fmt.Errorf("cycle found: %s", strings.Join(path, " -> "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !contains(finished, v.Key) {
|
||||||
|
if _, _, err := g.visit(v.Key, path, discovered, finished); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
discovered = remove(discovered, key)
|
||||||
|
finished = append(finished, key)
|
||||||
|
return discovered, finished, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func remove(slice []string, item string) []string {
|
||||||
|
var s []string
|
||||||
|
for _, i := range slice {
|
||||||
|
if i != item {
|
||||||
|
s = append(s, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user