From 2ded8f25ecf7c435ea12006d69303084f3adbe5c Mon Sep 17 00:00:00 2001 From: Paul Meyer Date: Wed, 25 Sep 2019 21:13:09 +0000 Subject: [PATCH] Add metadata client --- builder/azure/common/client/metadata.go | 81 ++++++++++++++++++++ builder/azure/common/client/metadata_test.go | 33 ++++++++ 2 files changed, 114 insertions(+) create mode 100644 builder/azure/common/client/metadata.go create mode 100644 builder/azure/common/client/metadata_test.go diff --git a/builder/azure/common/client/metadata.go b/builder/azure/common/client/metadata.go new file mode 100644 index 000000000..ad76cb007 --- /dev/null +++ b/builder/azure/common/client/metadata.go @@ -0,0 +1,81 @@ +package client + +import ( + "fmt" + "net/http" + "time" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" +) + +// DefaultMetadataClient is the default instance metadata client for Azure. Replace this variable for testing purposes only +var DefaultMetadataClient = NewMetadataClient() + +// MetadataClient holds methods that Packer uses to get information about the current VM +type MetadataClientAPI interface { + GetComputeInfo() (*ComputeInfo, error) +} + +type ComputeInfo struct{ + Name string + ResourceGroupName string + SubscriptionID string +Location string +} + +// metadataClient implements MetadataClient +type metadataClient struct { + autorest.Sender +} + +var _ MetadataClientAPI = metadataClient{} + +const imdsURL = "http://169.254.169.254/metadata/instance?api-version=2017-08-01" + +// VMResourceID returns the resource ID of the current VM +func (client metadataClient) GetComputeInfo() (*ComputeInfo, error) { + req, err := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithHeader("Metadata", "true"), + autorest.WithBaseURL(imdsURL), + ).Prepare((&http.Request{})) + if err != nil { + return nil, err + } + + res, err := autorest.SendWithSender(client, req, + autorest.DoRetryForDuration(1*time.Minute, 5*time.Second)) + if err != nil { + return nil, err + } + + var vminfo struct { + ComputeInfo `json:"compute"` + } + + err = autorest.Respond( + res, + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&vminfo), + autorest.ByClosing()) + if err != nil { + return nil, err + } + return &vminfo.ComputeInfo, nil +} + +func(ci ComputeInfo) ResourceID() string{ + return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", + ci.SubscriptionID, + ci.ResourceGroupName, + ci.Name, + ) +} + +// NewMetadataClient creates a new instance metadata client +func NewMetadataClient() MetadataClientAPI { + return metadataClient{ + Sender: autorest.CreateSender(), + } +} diff --git a/builder/azure/common/client/metadata_test.go b/builder/azure/common/client/metadata_test.go new file mode 100644 index 000000000..dddc9a2a0 --- /dev/null +++ b/builder/azure/common/client/metadata_test.go @@ -0,0 +1,33 @@ +package client + +import ( + "fmt" + "testing" + + "github.com/Azure/go-autorest/autorest/azure" + + "github.com/hashicorp/packer/builder/azure/common" + "github.com/stretchr/testify/assert" +) + +func Test_MetadataReturnsComputeInfo(t *testing.T) { + if !common.IsAzure() { + t.Skipf("Not running on Azure, skipping live IMDS test") + } + mdc := NewMetadataClient() + info, err := mdc.GetComputeInfo() + assert.Nil(t, err) + + vm, err := azure.ParseResourceID(fmt.Sprintf( + "/subscriptions/%s"+ + "/resourceGroups/%s"+ + "/providers/Microsoft.Compute"+ + "/virtualMachines/%s", + info.SubscriptionID, + info.ResourceGroupName, + info.Name)) + assert.Nil(t, err, "%q is not parsable as an Azure resource info", info) + + assert.Regexp(t, "^[0-9a-fA-F-]{36}$", vm.SubscriptionID) + t.Logf("VM: %+v", vm) +}