Skip to content

Commit 8240a9b

Browse files
Merge pull request #268 from wking/get-updates-error-granularity
Bug 1685338: pkg/cvo: Reason granularity for RemoteFailed
2 parents ebb86aa + dca2686 commit 8240a9b

File tree

4 files changed

+64
-32
lines changed

4 files changed

+64
-32
lines changed

pkg/cincinnati/cincinnati.go

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,44 @@ 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
4057
// the current version within that graph (typically the root node), and then
4158
// finding all of the children. These children are the available updates for
4259
// the current version and their payloads indicate from where the actual update
4360
// image can be downloaded.
44-
func (c Client) GetUpdates(upstream string, arch string, channel string, version semver.Version) ([]Update, error) {
61+
func (c Client) GetUpdates(uri *url.URL, arch string, channel string, version semver.Version) ([]Update, error) {
4562
transport := http.Transport{}
4663
// Prepare parametrized cincinnati query.
47-
cincinnatiURL, err := url.Parse(upstream)
48-
if err != nil {
49-
return nil, fmt.Errorf("failed to parse upstream URL: %s", err)
50-
}
51-
queryParams := cincinnatiURL.Query()
64+
queryParams := uri.Query()
5265
queryParams.Add("arch", arch)
5366
queryParams.Add("channel", channel)
5467
queryParams.Add("id", c.id.String())
5568
queryParams.Add("version", version.String())
56-
cincinnatiURL.RawQuery = queryParams.Encode()
69+
uri.RawQuery = queryParams.Encode()
5770

5871
// Download the update graph.
59-
req, err := http.NewRequest("GET", cincinnatiURL.String(), nil)
72+
req, err := http.NewRequest("GET", uri.String(), nil)
6073
if err != nil {
61-
return nil, err
74+
return nil, &Error{Reason: "InvalidRequest", Message: err.Error(), cause: err}
6275
}
6376
req.Header.Add("Accept", GraphMediaType)
6477
if c.tlsConfig != nil {
@@ -72,23 +85,23 @@ func (c Client) GetUpdates(upstream string, arch string, channel string, version
7285
client := http.Client{Transport: &transport}
7386
resp, err := client.Do(req)
7487
if err != nil {
75-
return nil, err
88+
return nil, &Error{Reason: "RemoteFailed", Message: err.Error(), cause: err}
7689
}
7790
defer resp.Body.Close()
7891

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

8396
// Parse the graph.
8497
body, err := ioutil.ReadAll(resp.Body)
8598
if err != nil {
86-
return nil, err
99+
return nil, &Error{Reason: "ResponseFailed", Message: err.Error(), cause: err}
87100
}
88101

89102
var graph graph
90103
if err = json.Unmarshal(body, &graph); err != nil {
91-
return nil, err
104+
return nil, &Error{Reason: "ResponseInvalid", Message: err.Error(), cause: err}
92105
}
93106

94107
// Find the current version within the graph.
@@ -102,7 +115,10 @@ func (c Client) GetUpdates(upstream string, arch string, channel string, version
102115
}
103116
}
104117
if !found {
105-
return nil, fmt.Errorf("currently installed version %s not found in the %q channel", version, channel)
118+
return nil, &Error{
119+
Reason: "VersionNotFound",
120+
Message: fmt.Sprintf("currently installed version %s not found in the %q channel", version, channel),
121+
}
106122
}
107123

108124
// Find the children of the current version.

pkg/cincinnati/cincinnati_test.go

Lines changed: 7 additions & 2 deletions
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) {
@@ -127,7 +127,12 @@ func TestGetUpdates(t *testing.T) {
127127

128128
c := NewClient(clientID, proxyURL, tlsConfig)
129129

130-
updates, err := c.GetUpdates(ts.URL, arch, channelName, semver.MustParse(test.version))
130+
uri, err := url.Parse(ts.URL)
131+
if err != nil {
132+
t.Fatal(err)
133+
}
134+
135+
updates, err := c.GetUpdates(uri, arch, channelName, semver.MustParse(test.version))
131136
if test.err == "" {
132137
if err != nil {
133138
t.Fatalf("expected nil error, got: %v", err)

pkg/cvo/availableupdates.go

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

130+
upstreamURI, err := url.Parse(upstream)
131+
if err != nil {
132+
return nil, configv1.ClusterOperatorStatusCondition{
133+
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "InvalidURI",
134+
Message: fmt.Sprintf("failed to parse upstream URL: %s", err),
135+
}
136+
}
137+
138+
uuid, err := uuid.Parse(string(clusterID))
139+
if err != nil {
140+
return nil, configv1.ClusterOperatorStatusCondition{
141+
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "InvalidID",
142+
Message: fmt.Sprintf("invalid cluster ID: %s", err),
143+
}
144+
}
145+
130146
if len(arch) == 0 {
131147
return nil, configv1.ClusterOperatorStatusCondition{
132148
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "NoArchitecture",
@@ -157,12 +173,19 @@ func calculateAvailableUpdatesStatus(clusterID string, proxyURL *url.URL, tlsCon
157173
}
158174
}
159175

160-
updates, err := checkForUpdate(clusterID, proxyURL, tlsConfig, upstream, arch, channel, currentVersion)
176+
updates, err := cincinnati.NewClient(uuid, proxyURL, tlsConfig).GetUpdates(upstreamURI, arch, channel, currentVersion)
161177
if err != nil {
162178
klog.V(2).Infof("Upstream server %s could not return available updates: %v", upstream, err)
179+
if updateError, ok := err.(*cincinnati.Error); ok {
180+
return nil, configv1.ClusterOperatorStatusCondition{
181+
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: updateError.Reason,
182+
Message: fmt.Sprintf("Unable to retrieve available updates: %s", updateError.Message),
183+
}
184+
}
185+
// this should never happen
163186
return nil, configv1.ClusterOperatorStatusCondition{
164-
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "RemoteFailed",
165-
Message: fmt.Sprintf("Unable to retrieve available updates: %v", err),
187+
Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, Reason: "Unknown",
188+
Message: fmt.Sprintf("Unable to retrieve available updates: %s", err),
166189
}
167190
}
168191

@@ -182,18 +205,6 @@ func calculateAvailableUpdatesStatus(clusterID string, proxyURL *url.URL, tlsCon
182205
}
183206
}
184207

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-
197208
// getHTTPSProxyURL returns a url.URL object for the configured
198209
// https proxy only. It can be nil if does not exist or there is an error.
199210
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)