github.com/yoctocloud/packer@v0.6.2-0.20160520224004-e11a0a18423f/builder/azure/arm/azure_client.go (about)

     1  // Copyright (c) Microsoft Corporation. All rights reserved.
     2  // Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
     3  
     4  package arm
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"math"
    10  	"net/http"
    11  	"os"
    12  	"strconv"
    13  
    14  	"github.com/Azure/azure-sdk-for-go/arm/compute"
    15  	"github.com/Azure/azure-sdk-for-go/arm/network"
    16  	"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
    17  	armStorage "github.com/Azure/azure-sdk-for-go/arm/storage"
    18  	"github.com/Azure/azure-sdk-for-go/storage"
    19  	"github.com/Azure/go-autorest/autorest"
    20  	"github.com/Azure/go-autorest/autorest/azure"
    21  	"github.com/mitchellh/packer/builder/azure/common"
    22  	"github.com/mitchellh/packer/version"
    23  )
    24  
    25  const (
    26  	EnvPackerLogAzureMaxLen = "PACKER_LOG_AZURE_MAXLEN"
    27  )
    28  
    29  var (
    30  	packerUserAgent = fmt.Sprintf(";packer/%s", version.FormattedVersion())
    31  )
    32  
    33  type AzureClient struct {
    34  	storage.BlobStorageClient
    35  	resources.DeploymentsClient
    36  	resources.GroupsClient
    37  	network.PublicIPAddressesClient
    38  	compute.VirtualMachinesClient
    39  	common.VaultClient
    40  	armStorage.AccountsClient
    41  
    42  	InspectorMaxLength int
    43  	Template           *CaptureTemplate
    44  }
    45  
    46  func getCaptureResponse(body string) *CaptureTemplate {
    47  	var operation CaptureOperation
    48  	err := json.Unmarshal([]byte(body), &operation)
    49  	if err != nil {
    50  		return nil
    51  	}
    52  
    53  	if operation.Properties != nil && operation.Properties.Output != nil {
    54  		return operation.Properties.Output
    55  	}
    56  
    57  	return nil
    58  }
    59  
    60  // HACK(chrboum): This method is a hack.  It was written to work around this issue
    61  // (https://github.com/Azure/azure-sdk-for-go/issues/307) and to an extent this
    62  // issue (https://github.com/Azure/azure-rest-api-specs/issues/188).
    63  //
    64  // Capturing a VM is a long running operation that requires polling.  There are
    65  // couple different forms of polling, and the end result of a poll operation is
    66  // discarded by the SDK.  It is expected that any discarded data can be re-fetched,
    67  // so discarding it has minimal impact.  Unfortunately, there is no way to re-fetch
    68  // the template returned by a capture call that I am aware of.
    69  //
    70  // If the second issue were fixed the VM ID would be included when GET'ing a VM.  The
    71  // VM ID could be used to locate the captured VHD, and captured template.
    72  // Unfortunately, the VM ID is not included so this method cannot be used either.
    73  //
    74  // This code captures the template and saves it to the client (the AzureClient type).
    75  // It expects that the capture API is called only once, or rather you only care that the
    76  // last call's value is important because subsequent requests are not persisted.  There
    77  // is no care given to multiple threads writing this value because for our use case
    78  // it does not matter.
    79  func templateCapture(client *AzureClient) autorest.RespondDecorator {
    80  	return func(r autorest.Responder) autorest.Responder {
    81  		return autorest.ResponderFunc(func(resp *http.Response) error {
    82  			body, bodyString := handleBody(resp.Body, math.MaxInt64)
    83  			resp.Body = body
    84  
    85  			captureTemplate := getCaptureResponse(bodyString)
    86  			if captureTemplate != nil {
    87  				client.Template = captureTemplate
    88  			}
    89  
    90  			return r.Respond(resp)
    91  		})
    92  	}
    93  }
    94  
    95  // WAITING(chrboum): I have logged https://github.com/Azure/azure-sdk-for-go/issues/311 to get this
    96  // method included in the SDK.  It has been accepted, and I'll cut over to the official way
    97  // once it ships.
    98  func byConcatDecorators(decorators ...autorest.RespondDecorator) autorest.RespondDecorator {
    99  	return func(r autorest.Responder) autorest.Responder {
   100  		return autorest.DecorateResponder(r, decorators...)
   101  	}
   102  }
   103  
   104  func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string,
   105  	servicePrincipalToken, servicePrincipalTokenVault *azure.ServicePrincipalToken) (*AzureClient, error) {
   106  
   107  	var azureClient = &AzureClient{}
   108  
   109  	maxlen := getInspectorMaxLength()
   110  
   111  	azureClient.DeploymentsClient = resources.NewDeploymentsClient(subscriptionID)
   112  	azureClient.DeploymentsClient.Authorizer = servicePrincipalToken
   113  	azureClient.DeploymentsClient.RequestInspector = withInspection(maxlen)
   114  	azureClient.DeploymentsClient.ResponseInspector = byInspecting(maxlen)
   115  	azureClient.DeploymentsClient.UserAgent += packerUserAgent
   116  
   117  	azureClient.GroupsClient = resources.NewGroupsClient(subscriptionID)
   118  	azureClient.GroupsClient.Authorizer = servicePrincipalToken
   119  	azureClient.GroupsClient.RequestInspector = withInspection(maxlen)
   120  	azureClient.GroupsClient.ResponseInspector = byInspecting(maxlen)
   121  	azureClient.GroupsClient.UserAgent += packerUserAgent
   122  
   123  	azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClient(subscriptionID)
   124  	azureClient.PublicIPAddressesClient.Authorizer = servicePrincipalToken
   125  	azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen)
   126  	azureClient.PublicIPAddressesClient.ResponseInspector = byInspecting(maxlen)
   127  	azureClient.PublicIPAddressesClient.UserAgent += packerUserAgent
   128  
   129  	azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClient(subscriptionID)
   130  	azureClient.VirtualMachinesClient.Authorizer = servicePrincipalToken
   131  	azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen)
   132  	azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient))
   133  	azureClient.VirtualMachinesClient.UserAgent += packerUserAgent
   134  
   135  	azureClient.AccountsClient = armStorage.NewAccountsClient(subscriptionID)
   136  	azureClient.AccountsClient.Authorizer = servicePrincipalToken
   137  	azureClient.AccountsClient.RequestInspector = withInspection(maxlen)
   138  	azureClient.AccountsClient.ResponseInspector = byInspecting(maxlen)
   139  	azureClient.AccountsClient.UserAgent += packerUserAgent
   140  
   141  	azureClient.VaultClient = common.VaultClient{}
   142  	azureClient.VaultClient.Authorizer = servicePrincipalTokenVault
   143  	azureClient.VaultClient.RequestInspector = withInspection(maxlen)
   144  	azureClient.VaultClient.ResponseInspector = byInspecting(maxlen)
   145  	azureClient.VaultClient.UserAgent += packerUserAgent
   146  
   147  	accountKeys, err := azureClient.AccountsClient.ListKeys(resourceGroupName, storageAccountName)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	storageClient, err := storage.NewBasicClient(storageAccountName, *accountKeys.Key1)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	azureClient.BlobStorageClient = storageClient.GetBlobService()
   158  	return azureClient, nil
   159  }
   160  
   161  func getInspectorMaxLength() int64 {
   162  	value, ok := os.LookupEnv(EnvPackerLogAzureMaxLen)
   163  	if !ok {
   164  		return math.MaxInt64
   165  	}
   166  
   167  	i, err := strconv.ParseInt(value, 10, 64)
   168  	if err != nil {
   169  		return 0
   170  	}
   171  
   172  	if i < 0 {
   173  		return 0
   174  	}
   175  
   176  	return i
   177  }