Exemplo n.º 1
0
Arquivo: client.go Projeto: bac/juju
// WithOdataErrorUnlessStatusCode returns a RespondDecorator that emits an
// azure.RequestError by reading the response body unless the response HTTP status code
// is among the set passed.
//
// If there is a chance service may return responses other than the Azure error
// format and the response cannot be parsed into an error, a decoding error will
// be returned containing the response body. In any case, the Responder will
// return an error if the status code is not satisfied.
//
// If this Responder returns an error, the response body will be replaced with
// an in-memory reader, which needs no further closing.
//
// NOTE(axw) this function is based on go-autorest/autorest/azure.WithErrorUnlessStatusCode.
// The only difference is that we extract "odata.error", instead of "error",
// from the response body; and we do not extract the message, which is in a
// different format for odata.error, and irrelevant to us.
func WithOdataErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
	return func(r autorest.Responder) autorest.Responder {
		return autorest.ResponderFunc(func(resp *http.Response) error {
			err := r.Respond(resp)
			if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
				var oe odataRequestError
				defer resp.Body.Close()

				// Copy and replace the Body in case it does not contain an error object.
				// This will leave the Body available to the caller.
				b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &oe)
				resp.Body = ioutil.NopCloser(&b)
				if decodeErr != nil {
					return fmt.Errorf("ad: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
				} else if oe.ServiceError == nil {
					oe.ServiceError = &odataServiceError{Code: "Unknown"}
				}

				e := azure.RequestError{
					ServiceError: &azure.ServiceError{Code: oe.ServiceError.Code},
					RequestID:    azure.ExtractRequestID(resp),
				}
				if e.StatusCode == nil {
					e.StatusCode = resp.StatusCode
				}
				err = &e
			}
			return err
		})
	}
}
Exemplo n.º 2
0
// WithErrorUnlessStatusCode returns a RespondDecorator that emits an
// azure.RequestError by reading the response body unless the response HTTP status code
// is among the set passed.
//
// If there is a chance service may return responses other than the Azure error
// format and the response cannot be parsed into an error, a decoding error will
// be returned containing the response body. In any case, the Responder will
// return an error if the status code is not satisfied.
//
// If this Responder returns an error, the response body will be replaced with
// an in-memory reader, which needs no further closing.
func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
	return func(r autorest.Responder) autorest.Responder {
		return autorest.ResponderFunc(func(resp *http.Response) error {
			err := r.Respond(resp)
			if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) {
				var e RequestError
				defer resp.Body.Close()

				b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e)
				resp.Body = ioutil.NopCloser(&b) // replace body with in-memory reader
				if decodeErr != nil || e.ServiceError == nil {
					return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), err)
				}

				e.RequestID = ExtractRequestID(resp)
				e.StatusCode = resp.StatusCode
				err = &e
			}
			return err
		})
	}
}
Exemplo n.º 3
0
// DoPollForAsynchronous returns a SendDecorator that polls if the http.Response is for an Azure
// long-running operation. It will delay between requests for the duration specified in the
// RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by
// closing the optional channel on the http.Request.
func DoPollForAsynchronous(delay time.Duration) autorest.SendDecorator {
	return func(s autorest.Sender) autorest.Sender {
		return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) {
			resp, err = s.Do(r)
			if err != nil {
				return resp, err
			}
			pollingCodes := []int{http.StatusAccepted, http.StatusCreated, http.StatusOK}
			if !autorest.ResponseHasStatusCode(resp, pollingCodes...) {
				return resp, nil
			}

			ps := pollingState{}
			for err == nil {
				err = updatePollingState(resp, &ps)
				if err != nil {
					break
				}
				if ps.hasTerminated() {
					if !ps.hasSucceeded() {
						err = ps
					}
					break
				}

				r, err = newPollingRequest(resp, ps)
				if err != nil {
					return resp, err
				}

				delay = autorest.GetRetryAfter(resp, delay)
				resp, err = autorest.SendWithSender(s, r,
					autorest.AfterDelay(delay))
			}

			return resp, err
		})
	}
}
Exemplo n.º 4
0
Arquivo: utils.go Projeto: bac/juju
// call will call the supplied function, with exponential backoff
// as long as the request returns an http.StatusTooManyRequests
// status.
func (c backoffAPIRequestCaller) call(f func() (autorest.Response, error)) error {
	var resp *http.Response
	return retry.Call(retry.CallArgs{
		Func: func() error {
			autorestResp, err := f()
			resp = autorestResp.Response
			return err
		},
		IsFatalError: func(err error) bool {
			return resp == nil || !autorest.ResponseHasStatusCode(resp, http.StatusTooManyRequests)
		},
		NotifyFunc: func(err error, attempt int) {
			logger.Debugf("attempt %d: %v", attempt, err)
		},
		Attempts:    -1,
		Delay:       retryDelay,
		MaxDelay:    maxRetryDelay,
		MaxDuration: maxRetryDuration,
		BackoffFunc: retry.DoubleDelay,
		Clock:       c.clock,
	})
}
Exemplo n.º 5
0
// IsAsynchronousResponse returns true if the passed response indicates that the request will
// complete asynchronously. Such responses have either an http.StatusCreated or an
// http.StatusAccepted status code and provide the Azure-AsyncOperation header.
func IsAsynchronousResponse(resp *http.Response) bool {
	return autorest.ResponseHasStatusCode(resp, http.StatusCreated, http.StatusAccepted) &&
		GetAsyncOperation(resp) != ""
}