Skip to content

Commit 1bc373b

Browse files
author
OpenShift Bot
committed
Merge pull request #4941 from kargakis/report-trigger-errors-via-api-field
Merged by openshift-bot
2 parents 3b7c5c8 + 0b38d81 commit 1bc373b

File tree

12 files changed

+627
-79
lines changed

12 files changed

+627
-79
lines changed

pkg/cmd/cli/cmd/deploy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ func (o *DeployOptions) cancel(config *deployapi.DeploymentConfig, out io.Writer
292292
fmt.Fprintf(out, "There have been no deployments for %s/%s\n", config.Namespace, config.Name)
293293
return nil
294294
}
295-
sort.Sort(deployutil.DeploymentsByLatestVersionDesc(deployments.Items))
295+
sort.Sort(deployutil.ByLatestVersionDesc(deployments.Items))
296296
failedCancellations := []string{}
297297
anyCancelled := false
298298
for _, deployment := range deployments.Items {

pkg/cmd/cli/cmd/rollback.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ func (o *RollbackOptions) findTargetDeployment(config *deployapi.DeploymentConfi
312312
if err != nil {
313313
return nil, err
314314
}
315-
sort.Sort(deployutil.DeploymentsByLatestVersionDesc(deployments.Items))
315+
sort.Sort(deployutil.ByLatestVersionDesc(deployments.Items))
316316

317317
// Find the target deployment for rollback. If a version was specified,
318318
// use the version for a search. Otherwise, use the last completed

pkg/cmd/cli/describe/deployments.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ func (d *DeploymentConfigDescriber) Describe(namespace, name string) (string, er
138138
formatString(out, "Strategy", deploymentConfig.Template.Strategy.Type)
139139
printStrategy(deploymentConfig.Template.Strategy, out)
140140
printReplicationControllerSpec(deploymentConfig.Template.ControllerTemplate, out)
141-
141+
if deploymentConfig.Details != nil && len(deploymentConfig.Details.Message) > 0 {
142+
fmt.Fprintf(out, "Warning:\t%s\n", deploymentConfig.Details.Message)
143+
}
142144
deploymentName := deployutil.LatestDeploymentNameForConfig(deploymentConfig)
143145
deployment, err := d.client.getDeployment(namespace, deploymentName)
144146
if err != nil {

pkg/cmd/infra/deployer/deployer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func (d *Deployer) Deploy(namespace, deploymentName string) error {
154154
deployments := unsortedDeployments.Items
155155

156156
// Sort all the deployments by version.
157-
sort.Sort(deployutil.DeploymentsByLatestVersionDesc(deployments))
157+
sort.Sort(deployutil.ByLatestVersionDesc(deployments))
158158

159159
// Find any last completed deployment.
160160
var from *kapi.ReplicationController

pkg/deploy/controller/configchange/controller.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
kapi "k8s.io/kubernetes/pkg/api"
99
kerrors "k8s.io/kubernetes/pkg/api/errors"
10-
"k8s.io/kubernetes/pkg/client/record"
1110

1211
deployapi "github.com/openshift/origin/pkg/deploy/api"
1312
deployutil "github.com/openshift/origin/pkg/deploy/util"
@@ -23,8 +22,6 @@ type DeploymentConfigChangeController struct {
2322
changeStrategy changeStrategy
2423
// decodeConfig knows how to decode the deploymentConfig from a deployment's annotations.
2524
decodeConfig func(deployment *kapi.ReplicationController) (*deployapi.DeploymentConfig, error)
26-
// recorder records events.
27-
recorder record.EventRecorder
2825
}
2926

3027
// fatalError is an error which can't be retried.
@@ -36,15 +33,7 @@ func (e fatalError) Error() string {
3633

3734
// Handle processes change triggers for config.
3835
func (c *DeploymentConfigChangeController) Handle(config *deployapi.DeploymentConfig) error {
39-
hasChangeTrigger := false
40-
for _, trigger := range config.Triggers {
41-
if trigger.Type == deployapi.DeploymentTriggerOnConfigChange {
42-
hasChangeTrigger = true
43-
break
44-
}
45-
}
46-
47-
if !hasChangeTrigger {
36+
if !deployutil.HasChangeTrigger(config) {
4837
glog.V(5).Infof("Ignoring DeploymentConfig %s; no change triggers detected", deployutil.LabelForDeploymentConfig(config))
4938
return nil
5039
}
@@ -55,10 +44,10 @@ func (c *DeploymentConfigChangeController) Handle(config *deployapi.DeploymentCo
5544
if kerrors.IsConflict(err) {
5645
return fatalError(fmt.Sprintf("DeploymentConfig %s updated since retrieval; aborting trigger: %v", deployutil.LabelForDeploymentConfig(config), err))
5746
}
58-
c.recorder.Eventf(config, "failedCreate", "Couldn't create initial deployment: %v", err)
47+
glog.V(4).Infof("Couldn't create initial deployment for deploymentConfig %q: %v", deployutil.LabelForDeploymentConfig(config), err)
5948
return nil
6049
}
61-
glog.V(4).Infof("Created initial Deployment for DeploymentConfig %s", deployutil.LabelForDeploymentConfig(config))
50+
glog.V(4).Infof("Created initial deployment for deploymentConfig %q", deployutil.LabelForDeploymentConfig(config))
6251
return nil
6352
}
6453

pkg/deploy/controller/configchange/factory.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ func (factory *DeploymentConfigChangeControllerFactory) Create() controller.Runn
6161
decodeConfig: func(deployment *kapi.ReplicationController) (*deployapi.DeploymentConfig, error) {
6262
return deployutil.DecodeDeploymentConfig(deployment, factory.Codec)
6363
},
64-
recorder: eventBroadcaster.NewRecorder(kapi.EventSource{Component: "deployer"}),
6564
}
6665

6766
return &controller.RetryController{

pkg/deploy/controller/deployerpod/controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func (c *DeployerPodController) cleanupFailedDeployment(deployment *kapi.Replica
140140
}
141141

142142
if ok && len(existingDeployments.Items) > 0 {
143-
sort.Sort(deployutil.DeploymentsByLatestVersionDesc(existingDeployments.Items))
143+
sort.Sort(deployutil.ByLatestVersionDesc(existingDeployments.Items))
144144
for index, existing := range existingDeployments.Items {
145145
// if a newer deployment exists:
146146
// - set the replicas for the current failed deployment to 0

pkg/deploy/controller/deploymentconfig/controller.go

Lines changed: 138 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,20 @@ import (
44
"fmt"
55
"sort"
66
"strconv"
7+
"strings"
78

89
"github.com/golang/glog"
910

1011
kapi "k8s.io/kubernetes/pkg/api"
1112
"k8s.io/kubernetes/pkg/api/errors"
12-
"k8s.io/kubernetes/pkg/client/record"
13+
"k8s.io/kubernetes/pkg/fields"
14+
"k8s.io/kubernetes/pkg/labels"
1315
"k8s.io/kubernetes/pkg/util"
1416

17+
osclient "github.com/openshift/origin/pkg/client"
1518
deployapi "github.com/openshift/origin/pkg/deploy/api"
1619
deployutil "github.com/openshift/origin/pkg/deploy/util"
20+
imageapi "github.com/openshift/origin/pkg/image/api"
1721
)
1822

1923
// DeploymentConfigController is responsible for creating a new deployment when:
@@ -31,9 +35,10 @@ import (
3135
type DeploymentConfigController struct {
3236
// deploymentClient provides access to deployments.
3337
deploymentClient deploymentClient
38+
// osClient provides access to Origin resources
39+
osClient osclient.Interface
3440
// makeDeployment knows how to make a deployment from a config.
3541
makeDeployment func(*deployapi.DeploymentConfig) (*kapi.ReplicationController, error)
36-
recorder record.EventRecorder
3742
}
3843

3944
// fatalError is an error which can't be retried.
@@ -43,33 +48,32 @@ type fatalError string
4348
type transientError string
4449

4550
func (e fatalError) Error() string {
46-
return fmt.Sprintf("fatal error handling DeploymentConfig: %s", string(e))
51+
return fmt.Sprintf("fatal error handling deployment config: %s", string(e))
4752
}
4853
func (e transientError) Error() string {
49-
return "transient error handling DeploymentConfig: " + string(e)
54+
return "transient error handling deployment config: " + string(e)
5055
}
5156

5257
// Handle processes config and creates a new deployment if necessary.
5358
func (c *DeploymentConfigController) Handle(config *deployapi.DeploymentConfig) error {
59+
// Inspect a deployment configuration every time the controller reconciles it
60+
details, existingDeployments, latestDeploymentExists, err := c.findDetails(config)
61+
if err != nil {
62+
return err
63+
}
64+
config, err = c.updateDetails(config, details)
65+
if err != nil {
66+
return transientError(err.Error())
67+
}
68+
5469
// Only deploy when the version has advanced past 0.
5570
if config.LatestVersion == 0 {
5671
glog.V(5).Infof("Waiting for first version of %s", deployutil.LabelForDeploymentConfig(config))
5772
return nil
5873
}
5974

60-
// Check if any existing inflight deployments (any non-terminal state).
61-
existingDeployments, err := c.deploymentClient.listDeploymentsForConfig(config.Namespace, config.Name)
62-
if err != nil {
63-
return fmt.Errorf("couldn't list Deployments for DeploymentConfig %s: %v", deployutil.LabelForDeploymentConfig(config), err)
64-
}
6575
var inflightDeployment *kapi.ReplicationController
66-
latestDeploymentExists := false
6776
for _, deployment := range existingDeployments.Items {
68-
// check if this is the latest deployment
69-
// we'll return after we've dealt with the multiple-active-deployments case
70-
if deployutil.DeploymentVersionFor(&deployment) == config.LatestVersion {
71-
latestDeploymentExists = true
72-
}
7377

7478
deploymentStatus := deployutil.DeploymentStatusFor(&deployment)
7579
switch deploymentStatus {
@@ -93,9 +97,9 @@ func (c *DeploymentConfigController) Handle(config *deployapi.DeploymentConfig)
9397
deploymentForCancellation.Annotations[deployapi.DeploymentCancelledAnnotation] = deployapi.DeploymentCancelledAnnotationValue
9498
deploymentForCancellation.Annotations[deployapi.DeploymentStatusReasonAnnotation] = deployapi.DeploymentCancelledNewerDeploymentExists
9599
if _, err := c.deploymentClient.updateDeployment(deploymentForCancellation.Namespace, deploymentForCancellation); err != nil {
96-
util.HandleError(fmt.Errorf("couldn't cancel Deployment %s: %v", deployutil.LabelForDeployment(deploymentForCancellation), err))
100+
util.HandleError(fmt.Errorf("couldn't cancel deployment %s: %v", deployutil.LabelForDeployment(deploymentForCancellation), err))
97101
}
98-
glog.V(4).Infof("Cancelled Deployment %s for DeploymentConfig %s", deployutil.LabelForDeployment(deploymentForCancellation), deployutil.LabelForDeploymentConfig(config))
102+
glog.V(4).Infof("Cancelled deployment %s for deployment config %s", deployutil.LabelForDeployment(deploymentForCancellation), deployutil.LabelForDeploymentConfig(config))
99103
}
100104
}
101105

@@ -107,22 +111,22 @@ func (c *DeploymentConfigController) Handle(config *deployapi.DeploymentConfig)
107111
// check to see if there are inflight deployments
108112
if inflightDeployment != nil {
109113
// raise a transientError so that the deployment config can be re-queued
110-
glog.V(4).Infof("Found previous inflight Deployment for %s - will requeue", deployutil.LabelForDeploymentConfig(config))
111-
return transientError(fmt.Sprintf("found previous inflight Deployment for %s - requeuing", deployutil.LabelForDeploymentConfig(config)))
114+
glog.V(4).Infof("Found previous inflight deployment for %s - will requeue", deployutil.LabelForDeploymentConfig(config))
115+
return transientError(fmt.Sprintf("found previous inflight deployment for %s - requeuing", deployutil.LabelForDeploymentConfig(config)))
112116
}
113117

114118
// Try and build a deployment for the config.
115119
deployment, err := c.makeDeployment(config)
116120
if err != nil {
117-
return fatalError(fmt.Sprintf("couldn't make Deployment from (potentially invalid) DeploymentConfig %s: %v", deployutil.LabelForDeploymentConfig(config), err))
121+
return fatalError(fmt.Sprintf("couldn't make deployment from (potentially invalid) deployment config %s: %v", deployutil.LabelForDeploymentConfig(config), err))
118122
}
119123

120124
// Compute the desired replicas for the deployment. Use the last completed
121125
// deployment's current replica count, or the config template if there is no
122126
// prior completed deployment available.
123127
desiredReplicas := config.Template.ControllerTemplate.Replicas
124128
if len(existingDeployments.Items) > 0 {
125-
sort.Sort(deployutil.DeploymentsByLatestVersionDesc(existingDeployments.Items))
129+
sort.Sort(deployutil.ByLatestVersionDesc(existingDeployments.Items))
126130
for _, existing := range existingDeployments.Items {
127131
if deployutil.DeploymentStatusFor(&existing) == deployapi.DeploymentStatusComplete {
128132
desiredReplicas = existing.Spec.Replicas
@@ -135,19 +139,18 @@ func (c *DeploymentConfigController) Handle(config *deployapi.DeploymentConfig)
135139

136140
// Create the deployment.
137141
if _, err := c.deploymentClient.createDeployment(config.Namespace, deployment); err == nil {
138-
glog.V(4).Infof("Created Deployment for DeploymentConfig %s", deployutil.LabelForDeploymentConfig(config))
142+
glog.V(4).Infof("Created deployment for deployment config %s", deployutil.LabelForDeploymentConfig(config))
139143
return nil
140144
} else {
141145
// If the deployment was already created, just move on. The cache could be stale, or another
142146
// process could have already handled this update.
143147
if errors.IsAlreadyExists(err) {
144-
glog.V(4).Infof("Deployment already exists for DeploymentConfig %s", deployutil.LabelForDeploymentConfig(config))
148+
glog.V(4).Infof("Deployment already exists for deployment config %s", deployutil.LabelForDeploymentConfig(config))
145149
return nil
146150
}
147151

148-
// log an event if the deployment could not be created that the user can discover
149-
c.recorder.Eventf(config, "failedCreate", "Error creating: %v", err)
150-
return fmt.Errorf("couldn't create Deployment for DeploymentConfig %s: %v", deployutil.LabelForDeploymentConfig(config), err)
152+
glog.Warningf("Cannot create latest deployment for deployment config %q: %v", deployutil.LabelForDeploymentConfig(config), err)
153+
return fmt.Errorf("couldn't create deployment for deployment config %s: %v", deployutil.LabelForDeploymentConfig(config), err)
151154
}
152155
}
153156

@@ -178,3 +181,112 @@ func (i *deploymentClientImpl) listDeploymentsForConfig(namespace, configName st
178181
func (i *deploymentClientImpl) updateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
179182
return i.updateDeploymentFunc(namespace, deployment)
180183
}
184+
185+
// findDetails inspects the given deployment configuration for any failure causes
186+
// and returns any found details about it
187+
func (c *DeploymentConfigController) findDetails(config *deployapi.DeploymentConfig) (string, *kapi.ReplicationControllerList, bool, error) {
188+
// Check if any existing inflight deployments (any non-terminal state).
189+
existingDeployments, err := c.deploymentClient.listDeploymentsForConfig(config.Namespace, config.Name)
190+
if err != nil {
191+
return "", nil, false, fmt.Errorf("couldn't list deployments for deployment config %q: %v", deployutil.LabelForDeploymentConfig(config), err)
192+
}
193+
// check if the latest deployment exists
194+
// we'll return after we've dealt with the multiple-active-deployments case
195+
latestDeploymentExists, latestDeploymentStatus := deployutil.LatestDeploymentInfo(config, existingDeployments)
196+
if latestDeploymentExists && latestDeploymentStatus != deployapi.DeploymentStatusFailed {
197+
// If the latest deployment exists and is not failed, clear the dc message
198+
return "", existingDeployments, latestDeploymentExists, nil
199+
}
200+
201+
// Rest of the code will handle non-existing or failed latest deployment causes
202+
// TODO: Inspect pod logs in case of failed latest
203+
204+
details := ""
205+
allDetails := []string{}
206+
willHaveImage, hasImageChangeTrigger := false, false
207+
if config.Details != nil && len(config.Details.Message) > 0 {
208+
// Populate details with the previous message so that in case we stumble upon
209+
// an unexpected client error, the message won't be overwritten
210+
details = config.Details.Message
211+
}
212+
// Look into image change triggers and find out possible deployment failures such as
213+
// missing image stream tags with or without build configurations pointing at them
214+
for _, trigger := range config.Triggers {
215+
if trigger.Type != deployapi.DeploymentTriggerOnImageChange || trigger.ImageChangeParams == nil || !trigger.ImageChangeParams.Automatic {
216+
continue
217+
}
218+
hasImageChangeTrigger = true
219+
name := trigger.ImageChangeParams.From.Name
220+
tag := trigger.ImageChangeParams.Tag
221+
istag := imageapi.JoinImageStreamTag(name, tag)
222+
223+
// Check if the image stream tag pointed by the trigger exists
224+
if _, err := c.osClient.ImageStreamTags(config.Namespace).Get(name, tag); err != nil {
225+
if !errors.IsNotFound(err) {
226+
glog.V(2).Infof("Error while trying to get image stream tag %q: %v", istag, err)
227+
return details, existingDeployments, latestDeploymentExists, nil
228+
}
229+
// In case the image stream tag was not found, then it either doesn't exist or doesn't exist yet
230+
// (a build configuration output points to it so it's going to be populated at some point in the
231+
// future)
232+
details = fmt.Sprintf("The image trigger for image stream tag %s will have no effect because image stream tag %s does not exist.", istag, istag)
233+
bcList, err := c.osClient.BuildConfigs(kapi.NamespaceAll).List(labels.Everything(), fields.Everything())
234+
if err != nil {
235+
glog.V(2).Infof("Error while trying to list build configs: %v", err)
236+
return details, existingDeployments, latestDeploymentExists, nil
237+
}
238+
239+
for _, bc := range bcList.Items {
240+
if bc.Spec.Output.To != nil && bc.Spec.Output.To.Kind == "ImageStreamTag" {
241+
parts := strings.Split(bc.Spec.Output.To.Name, ":")
242+
if len(parts) != 2 {
243+
glog.V(2).Infof("Invalid image stream tag: %q", bc.Spec.Output.To.Name)
244+
return details, existingDeployments, latestDeploymentExists, nil
245+
}
246+
if parts[0] == name && parts[1] == tag {
247+
details = fmt.Sprintf("The image trigger for image stream tag %s will have no effect because image stream tag %s does not exist. If image stream tag %s is expected, check buildconfig %s which produces image stream tag %s.", istag, istag, istag, bc.Name, istag)
248+
break
249+
}
250+
}
251+
}
252+
// Try to see if the image stream exists, if not then the build will never be able to update the
253+
// tag in question
254+
if _, err := c.osClient.ImageStreams(config.Namespace).Get(name); err != nil {
255+
glog.V(2).Infof("Error while trying to get image stream %q: %v", name, err)
256+
if errors.IsNotFound(err) {
257+
details = fmt.Sprintf("The image trigger for image stream tag %s will have no effect because image stream %s does not exist.", istag, name)
258+
}
259+
}
260+
} else {
261+
willHaveImage = true
262+
}
263+
allDetails = append(allDetails, details)
264+
}
265+
266+
if len(allDetails) > 1 {
267+
for i := range allDetails {
268+
allDetails[i] = fmt.Sprintf("* %s", allDetails[i])
269+
}
270+
// Prepend multiple errors warning
271+
multipleErrWarning := fmt.Sprintf("Deployment config %q blocked by multiple errors:\n", config.Name)
272+
allDetails = append([]string{multipleErrWarning}, allDetails...)
273+
}
274+
275+
if hasImageChangeTrigger && !willHaveImage {
276+
allDetails = append(allDetails, fmt.Sprintf("Deployment config %q will never have an image.", config.Name))
277+
}
278+
279+
return strings.Join(allDetails, "\n"), existingDeployments, latestDeploymentExists, nil
280+
}
281+
282+
// updateDetails updates a deployment configuration with the provided details
283+
func (c *DeploymentConfigController) updateDetails(config *deployapi.DeploymentConfig, details string) (*deployapi.DeploymentConfig, error) {
284+
if config.Details == nil {
285+
config.Details = new(deployapi.DeploymentDetails)
286+
}
287+
if details != config.Details.Message {
288+
config.Details.Message = details
289+
return c.osClient.DeploymentConfigs(config.Namespace).Update(config)
290+
}
291+
return config, nil
292+
}

0 commit comments

Comments
 (0)