Skip to content

Commit 7f96433

Browse files
committed
hack/cluster-version-util/task_graph: Task-node granularity
Make it easier to think about task-nodes as opaque objects by assigning them runlevel and component properties, and using that information to label names in the rendered graph.
1 parent a7d6e43 commit 7f96433

File tree

3 files changed

+117
-57
lines changed

3 files changed

+117
-57
lines changed

hack/cluster-version-util/task_graph.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212

1313
var (
1414
taskGraphOpts struct {
15-
parallel string
15+
parallel string
16+
granularity string
1617
}
1718
)
1819

@@ -24,7 +25,8 @@ func newTaskGraphCmd() *cobra.Command {
2425
RunE: runTaskGraphCmd,
2526
}
2627

27-
cmd.Flags().StringVar(&taskGraphOpts.parallel, "parallel", "by-number-and-component", "The output directory where the manifests will be rendered.")
28+
cmd.Flags().StringVar(&taskGraphOpts.parallel, "parallel", "by-number-and-component", "The parallelization strategy to use (by-number-and-component (default), flatten-by-number-and-component, or permute-flatten-by-number-and-component).")
29+
cmd.Flags().StringVar(&taskGraphOpts.granularity, "granularity", "task-node", "The output granularity to use (task-node (default) or manifest).")
2830
return cmd
2931
}
3032

@@ -59,7 +61,7 @@ func runTaskGraphCmd(cmd *cobra.Command, args []string) error {
5961
return fmt.Errorf("unrecognized parallel strategy %q", taskGraphOpts.parallel)
6062
}
6163

62-
fmt.Println(graph.Tree())
64+
fmt.Println(graph.Tree(taskGraphOpts.granularity))
6365

6466
return nil
6567
}

pkg/payload/task_graph.go

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"math/rand"
77
"regexp"
8+
"strconv"
89
"strings"
910
"sync"
1011

@@ -25,6 +26,20 @@ const (
2526
groupComponent = 2
2627
)
2728

29+
// matchValues takes a reMatchPattern match and returns the runlevel and component.
30+
func matchValues(match []string) (int, string) {
31+
if match == nil {
32+
return 0, ""
33+
}
34+
35+
runlevel, err := strconv.Atoi(match[groupNumber])
36+
if err != nil {
37+
panic(err)
38+
}
39+
40+
return runlevel, match[groupComponent]
41+
}
42+
2843
// ByNumberAndComponent creates parallelization for tasks whose original filenames are of the form
2944
// 0000_NN_NAME_* - files that share 0000_NN_NAME_ are run in serial, but chunks of files that have
3045
// the same 0000_NN but different NAME can be run in parallel. If the input is not sorted in an order
@@ -45,7 +60,6 @@ func ByNumberAndComponent(tasks []*Task) [][]*TaskNode {
4560
}
4661

4762
var buckets [][]*TaskNode
48-
var lastNode *TaskNode
4963
for i := 0; i < count; {
5064
matchBase := matches[i]
5165
j := i + 1
@@ -56,26 +70,31 @@ func ByNumberAndComponent(tasks []*Task) [][]*TaskNode {
5670
break
5771
}
5872
if matchBase[groupComponent] != matchNext[groupComponent] {
59-
groups = append(groups, &TaskNode{Tasks: tasks[i:j]})
73+
node := &TaskNode{
74+
Tasks: tasks[i:j],
75+
}
76+
node.runlevel, node.component = matchValues(matchBase)
77+
groups = append(groups, node)
6078
i = j
6179
}
6280
matchBase = matchNext
6381
}
6482
if len(groups) > 0 {
65-
groups = append(groups, &TaskNode{Tasks: tasks[i:j]})
83+
node := &TaskNode{
84+
Tasks: tasks[i:j],
85+
}
86+
node.runlevel, node.component = matchValues(matchBase)
87+
groups = append(groups, node)
6688
i = j
6789
buckets = append(buckets, groups)
68-
lastNode = nil
6990
continue
7091
}
71-
if lastNode == nil {
72-
lastNode = &TaskNode{Tasks: append([]*Task(nil), tasks[i:j]...)}
73-
i = j
74-
buckets = append(buckets, []*TaskNode{lastNode})
75-
continue
92+
node := &TaskNode{
93+
Tasks: append([]*Task(nil), tasks[i:j]...),
7694
}
77-
lastNode.Tasks = append(lastNode.Tasks, tasks[i:j]...)
95+
node.runlevel, node.component = matchValues(matchBase)
7896
i = j
97+
buckets = append(buckets, []*TaskNode{node})
7998
}
8099
return buckets
81100
}
@@ -105,21 +124,32 @@ func FlattenByNumberAndComponent(tasks []*Task) [][]*TaskNode {
105124
break
106125
}
107126
if matchBase[groupComponent] != matchNext[groupComponent] {
108-
groups = append(groups, &TaskNode{Tasks: tasks[i:j]})
127+
node := &TaskNode{
128+
Tasks: tasks[i:j],
129+
}
130+
node.runlevel, node.component = matchValues(matchBase)
131+
groups = append(groups, node)
109132
i = j
110133
}
111134
matchBase = matchNext
112135
}
113136
if len(groups) > 0 {
114-
groups = append(groups, &TaskNode{Tasks: tasks[i:j]})
137+
node := &TaskNode{
138+
Tasks: tasks[i:j],
139+
}
140+
node.runlevel, node.component = matchValues(matchBase)
141+
groups = append(groups, node)
115142
i = j
116143
lastNode = nil
117144
continue
118145
}
119146
if lastNode == nil {
120-
lastNode = &TaskNode{Tasks: append([]*Task(nil), tasks[i:j]...)}
121-
i = j
147+
lastNode = &TaskNode{
148+
Tasks: append([]*Task(nil), tasks[i:j]...),
149+
}
150+
lastNode.runlevel, lastNode.component = matchValues(matches[i])
122151
groups = append(groups, lastNode)
152+
i = j
123153
continue
124154
}
125155
lastNode.Tasks = append(lastNode.Tasks, tasks[i:j]...)
@@ -137,6 +167,12 @@ type TaskNode struct {
137167
Tasks []*Task
138168
// Out is a list of node indices to which there is an edge from this node (=dependents).
139169
Out []int
170+
171+
// runlevel is task's run level, extracted from the manifest filename.
172+
runlevel int
173+
174+
// component is task's component name, extracted from the manifest filename.
175+
component string
140176
}
141177

142178
func (n *TaskNode) String() string {
@@ -148,7 +184,7 @@ func (n *TaskNode) String() string {
148184
}
149185
arr = append(arr, t.Manifest.GVK.String())
150186
}
151-
return "{Tasks: " + strings.Join(arr, ", ") + "}"
187+
return fmt.Sprintf("{RunLevel: %d, Component: %q, Tasks: %s}", n.runlevel, n.component, strings.Join(arr, ", "))
152188
}
153189

154190
func (n *TaskNode) replaceIn(index, with int) {
@@ -370,21 +406,34 @@ func (g *TaskGraph) Roots() []int {
370406
}
371407

372408
// Tree renders the task graph in Graphviz DOT.
373-
func (g *TaskGraph) Tree() string {
409+
func (g *TaskGraph) Tree(granularity string) string {
374410
out := []string{
375411
"digraph tasks {",
376412
" labelloc=t;",
377413
" rankdir=TB;",
378414
}
379415
for i, node := range g.Nodes {
380-
label := make([]string, 0, len(node.Tasks))
381-
for _, task := range node.Tasks {
382-
label = append(label, strings.Replace(task.String(), "\"", "", -1))
416+
var label string
417+
if node.runlevel != 0 || node.component != "" {
418+
label = fmt.Sprintf("%02d-%s, %d manifests", node.runlevel, node.component, len(node.Tasks))
419+
} else {
420+
label = fmt.Sprintf("%d manifests", len(node.Tasks))
383421
}
384-
if len(label) == 0 {
385-
label = append(label, "no manifests")
422+
switch granularity {
423+
case "manifest":
424+
labels := make([]string, 0, len(node.Tasks))
425+
for _, task := range node.Tasks {
426+
labels = append(labels, strings.Replace(task.String(), "\"", "", -1))
427+
}
428+
if len(labels) > 0 {
429+
label = fmt.Sprintf("%s\\n%s", label, strings.Join(labels, "\\n"))
430+
}
431+
case "task-node":
432+
// nothing to append
433+
default:
434+
panic(fmt.Sprintf("unrecognized granularity %q", granularity))
386435
}
387-
out = append(out, fmt.Sprintf(" %d [ label=\"%s\" shape=\"box\" ];", i, strings.Join(label, "\\n")))
436+
out = append(out, fmt.Sprintf(" %d [ label=\"%s\" shape=\"box\" ];", i, label))
388437
}
389438
for i, node := range g.Nodes {
390439
for _, j := range node.Out {

0 commit comments

Comments
 (0)