Skip to content
This repository was archived by the owner on Oct 12, 2023. It is now read-only.

Commit d0f6cc3

Browse files
Return the http status code information in the error text for the "Managers" (#258)
The various manager APIs all had their own error handling code, which would result in them occasionally obscuring the actual HTTP response error. We're now respecting the HTTP status codes and anything >= 400 causes us to fail with an actual error with text, rather than an empty error. Fixes #229
1 parent 4974e2f commit d0f6cc3

File tree

4 files changed

+170
-34
lines changed

4 files changed

+170
-34
lines changed

queue_manager.go

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"encoding/xml"
66
"errors"
77
"io/ioutil"
8-
"net/http"
98
"strings"
109
"time"
1110

@@ -262,7 +261,7 @@ func (qm *QueueManager) Delete(ctx context.Context, name string) error {
262261
res, err := qm.entityManager.Delete(ctx, "/"+name)
263262
defer closeRes(ctx, res)
264263

265-
return err
264+
return checkForError(ctx, err, res)
266265
}
267266

268267
// Put creates or updates a Service Bus Queue
@@ -309,8 +308,7 @@ func (qm *QueueManager) Put(ctx context.Context, name string, opts ...QueueManag
309308
res, err := qm.entityManager.Put(ctx, "/"+name, reqBytes, mw...)
310309
defer closeRes(ctx, res)
311310

312-
if err != nil {
313-
tab.For(ctx).Error(err)
311+
if err := checkForError(ctx, err, res); err != nil {
314312
return nil, err
315313
}
316314

@@ -346,8 +344,7 @@ func (qm *QueueManager) List(ctx context.Context, options ...ListQueuesOption) (
346344
res, err := qm.entityManager.Get(ctx, basePath)
347345
defer closeRes(ctx, res)
348346

349-
if err != nil {
350-
tab.For(ctx).Error(err)
347+
if err := checkForError(ctx, err, res); err != nil {
351348
return nil, err
352349
}
353350

@@ -378,15 +375,10 @@ func (qm *QueueManager) Get(ctx context.Context, name string) (*QueueEntity, err
378375
res, err := qm.entityManager.Get(ctx, name)
379376
defer closeRes(ctx, res)
380377

381-
if err != nil {
382-
tab.For(ctx).Error(err)
378+
if err := checkForError(ctx, err, res); err != nil {
383379
return nil, err
384380
}
385381

386-
if res.StatusCode == http.StatusNotFound {
387-
return nil, ErrNotFound{EntityPath: res.Request.URL.Path}
388-
}
389-
390382
b, err := ioutil.ReadAll(res.Body)
391383
if err != nil {
392384
tab.For(ctx).Error(err)

subscription_manager.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ func (sm *SubscriptionManager) Delete(ctx context.Context, name string) error {
213213
res, err := sm.entityManager.Delete(ctx, sm.getResourceURI(name))
214214
defer closeRes(ctx, res)
215215

216-
return err
216+
return checkForError(ctx, err, res)
217217
}
218218

219219
// Put creates or updates a Service Bus Topic
@@ -260,7 +260,7 @@ func (sm *SubscriptionManager) Put(ctx context.Context, name string, opts ...Sub
260260
res, err := sm.entityManager.Put(ctx, sm.getResourceURI(name), reqBytes, mw...)
261261
defer closeRes(ctx, res)
262262

263-
if err != nil {
263+
if err := checkForError(ctx, err, res); err != nil {
264264
return nil, err
265265
}
266266

@@ -295,7 +295,7 @@ func (sm *SubscriptionManager) List(ctx context.Context, options ...ListSubscrip
295295
res, err := sm.entityManager.Get(ctx, basePath)
296296
defer closeRes(ctx, res)
297297

298-
if err != nil {
298+
if err := checkForError(ctx, err, res); err != nil {
299299
return nil, err
300300
}
301301

@@ -325,14 +325,10 @@ func (sm *SubscriptionManager) Get(ctx context.Context, name string) (*Subscript
325325
res, err := sm.entityManager.Get(ctx, sm.getResourceURI(name))
326326
defer closeRes(ctx, res)
327327

328-
if err != nil {
328+
if err := checkForError(ctx, err, res); err != nil {
329329
return nil, err
330330
}
331331

332-
if res.StatusCode == http.StatusNotFound {
333-
return nil, ErrNotFound{EntityPath: res.Request.URL.Path}
334-
}
335-
336332
b, err := ioutil.ReadAll(res.Body)
337333
if err != nil {
338334
return nil, err
@@ -361,14 +357,10 @@ func (sm *SubscriptionManager) ListRules(ctx context.Context, subscriptionName s
361357
res, err := sm.entityManager.Get(ctx, sm.getRulesResourceURI(subscriptionName))
362358
defer closeRes(ctx, res)
363359

364-
if err != nil {
360+
if err := checkForError(ctx, err, res); err != nil {
365361
return nil, err
366362
}
367363

368-
if res.StatusCode == http.StatusNotFound {
369-
return nil, ErrNotFound{EntityPath: res.Request.URL.Path}
370-
}
371-
372364
b, err := ioutil.ReadAll(res.Body)
373365
if err != nil {
374366
return nil, err
@@ -473,7 +465,7 @@ func (sm *SubscriptionManager) DeleteRule(ctx context.Context, subscriptionName,
473465
res, err := sm.entityManager.Delete(ctx, sm.getRuleResourceURI(subscriptionName, ruleName))
474466
defer closeRes(ctx, res)
475467

476-
return err
468+
return checkForError(ctx, err, res)
477469
}
478470

479471
func ruleEntryToEntity(entry *ruleEntry) *RuleEntity {

subscription_test.go

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ import (
2626
"context"
2727
"encoding/xml"
2828
"fmt"
29+
"os"
2930
"strings"
3031
"sync"
3132
"testing"
3233
"time"
3334

3435
"github.com/Azure/azure-amqp-common-go/v3/uuid"
3536
"github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2015-08-01/servicebus"
37+
"github.com/joho/godotenv"
3638
"github.com/stretchr/testify/assert"
3739
"github.com/stretchr/testify/require"
3840

@@ -458,7 +460,12 @@ func (suite *serviceBusSuite) testSubscriptionManager(tests map[string]func(cont
458460
defer func(sName string) {
459461
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
460462
defer cancel()
461-
if !suite.NoError(sm.Delete(ctx, sName)) {
463+
464+
err = sm.Delete(ctx, sName)
465+
466+
if !IsErrNotFound(err) && !suite.NoError(err) {
467+
// not all tests actually create a subscription (some of these tests are
468+
// basically unittests)
462469
suite.Fail(err.Error())
463470
}
464471
}(subName)
@@ -785,3 +792,130 @@ func checkZeroSubscriptionMessages(ctx context.Context, t *testing.T, topic *Top
785792

786793
assert.Fail(t, "message count never reached zero")
787794
}
795+
796+
func TestErrorMessagesWithMissingPrivileges(t *testing.T) {
797+
_ = godotenv.Load()
798+
799+
// we're obscuring the HTTP errors coming back from the service and
800+
// we shouldn't. Just testing some of the common scenarios with a
801+
// connection string that lacks Manage privileges.
802+
lowPrivCS := os.Getenv("SERVICEBUS_CONNECTION_STRING_NO_MANAGE")
803+
normalCS := os.Getenv("SERVICEBUS_CONNECTION_STRING")
804+
805+
if lowPrivCS == "" || normalCS == "" {
806+
t.Skip("Need both SERVICEBUS_CONNECTION_STRING_NO_MANAGE and SERVICEBUS_CONNECTION_STRING")
807+
}
808+
809+
nanoSeconds := time.Now().UnixNano()
810+
811+
topicName := fmt.Sprintf("topic-%d", nanoSeconds)
812+
queueName := fmt.Sprintf("queue-%d", nanoSeconds)
813+
subName := "subscription1"
814+
ruleName := "rule"
815+
816+
// create some entities that we need (there's a diff between something not being
817+
// found and something failing because of lack of authorization)
818+
cleanup := func() func() {
819+
ns, err := NewNamespace(NamespaceWithConnectionString(normalCS))
820+
qm := ns.NewQueueManager()
821+
822+
_, err = qm.Put(context.Background(), queueName)
823+
require.NoError(t, err)
824+
825+
tm := ns.NewTopicManager()
826+
_, err = tm.Put(context.Background(), topicName)
827+
require.NoError(t, err)
828+
829+
sm, err := ns.NewSubscriptionManager(topicName)
830+
require.NoError(t, err)
831+
832+
_, err = sm.Put(context.Background(), subName)
833+
require.NoError(t, err)
834+
835+
_, err = sm.PutRule(context.Background(), subName, ruleName, TrueFilter{})
836+
require.NoError(t, err)
837+
838+
return func() {
839+
require.NoError(t, tm.Delete(context.Background(), topicName)) // should delete the subscription
840+
require.NoError(t, qm.Delete(context.Background(), queueName))
841+
}
842+
}()
843+
defer cleanup()
844+
845+
ns, err := NewNamespace(NamespaceWithConnectionString(lowPrivCS))
846+
require.NoError(t, err)
847+
848+
ctx := context.Background()
849+
wg := sync.WaitGroup{}
850+
wg.Add(3)
851+
852+
go func() {
853+
defer wg.Done()
854+
855+
qm := ns.NewQueueManager()
856+
857+
_, err = qm.Get(ctx, "not-found-queue")
858+
require.True(t, IsErrNotFound(err))
859+
860+
_, err = qm.Get(ctx, queueName)
861+
require.EqualError(t, err, "request failed: 401 Unauthorized")
862+
863+
_, err = qm.List(ctx)
864+
require.EqualError(t, err, "request failed: 401 Unauthorized")
865+
866+
_, err = qm.Put(ctx, "canneverbecreated")
867+
require.EqualError(t, err, "request failed: 401 Unauthorized")
868+
869+
err = qm.Delete(ctx, queueName)
870+
require.EqualError(t, err, "request failed: 401 Unauthorized")
871+
}()
872+
873+
go func() {
874+
defer wg.Done()
875+
876+
tm := ns.NewTopicManager()
877+
878+
_, err = tm.Get(ctx, "not-found-topic")
879+
require.True(t, IsErrNotFound(err))
880+
881+
_, err = tm.Get(ctx, topicName)
882+
require.EqualError(t, err, "request failed: 401 Unauthorized")
883+
884+
_, err = tm.Put(ctx, "canneverbecreated")
885+
require.Contains(t, err.Error(), "error code: 401, Details: Authorization failed for specified action")
886+
887+
_, err = tm.List(ctx)
888+
require.Contains(t, err.Error(), "error code: 401, Details: Manage,EntityRead claims required for this operation")
889+
890+
err = tm.Delete(ctx, topicName)
891+
require.Contains(t, err.Error(), "request failed: 401 Unauthorized")
892+
}()
893+
894+
go func() {
895+
defer wg.Done()
896+
897+
sm, err := ns.NewSubscriptionManager(topicName)
898+
require.NoError(t, err)
899+
900+
_, err = sm.Get(ctx, "not-found-subscription")
901+
require.Contains(t, err.Error(), "request failed: 401 SubCode=40100: Unauthorized")
902+
903+
_, err = sm.Get(ctx, subName)
904+
require.Contains(t, err.Error(), "request failed: 401 SubCode=40100: Unauthorized")
905+
906+
_, err = sm.Put(ctx, subName)
907+
require.Contains(t, err.Error(), "request failed: 401 SubCode=40100: Unauthorized")
908+
909+
err = sm.Delete(ctx, subName)
910+
require.Contains(t, err.Error(), "request failed: 401 SubCode=40100: Unauthorized")
911+
912+
_, err = sm.List(ctx)
913+
require.Contains(t, err.Error(), "request failed: 401 SubCode=40100: Unauthorized")
914+
915+
_, err = sm.ListRules(ctx, subName)
916+
require.Contains(t, err.Error(), "request failed: 401 SubCode=40100: Unauthorized")
917+
918+
}()
919+
920+
wg.Wait()
921+
}

topic_manager.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/xml"
66
"errors"
7+
"fmt"
78
"io/ioutil"
89
"net/http"
910
"time"
@@ -93,7 +94,7 @@ func (tm *TopicManager) Delete(ctx context.Context, name string) error {
9394
res, err := tm.entityManager.Delete(ctx, "/"+name)
9495
defer closeRes(ctx, res)
9596

96-
return err
97+
return checkForError(ctx, err, res)
9798
}
9899

99100
// Put creates or updates a Service Bus Topic
@@ -200,15 +201,10 @@ func (tm *TopicManager) Get(ctx context.Context, name string) (*TopicEntity, err
200201
res, err := tm.entityManager.Get(ctx, name)
201202
defer closeRes(ctx, res)
202203

203-
if err != nil {
204-
tab.For(ctx).Error(err)
204+
if err := checkForError(ctx, err, res); err != nil {
205205
return nil, err
206206
}
207207

208-
if res.StatusCode == http.StatusNotFound {
209-
return nil, ErrNotFound{EntityPath: res.Request.URL.Path}
210-
}
211-
212208
b, err := ioutil.ReadAll(res.Body)
213209
if err != nil {
214210
tab.For(ctx).Error(err)
@@ -322,3 +318,25 @@ func TopicWithMessageTimeToLive(window *time.Duration) TopicManagementOption {
322318
return nil
323319
}
324320
}
321+
322+
func checkForError(ctxForLogging context.Context, err error, res *http.Response) error {
323+
if err != nil {
324+
tab.For(ctxForLogging).Error(err)
325+
return err
326+
}
327+
328+
// check the response as well
329+
if res.StatusCode == http.StatusNotFound {
330+
err := ErrNotFound{EntityPath: res.Request.URL.Path}
331+
tab.For(ctxForLogging).Error(err)
332+
return err
333+
}
334+
335+
if res.StatusCode >= 400 {
336+
err := fmt.Errorf("request failed: %s", res.Status)
337+
tab.For(ctxForLogging).Error(err)
338+
return err
339+
}
340+
341+
return nil
342+
}

0 commit comments

Comments
 (0)