Skip to content

Commit de1519d

Browse files
committed
tolerate multiple bcs pointing to same istag for oc status
1 parent b095e3a commit de1519d

File tree

6 files changed

+168
-53
lines changed

6 files changed

+168
-53
lines changed

pkg/api/graph/test/missing-istag.yaml

+32
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,38 @@ items:
3232
type: Generic
3333
- imageChange: {}
3434
type: ImageChange
35+
- apiVersion: v1
36+
kind: BuildConfig
37+
metadata:
38+
creationTimestamp: null
39+
labels:
40+
app: ruby
41+
name: ruby-hello-world-2
42+
spec:
43+
output:
44+
to:
45+
kind: ImageStreamTag
46+
name: ruby-goodbye-world:latest
47+
resources: {}
48+
source:
49+
git:
50+
uri: https://github.com/openshift/ruby-hello-world
51+
type: Git
52+
strategy:
53+
dockerStrategy:
54+
from:
55+
kind: ImageStreamTag
56+
name: ruby-22-centos7:latest
57+
type: Docker
58+
triggers:
59+
- github:
60+
secret: LyddbeCAaw1a0x08xz9n
61+
type: GitHub
62+
- generic:
63+
secret: ZnYJJeEvo1ri0Gk0f6YY
64+
type: Generic
65+
- imageChange: {}
66+
type: ImageChange
3567
- apiVersion: v1
3668
kind: ImageStream
3769
metadata:

pkg/api/graph/test/unpushable-build.yaml

+35
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,41 @@ items:
3535
type: ImageChange
3636
status:
3737
lastVersion: 0
38+
- apiVersion: v1
39+
kind: BuildConfig
40+
metadata:
41+
creationTimestamp: null
42+
labels:
43+
app: ruby
44+
name: ruby-hello-world-2
45+
namespace: example
46+
spec:
47+
output:
48+
to:
49+
kind: ImageStreamTag
50+
name: ruby-hello-world:latest
51+
resources: {}
52+
source:
53+
git:
54+
uri: https://github.com/openshift/ruby-hello-world
55+
type: Git
56+
strategy:
57+
dockerStrategy:
58+
from:
59+
kind: ImageStreamTag
60+
name: ruby-22-centos7:latest
61+
type: Docker
62+
triggers:
63+
- github:
64+
secret: LyddbeCAaw1a0x08xz9n
65+
type: GitHub
66+
- generic:
67+
secret: ZnYJJeEvo1ri0Gk0f6YY
68+
type: Generic
69+
- imageChange: {}
70+
type: ImageChange
71+
status:
72+
lastVersion: 0
3873
- apiVersion: v1
3974
kind: Build
4075
metadata:

pkg/build/graph/analysis/bc.go

+87-43
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,91 @@ func FindCircularBuilds(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
162162
return markers
163163
}
164164

165+
// multiBCStartBuildSuggestion builds the `oc start-build` suggestion string with multiple build configs
166+
func multiBCStartBuildSuggestion(bcNodes []*buildgraph.BuildConfigNode) string {
167+
var ret string
168+
if len(bcNodes) > 1 {
169+
ret = "Run one of the following commands: "
170+
}
171+
for i, bcNode := range bcNodes {
172+
// use of f.ResourceName(bcNode) will produce a string like oc start-build BuildConfig|example/ruby-hello-world
173+
ret = ret + fmt.Sprintf("oc start-build %s", bcNode.BuildConfig.GetName())
174+
if i < (len(bcNodes) - 1) {
175+
ret = ret + ", "
176+
}
177+
}
178+
return ret
179+
}
180+
181+
// bcNodesToRelatedNodes takes an array of BuildConfigNode's and returns an array of graph.Node's for the Marker.RelatedNodes field
182+
func bcNodesToRelatedNodes(bcNodes []*buildgraph.BuildConfigNode) []graph.Node {
183+
relatedNodes := []graph.Node{}
184+
for _, bcNode := range bcNodes {
185+
relatedNodes = append(relatedNodes, graph.Node(bcNode))
186+
}
187+
return relatedNodes
188+
}
189+
190+
// findPendingTagMarkers is the guts behind FindPendingTags .... break out some of the content and reduce some indentation
191+
func findPendingTagMarkers(istNode *imagegraph.ImageStreamTagNode, g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
192+
markers := []osgraph.Marker{}
193+
194+
buildFound := false
195+
bcNodes := buildedges.BuildConfigsForTag(g, graph.Node(istNode))
196+
for _, bcNode := range bcNodes {
197+
latestBuild := buildedges.GetLatestBuild(g, bcNode)
198+
199+
// A build config points to the non existent tag but no current build exists.
200+
if latestBuild == nil {
201+
continue
202+
}
203+
buildFound = true
204+
205+
// A build config points to the non existent tag but something is going on with
206+
// the latest build.
207+
// TODO: Handle other build phases.
208+
switch latestBuild.Build.Status.Phase {
209+
case buildapi.BuildPhaseCancelled:
210+
// TODO: Add a warning here.
211+
case buildapi.BuildPhaseError:
212+
// TODO: Add a warning here.
213+
case buildapi.BuildPhaseComplete:
214+
// We should never hit this. The output of our build is missing but the build is complete.
215+
// Most probably the user has messed up?
216+
case buildapi.BuildPhaseFailed:
217+
// Since the tag hasn't been populated yet, we assume there hasn't been a successful
218+
// build so far.
219+
markers = append(markers, osgraph.Marker{
220+
Node: graph.Node(latestBuild),
221+
RelatedNodes: []graph.Node{graph.Node(istNode), graph.Node(bcNode)},
222+
223+
Severity: osgraph.ErrorSeverity,
224+
Key: LatestBuildFailedErr,
225+
Message: fmt.Sprintf("%s has failed.", f.ResourceName(latestBuild)),
226+
Suggestion: osgraph.Suggestion(fmt.Sprintf("Inspect the build failure with 'oc logs -f bc/%s'", bcNode.BuildConfig.GetName())),
227+
})
228+
default:
229+
// Do nothing when latest build is new, pending, or running.
230+
}
231+
232+
}
233+
234+
// if no current builds exist for any of the build configs, append marker for that
235+
// but ignore ISTs which have no build configs
236+
if !buildFound && len(bcNodes) > 0 {
237+
markers = append(markers, osgraph.Marker{
238+
Node: graph.Node(istNode),
239+
RelatedNodes: bcNodesToRelatedNodes(bcNodes),
240+
241+
Severity: osgraph.WarningSeverity,
242+
Key: TagNotAvailableWarning,
243+
Message: fmt.Sprintf("%s needs to be imported or created by a build.", f.ResourceName(istNode)),
244+
Suggestion: osgraph.Suggestion(multiBCStartBuildSuggestion(bcNodes)),
245+
})
246+
}
247+
return markers
248+
}
249+
165250
// FindPendingTags inspects all imageStreamTags that serve as outputs to builds.
166251
//
167252
// Precedence of failures:
@@ -172,49 +257,8 @@ func FindPendingTags(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker {
172257

173258
for _, uncastIstNode := range g.NodesByKind(imagegraph.ImageStreamTagNodeKind) {
174259
istNode := uncastIstNode.(*imagegraph.ImageStreamTagNode)
175-
if bcNode := buildedges.BuildConfigForTag(g, uncastIstNode); bcNode != nil && !istNode.Found() {
176-
latestBuild := buildedges.GetLatestBuild(g, bcNode)
177-
178-
// A build config points to the non existent tag but no current build exists.
179-
if latestBuild == nil {
180-
markers = append(markers, osgraph.Marker{
181-
Node: graph.Node(bcNode),
182-
RelatedNodes: []graph.Node{uncastIstNode},
183-
184-
Severity: osgraph.WarningSeverity,
185-
Key: TagNotAvailableWarning,
186-
Message: fmt.Sprintf("%s needs to be imported or created by a build.", f.ResourceName(istNode)),
187-
Suggestion: osgraph.Suggestion(fmt.Sprintf("oc start-build %s", f.ResourceName(bcNode))),
188-
})
189-
continue
190-
}
191-
192-
// A build config points to the non existent tag but something is going on with
193-
// the latest build.
194-
// TODO: Handle other build phases.
195-
switch latestBuild.Build.Status.Phase {
196-
case buildapi.BuildPhaseCancelled:
197-
// TODO: Add a warning here.
198-
case buildapi.BuildPhaseError:
199-
// TODO: Add a warning here.
200-
case buildapi.BuildPhaseComplete:
201-
// We should never hit this. The output of our build is missing but the build is complete.
202-
// Most probably the user has messed up?
203-
case buildapi.BuildPhaseFailed:
204-
// Since the tag hasn't been populated yet, we assume there hasn't been a successful
205-
// build so far.
206-
markers = append(markers, osgraph.Marker{
207-
Node: graph.Node(latestBuild),
208-
RelatedNodes: []graph.Node{uncastIstNode, graph.Node(bcNode)},
209-
210-
Severity: osgraph.ErrorSeverity,
211-
Key: LatestBuildFailedErr,
212-
Message: fmt.Sprintf("%s has failed.", f.ResourceName(latestBuild)),
213-
Suggestion: osgraph.Suggestion(fmt.Sprintf("Inspect the build failure with 'oc logs %s'", f.ResourceName(latestBuild))),
214-
})
215-
default:
216-
// Do nothing when latest build is new, pending, or running.
217-
}
260+
if !istNode.Found() {
261+
markers = append(markers, findPendingTagMarkers(istNode, g, f)...)
218262
}
219263
}
220264

pkg/build/graph/analysis/bc_test.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestUnpushableBuild(t *testing.T) {
2424
imageedges.AddAllImageStreamImageRefEdges(g)
2525

2626
markers := FindUnpushableBuildConfigs(g, osgraph.DefaultNamer)
27-
if e, a := 1, len(markers); e != a {
27+
if e, a := 2, len(markers); e != a {
2828
t.Fatalf("expected %v, got %v", e, a)
2929
}
3030

@@ -33,9 +33,10 @@ func TestUnpushableBuild(t *testing.T) {
3333
}
3434

3535
actualBC := osgraph.GetTopLevelContainerNode(g, markers[0].Node)
36-
expectedBC := g.Find(osgraph.UniqueName("BuildConfig|example/ruby-hello-world"))
37-
if e, a := expectedBC.ID(), actualBC.ID(); e != a {
38-
t.Errorf("expected %v, got %v", e, a)
36+
expectedBC1 := g.Find(osgraph.UniqueName("BuildConfig|example/ruby-hello-world"))
37+
expectedBC2 := g.Find(osgraph.UniqueName("BuildConfig|example/ruby-hello-world-2"))
38+
if e1, e2, a := expectedBC1.ID(), expectedBC2.ID(), actualBC.ID(); e1 != a && e2 != a {
39+
t.Errorf("expected either %v or %v, got %v", e1, e2, a)
3940
}
4041

4142
actualIST := markers[0].RelatedNodes[0]
@@ -229,4 +230,7 @@ func TestLatestBuildFailed(t *testing.T) {
229230
if got, expected := markers[0].Key, LatestBuildFailedErr; got != expected {
230231
t.Fatalf("expected marker key %q, got %q", expected, got)
231232
}
233+
if !strings.Contains(markers[0].Suggestion.String(), "oc logs -f bc/ruby-hello-world") {
234+
t.Fatalf("expected oc logs -f bc/ruby-hello-world, got %s", markers[0].Suggestion.String())
235+
}
232236
}

pkg/build/graph/helpers.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,13 @@ func defaultNamespace(value, defaultValue string) string {
8282
return value
8383
}
8484

85-
// BuildConfigForTag returns the buildConfig that points to the provided imageStreamTag.
86-
// TODO: Handle multiple buildconfigs pointing to the same tag.
87-
func BuildConfigForTag(g osgraph.Graph, istag graph.Node) *buildgraph.BuildConfigNode {
85+
// BuildConfigsForTag returns the buildConfig that points to the provided imageStreamTag.
86+
func BuildConfigsForTag(g osgraph.Graph, istag graph.Node) []*buildgraph.BuildConfigNode {
87+
bcs := []*buildgraph.BuildConfigNode{}
8888
for _, bcNode := range g.PredecessorNodesByEdgeKind(istag, BuildOutputEdgeKind) {
89-
return bcNode.(*buildgraph.BuildConfigNode)
89+
bcs = append(bcs, bcNode.(*buildgraph.BuildConfigNode))
9090
}
91-
return nil
91+
return bcs
9292
}
9393

9494
// GetLatestBuild returns the latest build for the provided buildConfig.

pkg/deploy/graph/analysis/dc.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func ictMarker(g osgraph.Graph, f osgraph.Namer, dcNode *deploygraph.DeploymentC
6363
}
6464
}
6565

66-
if bcNode := buildedges.BuildConfigForTag(g, istNode); bcNode != nil {
66+
for _, bcNode := range buildedges.BuildConfigsForTag(g, istNode) {
6767
// Avoid warning for the dc image trigger in case there is a build in flight.
6868
if latestBuild := buildedges.GetLatestBuild(g, bcNode); latestBuild != nil && !buildutil.IsBuildComplete(latestBuild.Build) {
6969
return nil

0 commit comments

Comments
 (0)