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 }