Skip to content

Commit 0708065

Browse files
committed
pkg/cvo: Reason granularity for RemoteFailed
We want to be able to distinguish these conditions, which can be due to internal misconfiguration or external Cincinnati/network errors [1]. The former can be fixed by cluster admins. The latter could go either way. I dropped the len(upstream) guard from checkForUpdate because there's already an earlier guard in syncAvailableUpdates. The guard I'm removing is from db150e6 (cvo: Perform status updates in a single thread, 2018-11-03, #45). The covering guard is from the later 286641d (api: Update to objects from openshift/api, 2018-11-15, #55). Personally, I'd rather have GetUpdates return an *Error, so we could dispense with the cast and unused Unknown-reason fallback. But Abhinav wanted the explicit cast in return for a more familiar error type [2]. [1]: https://bugzilla.redhat.com/show_bug.cgi?id=1685338 [2]: #268 (comment)
1 parent ebb86aa commit 0708065

File tree

4 files changed

+47
-24
lines changed

4 files changed

+47
-24
lines changed

pkg/cincinnati/cincinnati.go

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ func NewClient(id uuid.UUID, proxyURL *url.URL, tlsConfig *tls.Config) Client {
3434
// Update is a single node from the update graph.
3535
type Update node
3636

37+
// Error is returned when are unable to get updates.
38+
type Error struct {
39+
// Reason is the reason suggested for the ClusterOperator status condition.
40+
Reason string
41+
42+
// Message is the message suggested for the ClusterOperator status condition.
43+
Message string
44+
45+
// cause is the upstream error, if any, being wrapped by this error.
46+
cause error
47+
}
48+
49+
// Error serializes the error as a string, to satisfy the error interface.
50+
func (err *Error) Error() string {
51+
return fmt.Sprintf("%s: %s", err.Reason, err.Message)
52+
}
53+
3754
// GetUpdates fetches the next-applicable update payloads from the specified
3855
// upstream Cincinnati stack given the current version and channel. The next-
3956
// applicable updates are determined by downloading the update graph, finding
@@ -46,7 +63,7 @@ func (c Client) GetUpdates(upstream string, arch string, channel string, version
4663
// Prepare parametrized cincinnati query.
4764
cincinnatiURL, err := url.Parse(upstream)
4865
if err != nil {
49-
return nil, fmt.Errorf("failed to parse upstream URL: %s", err)
66+
return nil, &Error{Reason: "InvalidURI", Message: fmt.Sprintf("failed to parse upstream URL: %s", err)}
5067
}
5168
queryParams := cincinnatiURL.Query()
5269
queryParams.Add("arch", arch)
@@ -58,7 +75,7 @@ func (c Client) GetUpdates(upstream string, arch string, channel string, version
5875
// Download the update graph.
5976
req, err := http.NewRequest("GET", cincinnatiURL.String(), nil)
6077
if err != nil {
61-
return nil, err
78+
return nil, &Error{Reason: "InvalidRequest", Message: err.Error(), cause: err}
6279
}
6380
req.Header.Add("Accept", GraphMediaType)
6481
if c.tlsConfig != nil {
@@ -72,23 +89,23 @@ func (c Client) GetUpdates(upstream string, arch string, channel string, version
7289
client := http.Client{Transport: &transport}
7390
resp, err := client.Do(req)
7491
if err != nil {
75-
return nil, err
92+
return nil, &Error{Reason: "RemoteFailed", Message: err.Error(), cause: err}
7693
}
7794
defer resp.Body.Close()
7895

7996
if resp.StatusCode != http.StatusOK {
80-
return nil, fmt.Errorf("unexpected HTTP status: %s", resp.Status)
97+
return nil, &Error{Reason: "ResponseFailed", Message: fmt.Sprintf("unexpected HTTP status: %s", resp.Status)}
8198
}
8299

83100
// Parse the graph.
84101
body, err := ioutil.ReadAll(resp.Body)
85102
if err != nil {
86-
return nil, err
103+
return nil, &Error{Reason: "ResponseFailed", Message: err.Error(), cause: err}
87104
}
88105

89106
var graph graph
90107
if err = json.Unmarshal(body, &graph); err != nil {
91-
return nil, err
108+
return nil, &Error{Reason: "ResponseInvalid", Message: err.Error(), cause: err}
92109
}
93110

94111
// Find the current version within the graph.
@@ -102,7 +119,10 @@ func (c Client) GetUpdates(upstream string, arch string, channel string, version
102119
}
103120
}
104121
if !found {
105-
return nil, fmt.Errorf("currently installed version %s not found in the %q channel", version, channel)
122+
return nil, &Error{
123+
Reason: "VersionNotFound",
124+
Message: fmt.Sprintf("currently installed version %s not found in the %q channel", version, channel),
125+
}
106126
}
107127

108128
// Find the children of the current version.

pkg/cincinnati/cincinnati_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestGetUpdates(t *testing.T) {
4949
name: "unknown version",
5050
version: "4.0.0-3",
5151
expectedQuery: "arch=test-arch&channel=test-channel&id=01234567-0123-0123-0123-0123456789ab&version=4.0.0-3",
52-
err: "currently installed version 4.0.0-3 not found in the \"test-channel\" channel",
52+
err: "VersionNotFound: currently installed version 4.0.0-3 not found in the \"test-channel\" channel",
5353
}}
5454
for _, test := range tests {
5555
t.Run(test.name, func(t *testing.T) {

pkg/cvo/availableupdates.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ func calculateAvailableUpdatesStatus(clusterID string, proxyURL *url.URL, tlsCon
127127
}
128128
}
129129

130+
uuid, err := uuid.Parse(string(clusterID))
131+
if err != nil {
132+
return nil, configv1.ClusterOperatorStatusCondition{
133+
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "InvalidID",
134+
Message: fmt.Sprintf("invalid cluster ID: %s", err),
135+
}
136+
}
137+
130138
if len(arch) == 0 {
131139
return nil, configv1.ClusterOperatorStatusCondition{
132140
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "NoArchitecture",
@@ -157,12 +165,19 @@ func calculateAvailableUpdatesStatus(clusterID string, proxyURL *url.URL, tlsCon
157165
}
158166
}
159167

160-
updates, err := checkForUpdate(clusterID, proxyURL, tlsConfig, upstream, arch, channel, currentVersion)
168+
updates, err := cincinnati.NewClient(uuid, proxyURL, tlsConfig).GetUpdates(upstream, arch, channel, currentVersion)
161169
if err != nil {
162170
klog.V(2).Infof("Upstream server %s could not return available updates: %v", upstream, err)
171+
if updateError, ok := err.(*cincinnati.Error); ok {
172+
return nil, configv1.ClusterOperatorStatusCondition{
173+
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: updateError.Reason,
174+
Message: fmt.Sprintf("Unable to retrieve available updates: %s", updateError.Message),
175+
}
176+
}
177+
// this should never happen
163178
return nil, configv1.ClusterOperatorStatusCondition{
164-
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "RemoteFailed",
165-
Message: fmt.Sprintf("Unable to retrieve available updates: %v", err),
179+
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "Unknown",
180+
Message: fmt.Sprintf("Unable to retrieve available updates: %s", err),
166181
}
167182
}
168183

@@ -182,18 +197,6 @@ func calculateAvailableUpdatesStatus(clusterID string, proxyURL *url.URL, tlsCon
182197
}
183198
}
184199

185-
func checkForUpdate(clusterID string, proxyURL *url.URL, tlsConfig *tls.Config, upstream, arch, channel string, currentVersion semver.Version) ([]cincinnati.Update, error) {
186-
uuid, err := uuid.Parse(string(clusterID))
187-
if err != nil {
188-
return nil, err
189-
}
190-
191-
if len(upstream) == 0 {
192-
return nil, fmt.Errorf("no upstream URL set for cluster version")
193-
}
194-
return cincinnati.NewClient(uuid, proxyURL, tlsConfig).GetUpdates(upstream, arch, channel, currentVersion)
195-
}
196-
197200
// getHTTPSProxyURL returns a url.URL object for the configured
198201
// https proxy only. It can be nil if does not exist or there is an error.
199202
func (optr *Operator) getHTTPSProxyURL() (*url.URL, string, error) {

pkg/cvo/cvo_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2451,7 +2451,7 @@ func TestOperator_availableUpdatesSync(t *testing.T) {
24512451
Condition: configv1.ClusterOperatorStatusCondition{
24522452
Type: configv1.RetrievedUpdates,
24532453
Status: configv1.ConditionFalse,
2454-
Reason: "RemoteFailed",
2454+
Reason: "ResponseFailed",
24552455
Message: "Unable to retrieve available updates: unexpected HTTP status: 500 Internal Server Error",
24562456
},
24572457
},

0 commit comments

Comments
 (0)