Skip to content

Commit 86ffba2

Browse files
author
Matthew Barnes
committed
backend: Include status body in POST notification to ARM
Async Operation Callbacks protocol requires sending a status payload in the request body of the notification callback. https://eng.ms/docs/products/arm/api_contracts/asyncoperationcallback#callback-request-body
1 parent 33565f9 commit 86ffba2

File tree

2 files changed

+42
-6
lines changed

2 files changed

+42
-6
lines changed

backend/operations_scanner.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package main
44
// Licensed under the Apache License 2.0.
55

66
import (
7+
"bytes"
78
"context"
89
"errors"
910
"fmt"
@@ -357,7 +358,7 @@ func (s *OperationsScanner) updateOperationStatus(ctx context.Context, op operat
357358
// operation if the initial request included an "Azure-AsyncNotificationUri" header.
358359
func (s *OperationsScanner) maybePostAsyncNotification(ctx context.Context, op operation) {
359360
if len(op.doc.NotificationURI) > 0 {
360-
err := s.postAsyncNotification(ctx, op.doc.NotificationURI)
361+
err := s.postAsyncNotification(ctx, op.id)
361362
if err == nil {
362363
op.logger.Info("Posted async notification")
363364
} else {
@@ -366,13 +367,26 @@ func (s *OperationsScanner) maybePostAsyncNotification(ctx context.Context, op o
366367
}
367368
}
368369

369-
// postAsyncNotification submits an empty POST request to the given URL.
370-
func (s *OperationsScanner) postAsyncNotification(ctx context.Context, url string) error {
371-
request, err := http.NewRequestWithContext(ctx, http.MethodPost, url, nil)
370+
// postAsyncNotification submits an POST request with status payload to the given URL.
371+
func (s *OperationsScanner) postAsyncNotification(ctx context.Context, operationID string) error {
372+
// Refetch the operation document to provide the latest status.
373+
doc, err := s.dbClient.GetOperationDoc(ctx, operationID)
372374
if err != nil {
373375
return err
374376
}
375377

378+
data, err := arm.Marshal(doc.ToStatus())
379+
if err != nil {
380+
return err
381+
}
382+
383+
request, err := http.NewRequestWithContext(ctx, http.MethodPost, doc.NotificationURI, bytes.NewBuffer(data))
384+
if err != nil {
385+
return err
386+
}
387+
388+
request.Header.Set("Content-Type", "application/json")
389+
376390
response, err := s.notificationClient.Do(request)
377391
if err != nil {
378392
return err

backend/operations_scanner_test.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ func TestSetDeleteOperationAsCompleted(t *testing.T) {
7070
t.Fatal(err)
7171
}
7272

73+
operationID, err := azcorearm.ParseResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.RedHatOpenShift/locations/oz/hcpOperationsStatus/operationID")
74+
if err != nil {
75+
t.Fatal(err)
76+
}
77+
7378
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
7479
if r.Method == http.MethodPost {
7580
request = r
@@ -83,11 +88,12 @@ func TestSetDeleteOperationAsCompleted(t *testing.T) {
8388
}
8489

8590
operationDoc := database.NewOperationDocument(database.OperationRequestDelete, resourceID, internalID)
91+
operationDoc.OperationID = operationID
8692
operationDoc.NotificationURI = server.URL
8793
operationDoc.Status = tt.operationStatus
8894

8995
op := operation{
90-
id: "this operation",
96+
id: operationID.Name,
9197
doc: operationDoc,
9298
logger: slog.Default(),
9399
}
@@ -104,6 +110,11 @@ func TestSetDeleteOperationAsCompleted(t *testing.T) {
104110
DoAndReturn(func(ctx context.Context, operationID string, callback func(*database.OperationDocument) bool) (bool, error) {
105111
return callback(operationDoc), nil
106112
})
113+
if tt.expectAsyncNotification {
114+
mockDBClient.EXPECT().
115+
GetOperationDoc(gomock.Any(), op.id).
116+
Return(operationDoc, nil)
117+
}
107118

108119
err = scanner.setDeleteOperationAsCompleted(ctx, op)
109120

@@ -224,6 +235,11 @@ func TestUpdateOperationStatus(t *testing.T) {
224235
t.Fatal(err)
225236
}
226237

238+
operationID, err := azcorearm.ParseResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.RedHatOpenShift/locations/oz/hcpOperationsStatus/operationID")
239+
if err != nil {
240+
t.Fatal(err)
241+
}
242+
227243
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
228244
if r.Method == http.MethodPost {
229245
request = r
@@ -237,11 +253,12 @@ func TestUpdateOperationStatus(t *testing.T) {
237253
}
238254

239255
operationDoc := database.NewOperationDocument(database.OperationRequestCreate, resourceID, internalID)
256+
operationDoc.OperationID = operationID
240257
operationDoc.NotificationURI = server.URL
241258
operationDoc.Status = tt.currentOperationStatus
242259

243260
op := operation{
244-
id: "this operation",
261+
id: operationID.Name,
245262
doc: operationDoc,
246263
logger: slog.Default(),
247264
}
@@ -272,6 +289,11 @@ func TestUpdateOperationStatus(t *testing.T) {
272289
return false, database.ErrNotFound
273290
}
274291
})
292+
if tt.expectAsyncNotification {
293+
mockDBClient.EXPECT().
294+
GetOperationDoc(gomock.Any(), op.id).
295+
Return(operationDoc, nil)
296+
}
275297

276298
err = scanner.updateOperationStatus(ctx, op, tt.updatedOperationStatus, nil)
277299

0 commit comments

Comments
 (0)