1 package azure
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import (
18 "bytes"
19 "context"
20 "encoding/json"
21 "fmt"
22 "io/ioutil"
23 "net/http"
24 "net/url"
25 "strings"
26 "time"
27
28 "github.com/Azure/go-autorest/autorest"
29 "github.com/Azure/go-autorest/logger"
30 "github.com/Azure/go-autorest/tracing"
31 )
32
33 const (
34 headerAsyncOperation = "Azure-AsyncOperation"
35 )
36
37 const (
38 operationInProgress string = "InProgress"
39 operationCanceled string = "Canceled"
40 operationFailed string = "Failed"
41 operationSucceeded string = "Succeeded"
42 )
43
44 var pollingCodes = [...]int{http.StatusNoContent, http.StatusAccepted, http.StatusCreated, http.StatusOK}
45
46
47 type FutureAPI interface {
48
49 Response() *http.Response
50
51
52 Status() string
53
54
55 PollingMethod() PollingMethodType
56
57
58 DoneWithContext(context.Context, autorest.Sender) (bool, error)
59
60
61
62
63
64 GetPollingDelay() (time.Duration, bool)
65
66
67
68
69
70
71
72
73
74 WaitForCompletionRef(context.Context, autorest.Client) error
75
76
77 MarshalJSON() ([]byte, error)
78
79
80 UnmarshalJSON([]byte) error
81
82
83 PollingURL() string
84
85
86
87 GetResult(autorest.Sender) (*http.Response, error)
88 }
89
90 var _ FutureAPI = (*Future)(nil)
91
92
93
94 type Future struct {
95 pt pollingTracker
96 }
97
98
99
100 func NewFutureFromResponse(resp *http.Response) (Future, error) {
101 pt, err := createPollingTracker(resp)
102 return Future{pt: pt}, err
103 }
104
105
106 func (f Future) Response() *http.Response {
107 if f.pt == nil {
108 return nil
109 }
110 return f.pt.latestResponse()
111 }
112
113
114 func (f Future) Status() string {
115 if f.pt == nil {
116 return ""
117 }
118 return f.pt.pollingStatus()
119 }
120
121
122 func (f Future) PollingMethod() PollingMethodType {
123 if f.pt == nil {
124 return PollingUnknown
125 }
126 return f.pt.pollingMethod()
127 }
128
129
130 func (f *Future) DoneWithContext(ctx context.Context, sender autorest.Sender) (done bool, err error) {
131 ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.DoneWithContext")
132 defer func() {
133 sc := -1
134 resp := f.Response()
135 if resp != nil {
136 sc = resp.StatusCode
137 }
138 tracing.EndSpan(ctx, sc, err)
139 }()
140
141 if f.pt == nil {
142 return false, autorest.NewError("Future", "Done", "future is not initialized")
143 }
144 if f.pt.hasTerminated() {
145 return true, f.pt.pollingError()
146 }
147 if err := f.pt.pollForStatus(ctx, sender); err != nil {
148 return false, err
149 }
150 if err := f.pt.checkForErrors(); err != nil {
151 return f.pt.hasTerminated(), err
152 }
153 if err := f.pt.updatePollingState(f.pt.provisioningStateApplicable()); err != nil {
154 return false, err
155 }
156 if err := f.pt.initPollingMethod(); err != nil {
157 return false, err
158 }
159 if err := f.pt.updatePollingMethod(); err != nil {
160 return false, err
161 }
162 return f.pt.hasTerminated(), f.pt.pollingError()
163 }
164
165
166
167
168
169 func (f Future) GetPollingDelay() (time.Duration, bool) {
170 if f.pt == nil {
171 return 0, false
172 }
173 resp := f.pt.latestResponse()
174 if resp == nil {
175 return 0, false
176 }
177
178 retry := resp.Header.Get(autorest.HeaderRetryAfter)
179 if retry == "" {
180 return 0, false
181 }
182
183 d, err := time.ParseDuration(retry + "s")
184 if err != nil {
185 panic(err)
186 }
187
188 return d, true
189 }
190
191
192
193
194
195
196
197
198
199 func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Client) (err error) {
200 ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.WaitForCompletionRef")
201 defer func() {
202 sc := -1
203 resp := f.Response()
204 if resp != nil {
205 sc = resp.StatusCode
206 }
207 tracing.EndSpan(ctx, sc, err)
208 }()
209 cancelCtx := ctx
210
211 _, hasDeadline := ctx.Deadline()
212 if d := client.PollingDuration; !hasDeadline && d != 0 {
213 var cancel context.CancelFunc
214 cancelCtx, cancel = context.WithTimeout(ctx, d)
215 defer cancel()
216 }
217
218 if delay, ok := f.GetPollingDelay(); ok {
219 logger.Instance.Writeln(logger.LogInfo, "WaitForCompletionRef: initial polling delay")
220 if delayElapsed := autorest.DelayForBackoff(delay, 0, cancelCtx.Done()); !delayElapsed {
221 err = cancelCtx.Err()
222 return
223 }
224 }
225 done, err := f.DoneWithContext(ctx, client)
226 for attempts := 0; !done; done, err = f.DoneWithContext(ctx, client) {
227 if attempts >= client.RetryAttempts {
228 return autorest.NewErrorWithError(err, "Future", "WaitForCompletion", f.pt.latestResponse(), "the number of retries has been exceeded")
229 }
230
231
232 var delayAttempt int
233 var delay time.Duration
234 if err == nil {
235
236 var ok bool
237 delay, ok = f.GetPollingDelay()
238 if !ok {
239 logger.Instance.Writeln(logger.LogInfo, "WaitForCompletionRef: Using client polling delay")
240 delay = client.PollingDelay
241 }
242 } else {
243
244
245
246 logger.Instance.Writef(logger.LogError, "WaitForCompletionRef: %s\n", err)
247 delayAttempt = attempts
248 delay = client.RetryDuration
249 attempts++
250 }
251
252 delayElapsed := autorest.DelayForBackoff(delay, delayAttempt, cancelCtx.Done())
253 if !delayElapsed {
254 return autorest.NewErrorWithError(cancelCtx.Err(), "Future", "WaitForCompletion", f.pt.latestResponse(), "context has been cancelled")
255 }
256 }
257 return
258 }
259
260
261 func (f Future) MarshalJSON() ([]byte, error) {
262 return json.Marshal(f.pt)
263 }
264
265
266 func (f *Future) UnmarshalJSON(data []byte) error {
267
268 obj := map[string]interface{}{}
269 err := json.Unmarshal(data, &obj)
270 if err != nil {
271 return err
272 }
273 if obj["method"] == nil {
274 return autorest.NewError("Future", "UnmarshalJSON", "missing 'method' property")
275 }
276 method := obj["method"].(string)
277 switch strings.ToUpper(method) {
278 case http.MethodDelete:
279 f.pt = &pollingTrackerDelete{}
280 case http.MethodPatch:
281 f.pt = &pollingTrackerPatch{}
282 case http.MethodPost:
283 f.pt = &pollingTrackerPost{}
284 case http.MethodPut:
285 f.pt = &pollingTrackerPut{}
286 default:
287 return autorest.NewError("Future", "UnmarshalJSON", "unsupoorted method '%s'", method)
288 }
289
290 return json.Unmarshal(data, &f.pt)
291 }
292
293
294 func (f Future) PollingURL() string {
295 if f.pt == nil {
296 return ""
297 }
298 return f.pt.pollingURL()
299 }
300
301
302
303 func (f Future) GetResult(sender autorest.Sender) (*http.Response, error) {
304 if f.pt.finalGetURL() == "" {
305
306
307
308 if lr := f.pt.latestResponse(); lr != nil && f.pt.hasSucceeded() {
309 return lr, nil
310 }
311 return nil, autorest.NewError("Future", "GetResult", "missing URL for retrieving result")
312 }
313 req, err := http.NewRequest(http.MethodGet, f.pt.finalGetURL(), nil)
314 if err != nil {
315 return nil, err
316 }
317 resp, err := sender.Do(req)
318 if err == nil && resp.Body != nil {
319
320 defer resp.Body.Close()
321 b, err := ioutil.ReadAll(resp.Body)
322 if err != nil {
323 return resp, err
324 }
325 resp.Body = ioutil.NopCloser(bytes.NewReader(b))
326 }
327 return resp, err
328 }
329
330 type pollingTracker interface {
331
332
333
334 updatePollingMethod() error
335
336
337 checkForErrors() error
338
339
340 provisioningStateApplicable() bool
341
342
343
344
345
346 initPollingMethod() error
347
348
349 initializeState() error
350
351
352 pollForStatus(ctx context.Context, sender autorest.Sender) error
353
354
355 updatePollingState(provStateApl bool) error
356
357
358 pollingError() error
359
360
361 pollingMethod() PollingMethodType
362
363
364 pollingStatus() string
365
366
367 pollingURL() string
368
369
370 finalGetURL() string
371
372
373 hasTerminated() bool
374
375
376 hasFailed() bool
377
378
379 hasSucceeded() bool
380
381
382 latestResponse() *http.Response
383 }
384
385 type pollingTrackerBase struct {
386
387 resp *http.Response
388
389
390 Method string `json:"method"`
391
392
393 rawBody map[string]interface{}
394
395
396 Pm PollingMethodType `json:"pollingMethod"`
397
398
399 URI string `json:"pollingURI"`
400
401
402 State string `json:"lroState"`
403
404
405 FinalGetURI string `json:"resultURI"`
406
407
408 Err *ServiceError `json:"error,omitempty"`
409 }
410
411 func (pt *pollingTrackerBase) initializeState() error {
412
413
414 pt.Method = pt.resp.Request.Method
415 if err := pt.updateRawBody(); err != nil {
416 return err
417 }
418 switch pt.resp.StatusCode {
419 case http.StatusOK:
420 if ps := pt.getProvisioningState(); ps != nil {
421 pt.State = *ps
422 if pt.hasFailed() {
423 pt.updateErrorFromResponse()
424 return pt.pollingError()
425 }
426 } else {
427 pt.State = operationSucceeded
428 }
429 case http.StatusCreated:
430 if ps := pt.getProvisioningState(); ps != nil {
431 pt.State = *ps
432 } else {
433 pt.State = operationInProgress
434 }
435 case http.StatusAccepted:
436 pt.State = operationInProgress
437 case http.StatusNoContent:
438 pt.State = operationSucceeded
439 default:
440 pt.State = operationFailed
441 pt.updateErrorFromResponse()
442 return pt.pollingError()
443 }
444 return pt.initPollingMethod()
445 }
446
447 func (pt pollingTrackerBase) getProvisioningState() *string {
448 if pt.rawBody != nil && pt.rawBody["properties"] != nil {
449 p := pt.rawBody["properties"].(map[string]interface{})
450 if ps := p["provisioningState"]; ps != nil {
451 s := ps.(string)
452 return &s
453 }
454 }
455 return nil
456 }
457
458 func (pt *pollingTrackerBase) updateRawBody() error {
459 pt.rawBody = map[string]interface{}{}
460 if pt.resp.ContentLength != 0 {
461 defer pt.resp.Body.Close()
462 b, err := ioutil.ReadAll(pt.resp.Body)
463 if err != nil {
464 return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to read response body")
465 }
466
467 pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
468
469 if len(b) == 0 {
470 return nil
471 }
472 if err = json.Unmarshal(b, &pt.rawBody); err != nil {
473 return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to unmarshal response body")
474 }
475 }
476 return nil
477 }
478
479 func (pt *pollingTrackerBase) pollForStatus(ctx context.Context, sender autorest.Sender) error {
480 req, err := http.NewRequest(http.MethodGet, pt.URI, nil)
481 if err != nil {
482 return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed to create HTTP request")
483 }
484
485 req = req.WithContext(ctx)
486 preparer := autorest.CreatePreparer(autorest.GetPrepareDecorators(ctx)...)
487 req, err = preparer.Prepare(req)
488 if err != nil {
489 return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed preparing HTTP request")
490 }
491 pt.resp, err = sender.Do(req)
492 if err != nil {
493 return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed to send HTTP request")
494 }
495 if autorest.ResponseHasStatusCode(pt.resp, pollingCodes[:]...) {
496
497 pt.Err = nil
498 err = pt.updateRawBody()
499 } else {
500
501 pt.updateErrorFromResponse()
502 err = pt.pollingError()
503 }
504 return err
505 }
506
507
508
509
510 func (pt *pollingTrackerBase) updateErrorFromResponse() {
511 var err error
512 if pt.resp.ContentLength != 0 {
513 type respErr struct {
514 ServiceError *ServiceError `json:"error"`
515 }
516 re := respErr{}
517 defer pt.resp.Body.Close()
518 var b []byte
519 if b, err = ioutil.ReadAll(pt.resp.Body); err != nil {
520 goto Default
521 }
522
523 pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
524 if len(b) == 0 {
525 goto Default
526 }
527 if err = json.Unmarshal(b, &re); err != nil {
528 goto Default
529 }
530
531 if re.ServiceError == nil {
532 err = json.Unmarshal(b, &re.ServiceError)
533 if err != nil {
534 goto Default
535 }
536 }
537
538
539 if re.ServiceError.Code != "" {
540 pt.Err = re.ServiceError
541 return
542 }
543 }
544 Default:
545 se := &ServiceError{
546 Code: pt.pollingStatus(),
547 Message: "The async operation failed.",
548 }
549 if err != nil {
550 se.InnerError = make(map[string]interface{})
551 se.InnerError["unmarshalError"] = err.Error()
552 }
553
554
555 if len(pt.rawBody) > 0 {
556 se.AdditionalInfo = []map[string]interface{}{
557 pt.rawBody,
558 }
559 }
560 pt.Err = se
561 }
562
563 func (pt *pollingTrackerBase) updatePollingState(provStateApl bool) error {
564 if pt.Pm == PollingAsyncOperation && pt.rawBody["status"] != nil {
565 pt.State = pt.rawBody["status"].(string)
566 } else {
567 if pt.resp.StatusCode == http.StatusAccepted {
568 pt.State = operationInProgress
569 } else if provStateApl {
570 if ps := pt.getProvisioningState(); ps != nil {
571 pt.State = *ps
572 } else {
573 pt.State = operationSucceeded
574 }
575 } else {
576 return autorest.NewError("pollingTrackerBase", "updatePollingState", "the response from the async operation has an invalid status code")
577 }
578 }
579
580 if pt.hasFailed() {
581 pt.updateErrorFromResponse()
582 }
583 return nil
584 }
585
586 func (pt pollingTrackerBase) pollingError() error {
587 if pt.Err == nil {
588 return nil
589 }
590 return pt.Err
591 }
592
593 func (pt pollingTrackerBase) pollingMethod() PollingMethodType {
594 return pt.Pm
595 }
596
597 func (pt pollingTrackerBase) pollingStatus() string {
598 return pt.State
599 }
600
601 func (pt pollingTrackerBase) pollingURL() string {
602 return pt.URI
603 }
604
605 func (pt pollingTrackerBase) finalGetURL() string {
606 return pt.FinalGetURI
607 }
608
609 func (pt pollingTrackerBase) hasTerminated() bool {
610 return strings.EqualFold(pt.State, operationCanceled) || strings.EqualFold(pt.State, operationFailed) || strings.EqualFold(pt.State, operationSucceeded)
611 }
612
613 func (pt pollingTrackerBase) hasFailed() bool {
614 return strings.EqualFold(pt.State, operationCanceled) || strings.EqualFold(pt.State, operationFailed)
615 }
616
617 func (pt pollingTrackerBase) hasSucceeded() bool {
618 return strings.EqualFold(pt.State, operationSucceeded)
619 }
620
621 func (pt pollingTrackerBase) latestResponse() *http.Response {
622 return pt.resp
623 }
624
625
626 func (pt pollingTrackerBase) baseCheckForErrors() error {
627
628 if pt.Pm == PollingAsyncOperation {
629 if pt.resp.Body == nil || pt.resp.ContentLength == 0 {
630 return autorest.NewError("pollingTrackerBase", "baseCheckForErrors", "for Azure-AsyncOperation response body cannot be nil")
631 }
632 if pt.rawBody["status"] == nil {
633 return autorest.NewError("pollingTrackerBase", "baseCheckForErrors", "missing status property in Azure-AsyncOperation response body")
634 }
635 }
636 return nil
637 }
638
639
640 func (pt *pollingTrackerBase) initPollingMethod() error {
641 if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
642 return err
643 } else if ao != "" {
644 pt.URI = ao
645 pt.Pm = PollingAsyncOperation
646 return nil
647 }
648 if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
649 return err
650 } else if lh != "" {
651 pt.URI = lh
652 pt.Pm = PollingLocation
653 return nil
654 }
655
656 return nil
657 }
658
659
660
661 type pollingTrackerDelete struct {
662 pollingTrackerBase
663 }
664
665 func (pt *pollingTrackerDelete) updatePollingMethod() error {
666
667 if pt.resp.StatusCode == http.StatusCreated {
668 if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
669 return err
670 } else if lh == "" {
671 return autorest.NewError("pollingTrackerDelete", "updateHeaders", "missing Location header in 201 response")
672 } else {
673 pt.URI = lh
674 }
675 pt.Pm = PollingLocation
676 pt.FinalGetURI = pt.URI
677 }
678
679 if pt.resp.StatusCode == http.StatusAccepted {
680 ao, err := getURLFromAsyncOpHeader(pt.resp)
681 if err != nil {
682 return err
683 } else if ao != "" {
684 pt.URI = ao
685 pt.Pm = PollingAsyncOperation
686 }
687
688
689 if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
690 return err
691 } else if lh != "" {
692 if ao == "" {
693 pt.URI = lh
694 pt.Pm = PollingLocation
695 }
696
697 pt.FinalGetURI = lh
698 }
699
700 if pt.URI == "" {
701 return autorest.NewError("pollingTrackerPost", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
702 }
703 }
704 return nil
705 }
706
707 func (pt pollingTrackerDelete) checkForErrors() error {
708 return pt.baseCheckForErrors()
709 }
710
711 func (pt pollingTrackerDelete) provisioningStateApplicable() bool {
712 return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusNoContent
713 }
714
715
716
717 type pollingTrackerPatch struct {
718 pollingTrackerBase
719 }
720
721 func (pt *pollingTrackerPatch) updatePollingMethod() error {
722
723 if pt.URI == "" {
724 pt.URI = pt.resp.Request.URL.String()
725 }
726 if pt.FinalGetURI == "" {
727 pt.FinalGetURI = pt.resp.Request.URL.String()
728 }
729 if pt.Pm == PollingUnknown {
730 pt.Pm = PollingRequestURI
731 }
732
733 if pt.resp.StatusCode == http.StatusCreated {
734 if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
735 return err
736 } else if ao != "" {
737 pt.URI = ao
738 pt.Pm = PollingAsyncOperation
739 }
740 }
741
742
743 if pt.resp.StatusCode == http.StatusAccepted {
744 ao, err := getURLFromAsyncOpHeader(pt.resp)
745 if err != nil {
746 return err
747 } else if ao != "" {
748 pt.URI = ao
749 pt.Pm = PollingAsyncOperation
750 }
751 if ao == "" {
752 if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
753 return err
754 } else if lh == "" {
755 return autorest.NewError("pollingTrackerPatch", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
756 } else {
757 pt.URI = lh
758 pt.Pm = PollingLocation
759 }
760 }
761 }
762 return nil
763 }
764
765 func (pt pollingTrackerPatch) checkForErrors() error {
766 return pt.baseCheckForErrors()
767 }
768
769 func (pt pollingTrackerPatch) provisioningStateApplicable() bool {
770 return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusCreated
771 }
772
773
774
775 type pollingTrackerPost struct {
776 pollingTrackerBase
777 }
778
779 func (pt *pollingTrackerPost) updatePollingMethod() error {
780
781 if pt.resp.StatusCode == http.StatusCreated {
782 if lh, err := getURLFromLocationHeader(pt.resp); err != nil {
783 return err
784 } else if lh == "" {
785 return autorest.NewError("pollingTrackerPost", "updateHeaders", "missing Location header in 201 response")
786 } else {
787 pt.URI = lh
788 pt.FinalGetURI = lh
789 pt.Pm = PollingLocation
790 }
791 }
792
793 if pt.resp.StatusCode == http.StatusAccepted {
794 ao, err := getURLFromAsyncOpHeader(pt.resp)
795 if err != nil {
796 return err
797 } else if ao != "" {
798 pt.URI = ao
799 pt.Pm = PollingAsyncOperation
800 }
801
802
803 if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
804 return err
805 } else if lh != "" {
806 if ao == "" {
807 pt.URI = lh
808 pt.Pm = PollingLocation
809 }
810
811 pt.FinalGetURI = lh
812 }
813
814 if pt.URI == "" {
815 return autorest.NewError("pollingTrackerPost", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
816 }
817 }
818 return nil
819 }
820
821 func (pt pollingTrackerPost) checkForErrors() error {
822 return pt.baseCheckForErrors()
823 }
824
825 func (pt pollingTrackerPost) provisioningStateApplicable() bool {
826 return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusNoContent
827 }
828
829
830
831 type pollingTrackerPut struct {
832 pollingTrackerBase
833 }
834
835 func (pt *pollingTrackerPut) updatePollingMethod() error {
836
837 if pt.URI == "" {
838 pt.URI = pt.resp.Request.URL.String()
839 }
840 if pt.FinalGetURI == "" {
841 pt.FinalGetURI = pt.resp.Request.URL.String()
842 }
843 if pt.Pm == PollingUnknown {
844 pt.Pm = PollingRequestURI
845 }
846
847 if pt.resp.StatusCode == http.StatusCreated {
848 if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil {
849 return err
850 } else if ao != "" {
851 pt.URI = ao
852 pt.Pm = PollingAsyncOperation
853 }
854 }
855
856 if pt.resp.StatusCode == http.StatusAccepted {
857 ao, err := getURLFromAsyncOpHeader(pt.resp)
858 if err != nil {
859 return err
860 } else if ao != "" {
861 pt.URI = ao
862 pt.Pm = PollingAsyncOperation
863 }
864
865
866 if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" {
867 return err
868 } else if lh != "" {
869 if ao == "" {
870 pt.URI = lh
871 pt.Pm = PollingLocation
872 }
873 }
874
875 if pt.URI == "" {
876 return autorest.NewError("pollingTrackerPut", "updateHeaders", "didn't get any suitable polling URLs in 202 response")
877 }
878 }
879 return nil
880 }
881
882 func (pt pollingTrackerPut) checkForErrors() error {
883 err := pt.baseCheckForErrors()
884 if err != nil {
885 return err
886 }
887
888 ao, err := getURLFromAsyncOpHeader(pt.resp)
889 if err != nil {
890 return err
891 }
892 lh, err := getURLFromLocationHeader(pt.resp)
893 if err != nil {
894 return err
895 }
896 if ao == "" && lh == "" && len(pt.rawBody) == 0 {
897 return autorest.NewError("pollingTrackerPut", "checkForErrors", "the response did not contain a body")
898 }
899 return nil
900 }
901
902 func (pt pollingTrackerPut) provisioningStateApplicable() bool {
903 return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusCreated
904 }
905
906
907 func createPollingTracker(resp *http.Response) (pollingTracker, error) {
908 var pt pollingTracker
909 switch strings.ToUpper(resp.Request.Method) {
910 case http.MethodDelete:
911 pt = &pollingTrackerDelete{pollingTrackerBase: pollingTrackerBase{resp: resp}}
912 case http.MethodPatch:
913 pt = &pollingTrackerPatch{pollingTrackerBase: pollingTrackerBase{resp: resp}}
914 case http.MethodPost:
915 pt = &pollingTrackerPost{pollingTrackerBase: pollingTrackerBase{resp: resp}}
916 case http.MethodPut:
917 pt = &pollingTrackerPut{pollingTrackerBase: pollingTrackerBase{resp: resp}}
918 default:
919 return nil, autorest.NewError("azure", "createPollingTracker", "unsupported HTTP method %s", resp.Request.Method)
920 }
921 if err := pt.initializeState(); err != nil {
922 return pt, err
923 }
924
925
926
927 return pt, pt.updatePollingMethod()
928 }
929
930
931
932 func getURLFromAsyncOpHeader(resp *http.Response) (string, error) {
933 s := resp.Header.Get(http.CanonicalHeaderKey(headerAsyncOperation))
934 if s == "" {
935 return "", nil
936 }
937 if !isValidURL(s) {
938 return "", autorest.NewError("azure", "getURLFromAsyncOpHeader", "invalid polling URL '%s'", s)
939 }
940 return s, nil
941 }
942
943
944
945 func getURLFromLocationHeader(resp *http.Response) (string, error) {
946 s := resp.Header.Get(http.CanonicalHeaderKey(autorest.HeaderLocation))
947 if s == "" {
948 return "", nil
949 }
950 if !isValidURL(s) {
951 return "", autorest.NewError("azure", "getURLFromLocationHeader", "invalid polling URL '%s'", s)
952 }
953 return s, nil
954 }
955
956
957 func isValidURL(s string) bool {
958 u, err := url.Parse(s)
959 return err == nil && u.IsAbs()
960 }
961
962
963 type PollingMethodType string
964
965 const (
966
967 PollingAsyncOperation PollingMethodType = "AsyncOperation"
968
969
970 PollingLocation PollingMethodType = "Location"
971
972
973 PollingRequestURI PollingMethodType = "RequestURI"
974
975
976 PollingUnknown PollingMethodType = ""
977 )
978
979
980 type AsyncOpIncompleteError struct {
981
982 FutureType string
983 }
984
985
986 func (e AsyncOpIncompleteError) Error() string {
987 return fmt.Sprintf("%s: asynchronous operation has not completed", e.FutureType)
988 }
989
990
991 func NewAsyncOpIncompleteError(futureType string) AsyncOpIncompleteError {
992 return AsyncOpIncompleteError{
993 FutureType: futureType,
994 }
995 }
996
View as plain text