| ... | ... |
@@ -8,12 +8,16 @@ import ( |
| 8 | 8 |
osgraph "github.com/openshift/origin/pkg/api/graph" |
| 9 | 9 |
buildedges "github.com/openshift/origin/pkg/build/graph" |
| 10 | 10 |
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes" |
| 11 |
+ imageedges "github.com/openshift/origin/pkg/image/graph" |
|
| 12 |
+ imagegraph "github.com/openshift/origin/pkg/image/graph/nodes" |
|
| 11 | 13 |
) |
| 12 | 14 |
|
| 13 | 15 |
// ImagePipeline represents a build, its output, and any inputs. The input |
| 14 | 16 |
// to a build may be another ImagePipeline. |
| 15 | 17 |
type ImagePipeline struct {
|
| 16 |
- Image ImageTagLocation |
|
| 18 |
+ Image ImageTagLocation |
|
| 19 |
+ DestinationResolved bool |
|
| 20 |
+ |
|
| 17 | 21 |
Build *buildgraph.BuildConfigNode |
| 18 | 22 |
// If set, the base image used by the build |
| 19 | 23 |
BaseImage ImageTagLocation |
| ... | ... |
@@ -95,6 +99,13 @@ func NewImagePipelineFromImageTagLocation(g osgraph.Graph, node graph.Node, imag |
| 95 | 95 |
flow.Source = src |
| 96 | 96 |
} |
| 97 | 97 |
|
| 98 |
+ for _, input := range g.SuccessorNodesByEdgeKind(node, imageedges.ReferencedImageStreamGraphEdgeKind) {
|
|
| 99 |
+ covered.Insert(input.ID()) |
|
| 100 |
+ imageStreamNode := input.(*imagegraph.ImageStreamNode) |
|
| 101 |
+ |
|
| 102 |
+ flow.DestinationResolved = (len(imageStreamNode.Status.DockerImageRepository) != 0) |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 98 | 105 |
return flow, covered |
| 99 | 106 |
} |
| 100 | 107 |
|
| 101 | 108 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,161 @@ |
| 0 |
+apiVersion: v1 |
|
| 1 |
+items: |
|
| 2 |
+- apiVersion: v1 |
|
| 3 |
+ kind: BuildConfig |
|
| 4 |
+ metadata: |
|
| 5 |
+ creationTimestamp: null |
|
| 6 |
+ labels: |
|
| 7 |
+ app: ruby |
|
| 8 |
+ name: ruby-hello-world |
|
| 9 |
+ spec: |
|
| 10 |
+ output: |
|
| 11 |
+ to: |
|
| 12 |
+ kind: ImageStreamTag |
|
| 13 |
+ name: ruby-hello-world:latest |
|
| 14 |
+ resources: {}
|
|
| 15 |
+ source: |
|
| 16 |
+ git: |
|
| 17 |
+ uri: https://github.com/openshift/ruby-hello-world |
|
| 18 |
+ type: Git |
|
| 19 |
+ strategy: |
|
| 20 |
+ dockerStrategy: |
|
| 21 |
+ from: |
|
| 22 |
+ kind: ImageStreamTag |
|
| 23 |
+ name: ruby-20-centos7:latest |
|
| 24 |
+ type: Docker |
|
| 25 |
+ triggers: |
|
| 26 |
+ - github: |
|
| 27 |
+ secret: LyddbeCAaw1a0x08xz9n |
|
| 28 |
+ type: GitHub |
|
| 29 |
+ - generic: |
|
| 30 |
+ secret: ZnYJJeEvo1ri0Gk0f6YY |
|
| 31 |
+ type: Generic |
|
| 32 |
+ - imageChange: {}
|
|
| 33 |
+ type: ImageChange |
|
| 34 |
+ status: |
|
| 35 |
+ lastVersion: 0 |
|
| 36 |
+- apiVersion: v1 |
|
| 37 |
+ kind: Build |
|
| 38 |
+ metadata: |
|
| 39 |
+ creationTimestamp: null |
|
| 40 |
+ labels: |
|
| 41 |
+ app: ruby |
|
| 42 |
+ buildconfig: ruby-hello-world |
|
| 43 |
+ name: ruby-hello-world-1 |
|
| 44 |
+ spec: |
|
| 45 |
+ output: |
|
| 46 |
+ to: |
|
| 47 |
+ kind: ImageStreamTag |
|
| 48 |
+ name: ruby-hello-world:latest |
|
| 49 |
+ resources: {}
|
|
| 50 |
+ serviceAccount: builder |
|
| 51 |
+ source: |
|
| 52 |
+ git: |
|
| 53 |
+ uri: https://github.com/openshift/ruby-hello-world |
|
| 54 |
+ type: Git |
|
| 55 |
+ strategy: |
|
| 56 |
+ dockerStrategy: |
|
| 57 |
+ from: |
|
| 58 |
+ kind: DockerImage |
|
| 59 |
+ name: openshift/ruby-20-centos7:latest |
|
| 60 |
+ type: Docker |
|
| 61 |
+ status: |
|
| 62 |
+ config: |
|
| 63 |
+ name: ruby-hello-world |
|
| 64 |
+ phase: New |
|
| 65 |
+- apiVersion: v1 |
|
| 66 |
+ kind: ImageStream |
|
| 67 |
+ metadata: |
|
| 68 |
+ annotations: |
|
| 69 |
+ openshift.io/image.dockerRepositoryCheck: 2015-07-06T19:05:12Z |
|
| 70 |
+ creationTimestamp: null |
|
| 71 |
+ labels: |
|
| 72 |
+ app: ruby |
|
| 73 |
+ name: ruby-20-centos7 |
|
| 74 |
+ spec: |
|
| 75 |
+ dockerImageRepository: openshift/ruby-20-centos7 |
|
| 76 |
+ status: |
|
| 77 |
+ dockerImageRepository: openshift/ruby-20-centos7 |
|
| 78 |
+- apiVersion: v1 |
|
| 79 |
+ kind: ImageStream |
|
| 80 |
+ metadata: |
|
| 81 |
+ creationTimestamp: null |
|
| 82 |
+ labels: |
|
| 83 |
+ app: ruby |
|
| 84 |
+ name: ruby-hello-world |
|
| 85 |
+ spec: {}
|
|
| 86 |
+ status: |
|
| 87 |
+ dockerImageRepository: 172.30.222.57:5000/foo/ruby-hello-world |
|
| 88 |
+- apiVersion: v1 |
|
| 89 |
+ kind: DeploymentConfig |
|
| 90 |
+ metadata: |
|
| 91 |
+ creationTimestamp: null |
|
| 92 |
+ labels: |
|
| 93 |
+ app: ruby |
|
| 94 |
+ name: ruby-hello-world |
|
| 95 |
+ spec: |
|
| 96 |
+ replicas: 1 |
|
| 97 |
+ selector: |
|
| 98 |
+ deploymentconfig: ruby-hello-world |
|
| 99 |
+ strategy: |
|
| 100 |
+ resources: {}
|
|
| 101 |
+ rollingParams: |
|
| 102 |
+ intervalSeconds: 1 |
|
| 103 |
+ timeoutSeconds: 600 |
|
| 104 |
+ updatePeriodSeconds: 1 |
|
| 105 |
+ type: Rolling |
|
| 106 |
+ template: |
|
| 107 |
+ metadata: |
|
| 108 |
+ creationTimestamp: null |
|
| 109 |
+ labels: |
|
| 110 |
+ deploymentconfig: ruby-hello-world |
|
| 111 |
+ spec: |
|
| 112 |
+ containers: |
|
| 113 |
+ - image: library/ruby-hello-world:latest |
|
| 114 |
+ imagePullPolicy: Always |
|
| 115 |
+ name: ruby-hello-world |
|
| 116 |
+ ports: |
|
| 117 |
+ - containerPort: 8080 |
|
| 118 |
+ name: ruby-hello-world-tcp-8080 |
|
| 119 |
+ protocol: TCP |
|
| 120 |
+ resources: {}
|
|
| 121 |
+ securityContext: |
|
| 122 |
+ capabilities: {}
|
|
| 123 |
+ privileged: false |
|
| 124 |
+ terminationMessagePath: /dev/termination-log |
|
| 125 |
+ dnsPolicy: ClusterFirst |
|
| 126 |
+ restartPolicy: Always |
|
| 127 |
+ triggers: |
|
| 128 |
+ - type: ConfigChange |
|
| 129 |
+ - imageChangeParams: |
|
| 130 |
+ automatic: true |
|
| 131 |
+ containerNames: |
|
| 132 |
+ - ruby-hello-world |
|
| 133 |
+ from: |
|
| 134 |
+ kind: ImageStreamTag |
|
| 135 |
+ name: ruby-hello-world:latest |
|
| 136 |
+ type: ImageChange |
|
| 137 |
+ status: {}
|
|
| 138 |
+- apiVersion: v1 |
|
| 139 |
+ kind: Service |
|
| 140 |
+ metadata: |
|
| 141 |
+ creationTimestamp: null |
|
| 142 |
+ labels: |
|
| 143 |
+ app: ruby |
|
| 144 |
+ name: ruby-hello-world |
|
| 145 |
+ spec: |
|
| 146 |
+ portalIP: "" |
|
| 147 |
+ ports: |
|
| 148 |
+ - name: ruby-hello-world-tcp-8080 |
|
| 149 |
+ nodePort: 0 |
|
| 150 |
+ port: 8080 |
|
| 151 |
+ protocol: TCP |
|
| 152 |
+ targetPort: 8080 |
|
| 153 |
+ selector: |
|
| 154 |
+ deploymentconfig: ruby-hello-world |
|
| 155 |
+ sessionAffinity: None |
|
| 156 |
+ type: ClusterIP |
|
| 157 |
+ status: |
|
| 158 |
+ loadBalancer: {}
|
|
| 159 |
+kind: List |
|
| 160 |
+metadata: {}
|
| 0 | 161 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,161 @@ |
| 0 |
+apiVersion: v1 |
|
| 1 |
+items: |
|
| 2 |
+- apiVersion: v1 |
|
| 3 |
+ kind: BuildConfig |
|
| 4 |
+ metadata: |
|
| 5 |
+ creationTimestamp: null |
|
| 6 |
+ labels: |
|
| 7 |
+ app: ruby |
|
| 8 |
+ name: ruby-hello-world |
|
| 9 |
+ spec: |
|
| 10 |
+ output: |
|
| 11 |
+ to: |
|
| 12 |
+ kind: ImageStreamTag |
|
| 13 |
+ name: ruby-hello-world:latest |
|
| 14 |
+ resources: {}
|
|
| 15 |
+ source: |
|
| 16 |
+ git: |
|
| 17 |
+ uri: https://github.com/openshift/ruby-hello-world |
|
| 18 |
+ type: Git |
|
| 19 |
+ strategy: |
|
| 20 |
+ dockerStrategy: |
|
| 21 |
+ from: |
|
| 22 |
+ kind: ImageStreamTag |
|
| 23 |
+ name: ruby-20-centos7:latest |
|
| 24 |
+ type: Docker |
|
| 25 |
+ triggers: |
|
| 26 |
+ - github: |
|
| 27 |
+ secret: LyddbeCAaw1a0x08xz9n |
|
| 28 |
+ type: GitHub |
|
| 29 |
+ - generic: |
|
| 30 |
+ secret: ZnYJJeEvo1ri0Gk0f6YY |
|
| 31 |
+ type: Generic |
|
| 32 |
+ - imageChange: {}
|
|
| 33 |
+ type: ImageChange |
|
| 34 |
+ status: |
|
| 35 |
+ lastVersion: 0 |
|
| 36 |
+- apiVersion: v1 |
|
| 37 |
+ kind: Build |
|
| 38 |
+ metadata: |
|
| 39 |
+ creationTimestamp: null |
|
| 40 |
+ labels: |
|
| 41 |
+ app: ruby |
|
| 42 |
+ buildconfig: ruby-hello-world |
|
| 43 |
+ name: ruby-hello-world-1 |
|
| 44 |
+ spec: |
|
| 45 |
+ output: |
|
| 46 |
+ to: |
|
| 47 |
+ kind: ImageStreamTag |
|
| 48 |
+ name: ruby-hello-world:latest |
|
| 49 |
+ resources: {}
|
|
| 50 |
+ serviceAccount: builder |
|
| 51 |
+ source: |
|
| 52 |
+ git: |
|
| 53 |
+ uri: https://github.com/openshift/ruby-hello-world |
|
| 54 |
+ type: Git |
|
| 55 |
+ strategy: |
|
| 56 |
+ dockerStrategy: |
|
| 57 |
+ from: |
|
| 58 |
+ kind: DockerImage |
|
| 59 |
+ name: openshift/ruby-20-centos7:latest |
|
| 60 |
+ type: Docker |
|
| 61 |
+ status: |
|
| 62 |
+ config: |
|
| 63 |
+ name: ruby-hello-world |
|
| 64 |
+ phase: New |
|
| 65 |
+- apiVersion: v1 |
|
| 66 |
+ kind: ImageStream |
|
| 67 |
+ metadata: |
|
| 68 |
+ annotations: |
|
| 69 |
+ openshift.io/image.dockerRepositoryCheck: 2015-07-06T19:05:12Z |
|
| 70 |
+ creationTimestamp: null |
|
| 71 |
+ labels: |
|
| 72 |
+ app: ruby |
|
| 73 |
+ name: ruby-20-centos7 |
|
| 74 |
+ spec: |
|
| 75 |
+ dockerImageRepository: openshift/ruby-20-centos7 |
|
| 76 |
+ status: |
|
| 77 |
+ dockerImageRepository: openshift/ruby-20-centos7 |
|
| 78 |
+- apiVersion: v1 |
|
| 79 |
+ kind: ImageStream |
|
| 80 |
+ metadata: |
|
| 81 |
+ creationTimestamp: null |
|
| 82 |
+ labels: |
|
| 83 |
+ app: ruby |
|
| 84 |
+ name: ruby-hello-world |
|
| 85 |
+ spec: {}
|
|
| 86 |
+ status: |
|
| 87 |
+ dockerImageRepository: "" |
|
| 88 |
+- apiVersion: v1 |
|
| 89 |
+ kind: DeploymentConfig |
|
| 90 |
+ metadata: |
|
| 91 |
+ creationTimestamp: null |
|
| 92 |
+ labels: |
|
| 93 |
+ app: ruby |
|
| 94 |
+ name: ruby-hello-world |
|
| 95 |
+ spec: |
|
| 96 |
+ replicas: 1 |
|
| 97 |
+ selector: |
|
| 98 |
+ deploymentconfig: ruby-hello-world |
|
| 99 |
+ strategy: |
|
| 100 |
+ resources: {}
|
|
| 101 |
+ rollingParams: |
|
| 102 |
+ intervalSeconds: 1 |
|
| 103 |
+ timeoutSeconds: 600 |
|
| 104 |
+ updatePeriodSeconds: 1 |
|
| 105 |
+ type: Rolling |
|
| 106 |
+ template: |
|
| 107 |
+ metadata: |
|
| 108 |
+ creationTimestamp: null |
|
| 109 |
+ labels: |
|
| 110 |
+ deploymentconfig: ruby-hello-world |
|
| 111 |
+ spec: |
|
| 112 |
+ containers: |
|
| 113 |
+ - image: library/ruby-hello-world:latest |
|
| 114 |
+ imagePullPolicy: Always |
|
| 115 |
+ name: ruby-hello-world |
|
| 116 |
+ ports: |
|
| 117 |
+ - containerPort: 8080 |
|
| 118 |
+ name: ruby-hello-world-tcp-8080 |
|
| 119 |
+ protocol: TCP |
|
| 120 |
+ resources: {}
|
|
| 121 |
+ securityContext: |
|
| 122 |
+ capabilities: {}
|
|
| 123 |
+ privileged: false |
|
| 124 |
+ terminationMessagePath: /dev/termination-log |
|
| 125 |
+ dnsPolicy: ClusterFirst |
|
| 126 |
+ restartPolicy: Always |
|
| 127 |
+ triggers: |
|
| 128 |
+ - type: ConfigChange |
|
| 129 |
+ - imageChangeParams: |
|
| 130 |
+ automatic: true |
|
| 131 |
+ containerNames: |
|
| 132 |
+ - ruby-hello-world |
|
| 133 |
+ from: |
|
| 134 |
+ kind: ImageStreamTag |
|
| 135 |
+ name: ruby-hello-world:latest |
|
| 136 |
+ type: ImageChange |
|
| 137 |
+ status: {}
|
|
| 138 |
+- apiVersion: v1 |
|
| 139 |
+ kind: Service |
|
| 140 |
+ metadata: |
|
| 141 |
+ creationTimestamp: null |
|
| 142 |
+ labels: |
|
| 143 |
+ app: ruby |
|
| 144 |
+ name: ruby-hello-world |
|
| 145 |
+ spec: |
|
| 146 |
+ portalIP: "" |
|
| 147 |
+ ports: |
|
| 148 |
+ - name: ruby-hello-world-tcp-8080 |
|
| 149 |
+ nodePort: 0 |
|
| 150 |
+ port: 8080 |
|
| 151 |
+ protocol: TCP |
|
| 152 |
+ targetPort: 8080 |
|
| 153 |
+ selector: |
|
| 154 |
+ deploymentconfig: ruby-hello-world |
|
| 155 |
+ sessionAffinity: None |
|
| 156 |
+ type: ClusterIP |
|
| 157 |
+ status: |
|
| 158 |
+ loadBalancer: {}
|
|
| 159 |
+kind: List |
|
| 160 |
+metadata: {}
|
| ... | ... |
@@ -24,6 +24,7 @@ import ( |
| 24 | 24 |
deployedges "github.com/openshift/origin/pkg/deploy/graph" |
| 25 | 25 |
deploygraph "github.com/openshift/origin/pkg/deploy/graph/nodes" |
| 26 | 26 |
deployutil "github.com/openshift/origin/pkg/deploy/util" |
| 27 |
+ imageedges "github.com/openshift/origin/pkg/image/graph" |
|
| 27 | 28 |
imagegraph "github.com/openshift/origin/pkg/image/graph/nodes" |
| 28 | 29 |
projectapi "github.com/openshift/origin/pkg/project/api" |
| 29 | 30 |
) |
| ... | ... |
@@ -88,6 +89,8 @@ func (d *ProjectStatusDescriber) MakeGraph(namespace string) (osgraph.Graph, err |
| 88 | 88 |
kubeedges.AddExposedPodTemplateSpecEdges(g, service) |
| 89 | 89 |
} |
| 90 | 90 |
|
| 91 |
+ imageedges.AddAllImageStreamRefEdges(g) |
|
| 92 |
+ |
|
| 91 | 93 |
return g, nil |
| 92 | 94 |
} |
| 93 | 95 |
|
| ... | ... |
@@ -135,7 +138,7 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error |
| 135 | 135 |
for _, standaloneBC := range standaloneBCs {
|
| 136 | 136 |
fmt.Fprintln(out) |
| 137 | 137 |
printLines(out, indent, 0, describeStandaloneBuildGroup(standaloneBC, namespace)...) |
| 138 |
- printLines(out, indent, 1, describeAdditionalBuildDetail(standaloneBC.Build, true)...) |
|
| 138 |
+ printLines(out, indent, 1, describeAdditionalBuildDetail(standaloneBC.Build, standaloneBC.DestinationResolved, true)...) |
|
| 139 | 139 |
} |
| 140 | 140 |
|
| 141 | 141 |
if (len(services) == 0) && (len(standaloneDCs) == 0) && (len(standaloneBCs) == 0) {
|
| ... | ... |
@@ -145,6 +148,11 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error |
| 145 | 145 |
|
| 146 | 146 |
} else {
|
| 147 | 147 |
fmt.Fprintln(out) |
| 148 |
+ |
|
| 149 |
+ if hasUnresolvedImageStreamTag(g) {
|
|
| 150 |
+ fmt.Fprintln(out, "Warning: Some of your builds are pointing to image streams, but the administrator has not configured the integrated Docker registry (oadm registry).") |
|
| 151 |
+ |
|
| 152 |
+ } |
|
| 148 | 153 |
fmt.Fprintln(out, "To see more, use 'oc describe service <name>' or 'oc describe dc <name>'.") |
| 149 | 154 |
fmt.Fprintln(out, "You can use 'oc get all' to see a list of other objects.") |
| 150 | 155 |
} |
| ... | ... |
@@ -153,6 +161,22 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error |
| 153 | 153 |
}) |
| 154 | 154 |
} |
| 155 | 155 |
|
| 156 |
+// hasUnresolvedImageStreamTag checks all build configs that will output to an IST backed by an ImageStream and checks to make sure their builds can push. |
|
| 157 |
+func hasUnresolvedImageStreamTag(g osgraph.Graph) bool {
|
|
| 158 |
+ for _, bcNode := range g.NodesByKind(buildgraph.BuildConfigNodeKind) {
|
|
| 159 |
+ for _, istNode := range g.SuccessorNodesByEdgeKind(bcNode, buildedges.BuildOutputEdgeKind) {
|
|
| 160 |
+ for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(istNode, imageedges.ReferencedImageStreamGraphEdgeKind) {
|
|
| 161 |
+ imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode) |
|
| 162 |
+ if len(imageStreamNode.Status.DockerImageRepository) == 0 {
|
|
| 163 |
+ return true |
|
| 164 |
+ } |
|
| 165 |
+ } |
|
| 166 |
+ } |
|
| 167 |
+ } |
|
| 168 |
+ |
|
| 169 |
+ return false |
|
| 170 |
+} |
|
| 171 |
+ |
|
| 156 | 172 |
func printLines(out io.Writer, indent string, depth int, lines ...string) {
|
| 157 | 173 |
for i, s := range lines {
|
| 158 | 174 |
fmt.Fprintf(out, strings.Repeat(indent, depth)) |
| ... | ... |
@@ -172,7 +196,7 @@ func describeDeploymentInServiceGroup(deploy graphview.DeploymentConfigPipeline) |
| 172 | 172 |
lines[0] = segments[0] + " <-" |
| 173 | 173 |
lines = append(lines, segments[1]) |
| 174 | 174 |
} |
| 175 |
- lines = append(lines, describeAdditionalBuildDetail(deploy.Images[0].Build, includeLastPass)...) |
|
| 175 |
+ lines = append(lines, describeAdditionalBuildDetail(deploy.Images[0].Build, deploy.Images[0].DestinationResolved, includeLastPass)...) |
|
| 176 | 176 |
lines = append(lines, describeDeployments(deploy.Deployment, 3)...) |
| 177 | 177 |
return lines |
| 178 | 178 |
} |
| ... | ... |
@@ -180,7 +204,7 @@ func describeDeploymentInServiceGroup(deploy graphview.DeploymentConfigPipeline) |
| 180 | 180 |
lines := []string{fmt.Sprintf("%s deploys: %s", deploy.Deployment.Name, describeDeploymentConfigTrigger(deploy.Deployment.DeploymentConfig))}
|
| 181 | 181 |
for _, image := range deploy.Images {
|
| 182 | 182 |
lines = append(lines, describeImageInPipeline(image, deploy.Deployment.Namespace)) |
| 183 |
- lines = append(lines, describeAdditionalBuildDetail(image.Build, includeLastPass)...) |
|
| 183 |
+ lines = append(lines, describeAdditionalBuildDetail(image.Build, image.DestinationResolved, includeLastPass)...) |
|
| 184 | 184 |
lines = append(lines, describeDeployments(deploy.Deployment, 3)...) |
| 185 | 185 |
} |
| 186 | 186 |
return lines |
| ... | ... |
@@ -263,7 +287,7 @@ func describeBuildInPipeline(build *buildapi.BuildConfig, baseImage graphview.Im |
| 263 | 263 |
} |
| 264 | 264 |
} |
| 265 | 265 |
|
| 266 |
-func describeAdditionalBuildDetail(build *buildgraph.BuildConfigNode, includeSuccess bool) []string {
|
|
| 266 |
+func describeAdditionalBuildDetail(build *buildgraph.BuildConfigNode, pushTargetResolved bool, includeSuccess bool) []string {
|
|
| 267 | 267 |
if build == nil {
|
| 268 | 268 |
return nil |
| 269 | 269 |
} |
| ... | ... |
@@ -281,17 +305,17 @@ func describeAdditionalBuildDetail(build *buildgraph.BuildConfigNode, includeSuc |
| 281 | 281 |
} |
| 282 | 282 |
|
| 283 | 283 |
if pass != nil && includeSuccess {
|
| 284 |
- out = append(out, describeBuildStatus(pass, &passTime, build.BuildConfig.Name)) |
|
| 284 |
+ out = append(out, describeBuildStatus(pass, &passTime, build.BuildConfig.Name, pushTargetResolved)) |
|
| 285 | 285 |
} |
| 286 | 286 |
if fail != nil {
|
| 287 |
- out = append(out, describeBuildStatus(fail, &failTime, build.BuildConfig.Name)) |
|
| 287 |
+ out = append(out, describeBuildStatus(fail, &failTime, build.BuildConfig.Name, pushTargetResolved)) |
|
| 288 | 288 |
} |
| 289 | 289 |
|
| 290 | 290 |
active := build.ActiveBuilds |
| 291 | 291 |
if len(active) > 0 {
|
| 292 | 292 |
activeOut := []string{}
|
| 293 | 293 |
for i := range active {
|
| 294 |
- activeOut = append(activeOut, describeBuildStatus(&active[i], nil, build.BuildConfig.Name)) |
|
| 294 |
+ activeOut = append(activeOut, describeBuildStatus(&active[i], nil, build.BuildConfig.Name, pushTargetResolved)) |
|
| 295 | 295 |
} |
| 296 | 296 |
|
| 297 | 297 |
if buildTimestamp(&active[0]).Before(last) {
|
| ... | ... |
@@ -306,7 +330,13 @@ func describeAdditionalBuildDetail(build *buildgraph.BuildConfigNode, includeSuc |
| 306 | 306 |
return out |
| 307 | 307 |
} |
| 308 | 308 |
|
| 309 |
-func describeBuildStatus(build *buildapi.Build, t *util.Time, parentName string) string {
|
|
| 309 |
+func describeBuildStatus(build *buildapi.Build, t *util.Time, parentName string, pushTargetResolved bool) string {
|
|
| 310 |
+ imageStreamFailure := "" |
|
| 311 |
+ // if we're using an image stream and that image stream is the internal registry and that registry doesn't exist |
|
| 312 |
+ if (build.Parameters.Output.To != nil) && !pushTargetResolved {
|
|
| 313 |
+ imageStreamFailure = " (can't push to image)" |
|
| 314 |
+ } |
|
| 315 |
+ |
|
| 310 | 316 |
if t == nil {
|
| 311 | 317 |
ts := buildTimestamp(build) |
| 312 | 318 |
t = &ts |
| ... | ... |
@@ -328,14 +358,14 @@ func describeBuildStatus(build *buildapi.Build, t *util.Time, parentName string) |
| 328 | 328 |
} |
| 329 | 329 |
switch build.Status {
|
| 330 | 330 |
case buildapi.BuildStatusComplete: |
| 331 |
- return fmt.Sprintf("build %s succeeded %s ago%s", name, time, revision)
|
|
| 331 |
+ return fmt.Sprintf("build %s succeeded %s ago%s%s", name, time, revision, imageStreamFailure)
|
|
| 332 | 332 |
case buildapi.BuildStatusError: |
| 333 |
- return fmt.Sprintf("build %s stopped with an error %s ago%s", name, time, revision)
|
|
| 333 |
+ return fmt.Sprintf("build %s stopped with an error %s ago%s%s", name, time, revision, imageStreamFailure)
|
|
| 334 | 334 |
case buildapi.BuildStatusFailed: |
| 335 |
- return fmt.Sprintf("build %s failed %s ago%s", name, time, revision)
|
|
| 335 |
+ return fmt.Sprintf("build %s failed %s ago%s%s", name, time, revision, imageStreamFailure)
|
|
| 336 | 336 |
default: |
| 337 | 337 |
status := strings.ToLower(string(build.Status)) |
| 338 |
- return fmt.Sprintf("build %s %s for %s%s", name, status, time, revision)
|
|
| 338 |
+ return fmt.Sprintf("build %s %s for %s%s%s", name, status, time, revision, imageStreamFailure)
|
|
| 339 | 339 |
} |
| 340 | 340 |
} |
| 341 | 341 |
|
| ... | ... |
@@ -10,7 +10,10 @@ import ( |
| 10 | 10 |
ktestclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient" |
| 11 | 11 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
| 12 | 12 |
|
| 13 |
+ osgraphtest "github.com/openshift/origin/pkg/api/graph/test" |
|
| 14 |
+ buildedges "github.com/openshift/origin/pkg/build/graph" |
|
| 13 | 15 |
"github.com/openshift/origin/pkg/client/testclient" |
| 16 |
+ imageedges "github.com/openshift/origin/pkg/image/graph" |
|
| 14 | 17 |
projectapi "github.com/openshift/origin/pkg/project/api" |
| 15 | 18 |
) |
| 16 | 19 |
|
| ... | ... |
@@ -22,6 +25,34 @@ func mustParseTime(t string) time.Time {
|
| 22 | 22 |
return out |
| 23 | 23 |
} |
| 24 | 24 |
|
| 25 |
+func TestUnpushableBuild(t *testing.T) {
|
|
| 26 |
+ g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/unpushable-build.yaml")
|
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ buildedges.AddAllInputOutputEdges(g) |
|
| 32 |
+ imageedges.AddAllImageStreamRefEdges(g) |
|
| 33 |
+ |
|
| 34 |
+ if e, a := true, hasUnresolvedImageStreamTag(g); e != a {
|
|
| 35 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 36 |
+ } |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func TestPushableBuild(t *testing.T) {
|
|
| 40 |
+ g, _, err := osgraphtest.BuildGraph("../../../api/graph/test/pushable-build.yaml")
|
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ t.Fatalf("unexpected error: %v", err)
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ buildedges.AddAllInputOutputEdges(g) |
|
| 46 |
+ imageedges.AddAllImageStreamRefEdges(g) |
|
| 47 |
+ |
|
| 48 |
+ if e, a := false, hasUnresolvedImageStreamTag(g); e != a {
|
|
| 49 |
+ t.Errorf("expected %v, got %v", e, a)
|
|
| 50 |
+ } |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 25 | 53 |
func TestProjectStatus(t *testing.T) {
|
| 26 | 54 |
testCases := map[string]struct {
|
| 27 | 55 |
Path string |
| 28 | 56 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,34 @@ |
| 0 |
+package graph |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/gonum/graph" |
|
| 4 |
+ |
|
| 5 |
+ osgraph "github.com/openshift/origin/pkg/api/graph" |
|
| 6 |
+ imageapi "github.com/openshift/origin/pkg/image/api" |
|
| 7 |
+ imagegraph "github.com/openshift/origin/pkg/image/graph/nodes" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+const ( |
|
| 11 |
+ // ReferencedImageStreamGraphEdgeKind is an edge that goes from an ImageStreamTag node back to an ImageStream |
|
| 12 |
+ ReferencedImageStreamGraphEdgeKind = "ReferencedImageStreamGraphEdge" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+// AddImageStreamRefEdge ensures that a directed edge exists between an IST Node and the IS it references |
|
| 16 |
+func AddImageStreamRefEdge(g osgraph.MutableUniqueGraph, node *imagegraph.ImageStreamTagNode) {
|
|
| 17 |
+ isName, _, _ := imageapi.SplitImageStreamTag(node.Name) |
|
| 18 |
+ imageStream := &imageapi.ImageStream{}
|
|
| 19 |
+ imageStream.Namespace = node.Namespace |
|
| 20 |
+ imageStream.Name = isName |
|
| 21 |
+ |
|
| 22 |
+ imageStreamNode := imagegraph.FindOrCreateSyntheticImageStreamNode(g, imageStream) |
|
| 23 |
+ g.AddEdge(node, imageStreamNode, ReferencedImageStreamGraphEdgeKind) |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+// AddAllImageStreamRefEdges calls AddImageStreamRefEdge for every ImageStreamTagNode in the graph |
|
| 27 |
+func AddAllImageStreamRefEdges(g osgraph.MutableUniqueGraph) {
|
|
| 28 |
+ for _, node := range g.(graph.Graph).NodeList() {
|
|
| 29 |
+ if istNode, ok := node.(*imagegraph.ImageStreamTagNode); ok {
|
|
| 30 |
+ AddImageStreamRefEdge(g, istNode) |
|
| 31 |
+ } |
|
| 32 |
+ } |
|
| 33 |
+} |
| ... | ... |
@@ -85,7 +85,7 @@ func EnsureImageStreamTagNode(g osgraph.MutableUniqueGraph, ist *imageapi.ImageS |
| 85 | 85 |
return osgraph.EnsureUnique(g, |
| 86 | 86 |
ImageStreamTagNodeName(ist), |
| 87 | 87 |
func(node osgraph.Node) graph.Node {
|
| 88 |
- return &ImageStreamTagNode{node, ist, false}
|
|
| 88 |
+ return &ImageStreamTagNode{node, ist, true}
|
|
| 89 | 89 |
}, |
| 90 | 90 |
).(*ImageStreamTagNode) |
| 91 | 91 |
} |
| ... | ... |
@@ -95,21 +95,31 @@ func FindOrCreateSyntheticImageStreamTagNode(g osgraph.MutableUniqueGraph, ist * |
| 95 | 95 |
return osgraph.EnsureUnique(g, |
| 96 | 96 |
ImageStreamTagNodeName(ist), |
| 97 | 97 |
func(node osgraph.Node) graph.Node {
|
| 98 |
- return &ImageStreamTagNode{node, ist, true}
|
|
| 98 |
+ return &ImageStreamTagNode{node, ist, false}
|
|
| 99 | 99 |
}, |
| 100 | 100 |
).(*ImageStreamTagNode) |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 | 103 |
// EnsureImageStreamNode adds a graph node for the Image Stream if it does not already exist. |
| 104 |
-func EnsureImageStreamNode(g osgraph.MutableUniqueGraph, stream *imageapi.ImageStream) graph.Node {
|
|
| 104 |
+func EnsureImageStreamNode(g osgraph.MutableUniqueGraph, is *imageapi.ImageStream) graph.Node {
|
|
| 105 | 105 |
return osgraph.EnsureUnique(g, |
| 106 |
- ImageStreamNodeName(stream), |
|
| 106 |
+ ImageStreamNodeName(is), |
|
| 107 | 107 |
func(node osgraph.Node) graph.Node {
|
| 108 |
- return &ImageStreamNode{node, stream}
|
|
| 108 |
+ return &ImageStreamNode{node, is, true}
|
|
| 109 | 109 |
}, |
| 110 | 110 |
) |
| 111 | 111 |
} |
| 112 | 112 |
|
| 113 |
+// FindOrCreateSyntheticImageStreamNode returns the existing ISNode or creates a synthetic node in its place |
|
| 114 |
+func FindOrCreateSyntheticImageStreamNode(g osgraph.MutableUniqueGraph, is *imageapi.ImageStream) *ImageStreamNode {
|
|
| 115 |
+ return osgraph.EnsureUnique(g, |
|
| 116 |
+ ImageStreamNodeName(is), |
|
| 117 |
+ func(node osgraph.Node) graph.Node {
|
|
| 118 |
+ return &ImageStreamNode{node, is, false}
|
|
| 119 |
+ }, |
|
| 120 |
+ ).(*ImageStreamNode) |
|
| 121 |
+} |
|
| 122 |
+ |
|
| 113 | 123 |
// EnsureImageLayerNode adds a graph node for the layer if it does not already exist. |
| 114 | 124 |
func EnsureImageLayerNode(g osgraph.MutableUniqueGraph, layer string) graph.Node {
|
| 115 | 125 |
return osgraph.EnsureUnique(g, |
| ... | ... |
@@ -25,6 +25,12 @@ func ImageStreamNodeName(o *imageapi.ImageStream) osgraph.UniqueName {
|
| 25 | 25 |
type ImageStreamNode struct {
|
| 26 | 26 |
osgraph.Node |
| 27 | 27 |
*imageapi.ImageStream |
| 28 |
+ |
|
| 29 |
+ IsFound bool |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func (n ImageStreamNode) Found() bool {
|
|
| 33 |
+ return n.IsFound |
|
| 28 | 34 |
} |
| 29 | 35 |
|
| 30 | 36 |
func (n ImageStreamNode) Object() interface{} {
|
| ... | ... |
@@ -47,11 +53,11 @@ type ImageStreamTagNode struct {
|
| 47 | 47 |
osgraph.Node |
| 48 | 48 |
*imageapi.ImageStreamTag |
| 49 | 49 |
|
| 50 |
- Synthetic bool |
|
| 50 |
+ IsFound bool |
|
| 51 | 51 |
} |
| 52 | 52 |
|
| 53 |
-func (n ImageStreamTagNode) IsSynthetic() bool {
|
|
| 54 |
- return n.Synthetic |
|
| 53 |
+func (n ImageStreamTagNode) Found() bool {
|
|
| 54 |
+ return n.IsFound |
|
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 | 57 |
func (n ImageStreamTagNode) ImageSpec() string {
|