Back to godoc.org
github.com/Azure/go-autorest/autorest/azure

package azure

v12.0.0+incompatible
Latest Go to latest
Published: Apr 17, 2019 | License: Apache-2.0 | Module: github.com/Azure/go-autorest

Overview

Package azure provides Azure-specific implementations used with AutoRest. See the included examples for more detail.

Index

Examples

Constants

const (
	// HeaderClientID is the Azure extension header to set a user-specified request ID.
	HeaderClientID = "x-ms-client-request-id"

	// HeaderReturnClientID is the Azure extension header to set if the user-specified request ID
	// should be included in the response.
	HeaderReturnClientID = "x-ms-return-client-request-id"

	// HeaderRequestID is the Azure extension header of the service generated request ID returned
	// in the response.
	HeaderRequestID = "x-ms-request-id"
)
const (
	// EnvironmentFilepathName captures the name of the environment variable containing the path to the file
	// to be used while populating the Azure Environment.
	EnvironmentFilepathName = "AZURE_ENVIRONMENT_FILEPATH"

	// NotAvailable is used for endpoints and resource IDs that are not available for a given cloud.
	NotAvailable = "N/A"
)

Variables

var (
	// PublicCloud is the default public Azure cloud environment
	PublicCloud = Environment{
		Name:                         "AzurePublicCloud",
		ManagementPortalURL:          "https://manage.windowsazure.com/",
		PublishSettingsURL:           "https://manage.windowsazure.com/publishsettings/index",
		ServiceManagementEndpoint:    "https://management.core.windows.net/",
		ResourceManagerEndpoint:      "https://management.azure.com/",
		ActiveDirectoryEndpoint:      "https://login.microsoftonline.com/",
		GalleryEndpoint:              "https://gallery.azure.com/",
		KeyVaultEndpoint:             "https://vault.azure.net/",
		GraphEndpoint:                "https://graph.windows.net/",
		ServiceBusEndpoint:           "https://servicebus.windows.net/",
		BatchManagementEndpoint:      "https://batch.core.windows.net/",
		StorageEndpointSuffix:        "core.windows.net",
		SQLDatabaseDNSSuffix:         "database.windows.net",
		TrafficManagerDNSSuffix:      "trafficmanager.net",
		KeyVaultDNSSuffix:            "vault.azure.net",
		ServiceBusEndpointSuffix:     "servicebus.windows.net",
		ServiceManagementVMDNSSuffix: "cloudapp.net",
		ResourceManagerVMDNSSuffix:   "cloudapp.azure.com",
		ContainerRegistryDNSSuffix:   "azurecr.io",
		CosmosDBDNSSuffix:            "documents.azure.com",
		TokenAudience:                "https://management.azure.com/",
		ResourceIdentifiers: ResourceIdentifier{
			Graph:               "https://graph.windows.net/",
			KeyVault:            "https://vault.azure.net",
			Datalake:            "https://datalake.azure.net/",
			Batch:               "https://batch.core.windows.net/",
			OperationalInsights: "https://api.loganalytics.io",
		},
	}

	// USGovernmentCloud is the cloud environment for the US Government
	USGovernmentCloud = Environment{
		Name:                         "AzureUSGovernmentCloud",
		ManagementPortalURL:          "https://manage.windowsazure.us/",
		PublishSettingsURL:           "https://manage.windowsazure.us/publishsettings/index",
		ServiceManagementEndpoint:    "https://management.core.usgovcloudapi.net/",
		ResourceManagerEndpoint:      "https://management.usgovcloudapi.net/",
		ActiveDirectoryEndpoint:      "https://login.microsoftonline.us/",
		GalleryEndpoint:              "https://gallery.usgovcloudapi.net/",
		KeyVaultEndpoint:             "https://vault.usgovcloudapi.net/",
		GraphEndpoint:                "https://graph.windows.net/",
		ServiceBusEndpoint:           "https://servicebus.usgovcloudapi.net/",
		BatchManagementEndpoint:      "https://batch.core.usgovcloudapi.net/",
		StorageEndpointSuffix:        "core.usgovcloudapi.net",
		SQLDatabaseDNSSuffix:         "database.usgovcloudapi.net",
		TrafficManagerDNSSuffix:      "usgovtrafficmanager.net",
		KeyVaultDNSSuffix:            "vault.usgovcloudapi.net",
		ServiceBusEndpointSuffix:     "servicebus.usgovcloudapi.net",
		ServiceManagementVMDNSSuffix: "usgovcloudapp.net",
		ResourceManagerVMDNSSuffix:   "cloudapp.windowsazure.us",
		ContainerRegistryDNSSuffix:   "azurecr.us",
		CosmosDBDNSSuffix:            "documents.azure.us",
		TokenAudience:                "https://management.usgovcloudapi.net/",
		ResourceIdentifiers: ResourceIdentifier{
			Graph:               "https://graph.windows.net/",
			KeyVault:            "https://vault.usgovcloudapi.net",
			Datalake:            NotAvailable,
			Batch:               "https://batch.core.usgovcloudapi.net/",
			OperationalInsights: "https://api.loganalytics.us",
		},
	}

	// ChinaCloud is the cloud environment operated in China
	ChinaCloud = Environment{
		Name:                         "AzureChinaCloud",
		ManagementPortalURL:          "https://manage.chinacloudapi.com/",
		PublishSettingsURL:           "https://manage.chinacloudapi.com/publishsettings/index",
		ServiceManagementEndpoint:    "https://management.core.chinacloudapi.cn/",
		ResourceManagerEndpoint:      "https://management.chinacloudapi.cn/",
		ActiveDirectoryEndpoint:      "https://login.chinacloudapi.cn/",
		GalleryEndpoint:              "https://gallery.chinacloudapi.cn/",
		KeyVaultEndpoint:             "https://vault.azure.cn/",
		GraphEndpoint:                "https://graph.chinacloudapi.cn/",
		ServiceBusEndpoint:           "https://servicebus.chinacloudapi.cn/",
		BatchManagementEndpoint:      "https://batch.chinacloudapi.cn/",
		StorageEndpointSuffix:        "core.chinacloudapi.cn",
		SQLDatabaseDNSSuffix:         "database.chinacloudapi.cn",
		TrafficManagerDNSSuffix:      "trafficmanager.cn",
		KeyVaultDNSSuffix:            "vault.azure.cn",
		ServiceBusEndpointSuffix:     "servicebus.chinacloudapi.cn",
		ServiceManagementVMDNSSuffix: "chinacloudapp.cn",
		ResourceManagerVMDNSSuffix:   "cloudapp.azure.cn",
		ContainerRegistryDNSSuffix:   "azurecr.cn",
		CosmosDBDNSSuffix:            "documents.azure.cn",
		TokenAudience:                "https://management.chinacloudapi.cn/",
		ResourceIdentifiers: ResourceIdentifier{
			Graph:               "https://graph.chinacloudapi.cn/",
			KeyVault:            "https://vault.azure.cn",
			Datalake:            NotAvailable,
			Batch:               "https://batch.chinacloudapi.cn/",
			OperationalInsights: NotAvailable,
		},
	}

	// GermanCloud is the cloud environment operated in Germany
	GermanCloud = Environment{
		Name:                         "AzureGermanCloud",
		ManagementPortalURL:          "http://portal.microsoftazure.de/",
		PublishSettingsURL:           "https://manage.microsoftazure.de/publishsettings/index",
		ServiceManagementEndpoint:    "https://management.core.cloudapi.de/",
		ResourceManagerEndpoint:      "https://management.microsoftazure.de/",
		ActiveDirectoryEndpoint:      "https://login.microsoftonline.de/",
		GalleryEndpoint:              "https://gallery.cloudapi.de/",
		KeyVaultEndpoint:             "https://vault.microsoftazure.de/",
		GraphEndpoint:                "https://graph.cloudapi.de/",
		ServiceBusEndpoint:           "https://servicebus.cloudapi.de/",
		BatchManagementEndpoint:      "https://batch.cloudapi.de/",
		StorageEndpointSuffix:        "core.cloudapi.de",
		SQLDatabaseDNSSuffix:         "database.cloudapi.de",
		TrafficManagerDNSSuffix:      "azuretrafficmanager.de",
		KeyVaultDNSSuffix:            "vault.microsoftazure.de",
		ServiceBusEndpointSuffix:     "servicebus.cloudapi.de",
		ServiceManagementVMDNSSuffix: "azurecloudapp.de",
		ResourceManagerVMDNSSuffix:   "cloudapp.microsoftazure.de",
		ContainerRegistryDNSSuffix:   NotAvailable,
		CosmosDBDNSSuffix:            "documents.microsoftazure.de",
		TokenAudience:                "https://management.microsoftazure.de/",
		ResourceIdentifiers: ResourceIdentifier{
			Graph:               "https://graph.cloudapi.de/",
			KeyVault:            "https://vault.microsoftazure.de",
			Datalake:            NotAvailable,
			Batch:               "https://batch.cloudapi.de/",
			OperationalInsights: NotAvailable,
		},
	}
)

func DoRetryWithRegistration

func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator

DoRetryWithRegistration tries to register the resource provider in case it is unregistered. It also handles request retries

func ExtractClientID

func ExtractClientID(resp *http.Response) string

ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the http.Request sent to the service (and returned in the http.Response)

func ExtractRequestID

func ExtractRequestID(resp *http.Response) string

ExtractRequestID extracts the Azure server generated request identifier from the x-ms-request-id header.

func IsAzureError

func IsAzureError(e error) bool

IsAzureError returns true if the passed error is an Azure Service error; false otherwise.

func WithClientID

func WithClientID(uuid string) autorest.PrepareDecorator

WithClientID returns a PrepareDecorator that adds an HTTP extension header of x-ms-client-request-id whose value is passed, undecorated UUID (e.g., "0F39878C-5F76-4DB8-A25D-61D2C193C3CA").

Example

Use a Client Inspector to set the request identifier.

Code:

package azure

// Copyright 2017 Microsoft Corporation
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"strconv"
	"testing"
	"time"

	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/mocks"
)

const (
	headerAuthorization = "Authorization"
	longDelay           = 5 * time.Second
	retryDelay          = 10 * time.Millisecond
	testLogPrefix       = "azure:"
)

// Use a Client Inspector to set the request identifier.
func ExampleWithClientID() {
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	req, _ := autorest.Prepare(&http.Request{},
		autorest.AsGet(),
		autorest.WithBaseURL("https://microsoft.com/a/b/c/"))

	c := autorest.Client{Sender: mocks.NewSender()}
	c.RequestInspector = WithReturningClientID(uuid)

	autorest.SendWithSender(c, req)
	fmt.Printf("Inspector added the %s header with the value %s\n",
		HeaderClientID, req.Header.Get(HeaderClientID))
	fmt.Printf("Inspector added the %s header with the value %s\n",
		HeaderReturnClientID, req.Header.Get(HeaderReturnClientID))
	// Output:
	// Inspector added the x-ms-client-request-id header with the value 71FDB9F4-5E49-4C12-B266-DE7B4FD999A6
	// Inspector added the x-ms-return-client-request-id header with the value true
}

func TestWithReturningClientIDReturnsError(t *testing.T) {
	var errIn error
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	_, errOut := autorest.Prepare(&http.Request{},
		withErrorPrepareDecorator(&errIn),
		WithReturningClientID(uuid))

	if errOut == nil || errIn != errOut {
		t.Fatalf("azure: WithReturningClientID failed to exit early when receiving an error -- expected (%v), received (%v)",
			errIn, errOut)
	}
}

func TestWithClientID(t *testing.T) {
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	req, _ := autorest.Prepare(&http.Request{},
		WithClientID(uuid))

	if req.Header.Get(HeaderClientID) != uuid {
		t.Fatalf("azure: WithClientID failed to set %s -- expected %s, received %s",
			HeaderClientID, uuid, req.Header.Get(HeaderClientID))
	}
}

func TestWithReturnClientID(t *testing.T) {
	b := false
	req, _ := autorest.Prepare(&http.Request{},
		WithReturnClientID(b))

	if req.Header.Get(HeaderReturnClientID) != strconv.FormatBool(b) {
		t.Fatalf("azure: WithReturnClientID failed to set %s -- expected %s, received %s",
			HeaderClientID, strconv.FormatBool(b), req.Header.Get(HeaderClientID))
	}
}

func TestExtractClientID(t *testing.T) {
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	resp := mocks.NewResponse()
	mocks.SetResponseHeader(resp, HeaderClientID, uuid)

	if ExtractClientID(resp) != uuid {
		t.Fatalf("azure: ExtractClientID failed to extract the %s -- expected %s, received %s",
			HeaderClientID, uuid, ExtractClientID(resp))
	}
}

func TestExtractRequestID(t *testing.T) {
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	resp := mocks.NewResponse()
	mocks.SetResponseHeader(resp, HeaderRequestID, uuid)

	if ExtractRequestID(resp) != uuid {
		t.Fatalf("azure: ExtractRequestID failed to extract the %s -- expected %s, received %s",
			HeaderRequestID, uuid, ExtractRequestID(resp))
	}
}

func TestIsAzureError_ReturnsTrueForAzureError(t *testing.T) {
	if !IsAzureError(&RequestError{}) {
		t.Fatalf("azure: IsAzureError failed to return true for an Azure Service error")
	}
}

func TestIsAzureError_ReturnsFalseForNonAzureError(t *testing.T) {
	if IsAzureError(fmt.Errorf("An Error")) {
		t.Fatalf("azure: IsAzureError return true for an non-Azure Service error")
	}
}

func TestNewErrorWithError_UsesReponseStatusCode(t *testing.T) {
	e := NewErrorWithError(fmt.Errorf("Error"), "packageType", "method", mocks.NewResponseWithStatus("Forbidden", http.StatusForbidden), "message")
	if e.StatusCode != http.StatusForbidden {
		t.Fatalf("azure: NewErrorWithError failed to use the Status Code of the passed Response -- expected %v, received %v", http.StatusForbidden, e.StatusCode)
	}
}

func TestNewErrorWithError_ReturnsUnwrappedError(t *testing.T) {
	e1 := RequestError{}
	e1.ServiceError = &ServiceError{Code: "42", Message: "A Message"}
	e1.StatusCode = 200
	e1.RequestID = "A RequestID"
	e2 := NewErrorWithError(&e1, "packageType", "method", nil, "message")

	if !reflect.DeepEqual(e1, e2) {
		t.Fatalf("azure: NewErrorWithError wrapped an RequestError -- expected %T, received %T", e1, e2)
	}
}

func TestNewErrorWithError_WrapsAnError(t *testing.T) {
	e1 := fmt.Errorf("Inner Error")
	var e2 interface{} = NewErrorWithError(e1, "packageType", "method", nil, "message")

	if _, ok := e2.(RequestError); !ok {
		t.Fatalf("azure: NewErrorWithError failed to wrap a standard error -- received %T", e2)
	}
}

func TestWithErrorUnlessStatusCode_NotAnAzureError(t *testing.T) {
	body := `<html>
		<head>
			<title>IIS Error page</title>
		</head>
		<body>Some non-JSON error page</body>
	</html>`
	r := mocks.NewResponseWithContent(body)
	r.Request = mocks.NewRequest()
	r.StatusCode = http.StatusBadRequest
	r.Status = http.StatusText(r.StatusCode)

	err := autorest.Respond(r,
		WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByClosing())
	ok, _ := err.(*RequestError)
	if ok != nil {
		t.Fatalf("azure: azure.RequestError returned from malformed response: %v", err)
	}

	// the error body should still be there
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatal(err)
	}
	if string(b) != body {
		t.Fatalf("response body is wrong. got=%q exptected=%q", string(b), body)
	}
}

func TestWithErrorUnlessStatusCode_FoundAzureErrorWithoutDetails(t *testing.T) {
	j := `{
		"error": {
			"code": "InternalError",
			"message": "Azure is having trouble right now."
		}
	}`
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	r := mocks.NewResponseWithContent(j)
	mocks.SetResponseHeader(r, HeaderRequestID, uuid)
	r.Request = mocks.NewRequest()
	r.StatusCode = http.StatusInternalServerError
	r.Status = http.StatusText(r.StatusCode)

	err := autorest.Respond(r,
		WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByClosing())

	if err == nil {
		t.Fatalf("azure: returned nil error for proper error response")
	}
	azErr, ok := err.(*RequestError)
	if !ok {
		t.Fatalf("azure: returned error is not azure.RequestError: %T", err)
	}

	expected := "autorest/azure: Service returned an error. Status=500 Code=\"InternalError\" Message=\"Azure is having trouble right now.\""
	if !reflect.DeepEqual(expected, azErr.Error()) {
		t.Fatalf("azure: service error is not unmarshaled properly.\nexpected=%v\ngot=%v", expected, azErr.Error())
	}

	if expected := http.StatusInternalServerError; azErr.StatusCode != expected {
		t.Fatalf("azure: got wrong StatusCode=%d Expected=%d", azErr.StatusCode, expected)
	}
	if expected := uuid; azErr.RequestID != expected {
		t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID)
	}

	_ = azErr.Error()

	// the error body should still be there
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatal(err)
	}
	if string(b) != j {
		t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j)
	}

}

func TestWithErrorUnlessStatusCode_FoundAzureFullError(t *testing.T) {
	j := `{
		"error": {
			"code": "InternalError",
			"message": "Azure is having trouble right now.",
			"target": "target1",
			"details": [{"code": "conflict1", "message":"error message1"}, 
						{"code": "conflict2", "message":"error message2"}],
			"innererror": { "customKey": "customValue" },
			"additionalInfo": [{"type": "someErrorType", "info": {"someProperty": "someValue"}}]
		}
	}`
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	r := mocks.NewResponseWithContent(j)
	mocks.SetResponseHeader(r, HeaderRequestID, uuid)
	r.Request = mocks.NewRequest()
	r.StatusCode = http.StatusInternalServerError
	r.Status = http.StatusText(r.StatusCode)

	err := autorest.Respond(r,
		WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByClosing())

	if err == nil {
		t.Fatalf("azure: returned nil error for proper error response")
	}
	azErr, ok := err.(*RequestError)
	if !ok {
		t.Fatalf("azure: returned error is not azure.RequestError: %T", err)
	}

	if expected := "InternalError"; azErr.ServiceError.Code != expected {
		t.Fatalf("azure: wrong error code. expected=%q; got=%q", expected, azErr.ServiceError.Code)
	}

	if azErr.ServiceError.Message == "" {
		t.Fatalf("azure: error message is not unmarshaled properly")
	}

	if *azErr.ServiceError.Target == "" {
		t.Fatalf("azure: error target is not unmarshaled properly")
	}

	d, _ := json.Marshal(azErr.ServiceError.Details)
	if string(d) != `[{"code":"conflict1","message":"error message1"},{"code":"conflict2","message":"error message2"}]` {
		t.Fatalf("azure: error details is not unmarshaled properly")
	}

	i, _ := json.Marshal(azErr.ServiceError.InnerError)
	if string(i) != `{"customKey":"customValue"}` {
		t.Fatalf("azure: inner error is not unmarshaled properly")
	}

	a, _ := json.Marshal(azErr.ServiceError.AdditionalInfo)
	if string(a) != `[{"info":{"someProperty":"someValue"},"type":"someErrorType"}]` {
		t.Fatalf("azure: error additional info is not unmarshaled properly")
	}

	if expected := http.StatusInternalServerError; azErr.StatusCode != expected {
		t.Fatalf("azure: got wrong StatusCode=%v Expected=%d", azErr.StatusCode, expected)
	}
	if expected := uuid; azErr.RequestID != expected {
		t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID)
	}

	_ = azErr.Error()

	// the error body should still be there
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatal(err)
	}
	if string(b) != j {
		t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j)
	}

}

func TestWithErrorUnlessStatusCode_NoAzureError(t *testing.T) {
	j := `{
		"Status":"NotFound"
	}`
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	r := mocks.NewResponseWithContent(j)
	mocks.SetResponseHeader(r, HeaderRequestID, uuid)
	r.Request = mocks.NewRequest()
	r.StatusCode = http.StatusInternalServerError
	r.Status = http.StatusText(r.StatusCode)

	err := autorest.Respond(r,
		WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByClosing())
	if err == nil {
		t.Fatalf("azure: returned nil error for proper error response")
	}
	azErr, ok := err.(*RequestError)
	if !ok {
		t.Fatalf("azure: returned error is not azure.RequestError: %T", err)
	}

	expected := &ServiceError{
		Code:    "Unknown",
		Message: "Unknown service error",
		Details: []map[string]interface{}{
			{"Status": "NotFound"},
		},
	}

	if !reflect.DeepEqual(expected, azErr.ServiceError) {
		t.Fatalf("azure: service error is not unmarshaled properly. expected=%q\ngot=%q", expected, azErr.ServiceError)
	}

	if expected := http.StatusInternalServerError; azErr.StatusCode != expected {
		t.Fatalf("azure: got wrong StatusCode=%v Expected=%d", azErr.StatusCode, expected)
	}
	if expected := uuid; azErr.RequestID != expected {
		t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID)
	}

	_ = azErr.Error()

	// the error body should still be there
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatal(err)
	}
	if string(b) != j {
		t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j)
	}

}

func TestWithErrorUnlessStatusCode_UnwrappedError(t *testing.T) {
	j := `{
		"code": "InternalError",
		"message": "Azure is having trouble right now.",
		"target": "target1",
		"details": [{"code": "conflict1", "message":"error message1"},
					{"code": "conflict2", "message":"error message2"}],
		"innererror": { "customKey": "customValue" },
		"additionalInfo": [{"type": "someErrorType", "info": {"someProperty": "someValue"}}]
    }`
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	r := mocks.NewResponseWithContent(j)
	mocks.SetResponseHeader(r, HeaderRequestID, uuid)
	r.Request = mocks.NewRequest()
	r.StatusCode = http.StatusInternalServerError
	r.Status = http.StatusText(r.StatusCode)

	err := autorest.Respond(r,
		WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByClosing())

	if err == nil {
		t.Fatal("azure: returned nil error for proper error response")
	}

	azErr, ok := err.(*RequestError)
	if !ok {
		t.Fatalf("returned error is not azure.RequestError: %T", err)
	}

	if expected := http.StatusInternalServerError; azErr.StatusCode != expected {
		t.Logf("Incorrect StatusCode got: %v want: %d", azErr.StatusCode, expected)
		t.Fail()
	}

	if expected := "Azure is having trouble right now."; azErr.Message != expected {
		t.Logf("Incorrect Message\n\tgot:  %q\n\twant: %q", azErr.Message, expected)
		t.Fail()
	}

	if expected := uuid; azErr.RequestID != expected {
		t.Logf("Incorrect request ID\n\tgot:  %q\n\twant: %q", azErr.RequestID, expected)
		t.Fail()
	}

	if azErr.ServiceError == nil {
		t.Logf("`ServiceError` was nil when it shouldn't have been.")
		t.Fail()
	}

	if expected := "target1"; *azErr.ServiceError.Target != expected {
		t.Logf("Incorrect Target\n\tgot:  %q\n\twant: %q", *azErr.ServiceError.Target, expected)
		t.Fail()
	}

	expectedServiceErrorDetails := `[{"code":"conflict1","message":"error message1"},{"code":"conflict2","message":"error message2"}]`
	if azErr.ServiceError.Details == nil {
		t.Logf("`ServiceError.Details` was nil when it should have been %q", expectedServiceErrorDetails)
		t.Fail()
	} else if details, _ := json.Marshal(azErr.ServiceError.Details); expectedServiceErrorDetails != string(details) {
		t.Logf("Error details was not unmarshaled properly.\n\tgot:  %q\n\twant: %q", string(details), expectedServiceErrorDetails)
		t.Fail()
	}

	expectedServiceErrorInnerError := `{"customKey":"customValue"}`
	if azErr.ServiceError.InnerError == nil {
		t.Logf("`ServiceError.InnerError` was nil when it should have been %q", expectedServiceErrorInnerError)
		t.Fail()
	} else if innerError, _ := json.Marshal(azErr.ServiceError.InnerError); expectedServiceErrorInnerError != string(innerError) {
		t.Logf("Inner error was not unmarshaled properly.\n\tgot:  %q\n\twant: %q", string(innerError), expectedServiceErrorInnerError)
		t.Fail()
	}

	expectedServiceErrorAdditionalInfo := `[{"info":{"someProperty":"someValue"},"type":"someErrorType"}]`
	if azErr.ServiceError.AdditionalInfo == nil {
		t.Logf("`ServiceError.AdditionalInfo` was nil when it should have been %q", expectedServiceErrorAdditionalInfo)
		t.Fail()
	} else if additionalInfo, _ := json.Marshal(azErr.ServiceError.AdditionalInfo); expectedServiceErrorAdditionalInfo != string(additionalInfo) {
		t.Logf("Additional info was not unmarshaled properly.\n\tgot:  %q\n\twant: %q", string(additionalInfo), expectedServiceErrorAdditionalInfo)
		t.Fail()
	}

	// the error body should still be there
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Error(err)
	}
	if string(b) != j {
		t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j)
	}

}

func TestRequestErrorString_WithError(t *testing.T) {
	j := `{
		"error": {
			"code": "InternalError",
			"message": "Conflict",
			"target": "target1",
			"details": [{"code": "conflict1", "message":"error message1"}],
			"innererror": { "customKey": "customValue" },
			"additionalInfo": [{"type": "someErrorType", "info": {"someProperty": "someValue"}}]
		}
	}`
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	r := mocks.NewResponseWithContent(j)
	mocks.SetResponseHeader(r, HeaderRequestID, uuid)
	r.Request = mocks.NewRequest()
	r.StatusCode = http.StatusInternalServerError
	r.Status = http.StatusText(r.StatusCode)

	err := autorest.Respond(r,
		WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByClosing())

	if err == nil {
		t.Fatalf("azure: returned nil error for proper error response")
	}
	azErr, _ := err.(*RequestError)
	expected := "autorest/azure: Service returned an error. Status=500 Code=\"InternalError\" Message=\"Conflict\" Target=\"target1\" Details=[{\"code\":\"conflict1\",\"message\":\"error message1\"}] InnerError={\"customKey\":\"customValue\"} AdditionalInfo=[{\"info\":{\"someProperty\":\"someValue\"},\"type\":\"someErrorType\"}]"
	if expected != azErr.Error() {
		t.Fatalf("azure: send wrong RequestError.\nexpected=%v\ngot=%v", expected, azErr.Error())
	}
}

func TestRequestErrorString_WithErrorNonConforming(t *testing.T) {
	j := `{
		"error": {
			"code": "InternalError",
			"message": "Conflict",
			"details": {"code": "conflict1", "message":"error message1"}
		}
	}`
	uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6"
	r := mocks.NewResponseWithContent(j)
	mocks.SetResponseHeader(r, HeaderRequestID, uuid)
	r.Request = mocks.NewRequest()
	r.StatusCode = http.StatusInternalServerError
	r.Status = http.StatusText(r.StatusCode)

	err := autorest.Respond(r,
		WithErrorUnlessStatusCode(http.StatusOK),
		autorest.ByClosing())

	if err == nil {
		t.Fatalf("azure: returned nil error for proper error response")
	}
	azErr, _ := err.(*RequestError)
	expected := "autorest/azure: Service returned an error. Status=500 Code=\"InternalError\" Message=\"Conflict\" Details=[{\"code\":\"conflict1\",\"message\":\"error message1\"}]"
	if expected != azErr.Error() {
		t.Fatalf("azure: send wrong RequestError.\nexpected=%v\ngot=%v", expected, azErr.Error())
	}
}

func TestParseResourceID_WithValidBasicResourceID(t *testing.T) {

	basicResourceID := "/subscriptions/subid-3-3-4/resourceGroups/regGroupVladdb/providers/Microsoft.Network/LoadBalancer/testResourceName"
	want := Resource{
		SubscriptionID: "subid-3-3-4",
		ResourceGroup:  "regGroupVladdb",
		Provider:       "Microsoft.Network",
		ResourceType:   "LoadBalancer",
		ResourceName:   "testResourceName",
	}
	got, err := ParseResourceID(basicResourceID)

	if err != nil {
		t.Fatalf("azure: error returned while parsing valid resourceId")
	}

	if got != want {
		t.Logf("got:  %+v\nwant: %+v", got, want)
		t.Fail()
	}
}

func TestParseResourceID_WithValidSubResourceID(t *testing.T) {
	subresourceID := "/subscriptions/subid-3-3-4/resourceGroups/regGroupVladdb/providers/Microsoft.Network/LoadBalancer/resource/is/a/subresource/actualresourceName"
	want := Resource{
		SubscriptionID: "subid-3-3-4",
		ResourceGroup:  "regGroupVladdb",
		Provider:       "Microsoft.Network",
		ResourceType:   "LoadBalancer",
		ResourceName:   "actualresourceName",
	}
	got, err := ParseResourceID(subresourceID)

	if err != nil {
		t.Fatalf("azure: error returned while parsing valid resourceId")
	}

	if got != want {
		t.Logf("got:  %+v\nwant: %+v", got, want)
		t.Fail()
	}
}

func TestParseResourceID_WithIncompleteResourceID(t *testing.T) {
	basicResourceID := "/subscriptions/subid-3-3-4/resourceGroups/regGroupVladdb/providers/Microsoft.Network/"
	want := Resource{}

	got, err := ParseResourceID(basicResourceID)

	if err == nil {
		t.Fatalf("azure: no error returned on incomplete resource id")
	}

	if got != want {
		t.Logf("got:  %+v\nwant: %+v", got, want)
		t.Fail()
	}
}

func TestParseResourceID_WithMalformedResourceID(t *testing.T) {
	malformedResourceID := "/providers/subid-3-3-4/resourceGroups/regGroupVladdb/subscriptions/Microsoft.Network/LoadBalancer/testResourceName"
	want := Resource{}

	got, err := ParseResourceID(malformedResourceID)

	if err == nil {
		t.Fatalf("azure: error returned while parsing malformed resourceID")
	}

	if got != want {
		t.Logf("got:  %+v\nwant: %+v", got, want)
		t.Fail()
	}
}

func withErrorPrepareDecorator(e *error) autorest.PrepareDecorator {
	return func(p autorest.Preparer) autorest.Preparer {
		return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
			*e = fmt.Errorf("azure: Faux Prepare Error")
			return r, *e
		})
	}
}

func withAsyncResponseDecorator(n int) autorest.SendDecorator {
	i := 0
	return func(s autorest.Sender) autorest.Sender {
		return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
			resp, err := s.Do(r)
			if err == nil {
				if i < n {
					resp.StatusCode = http.StatusCreated
					resp.Header = http.Header{}
					resp.Header.Add(http.CanonicalHeaderKey(headerAsyncOperation), mocks.TestURL)
					i++
				} else {
					resp.StatusCode = http.StatusOK
					resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation))
				}
			}
			return resp, err
		})
	}
}

type mockAuthorizer struct{}

func (ma mockAuthorizer) WithAuthorization() autorest.PrepareDecorator {
	return autorest.WithHeader(headerAuthorization, mocks.TestAuthorizationHeader)
}

type mockFailingAuthorizer struct{}

func (mfa mockFailingAuthorizer) WithAuthorization() autorest.PrepareDecorator {
	return func(p autorest.Preparer) autorest.Preparer {
		return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
			return r, fmt.Errorf("ERROR: mockFailingAuthorizer returned expected error")
		})
	}
}

type mockInspector struct {
	wasInvoked bool
}

func (mi *mockInspector) WithInspection() autorest.PrepareDecorator {
	return func(p autorest.Preparer) autorest.Preparer {
		return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) {
			mi.wasInvoked = true
			return p.Prepare(r)
		})
	}
}

func (mi *mockInspector) ByInspecting() autorest.RespondDecorator {
	return func(r autorest.Responder) autorest.Responder {
		return autorest.ResponderFunc(func(resp *http.Response) error {
			mi.wasInvoked = true
			return r.Respond(resp)
		})
	}
}

Output:

Inspector added the x-ms-client-request-id header with the value 71FDB9F4-5E49-4C12-B266-DE7B4FD999A6
Inspector added the x-ms-return-client-request-id header with the value true

func WithErrorUnlessStatusCode

func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator

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 WithReturnClientID

func WithReturnClientID(b bool) autorest.PrepareDecorator

WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of x-ms-return-client-request-id whose boolean value indicates if the value of the x-ms-client-request-id header should be included in the http.Response.

func WithReturningClientID

func WithReturningClientID(uuid string) autorest.PrepareDecorator

WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of x-ms-client-request-id whose value is the passed, undecorated UUID (e.g., "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id header to true such that UUID accompanies the http.Response.

type AsyncOpIncompleteError

type AsyncOpIncompleteError struct {
	// FutureType is the name of the type composed of a azure.Future.
	FutureType string
}

AsyncOpIncompleteError is the type that's returned from a future that has not completed.

func NewAsyncOpIncompleteError

func NewAsyncOpIncompleteError(futureType string) AsyncOpIncompleteError

NewAsyncOpIncompleteError creates a new AsyncOpIncompleteError with the specified parameters.

func (AsyncOpIncompleteError) Error

func (e AsyncOpIncompleteError) Error() string

Error returns an error message including the originating type name of the error.

type Environment

type Environment struct {
	Name                         string             `json:"name"`
	ManagementPortalURL          string             `json:"managementPortalURL"`
	PublishSettingsURL           string             `json:"publishSettingsURL"`
	ServiceManagementEndpoint    string             `json:"serviceManagementEndpoint"`
	ResourceManagerEndpoint      string             `json:"resourceManagerEndpoint"`
	ActiveDirectoryEndpoint      string             `json:"activeDirectoryEndpoint"`
	GalleryEndpoint              string             `json:"galleryEndpoint"`
	KeyVaultEndpoint             string             `json:"keyVaultEndpoint"`
	GraphEndpoint                string             `json:"graphEndpoint"`
	ServiceBusEndpoint           string             `json:"serviceBusEndpoint"`
	BatchManagementEndpoint      string             `json:"batchManagementEndpoint"`
	StorageEndpointSuffix        string             `json:"storageEndpointSuffix"`
	SQLDatabaseDNSSuffix         string             `json:"sqlDatabaseDNSSuffix"`
	TrafficManagerDNSSuffix      string             `json:"trafficManagerDNSSuffix"`
	KeyVaultDNSSuffix            string             `json:"keyVaultDNSSuffix"`
	ServiceBusEndpointSuffix     string             `json:"serviceBusEndpointSuffix"`
	ServiceManagementVMDNSSuffix string             `json:"serviceManagementVMDNSSuffix"`
	ResourceManagerVMDNSSuffix   string             `json:"resourceManagerVMDNSSuffix"`
	ContainerRegistryDNSSuffix   string             `json:"containerRegistryDNSSuffix"`
	CosmosDBDNSSuffix            string             `json:"cosmosDBDNSSuffix"`
	TokenAudience                string             `json:"tokenAudience"`
	ResourceIdentifiers          ResourceIdentifier `json:"resourceIdentifiers"`
}

Environment represents a set of endpoints for each of Azure's Clouds.

func EnvironmentFromFile

func EnvironmentFromFile(location string) (unmarshaled Environment, err error)

EnvironmentFromFile loads an Environment from a configuration file available on disk. This function is particularly useful in the Hybrid Cloud model, where one must define their own endpoints.

func EnvironmentFromName

func EnvironmentFromName(name string) (Environment, error)

EnvironmentFromName returns an Environment based on the common name specified.

func EnvironmentFromURL

func EnvironmentFromURL(resourceManagerEndpoint string, properties ...OverrideProperty) (environment Environment, err error)

EnvironmentFromURL loads an Environment from a URL This function is particularly useful in the Hybrid Cloud model, where one may define their own endpoints.

type EnvironmentProperty

type EnvironmentProperty string

EnvironmentProperty represent property names that clients can override

const (
	// EnvironmentName ...
	EnvironmentName EnvironmentProperty = "name"
	// EnvironmentManagementPortalURL ..
	EnvironmentManagementPortalURL EnvironmentProperty = "managementPortalURL"
	// EnvironmentPublishSettingsURL ...
	EnvironmentPublishSettingsURL EnvironmentProperty = "publishSettingsURL"
	// EnvironmentServiceManagementEndpoint ...
	EnvironmentServiceManagementEndpoint EnvironmentProperty = "serviceManagementEndpoint"
	// EnvironmentResourceManagerEndpoint ...
	EnvironmentResourceManagerEndpoint EnvironmentProperty = "resourceManagerEndpoint"
	// EnvironmentActiveDirectoryEndpoint ...
	EnvironmentActiveDirectoryEndpoint EnvironmentProperty = "activeDirectoryEndpoint"
	// EnvironmentGalleryEndpoint ...
	EnvironmentGalleryEndpoint EnvironmentProperty = "galleryEndpoint"
	// EnvironmentKeyVaultEndpoint ...
	EnvironmentKeyVaultEndpoint EnvironmentProperty = "keyVaultEndpoint"
	// EnvironmentGraphEndpoint ...
	EnvironmentGraphEndpoint EnvironmentProperty = "graphEndpoint"
	// EnvironmentServiceBusEndpoint ...
	EnvironmentServiceBusEndpoint EnvironmentProperty = "serviceBusEndpoint"
	// EnvironmentBatchManagementEndpoint ...
	EnvironmentBatchManagementEndpoint EnvironmentProperty = "batchManagementEndpoint"
	// EnvironmentStorageEndpointSuffix ...
	EnvironmentStorageEndpointSuffix EnvironmentProperty = "storageEndpointSuffix"
	// EnvironmentSQLDatabaseDNSSuffix ...
	EnvironmentSQLDatabaseDNSSuffix EnvironmentProperty = "sqlDatabaseDNSSuffix"
	// EnvironmentTrafficManagerDNSSuffix ...
	EnvironmentTrafficManagerDNSSuffix EnvironmentProperty = "trafficManagerDNSSuffix"
	// EnvironmentKeyVaultDNSSuffix ...
	EnvironmentKeyVaultDNSSuffix EnvironmentProperty = "keyVaultDNSSuffix"
	// EnvironmentServiceBusEndpointSuffix ...
	EnvironmentServiceBusEndpointSuffix EnvironmentProperty = "serviceBusEndpointSuffix"
	// EnvironmentServiceManagementVMDNSSuffix ...
	EnvironmentServiceManagementVMDNSSuffix EnvironmentProperty = "serviceManagementVMDNSSuffix"
	// EnvironmentResourceManagerVMDNSSuffix ...
	EnvironmentResourceManagerVMDNSSuffix EnvironmentProperty = "resourceManagerVMDNSSuffix"
	// EnvironmentContainerRegistryDNSSuffix ...
	EnvironmentContainerRegistryDNSSuffix EnvironmentProperty = "containerRegistryDNSSuffix"
	// EnvironmentTokenAudience ...
	EnvironmentTokenAudience EnvironmentProperty = "tokenAudience"
)

type Future

type Future struct {
	// contains filtered or unexported fields
}

Future provides a mechanism to access the status and results of an asynchronous request. Since futures are stateful they should be passed by value to avoid race conditions.

func NewFutureFromResponse

func NewFutureFromResponse(resp *http.Response) (Future, error)

NewFutureFromResponse returns a new Future object initialized with the initial response from an asynchronous operation.

func (*Future) DoneWithContext

func (f *Future) DoneWithContext(ctx context.Context, sender autorest.Sender) (done bool, err error)

DoneWithContext queries the service to see if the operation has completed.

func (Future) GetPollingDelay

func (f Future) GetPollingDelay() (time.Duration, bool)

GetPollingDelay returns a duration the application should wait before checking the status of the asynchronous request and true; this value is returned from the service via the Retry-After response header. If the header wasn't returned then the function returns the zero-value time.Duration and false.

func (Future) GetResult

func (f Future) GetResult(sender autorest.Sender) (*http.Response, error)

GetResult should be called once polling has completed successfully. It makes the final GET call to retrieve the resultant payload.

func (Future) MarshalJSON

func (f Future) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (Future) PollingMethod

func (f Future) PollingMethod() PollingMethodType

PollingMethod returns the method used to monitor the status of the asynchronous operation.

func (Future) PollingURL

func (f Future) PollingURL() string

PollingURL returns the URL used for retrieving the status of the long-running operation.

func (Future) Response

func (f Future) Response() *http.Response

Response returns the last HTTP response.

func (Future) Status

func (f Future) Status() string

Status returns the last status message of the operation.

func (*Future) UnmarshalJSON

func (f *Future) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

func (*Future) WaitForCompletionRef

func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Client) (err error)

WaitForCompletionRef will return when one of the following conditions is met: the long running operation has completed, the provided context is cancelled, or the client's polling duration has been exceeded. It will retry failed polling attempts based on the retry value defined in the client up to the maximum retry attempts. If no deadline is specified in the context then the client.PollingDuration will be used to determine if a default deadline should be used. If PollingDuration is greater than zero the value will be used as the context's timeout. If PollingDuration is zero then no default deadline will be used.

type OverrideProperty

type OverrideProperty struct {
	Key   EnvironmentProperty
	Value string
}

OverrideProperty represents property name and value that clients can override

type PollingMethodType

type PollingMethodType string

PollingMethodType defines a type used for enumerating polling mechanisms.

const (
	// PollingAsyncOperation indicates the polling method uses the Azure-AsyncOperation header.
	PollingAsyncOperation PollingMethodType = "AsyncOperation"

	// PollingLocation indicates the polling method uses the Location header.
	PollingLocation PollingMethodType = "Location"

	// PollingRequestURI indicates the polling method uses the original request URI.
	PollingRequestURI PollingMethodType = "RequestURI"

	// PollingUnknown indicates an unknown polling method and is the default value.
	PollingUnknown PollingMethodType = ""
)

type RequestError

type RequestError struct {
	autorest.DetailedError

	// The error returned by the Azure service.
	ServiceError *ServiceError `json:"error"`

	// The request id (from the x-ms-request-id-header) of the request.
	RequestID string
}

RequestError describes an error response returned by Azure service.

func NewErrorWithError

func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError

NewErrorWithError creates a new Error conforming object from the passed packageType, method, statusCode of the given resp (UndefinedStatusCode if resp is nil), message, and original error. message is treated as a format string to which the optional args apply.

func (RequestError) Error

func (e RequestError) Error() string

Error returns a human-friendly error message from service error.

type Resource

type Resource struct {
	SubscriptionID string
	ResourceGroup  string
	Provider       string
	ResourceType   string
	ResourceName   string
}

Resource contains details about an Azure resource.

func ParseResourceID

func ParseResourceID(resourceID string) (Resource, error)

ParseResourceID parses a resource ID into a ResourceDetails struct. See https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-resource#return-value-4.

type ResourceIdentifier

type ResourceIdentifier struct {
	Graph               string `json:"graph"`
	KeyVault            string `json:"keyVault"`
	Datalake            string `json:"datalake"`
	Batch               string `json:"batch"`
	OperationalInsights string `json:"operationalInsights"`
}

ResourceIdentifier contains a set of Azure resource IDs.

type ServiceError

type ServiceError struct {
	Code           string                   `json:"code"`
	Message        string                   `json:"message"`
	Target         *string                  `json:"target"`
	Details        []map[string]interface{} `json:"details"`
	InnerError     map[string]interface{}   `json:"innererror"`
	AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
}

ServiceError encapsulates the error response from an Azure service. It adhears to the OData v4 specification for error responses.

func (ServiceError) Error

func (se ServiceError) Error() string

func (*ServiceError) UnmarshalJSON

func (se *ServiceError) UnmarshalJSON(b []byte) error

UnmarshalJSON implements the json.Unmarshaler interface for the ServiceError type.

Documentation was rendered with GOOS=linux and GOARCH=amd64.

Jump to identifier

Keyboard shortcuts

? : This menu
f or F : Jump to identifier