/
target.go
141 lines (124 loc) · 3.49 KB
/
target.go
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package crane
import (
"fmt"
"sort"
"strings"
)
type Target struct {
initial []string
dependencies []string
affected []string
}
// NewTarget receives the specified target
// and determines which containers should be targeted.
// The target might be extended depending whether the
// dynamic targets "dependencies" and/or "affected"
// are included in the targetFlag.
// Additionally, the target is sorted alphabetically.
func NewTarget(graph DependencyGraph, targetFlag string) (target Target, err error) {
targetParts := strings.Split(targetFlag, "+")
targetName := targetParts[0]
extendDependencies := false
extendAffected := false
for _, v := range targetParts[1:] {
if v == "dependencies" || v == "d" {
extendDependencies = true
} else if v == "affected" || v == "a" {
extendAffected = true
} else {
err = fmt.Errorf("Unknown target extension %s. Available options are 'dependencies'/'d' and 'affected'/'a'", v)
return
}
}
target = Target{
initial: cfg.ContainersForReference(targetName),
dependencies: []string{},
affected: []string{},
}
includedSet := make(map[string]bool)
cascadingSeeds := []string{}
if extendDependencies {
// start from the explicitly targeted target
includedSet = make(map[string]bool)
cascadingSeeds = []string{}
for _, name := range target.initial {
includedSet[name] = true
cascadingSeeds = append(cascadingSeeds, name)
}
// Cascade until the graph has been fully traversed
// according to the cascading flags.
for len(cascadingSeeds) > 0 {
nextCascadingSeeds := []string{}
for _, seed := range cascadingSeeds {
if dependencies, ok := graph[seed]; ok {
// Queue direct dependencies if we haven't already considered them
for _, name := range dependencies.All {
if _, alreadyIncluded := includedSet[name]; !alreadyIncluded {
includedSet[name] = true
nextCascadingSeeds = append(nextCascadingSeeds, name)
}
}
}
}
cascadingSeeds = nextCascadingSeeds
}
for name, _ := range includedSet {
if !includes(target.initial, name) {
target.dependencies = append(target.dependencies, name)
}
}
sort.Strings(target.dependencies)
}
if extendAffected {
// start from the explicitly targeted target
includedSet = make(map[string]bool)
cascadingSeeds = []string{}
for _, name := range target.initial {
includedSet[name] = true
cascadingSeeds = append(cascadingSeeds, name)
}
for len(cascadingSeeds) > 0 {
nextCascadingSeeds := []string{}
for _, seed := range cascadingSeeds {
for name, dependencies := range graph {
if _, alreadyIncluded := includedSet[name]; !alreadyIncluded {
if dependencies.includes(seed) {
includedSet[name] = true
nextCascadingSeeds = append(nextCascadingSeeds, name)
}
}
}
}
cascadingSeeds = nextCascadingSeeds
}
for name, _ := range includedSet {
if !includes(target.initial, name) {
target.affected = append(target.affected, name)
}
}
sort.Strings(target.affected)
}
return
}
// includes checks whether the given needle is
// included in the target
func (t Target) includes(needle string) bool {
for _, name := range t.all() {
if name == needle {
return true
}
}
return false
}
// Return all targeted containers, sorted alphabetically
func (t Target) all() []string {
all := t.initial
for _, name := range t.dependencies {
all = append(all, name)
}
for _, name := range t.affected {
all = append(all, name)
}
sort.Strings(all)
return all
}