func newPollingRequest(resp *http.Response, ps pollingState) (*http.Request, error) { req := resp.Request if req == nil { return nil, autorest.NewError("azure", "newPollingRequest", "Azure Polling Error - Original HTTP request is missing") } reqPoll, err := autorest.Prepare(&http.Request{Cancel: req.Cancel}, autorest.AsGet(), autorest.WithBaseURL(ps.uri)) if err != nil { return nil, autorest.NewErrorWithError(err, "azure", "newPollingRequest", nil, "Failure creating poll request to %s", ps.uri) } return reqPoll, nil }
// updatePollingState maps the operation status -- retrieved from either a provisioningState // field, the status field of an OperationResource, or inferred from the HTTP status code -- // into a well-known states. Since the process begins from the initial request, the state // always comes from either a the provisioningState returned or is inferred from the HTTP // status code. Subsequent requests will read an Azure OperationResource object if the // service initially returned the Azure-AsyncOperation header. The responseFormat field notes // the expected response format. func updatePollingState(resp *http.Response, ps *pollingState) error { // Determine the response shape // -- The first response will always be a provisioningStatus response; only the polling requests, // depending on the header returned, may be something otherwise. var pt provisioningTracker if ps.responseFormat == usesOperationResponse { pt = &operationResource{} } else { pt = &provisioningStatus{} } // If this is the first request (that is, the polling response shape is unknown), determine how // to poll and what to expect if ps.responseFormat == formatIsUnknown { req := resp.Request if req == nil { return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Original HTTP request is missing") } // Prefer the Azure-AsyncOperation header ps.uri = getAsyncOperation(resp) if ps.uri != "" { ps.responseFormat = usesOperationResponse } else { ps.responseFormat = usesProvisioningStatus } // Else, use the Location header if ps.uri == "" { ps.uri = autorest.GetLocation(resp) } // Lastly, requests against an existing resource, use the last request URI if ps.uri == "" { m := strings.ToUpper(req.Method) if m == methodPatch || m == methodPut || m == methodGet { ps.uri = req.URL.String() } } } // Read and interpret the response (saving the Body in case no polling is necessary) b := &bytes.Buffer{} err := autorest.Respond(resp, autorest.ByCopying(b), autorest.ByUnmarshallingJSON(pt), autorest.ByClosing()) resp.Body = ioutil.NopCloser(b) if err != nil { return err } // Interpret the results // -- Terminal states apply regardless // -- Unknown states are per-service inprogress states // -- Otherwise, infer state from HTTP status code if pt.hasTerminated() { ps.state = pt.state() } else if pt.state() != "" { ps.state = operationInProgress } else { switch resp.StatusCode { case http.StatusAccepted: ps.state = operationInProgress case http.StatusNoContent, http.StatusCreated, http.StatusOK: ps.state = operationSucceeded default: ps.state = operationFailed } } if ps.state == operationInProgress && ps.uri == "" { return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Unable to obtain polling URI for %s %s", resp.Request.Method, resp.Request.URL) } // For failed operation, check for error code and message in // -- Operation resource // -- Response // -- Otherwise, Unknown if ps.hasFailed() { if ps.responseFormat == usesOperationResponse { or := pt.(*operationResource) ps.code = or.OperationError.Code ps.message = or.OperationError.Message } else { p := pt.(*provisioningStatus) if p.hasProvisioningError() { ps.code = p.ProvisioningError.Code ps.message = p.ProvisioningError.Message } else { ps.code = "Unknown" ps.message = "None" } } } return nil }