Skip to content

Commit e5d97bb

Browse files
authored
Add NotFound error handling to NMAgent client (#2163)
This PR makes some minor changes to the NMAgent client package as a part of a larger work item. This commit adds a NotFound method to the error returned by the NMAgent client. It also adds special handling to treat a 400 BadRequest as a NotFound when returned by the DeleteNetwork API call, since this case should be interpreted as a NotFound by the caller.
1 parent 35c6833 commit e5d97bb

File tree

3 files changed

+46
-14
lines changed

3 files changed

+46
-14
lines changed

nmagent/client.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (c *Client) JoinNetwork(ctx context.Context, jnr JoinNetworkRequest) error
6969
defer resp.Body.Close()
7070

7171
if resp.StatusCode != http.StatusOK {
72-
return die(resp.StatusCode, resp.Header, resp.Body)
72+
return die(resp.StatusCode, resp.Header, resp.Body, req.URL.Path)
7373
}
7474
return nil
7575
})
@@ -91,7 +91,7 @@ func (c *Client) DeleteNetwork(ctx context.Context, dnr DeleteNetworkRequest) er
9191
defer resp.Body.Close()
9292

9393
if resp.StatusCode != http.StatusOK {
94-
return die(resp.StatusCode, resp.Header, resp.Body)
94+
return die(resp.StatusCode, resp.Header, resp.Body, req.URL.Path)
9595
}
9696
return nil
9797
}
@@ -114,7 +114,7 @@ func (c *Client) GetNetworkConfiguration(ctx context.Context, gncr GetNetworkCon
114114
defer resp.Body.Close()
115115

116116
if resp.StatusCode != http.StatusOK {
117-
return die(resp.StatusCode, resp.Header, resp.Body)
117+
return die(resp.StatusCode, resp.Header, resp.Body, req.URL.Path)
118118
}
119119

120120
ct := resp.Header.Get(internal.HeaderContentType)
@@ -152,7 +152,7 @@ func (c *Client) GetNCVersion(ctx context.Context, ncvr NCVersionRequest) (NCVer
152152
defer resp.Body.Close()
153153

154154
if resp.StatusCode != http.StatusOK {
155-
return NCVersion{}, die(resp.StatusCode, resp.Header, resp.Body)
155+
return NCVersion{}, die(resp.StatusCode, resp.Header, resp.Body, req.URL.Path)
156156
}
157157

158158
var out NCVersion
@@ -179,7 +179,7 @@ func (c *Client) PutNetworkContainer(ctx context.Context, pncr *PutNetworkContai
179179
defer resp.Body.Close()
180180

181181
if resp.StatusCode != http.StatusOK {
182-
return die(resp.StatusCode, resp.Header, resp.Body)
182+
return die(resp.StatusCode, resp.Header, resp.Body, req.URL.Path)
183183
}
184184
return nil
185185
}
@@ -223,7 +223,7 @@ func (c *Client) DeleteNetworkContainer(ctx context.Context, dcr DeleteContainer
223223
defer resp.Body.Close()
224224

225225
if resp.StatusCode != http.StatusOK {
226-
return die(resp.StatusCode, resp.Header, resp.Body)
226+
return die(resp.StatusCode, resp.Header, resp.Body, req.URL.Path)
227227
}
228228

229229
return nil
@@ -242,7 +242,7 @@ func (c *Client) GetNCVersionList(ctx context.Context) (NCVersionList, error) {
242242
defer resp.Body.Close()
243243

244244
if resp.StatusCode != http.StatusOK {
245-
return NCVersionList{}, die(resp.StatusCode, resp.Header, resp.Body)
245+
return NCVersionList{}, die(resp.StatusCode, resp.Header, resp.Body, req.URL.Path)
246246
}
247247

248248
var out NCVersionList
@@ -270,7 +270,7 @@ func (c *Client) GetHomeAz(ctx context.Context) (AzResponse, error) {
270270
defer resp.Body.Close()
271271

272272
if resp.StatusCode != http.StatusOK {
273-
return homeAzResponse, die(resp.StatusCode, resp.Header, resp.Body)
273+
return homeAzResponse, die(resp.StatusCode, resp.Header, resp.Body, req.URL.Path)
274274
}
275275

276276
err = json.NewDecoder(resp.Body).Decode(&homeAzResponse)
@@ -281,7 +281,7 @@ func (c *Client) GetHomeAz(ctx context.Context) (AzResponse, error) {
281281
return homeAzResponse, nil
282282
}
283283

284-
func die(code int, headers http.Header, body io.ReadCloser) error {
284+
func die(code int, headers http.Header, body io.ReadCloser, path string) error {
285285
// nolint:errcheck // make a best effort to return whatever information we can
286286
// returning an error here without the code and source would
287287
// be less helpful
@@ -292,6 +292,7 @@ func die(code int, headers http.Header, body io.ReadCloser) error {
292292
// consumers to depend on an internal type (which they can't anyway)
293293
Source: internal.GetErrorSource(headers).String(),
294294
Body: bodyContent,
295+
Path: path,
295296
}
296297
}
297298

nmagent/client_test.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,32 +124,44 @@ func TestNMAgentClientJoinNetworkRetry(t *testing.T) {
124124

125125
func TestNMAgentClientDeleteNetwork(t *testing.T) {
126126
deleteNetTests := []struct {
127-
name string
128-
id string
129-
exp string
130-
respStatus int
131-
shouldErr bool
127+
name string
128+
id string
129+
exp string
130+
respStatus int
131+
shouldErr bool
132+
shouldNotFound bool
132133
}{
133134
{
134135
"happy path",
135136
"00000000-0000-0000-0000-000000000000",
136137
"/machine/plugins?comp=nmagent&type=NetworkManagement%2FjoinedVirtualNetworks%2F00000000-0000-0000-0000-000000000000%2Fapi-version%2F1%2Fmethod%2FDELETE",
137138
http.StatusOK,
138139
false,
140+
false,
139141
},
140142
{
141143
"empty network ID",
142144
"",
143145
"",
144146
http.StatusOK, // this shouldn't be checked
145147
true,
148+
false,
146149
},
147150
{
148151
"internal error",
149152
"00000000-0000-0000-0000-000000000000",
150153
"/machine/plugins?comp=nmagent&type=NetworkManagement%2FjoinedVirtualNetworks%2F00000000-0000-0000-0000-000000000000%2Fapi-version%2F1%2Fmethod%2FDELETE",
151154
http.StatusInternalServerError,
152155
true,
156+
false,
157+
},
158+
{
159+
"network does not exist",
160+
"00000000-0000-0000-0000-000000000000",
161+
"/machine/plugins?comp=nmagent&type=NetworkManagement%2FjoinedVirtualNetworks%2F00000000-0000-0000-0000-000000000000%2Fapi-version%2F1%2Fmethod%2FDELETE",
162+
http.StatusBadRequest,
163+
true,
164+
true,
153165
},
154166
}
155167

@@ -177,6 +189,12 @@ func TestNMAgentClientDeleteNetwork(t *testing.T) {
177189
err := client.DeleteNetwork(ctx, nmagent.DeleteNetworkRequest{test.id})
178190
checkErr(t, err, test.shouldErr)
179191

192+
var nmaError nmagent.Error
193+
errors.As(err, &nmaError)
194+
if nmaError.NotFound() != test.shouldNotFound {
195+
t.Error("unexpected NotFound value: got:", nmaError.NotFound(), "exp:", test.shouldNotFound)
196+
}
197+
180198
if got != test.exp {
181199
t.Error("received URL differs from expectation: got", got, "exp:", test.exp)
182200
}

nmagent/error.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import (
55
"fmt"
66
"io"
77
"net/http"
8+
"regexp"
89

910
"github.com/Azure/azure-container-networking/nmagent/internal"
1011
pkgerrors "github.com/pkg/errors"
1112
)
1213

14+
var deleteNetworkPattern = regexp.MustCompile(`/NetworkManagement/joinedVirtualNetworks/[^/]+/api-version/\d+/method/DELETE`)
15+
1316
// ContentError is encountered when an unexpected content type is obtained from
1417
// NMAgent.
1518
type ContentError struct {
@@ -52,6 +55,7 @@ type Error struct {
5255
Code int // the HTTP status code received
5356
Source string // the component responsible for producing the error
5457
Body []byte // the body of the error returned
58+
Path string // the path of the request that produced the error
5559
}
5660

5761
// Error constructs a string representation of this error in accordance with
@@ -103,3 +107,12 @@ func (e Error) StatusCode() int {
103107
func (e Error) Unauthorized() bool {
104108
return e.Code == http.StatusUnauthorized
105109
}
110+
111+
// NotFound reports whether the error was produced as a result of the resource
112+
// not existing.
113+
func (e Error) NotFound() bool {
114+
if deleteNetworkPattern.MatchString(e.Path) {
115+
return e.Code == http.StatusBadRequest || e.Code == http.StatusNotFound
116+
}
117+
return e.Code == http.StatusNotFound
118+
}

0 commit comments

Comments
 (0)