From 3c33aa4fc52c80ace98b3979d60f220e366dcfc2 Mon Sep 17 00:00:00 2001 From: Paul Meyer Date: Fri, 19 Apr 2019 22:39:24 +0000 Subject: [PATCH] Add metadata client --- builder/azure/chroot/metadata.go | 71 +++++++++++++++++++++++++++ builder/azure/chroot/metadata_test.go | 24 +++++++++ 2 files changed, 95 insertions(+) create mode 100644 builder/azure/chroot/metadata.go create mode 100644 builder/azure/chroot/metadata_test.go diff --git a/builder/azure/chroot/metadata.go b/builder/azure/chroot/metadata.go new file mode 100644 index 000000000..6dea16017 --- /dev/null +++ b/builder/azure/chroot/metadata.go @@ -0,0 +1,71 @@ +package chroot + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/hashicorp/go-retryablehttp" +) + +// 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 MetadataClient interface { + VMResourceID() (string, error) +} + +// metadataClient implements MetadataClient +type metadataClient struct{} + +const imdsURL = "http://169.254.169.254/metadata/instance?api-version=2017-08-01" + +// VMResourceID returns the resource ID of the current VM +func (metadataClient) VMResourceID() (string, error) { + wc := retryablehttp.NewClient() + wc.RetryMax = 5 + + req, err := retryablehttp.NewRequest(http.MethodGet, imdsURL, nil) + if err != nil { + return "", err + } + req.Header.Add("Metadata", "true") + + res, err := wc.Do(req) + if err != nil { + return "", err + } + defer res.Body.Close() + + d, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", err + } + + var vminfo struct { + Compute struct { + Name string + ResourceGroupName string + SubscriptionID string + } + } + + err = json.Unmarshal(d, &vminfo) + if err != nil { + return "", err + } + + return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", + vminfo.Compute.Name, + vminfo.Compute.ResourceGroupName, + vminfo.Compute.SubscriptionID, + ), nil + +} + +// NewMetadataClient creates a new instance metadata client +func NewMetadataClient() MetadataClient { + return metadataClient{} +} diff --git a/builder/azure/chroot/metadata_test.go b/builder/azure/chroot/metadata_test.go new file mode 100644 index 000000000..a707ea3ae --- /dev/null +++ b/builder/azure/chroot/metadata_test.go @@ -0,0 +1,24 @@ +package chroot + +import ( + "testing" + + "github.com/Azure/go-autorest/autorest/azure" + + "github.com/hashicorp/packer/builder/azure/common" + "github.com/stretchr/testify/assert" +) + +func Test_MetadataReturnsVMResourceID(t *testing.T) { + if !common.IsAzure() { + t.Skipf("Not running on Azure, skipping live IMDS test") + } + mdc := NewMetadataClient() + id, err := mdc.VMResourceID() + assert.Nil(t, err) + assert.NotEqual(t, id, "", "Expected VMResourceID to return non-empty string because we are running on Azure") + + vm, err := azure.ParseResourceID(id) + assert.Nil(t, err, "%q is not parsable as an Azure resource id", id) + t.Logf("VM: %+v", vm) +}