1// Copyright 2017 Microsoft Corporation 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package azure 16 17import ( 18 "context" 19 "net/http" 20 "sync" 21 "testing" 22 "time" 23 24 "github.com/Azure/go-autorest/autorest" 25 "github.com/Azure/go-autorest/autorest/mocks" 26) 27 28func TestDoRetryWithRegistration(t *testing.T) { 29 client := mocks.NewSender() 30 // first response, should retry because it is a transient error 31 client.AppendResponse(mocks.NewResponseWithStatus("Internal server error", http.StatusInternalServerError)) 32 // response indicates the resource provider has not been registered 33 client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{ 34 "error":{ 35 "code":"MissingSubscriptionRegistration", 36 "message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions.", 37 "details":[ 38 { 39 "code":"MissingSubscriptionRegistration", 40 "target":"Microsoft.EventGrid", 41 "message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions." 42 } 43 ] 44 } 45} 46`), http.StatusConflict, "MissingSubscriptionRegistration")) 47 // first poll response, still not ready 48 client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{ 49 "registrationState": "Registering" 50} 51`), http.StatusOK, "200 OK")) 52 // last poll response, respurce provider has been registered 53 client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{ 54 "registrationState": "Registered" 55} 56`), http.StatusOK, "200 OK")) 57 // retry original request, response is successful 58 client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK)) 59 60 req := mocks.NewRequestForURL("https://lol/subscriptions/rofl") 61 req.Body = mocks.NewBody("lolol") 62 r, err := autorest.SendWithSender(client, req, 63 DoRetryWithRegistration(autorest.Client{ 64 PollingDelay: time.Second, 65 PollingDuration: time.Second * 10, 66 RetryAttempts: 5, 67 RetryDuration: time.Second, 68 Sender: client, 69 }), 70 ) 71 if err != nil { 72 t.Fatalf("got error: %v", err) 73 } 74 75 autorest.Respond(r, 76 autorest.ByDiscardingBody(), 77 autorest.ByClosing(), 78 ) 79 80 if r.StatusCode != http.StatusOK { 81 t.Fatalf("azure: Sender#DoRetryWithRegistration -- Got: StatusCode %v; Want: StatusCode 200 OK", r.StatusCode) 82 } 83} 84 85func TestDoRetrySkipRegistration(t *testing.T) { 86 client := mocks.NewSender() 87 // first response, should retry because it is a transient error 88 client.AppendResponse(mocks.NewResponseWithStatus("Internal server error", http.StatusInternalServerError)) 89 // response indicates the resource provider has not been registered 90 client.AppendResponse(mocks.NewResponseWithBodyAndStatus(mocks.NewBody(`{ 91 "error":{ 92 "code":"MissingSubscriptionRegistration", 93 "message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions.", 94 "details":[ 95 { 96 "code":"MissingSubscriptionRegistration", 97 "target":"Microsoft.EventGrid", 98 "message":"The subscription registration is in 'Unregistered' state. The subscription must be registered to use namespace 'Microsoft.EventGrid'. See https://aka.ms/rps-not-found for how to register subscriptions." 99 } 100 ] 101 } 102}`), http.StatusConflict, "MissingSubscriptionRegistration")) 103 104 req := mocks.NewRequestForURL("https://lol/subscriptions/rofl") 105 req.Body = mocks.NewBody("lolol") 106 r, err := autorest.SendWithSender(client, req, 107 DoRetryWithRegistration(autorest.Client{ 108 PollingDelay: time.Second, 109 PollingDuration: time.Second * 10, 110 RetryAttempts: 5, 111 RetryDuration: time.Second, 112 Sender: client, 113 SkipResourceProviderRegistration: true, 114 }), 115 ) 116 if err != nil { 117 t.Fatalf("got error: %v", err) 118 } 119 120 autorest.Respond(r, 121 autorest.ByDiscardingBody(), 122 autorest.ByClosing(), 123 ) 124 125 if r.StatusCode != http.StatusConflict { 126 t.Fatalf("azure: Sender#DoRetryWithRegistration -- Got: StatusCode %v; Want: StatusCode 409 Conflict", r.StatusCode) 127 } 128} 129 130func TestDoRetryWithRegistration_CanBeCancelled(t *testing.T) { 131 ctx, cancel := context.WithCancel(context.Background()) 132 delay := 5 * time.Second 133 134 client := mocks.NewSender() 135 client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("Internal server error", http.StatusInternalServerError), 5) 136 137 var wg sync.WaitGroup 138 wg.Add(1) 139 start := time.Now() 140 end := time.Now() 141 var err error 142 143 go func() { 144 req := mocks.NewRequestForURL("https://lol/subscriptions/rofl") 145 req = req.WithContext(ctx) 146 req.Body = mocks.NewBody("lolol") 147 _, err = autorest.SendWithSender(client, req, 148 DoRetryWithRegistration(autorest.Client{ 149 PollingDelay: time.Second, 150 PollingDuration: delay, 151 RetryAttempts: 5, 152 RetryDuration: time.Second, 153 Sender: client, 154 SkipResourceProviderRegistration: true, 155 }), 156 ) 157 end = time.Now() 158 wg.Done() 159 }() 160 cancel() 161 wg.Wait() 162 time.Sleep(5 * time.Millisecond) 163 if err == nil { 164 t.Fatalf("azure: DoRetryWithRegistration didn't cancel") 165 } 166 if end.Sub(start) >= delay { 167 t.Fatalf("azure: DoRetryWithRegistration failed to cancel") 168 } 169} 170