github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/azure/arm/azure_client.go (about)

     1  package arm
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"math"
     7  	"net/http"
     8  	"net/url"
     9  	"os"
    10  	"strconv"
    11  
    12  	"github.com/Azure/azure-sdk-for-go/arm/compute"
    13  	"github.com/Azure/azure-sdk-for-go/arm/network"
    14  	"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
    15  	armStorage "github.com/Azure/azure-sdk-for-go/arm/storage"
    16  	"github.com/Azure/azure-sdk-for-go/storage"
    17  	"github.com/Azure/go-autorest/autorest"
    18  	"github.com/Azure/go-autorest/autorest/adal"
    19  	"github.com/Azure/go-autorest/autorest/azure"
    20  	"github.com/hashicorp/packer/builder/azure/common"
    21  	"github.com/hashicorp/packer/version"
    22  )
    23  
    24  const (
    25  	EnvPackerLogAzureMaxLen = "PACKER_LOG_AZURE_MAXLEN"
    26  )
    27  
    28  var (
    29  	packerUserAgent = fmt.Sprintf(";packer/%s", version.FormattedVersion())
    30  )
    31  
    32  type AzureClient struct {
    33  	storage.BlobStorageClient
    34  	resources.DeploymentsClient
    35  	resources.GroupsClient
    36  	network.PublicIPAddressesClient
    37  	network.InterfacesClient
    38  	network.SubnetsClient
    39  	network.VirtualNetworksClient
    40  	compute.ImagesClient
    41  	compute.VirtualMachinesClient
    42  	common.VaultClient
    43  	armStorage.AccountsClient
    44  
    45  	InspectorMaxLength int
    46  	Template           *CaptureTemplate
    47  	LastError          azureErrorResponse
    48  }
    49  
    50  func getCaptureResponse(body string) *CaptureTemplate {
    51  	var operation CaptureOperation
    52  	err := json.Unmarshal([]byte(body), &operation)
    53  	if err != nil {
    54  		return nil
    55  	}
    56  
    57  	if operation.Properties != nil && operation.Properties.Output != nil {
    58  		return operation.Properties.Output
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  // HACK(chrboum): This method is a hack.  It was written to work around this issue
    65  // (https://github.com/Azure/azure-sdk-for-go/issues/307) and to an extent this
    66  // issue (https://github.com/Azure/azure-rest-api-specs/issues/188).
    67  //
    68  // Capturing a VM is a long running operation that requires polling.  There are
    69  // couple different forms of polling, and the end result of a poll operation is
    70  // discarded by the SDK.  It is expected that any discarded data can be re-fetched,
    71  // so discarding it has minimal impact.  Unfortunately, there is no way to re-fetch
    72  // the template returned by a capture call that I am aware of.
    73  //
    74  // If the second issue were fixed the VM ID would be included when GET'ing a VM.  The
    75  // VM ID could be used to locate the captured VHD, and captured template.
    76  // Unfortunately, the VM ID is not included so this method cannot be used either.
    77  //
    78  // This code captures the template and saves it to the client (the AzureClient type).
    79  // It expects that the capture API is called only once, or rather you only care that the
    80  // last call's value is important because subsequent requests are not persisted.  There
    81  // is no care given to multiple threads writing this value because for our use case
    82  // it does not matter.
    83  func templateCapture(client *AzureClient) autorest.RespondDecorator {
    84  	return func(r autorest.Responder) autorest.Responder {
    85  		return autorest.ResponderFunc(func(resp *http.Response) error {
    86  			body, bodyString := handleBody(resp.Body, math.MaxInt64)
    87  			resp.Body = body
    88  
    89  			captureTemplate := getCaptureResponse(bodyString)
    90  			if captureTemplate != nil {
    91  				client.Template = captureTemplate
    92  			}
    93  
    94  			return r.Respond(resp)
    95  		})
    96  	}
    97  }
    98  
    99  func errorCapture(client *AzureClient) autorest.RespondDecorator {
   100  	return func(r autorest.Responder) autorest.Responder {
   101  		return autorest.ResponderFunc(func(resp *http.Response) error {
   102  			body, bodyString := handleBody(resp.Body, math.MaxInt64)
   103  			resp.Body = body
   104  
   105  			errorResponse := newAzureErrorResponse(bodyString)
   106  			if errorResponse != nil {
   107  				client.LastError = *errorResponse
   108  			}
   109  
   110  			return r.Respond(resp)
   111  		})
   112  	}
   113  }
   114  
   115  // WAITING(chrboum): I have logged https://github.com/Azure/azure-sdk-for-go/issues/311 to get this
   116  // method included in the SDK.  It has been accepted, and I'll cut over to the official way
   117  // once it ships.
   118  func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator {
   119  	return func(r autorest.Responder) autorest.Responder {
   120  		return autorest.DecorateResponder(r, decorators...)
   121  	}
   122  }
   123  
   124  func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string,
   125  	cloud *azure.Environment,
   126  	servicePrincipalToken, servicePrincipalTokenVault *adal.ServicePrincipalToken) (*AzureClient, error) {
   127  
   128  	var azureClient = &AzureClient{}
   129  
   130  	maxlen := getInspectorMaxLength()
   131  
   132  	azureClient.DeploymentsClient = resources.NewDeploymentsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   133  	azureClient.DeploymentsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   134  	azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen)
   135  	azureClient.DeploymentsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   136  	azureClient.DeploymentsClient.UserAgent += packerUserAgent
   137  
   138  	azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   139  	azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   140  	azureClient.GroupsClient.RequestInspector = withInspection(maxlen)
   141  	azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   142  	azureClient.GroupsClient.UserAgent += packerUserAgent
   143  
   144  	azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   145  	azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   146  	azureClient.ImagesClient.RequestInspector = withInspection(maxlen)
   147  	azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   148  	azureClient.ImagesClient.UserAgent += packerUserAgent
   149  
   150  	azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   151  	azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   152  	azureClient.InterfacesClient.RequestInspector = withInspection(maxlen)
   153  	azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   154  	azureClient.InterfacesClient.UserAgent += packerUserAgent
   155  
   156  	azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   157  	azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   158  	azureClient.SubnetsClient.RequestInspector = withInspection(maxlen)
   159  	azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   160  	azureClient.SubnetsClient.UserAgent += packerUserAgent
   161  
   162  	azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   163  	azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   164  	azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen)
   165  	azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   166  	azureClient.VirtualNetworksClient.UserAgent += packerUserAgent
   167  
   168  	azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   169  	azureClient.PublicIPAddressesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   170  	azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen)
   171  	azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   172  	azureClient.PublicIPAddressesClient.UserAgent += packerUserAgent
   173  
   174  	azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   175  	azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   176  	azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen)
   177  	azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient))
   178  	azureClient.VirtualMachinesClient.UserAgent += packerUserAgent
   179  
   180  	azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
   181  	azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
   182  	azureClient.AccountsClient.RequestInspector = withInspection(maxlen)
   183  	azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   184  	azureClient.AccountsClient.UserAgent += packerUserAgent
   185  
   186  	keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	azureClient.VaultClient = common.NewVaultClient(*keyVaultURL)
   192  	azureClient.VaultClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalTokenVault)
   193  	azureClient.VaultClient.RequestInspector = withInspection(maxlen)
   194  	azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
   195  	azureClient.VaultClient.UserAgent += packerUserAgent
   196  
   197  	// If this is a managed disk build, this should be ignored.
   198  	if resourceGroupName != "" && storageAccountName != "" {
   199  		accountKeys, err := azureClient.AccountsClient.ListKeys(resourceGroupName, storageAccountName)
   200  		if err != nil {
   201  			return nil, err
   202  		}
   203  
   204  		storageClient, err := storage.NewClient(
   205  			storageAccountName,
   206  			*(*accountKeys.Keys)[0].Value,
   207  			cloud.StorageEndpointSuffix,
   208  			storage.DefaultAPIVersion,
   209  			true /*useHttps*/)
   210  
   211  		if err != nil {
   212  			return nil, err
   213  		}
   214  
   215  		azureClient.BlobStorageClient = storageClient.GetBlobService()
   216  	}
   217  
   218  	return azureClient, nil
   219  }
   220  
   221  func getInspectorMaxLength() int64 {
   222  	value, ok := os.LookupEnv(EnvPackerLogAzureMaxLen)
   223  	if !ok {
   224  		return math.MaxInt64
   225  	}
   226  
   227  	i, err := strconv.ParseInt(value, 10, 64)
   228  	if err != nil {
   229  		return 0
   230  	}
   231  
   232  	if i < 0 {
   233  		return 0
   234  	}
   235  
   236  	return i
   237  }