2022-09-05 03:06:08 -04:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2022-03-22 11:03:15 -04:00
|
|
|
package azure
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"testing"
|
|
|
|
|
2022-10-24 10:58:21 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/cloud"
|
2022-10-06 06:14:41 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/role"
|
2022-03-22 11:03:15 -04:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"google.golang.org/grpc/test/bufconn"
|
|
|
|
)
|
|
|
|
|
2022-08-29 05:54:30 -04:00
|
|
|
func TestIMDSClient(t *testing.T) {
|
2022-10-06 06:14:41 -04:00
|
|
|
uidTags := []metadataTag{
|
2022-10-24 10:58:21 -04:00
|
|
|
{Name: cloud.TagUID, Value: "uid"},
|
|
|
|
{Name: cloud.TagRole, Value: "worker"},
|
2022-10-06 06:14:41 -04:00
|
|
|
}
|
2022-11-15 03:08:18 -05:00
|
|
|
osProfile := struct {
|
|
|
|
ComputerName string `json:"computerName,omitempty"`
|
|
|
|
}{
|
|
|
|
ComputerName: "computer-name",
|
|
|
|
}
|
|
|
|
|
2022-03-22 11:03:15 -04:00
|
|
|
response := metadataResponse{
|
2022-08-29 05:54:30 -04:00
|
|
|
Compute: metadataResponseCompute{
|
2022-11-15 03:08:18 -05:00
|
|
|
ResourceID: "resource-id",
|
|
|
|
SubscriptionID: "subscription-id",
|
|
|
|
ResourceGroup: "resource-group",
|
|
|
|
Tags: uidTags,
|
|
|
|
OSProfile: osProfile,
|
2022-08-29 05:54:30 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
responseWithoutID := metadataResponse{
|
|
|
|
Compute: metadataResponseCompute{
|
2022-11-15 03:08:18 -05:00
|
|
|
ResourceGroup: "resource-group",
|
|
|
|
SubscriptionID: "subscription-id",
|
|
|
|
Tags: uidTags,
|
|
|
|
OSProfile: osProfile,
|
2022-08-29 05:54:30 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
responseWithoutGroup := metadataResponse{
|
|
|
|
Compute: metadataResponseCompute{
|
2022-11-15 03:08:18 -05:00
|
|
|
ResourceID: "resource-id",
|
|
|
|
SubscriptionID: "subscription-id",
|
|
|
|
Tags: uidTags,
|
|
|
|
OSProfile: osProfile,
|
2022-03-22 11:03:15 -04:00
|
|
|
},
|
|
|
|
}
|
2022-08-29 05:54:30 -04:00
|
|
|
responseWithoutUID := metadataResponse{
|
|
|
|
Compute: metadataResponseCompute{
|
2022-11-15 03:08:18 -05:00
|
|
|
ResourceID: "resource-id",
|
|
|
|
SubscriptionID: "subscription-id",
|
|
|
|
ResourceGroup: "resource-group",
|
|
|
|
Tags: []metadataTag{{Name: cloud.TagRole, Value: "worker"}},
|
|
|
|
OSProfile: osProfile,
|
2022-10-06 06:14:41 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
responseWithoutRole := metadataResponse{
|
2022-11-15 03:08:18 -05:00
|
|
|
Compute: metadataResponseCompute{
|
|
|
|
ResourceID: "resource-id",
|
|
|
|
SubscriptionID: "subscription-id",
|
|
|
|
ResourceGroup: "resource-group",
|
|
|
|
Tags: []metadataTag{{Name: cloud.TagUID, Value: "uid"}},
|
|
|
|
OSProfile: osProfile,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
responseWithoutName := metadataResponse{
|
|
|
|
Compute: metadataResponseCompute{
|
|
|
|
ResourceID: "resource-id",
|
|
|
|
SubscriptionID: "subscription-id",
|
|
|
|
ResourceGroup: "resource-group",
|
|
|
|
Tags: uidTags,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
responseWithoutSubscriptionID := metadataResponse{
|
2022-10-06 06:14:41 -04:00
|
|
|
Compute: metadataResponseCompute{
|
|
|
|
ResourceID: "resource-id",
|
|
|
|
ResourceGroup: "resource-group",
|
2022-11-15 03:08:18 -05:00
|
|
|
Tags: uidTags,
|
|
|
|
OSProfile: osProfile,
|
2022-08-29 05:54:30 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-03-21 07:46:49 -04:00
|
|
|
defaultWantTags := map[string]string{
|
|
|
|
cloud.TagUID: "uid",
|
|
|
|
cloud.TagRole: "worker",
|
|
|
|
}
|
|
|
|
|
2022-03-22 11:03:15 -04:00
|
|
|
testCases := map[string]struct {
|
2022-08-29 05:54:30 -04:00
|
|
|
server httpBufconnServer
|
|
|
|
wantProviderIDErr bool
|
|
|
|
wantProviderID string
|
|
|
|
wantResourceGroupErr bool
|
|
|
|
wantResourceGroup string
|
|
|
|
wantUIDErr bool
|
|
|
|
wantUID string
|
2022-10-06 06:14:41 -04:00
|
|
|
wantRoleErr bool
|
|
|
|
wantRole role.Role
|
2022-11-15 03:08:18 -05:00
|
|
|
wantNameErr bool
|
|
|
|
wantName string
|
|
|
|
wantSubscriptionErr bool
|
|
|
|
wantSubscriptionID string
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTagsErr bool
|
|
|
|
wantTags map[string]string
|
2022-03-22 11:03:15 -04:00
|
|
|
}{
|
|
|
|
"metadata response parsed": {
|
2022-11-15 03:08:18 -05:00
|
|
|
server: newHTTPBufconnServerWithMetadataResponse(response),
|
|
|
|
wantProviderID: "resource-id",
|
|
|
|
wantResourceGroup: "resource-group",
|
|
|
|
wantUID: "uid",
|
|
|
|
wantRole: role.Worker,
|
|
|
|
wantName: "computer-name",
|
|
|
|
wantSubscriptionID: "subscription-id",
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTags: defaultWantTags,
|
2022-08-29 05:54:30 -04:00
|
|
|
},
|
|
|
|
"metadata response without resource ID": {
|
2022-11-15 03:08:18 -05:00
|
|
|
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutID),
|
|
|
|
wantProviderIDErr: true,
|
|
|
|
wantResourceGroup: "resource-group",
|
|
|
|
wantUID: "uid",
|
|
|
|
wantRole: role.Worker,
|
|
|
|
wantName: "computer-name",
|
|
|
|
wantSubscriptionID: "subscription-id",
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTags: defaultWantTags,
|
2022-08-29 05:54:30 -04:00
|
|
|
},
|
|
|
|
"metadata response without UID tag": {
|
2022-11-15 03:08:18 -05:00
|
|
|
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutUID),
|
|
|
|
wantProviderID: "resource-id",
|
|
|
|
wantResourceGroup: "resource-group",
|
|
|
|
wantUIDErr: true,
|
|
|
|
wantRole: role.Worker,
|
|
|
|
wantName: "computer-name",
|
|
|
|
wantSubscriptionID: "subscription-id",
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTags: map[string]string{cloud.TagRole: "worker"},
|
2022-10-06 06:14:41 -04:00
|
|
|
},
|
|
|
|
"metadata response without role tag": {
|
2022-11-15 03:08:18 -05:00
|
|
|
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutRole),
|
|
|
|
wantProviderID: "resource-id",
|
|
|
|
wantResourceGroup: "resource-group",
|
|
|
|
wantUID: "uid",
|
|
|
|
wantRoleErr: true,
|
|
|
|
wantName: "computer-name",
|
|
|
|
wantSubscriptionID: "subscription-id",
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTags: map[string]string{cloud.TagUID: "uid"},
|
2022-08-29 05:54:30 -04:00
|
|
|
},
|
|
|
|
"metadata response without resource group": {
|
|
|
|
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutGroup),
|
|
|
|
wantProviderID: "resource-id",
|
|
|
|
wantResourceGroupErr: true,
|
|
|
|
wantUID: "uid",
|
2022-10-06 06:14:41 -04:00
|
|
|
wantRole: role.Worker,
|
2022-11-15 03:08:18 -05:00
|
|
|
wantName: "computer-name",
|
|
|
|
wantSubscriptionID: "subscription-id",
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTags: defaultWantTags,
|
2022-11-15 03:08:18 -05:00
|
|
|
},
|
|
|
|
"metadata response without name": {
|
|
|
|
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutName),
|
|
|
|
wantProviderID: "resource-id",
|
|
|
|
wantResourceGroup: "resource-group",
|
|
|
|
wantUID: "uid",
|
|
|
|
wantRole: role.Worker,
|
|
|
|
wantNameErr: true,
|
|
|
|
wantSubscriptionID: "subscription-id",
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTags: defaultWantTags,
|
2022-11-15 03:08:18 -05:00
|
|
|
},
|
|
|
|
"metadata response without subscription ID": {
|
|
|
|
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutSubscriptionID),
|
|
|
|
wantProviderID: "resource-id",
|
|
|
|
wantResourceGroup: "resource-group",
|
|
|
|
wantUID: "uid",
|
|
|
|
wantRole: role.Worker,
|
|
|
|
wantName: "computer-name",
|
|
|
|
wantSubscriptionErr: true,
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTags: defaultWantTags,
|
2022-03-22 11:03:15 -04:00
|
|
|
},
|
|
|
|
"invalid imds response detected": {
|
|
|
|
server: newHTTPBufconnServer(func(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
fmt.Fprintln(writer, "invalid-result")
|
|
|
|
}),
|
2022-08-29 05:54:30 -04:00
|
|
|
wantProviderIDErr: true,
|
|
|
|
wantResourceGroupErr: true,
|
|
|
|
wantUIDErr: true,
|
2022-10-06 06:14:41 -04:00
|
|
|
wantRoleErr: true,
|
2022-11-15 03:08:18 -05:00
|
|
|
wantNameErr: true,
|
|
|
|
wantSubscriptionErr: true,
|
2023-03-21 07:46:49 -04:00
|
|
|
wantTagsErr: true,
|
2022-03-22 11:03:15 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range testCases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
assert := assert.New(t)
|
|
|
|
|
|
|
|
defer tc.server.Close()
|
|
|
|
|
|
|
|
hClient := http.Client{
|
|
|
|
Transport: &http.Transport{
|
|
|
|
DialContext: tc.server.DialContext,
|
|
|
|
Dial: tc.server.Dial,
|
|
|
|
DialTLSContext: tc.server.DialContext,
|
|
|
|
DialTLS: tc.server.Dial,
|
|
|
|
},
|
|
|
|
}
|
2023-03-21 07:46:49 -04:00
|
|
|
iClient := IMDSClient{client: &hClient}
|
2022-08-29 05:54:30 -04:00
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
2022-11-15 03:08:18 -05:00
|
|
|
id, err := iClient.providerID(ctx)
|
2022-08-29 05:54:30 -04:00
|
|
|
if tc.wantProviderIDErr {
|
|
|
|
assert.Error(err)
|
|
|
|
} else {
|
|
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(tc.wantProviderID, id)
|
|
|
|
}
|
|
|
|
|
2022-11-15 03:08:18 -05:00
|
|
|
group, err := iClient.resourceGroup(ctx)
|
2022-08-29 05:54:30 -04:00
|
|
|
if tc.wantResourceGroupErr {
|
|
|
|
assert.Error(err)
|
|
|
|
} else {
|
|
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(tc.wantResourceGroup, group)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
2022-11-15 03:08:18 -05:00
|
|
|
uid, err := iClient.uid(ctx)
|
2022-08-29 05:54:30 -04:00
|
|
|
if tc.wantUIDErr {
|
2022-03-22 11:03:15 -04:00
|
|
|
assert.Error(err)
|
2022-08-29 05:54:30 -04:00
|
|
|
} else {
|
|
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(tc.wantUID, uid)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
2022-10-06 06:14:41 -04:00
|
|
|
|
2022-11-15 03:08:18 -05:00
|
|
|
role, err := iClient.role(ctx)
|
2022-10-06 06:14:41 -04:00
|
|
|
if tc.wantRoleErr {
|
|
|
|
assert.Error(err)
|
|
|
|
} else {
|
|
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(tc.wantRole, role)
|
|
|
|
}
|
2022-11-15 03:08:18 -05:00
|
|
|
|
|
|
|
name, err := iClient.name(ctx)
|
|
|
|
if tc.wantNameErr {
|
|
|
|
assert.Error(err)
|
|
|
|
} else {
|
|
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(tc.wantName, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
subscriptionID, err := iClient.subscriptionID(ctx)
|
|
|
|
if tc.wantSubscriptionErr {
|
|
|
|
assert.Error(err)
|
|
|
|
} else {
|
|
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(tc.wantSubscriptionID, subscriptionID)
|
|
|
|
}
|
2023-03-21 07:46:49 -04:00
|
|
|
|
|
|
|
tags, err := iClient.Tags(ctx)
|
|
|
|
if tc.wantTagsErr {
|
|
|
|
assert.Error(err)
|
|
|
|
} else {
|
|
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(tc.wantTags, tags)
|
|
|
|
}
|
2022-03-22 11:03:15 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type httpBufconnServer struct {
|
|
|
|
*httptest.Server
|
|
|
|
*bufconn.Listener
|
|
|
|
}
|
|
|
|
|
2023-03-20 06:03:36 -04:00
|
|
|
func (s *httpBufconnServer) DialContext(ctx context.Context, _, _ string) (net.Conn, error) {
|
2022-03-22 11:03:15 -04:00
|
|
|
return s.Listener.DialContext(ctx)
|
|
|
|
}
|
|
|
|
|
2023-03-20 06:03:36 -04:00
|
|
|
func (s *httpBufconnServer) Dial(_, _ string) (net.Conn, error) {
|
2022-03-22 11:03:15 -04:00
|
|
|
return s.Listener.Dial()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *httpBufconnServer) Close() {
|
|
|
|
s.Server.Close()
|
|
|
|
s.Listener.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func newHTTPBufconnServer(handlerFunc http.HandlerFunc) httpBufconnServer {
|
|
|
|
server := httptest.NewUnstartedServer(handlerFunc)
|
|
|
|
listener := bufconn.Listen(1024)
|
|
|
|
server.Listener = listener
|
|
|
|
server.Start()
|
|
|
|
return httpBufconnServer{
|
|
|
|
Server: server,
|
|
|
|
Listener: listener,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newHTTPBufconnServerWithMetadataResponse(res metadataResponse) httpBufconnServer {
|
|
|
|
return newHTTPBufconnServer(func(writer http.ResponseWriter, request *http.Request) {
|
|
|
|
if request.Host != "169.254.169.254" || request.Header.Get("Metadata") != "True" || request.URL.Query().Get("format") != "json" || request.URL.Query().Get("api-version") != imdsAPIVersion {
|
|
|
|
writer.WriteHeader(http.StatusInternalServerError)
|
|
|
|
_, err := writer.Write([]byte("error"))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
rawResp, err := json.Marshal(res)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
_, err = writer.Write(rawResp)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|