-
Notifications
You must be signed in to change notification settings - Fork 132
Expand file tree
/
Copy pathsort.go
More file actions
99 lines (88 loc) · 2.87 KB
/
sort.go
File metadata and controls
99 lines (88 loc) · 2.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package dependency
import (
"fmt"
"slices"
"strings"
"github.com/fluxcd/pkg/apis/meta"
)
// Dependent interface defines methods that a Kubernetes resource object should
// implement in order to use the dependency package for ordering dependencies.
type Dependent interface {
GetName() string
GetNamespace() string
meta.ObjectWithDependencies
}
const (
unmarked = iota
permanentMark
temporaryMark
)
// Sort takes a slice of Dependent objects and returns a sorted slice of
// NamespacedObjectReference based on their dependencies. It performs a
// topological sort using a depth-first search algorithm, which has
// runtime complexity of O(|V| + |E|), where |V| is the number of
// vertices (objects) and |E| is the number of edges (dependencies).
//
// Reference:
// https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
func Sort(objects []Dependent) ([]meta.NamespacedObjectReference, error) {
// Build vertices and edges.
vertices := make([]meta.NamespacedObjectReference, 0, len(objects))
edges := make(map[meta.NamespacedObjectReference][]meta.NamespacedObjectReference)
for _, obj := range objects {
u := meta.NamespacedObjectReference{
Name: obj.GetName(),
Namespace: obj.GetNamespace(),
}
vertices = append(vertices, u)
for _, v := range obj.GetDependsOn() {
if v.Namespace == "" {
v.Namespace = obj.GetNamespace()
}
edges[u] = append(edges[u], v)
}
}
// Compute topological order with depth-first search.
var sorted []meta.NamespacedObjectReference
var depthFirstSearch func(u meta.NamespacedObjectReference) (cycle []string)
mark := make(map[meta.NamespacedObjectReference]byte)
depthFirstSearch = func(u meta.NamespacedObjectReference) []string {
mark[u] = temporaryMark
for _, v := range edges[u] {
if mark[v] == permanentMark {
continue
}
if mark[v] == temporaryMark {
// Cycle detected.
return []string{v.String(), u.String()}
}
if cycle := depthFirstSearch(v); len(cycle) > 0 {
return append(cycle, u.String())
}
}
mark[u] = permanentMark
sorted = append(sorted, u)
return nil
}
for _, u := range vertices {
if mark[u] == unmarked {
if cycle := depthFirstSearch(u); len(cycle) > 0 {
slices.Reverse(cycle)
return nil, fmt.Errorf("circular dependency detected: %v", strings.Join(cycle, " -> "))
}
}
}
return sorted, nil
}