github.phpd.cn/hashicorp/packer@v1.3.2/builder/azure/arm/azure_client.go (about) 1 package arm 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "math" 8 "net/http" 9 "net/url" 10 "os" 11 "strconv" 12 13 "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute" 14 "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-01-01/network" 15 "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" 16 armStorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage" 17 "github.com/Azure/azure-sdk-for-go/storage" 18 "github.com/Azure/go-autorest/autorest" 19 "github.com/Azure/go-autorest/autorest/adal" 20 "github.com/Azure/go-autorest/autorest/azure" 21 "github.com/hashicorp/packer/builder/azure/common" 22 "github.com/hashicorp/packer/helper/useragent" 23 ) 24 25 const ( 26 EnvPackerLogAzureMaxLen = "PACKER_LOG_AZURE_MAXLEN" 27 ) 28 29 type AzureClient struct { 30 storage.BlobStorageClient 31 resources.DeploymentsClient 32 resources.DeploymentOperationsClient 33 resources.GroupsClient 34 network.PublicIPAddressesClient 35 network.InterfacesClient 36 network.SubnetsClient 37 network.VirtualNetworksClient 38 compute.ImagesClient 39 compute.VirtualMachinesClient 40 common.VaultClient 41 armStorage.AccountsClient 42 compute.DisksClient 43 44 InspectorMaxLength int 45 Template *CaptureTemplate 46 LastError azureErrorResponse 47 VaultClientDelete common.VaultClient 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 = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentsClient.UserAgent) 137 138 azureClient.DeploymentOperationsClient = resources.NewDeploymentOperationsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 139 azureClient.DeploymentOperationsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 140 azureClient.DeploymentOperationsClient.RequestInspector = withInspection(maxlen) 141 azureClient.DeploymentOperationsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 142 azureClient.DeploymentOperationsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DeploymentOperationsClient.UserAgent) 143 144 azureClient.DisksClient = compute.NewDisksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 145 azureClient.DisksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 146 azureClient.DisksClient.RequestInspector = withInspection(maxlen) 147 azureClient.DisksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 148 azureClient.DisksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.DisksClient.UserAgent) 149 150 azureClient.GroupsClient = resources.NewGroupsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 151 azureClient.GroupsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 152 azureClient.GroupsClient.RequestInspector = withInspection(maxlen) 153 azureClient.GroupsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 154 azureClient.GroupsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.GroupsClient.UserAgent) 155 156 azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 157 azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 158 azureClient.ImagesClient.RequestInspector = withInspection(maxlen) 159 azureClient.ImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 160 azureClient.ImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.ImagesClient.UserAgent) 161 162 azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 163 azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 164 azureClient.InterfacesClient.RequestInspector = withInspection(maxlen) 165 azureClient.InterfacesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 166 azureClient.InterfacesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.InterfacesClient.UserAgent) 167 168 azureClient.SubnetsClient = network.NewSubnetsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 169 azureClient.SubnetsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 170 azureClient.SubnetsClient.RequestInspector = withInspection(maxlen) 171 azureClient.SubnetsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 172 azureClient.SubnetsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.SubnetsClient.UserAgent) 173 174 azureClient.VirtualNetworksClient = network.NewVirtualNetworksClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 175 azureClient.VirtualNetworksClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 176 azureClient.VirtualNetworksClient.RequestInspector = withInspection(maxlen) 177 azureClient.VirtualNetworksClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 178 azureClient.VirtualNetworksClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualNetworksClient.UserAgent) 179 180 azureClient.PublicIPAddressesClient = network.NewPublicIPAddressesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 181 azureClient.PublicIPAddressesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 182 azureClient.PublicIPAddressesClient.RequestInspector = withInspection(maxlen) 183 azureClient.PublicIPAddressesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 184 azureClient.PublicIPAddressesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.PublicIPAddressesClient.UserAgent) 185 186 azureClient.VirtualMachinesClient = compute.NewVirtualMachinesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 187 azureClient.VirtualMachinesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 188 azureClient.VirtualMachinesClient.RequestInspector = withInspection(maxlen) 189 azureClient.VirtualMachinesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), templateCapture(azureClient), errorCapture(azureClient)) 190 azureClient.VirtualMachinesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VirtualMachinesClient.UserAgent) 191 192 azureClient.AccountsClient = armStorage.NewAccountsClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 193 azureClient.AccountsClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 194 azureClient.AccountsClient.RequestInspector = withInspection(maxlen) 195 azureClient.AccountsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 196 azureClient.AccountsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.AccountsClient.UserAgent) 197 198 keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint) 199 if err != nil { 200 return nil, err 201 } 202 203 azureClient.VaultClient = common.NewVaultClient(*keyVaultURL) 204 azureClient.VaultClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalTokenVault) 205 azureClient.VaultClient.RequestInspector = withInspection(maxlen) 206 azureClient.VaultClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 207 azureClient.VaultClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClient.UserAgent) 208 209 // TODO(boumenot) - SDK still does not have a full KeyVault client. 210 // There are two ways that KeyVault has to be accessed, and each one has their own SPN. An authenticated SPN 211 // is tied to the URL, and the URL associated with getting the secret is different than the URL 212 // associated with deleting the KeyVault. As a result, I need to have *two* different clients to 213 // access KeyVault. I did not want to split it into two separate files, so I am starting with this. 214 // 215 // I do not like this implementation. It is getting long in the tooth, and should be re-examined now 216 // that we have a "working" solution. 217 azureClient.VaultClientDelete = common.NewVaultClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) 218 azureClient.VaultClientDelete.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) 219 azureClient.VaultClientDelete.RequestInspector = withInspection(maxlen) 220 azureClient.VaultClientDelete.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) 221 azureClient.VaultClientDelete.UserAgent = fmt.Sprintf("%s %s", useragent.String(), azureClient.VaultClientDelete.UserAgent) 222 223 // If this is a managed disk build, this should be ignored. 224 if resourceGroupName != "" && storageAccountName != "" { 225 accountKeys, err := azureClient.AccountsClient.ListKeys(context.TODO(), resourceGroupName, storageAccountName) 226 if err != nil { 227 return nil, err 228 } 229 230 storageClient, err := storage.NewClient( 231 storageAccountName, 232 *(*accountKeys.Keys)[0].Value, 233 cloud.StorageEndpointSuffix, 234 storage.DefaultAPIVersion, 235 true /*useHttps*/) 236 237 if err != nil { 238 return nil, err 239 } 240 241 azureClient.BlobStorageClient = storageClient.GetBlobService() 242 } 243 244 return azureClient, nil 245 } 246 247 func getInspectorMaxLength() int64 { 248 value, ok := os.LookupEnv(EnvPackerLogAzureMaxLen) 249 if !ok { 250 return math.MaxInt64 251 } 252 253 i, err := strconv.ParseInt(value, 10, 64) 254 if err != nil { 255 return 0 256 } 257 258 if i < 0 { 259 return 0 260 } 261 262 return i 263 }