1//go:build go1.16 2// +build go1.16 3 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// Licensed under the MIT License. 6 7package loc 8 9import ( 10 "errors" 11 "fmt" 12 "net/http" 13 14 armpollers "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/pollers" 15 "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers" 16 "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared" 17 "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" 18 "github.com/Azure/azure-sdk-for-go/sdk/internal/log" 19) 20 21// Kind is the identifier of this type in a resume token. 22const Kind = "ARM-Location" 23 24// Applicable returns true if the LRO is using Location. 25func Applicable(resp *http.Response) bool { 26 return resp.StatusCode == http.StatusAccepted && resp.Header.Get(shared.HeaderLocation) != "" 27} 28 29// Poller is an LRO poller that uses the Location pattern. 30type Poller struct { 31 // The poller's type, used for resume token processing. 32 Type string `json:"type"` 33 34 // The URL for polling. 35 PollURL string `json:"pollURL"` 36 37 // The LRO's current state. 38 CurState string `json:"state"` 39} 40 41// New creates a new Poller from the provided initial response. 42func New(resp *http.Response, pollerID string) (*Poller, error) { 43 log.Write(log.LongRunningOperation, "Using Location poller.") 44 locURL := resp.Header.Get(shared.HeaderLocation) 45 if locURL == "" { 46 return nil, errors.New("response is missing Location header") 47 } 48 if !pollers.IsValidURL(locURL) { 49 return nil, fmt.Errorf("invalid polling URL %s", locURL) 50 } 51 p := &Poller{ 52 Type: pollers.MakeID(pollerID, Kind), 53 PollURL: locURL, 54 CurState: pollers.StatusInProgress, 55 } 56 return p, nil 57} 58 59// URL returns the polling URL. 60func (p *Poller) URL() string { 61 return p.PollURL 62} 63 64// Done returns true if the LRO has reached a terminal state. 65func (p *Poller) Done() bool { 66 return pollers.IsTerminalState(p.Status()) 67} 68 69// Update updates the Poller from the polling response. 70func (p *Poller) Update(resp *http.Response) error { 71 // location polling can return an updated polling URL 72 if h := resp.Header.Get(shared.HeaderLocation); h != "" { 73 p.PollURL = h 74 } 75 if runtime.HasStatusCode(resp, http.StatusOK, http.StatusCreated) { 76 // if a 200/201 returns a provisioning state, use that instead 77 state, err := armpollers.GetProvisioningState(resp) 78 if err != nil && !errors.Is(err, shared.ErrNoBody) { 79 return err 80 } 81 if state != "" { 82 p.CurState = state 83 } else { 84 // a 200/201 with no provisioning state indicates success 85 p.CurState = pollers.StatusSucceeded 86 } 87 } else if resp.StatusCode == http.StatusNoContent { 88 p.CurState = pollers.StatusSucceeded 89 } else if resp.StatusCode > 399 && resp.StatusCode < 500 { 90 p.CurState = pollers.StatusFailed 91 } 92 // a 202 falls through, means the LRO is still in progress and we don't check for provisioning state 93 return nil 94} 95 96// FinalGetURL returns the empty string as no final GET is required for this poller type. 97func (p *Poller) FinalGetURL() string { 98 return "" 99} 100 101// Status returns the status of the LRO. 102func (p *Poller) Status() string { 103 return p.CurState 104} 105