Add constellation UID retrieval to cloudprovider metadata APIs

Signed-off-by: Malte Poll <mp@edgeless.systems>
This commit is contained in:
Malte Poll 2022-07-29 16:30:24 +02:00 committed by Malte Poll
parent 2f925b5955
commit 18a89d2881
7 changed files with 116 additions and 6 deletions

View File

@ -2,6 +2,7 @@ package azure
import (
"context"
"errors"
"fmt"
"net/http"
"regexp"
@ -16,8 +17,9 @@ import (
)
var (
publicIPAddressRegexp = regexp.MustCompile(`/subscriptions/[^/]+/resourceGroups/[^/]+/providers/Microsoft.Network/publicIPAddresses/(?P<IPname>[^/]+)`)
keyPathRegexp = regexp.MustCompile(`^\/home\/([^\/]+)\/\.ssh\/authorized_keys$`)
publicIPAddressRegexp = regexp.MustCompile(`/subscriptions/[^/]+/resourceGroups/[^/]+/providers/Microsoft.Network/publicIPAddresses/(?P<IPname>[^/]+)`)
keyPathRegexp = regexp.MustCompile(`^\/home\/([^\/]+)\/\.ssh\/authorized_keys$`)
resourceGroupNameRegexp = regexp.MustCompile(`^(.*)-([^-]+)$`)
)
// Metadata implements azure metadata APIs.
@ -183,6 +185,25 @@ func (m *Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
return *virtualNetwork.Properties.Subnets[0].Properties.AddressPrefix, nil
}
// UID retrieves the UID of the constellation.
func (m *Metadata) UID(ctx context.Context) (string, error) {
providerID, err := m.providerID(ctx)
if err != nil {
return "", err
}
_, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
if err != nil {
return "", err
}
uid, err := getUIDFromResourceGroup(resourceGroup)
if err != nil {
return "", err
}
return uid, nil
}
// getLoadBalancer retrieves the load balancer from cloud provider metadata.
func (m *Metadata) getLoadBalancer(ctx context.Context) (*armnetwork.LoadBalancer, error) {
providerID, err := m.providerID(ctx)
@ -314,3 +335,11 @@ func extractSSHKeys(sshConfig armcomputev2.SSHConfiguration) map[string][]string
}
return sshKeys
}
func getUIDFromResourceGroup(resourceGroup string) (string, error) {
matches := resourceGroupNameRegexp.FindStringSubmatch(resourceGroup)
if len(matches) != 3 {
return "", errors.New("error splitting resource group name")
}
return matches[2], nil
}

View File

@ -499,6 +499,58 @@ func TestProviderID(t *testing.T) {
}
}
func TestUID(t *testing.T) {
testCases := map[string]struct {
imdsAPI imdsAPI
wantErr bool
wantUID string
}{
"uid extraction from providerID works": {
imdsAPI: &stubIMDSAPI{
res: metadataResponse{Compute: struct {
ResourceID string `json:"resourceId,omitempty"`
}{"/subscriptions/subscription-id/resourceGroups/basename-uid/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"}},
},
wantUID: "uid",
},
"providerID does not contain uid": {
imdsAPI: &stubIMDSAPI{
res: metadataResponse{Compute: struct {
ResourceID string `json:"resourceId,omitempty"`
}{"/subscriptions/subscription-id/resourceGroups/invalid/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"}},
},
wantErr: true,
},
"providerID is invalid": {
imdsAPI: newInvalidIMDSStub(),
wantErr: true,
},
"imds retrieval fails": {
imdsAPI: &stubIMDSAPI{retrieveErr: errors.New("imds err")},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
metadata := Metadata{
imdsAPI: tc.imdsAPI,
}
uid, err := metadata.UID(context.Background())
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantUID, uid)
})
}
}
func TestExtractInstanceTags(t *testing.T) {
testCases := map[string]struct {
in map[string]*string

View File

@ -53,7 +53,7 @@ func NewClient(ctx context.Context) (*Client, error) {
// RetrieveInstances returns list of instances including their ips and metadata.
func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([]metadata.InstanceMetadata, error) {
uid, err := c.uid()
uid, err := c.UID()
if err != nil {
return nil, err
}
@ -214,7 +214,7 @@ func (c *Client) RetrieveSubnetworkAliasCIDR(ctx context.Context, project, zone,
// RetrieveLoadBalancerIP returns the IP address of the load balancer specified by project, zone and loadBalancerName.
func (c *Client) RetrieveLoadBalancerIP(ctx context.Context, project, zone string) (string, error) {
uid, err := c.uid()
uid, err := c.UID()
if err != nil {
return "", err
}
@ -284,8 +284,8 @@ func (c *Client) updateInstanceMetadata(ctx context.Context, project, zone, inst
return nil
}
// uid retrieves the current instances uid.
func (c *Client) uid() (string, error) {
// UID retrieves the current instances uid.
func (c *Client) UID() (string, error) {
// API endpoint: http://metadata.google.internal/computeMetadata/v1/instance/attributes/constellation-uid
uid, err := c.RetrieveInstanceMetadata(constellationUIDMetadataKey)
if err != nil {

View File

@ -10,6 +10,8 @@ import (
// API handles all GCP API requests.
type API interface {
// UID retrieves the current instances uid.
UID() (string, error)
// RetrieveInstances retrieves a list of all accessible GCP instances with their metadata.
RetrieveInstances(ctx context.Context, project, zone string) ([]metadata.InstanceMetadata, error)
// RetrieveInstances retrieves a single GCP instances with its metadata.
@ -122,6 +124,11 @@ func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
return m.api.RetrieveLoadBalancerIP(ctx, project, zone)
}
// UID retrieves the UID of the constellation.
func (m *Metadata) UID(ctx context.Context) (string, error) {
return m.api.UID()
}
// Supported is used to determine if metadata API is implemented for this cloud provider.
func (m *Metadata) Supported() bool {
return true

View File

@ -231,6 +231,8 @@ func TestGetInstance(t *testing.T) {
}
type stubGCPClient struct {
retrieveUIDValue string
retrieveUIDErr error
retrieveInstanceValue metadata.InstanceMetadata
retrieveInstanceErr error
retrieveInstancesValues []metadata.InstanceMetadata
@ -289,6 +291,10 @@ func (s *stubGCPClient) RetrieveLoadBalancerIP(ctx context.Context, project, zon
return s.loadBalancerIP, s.retrieveLoadBalancerErr
}
func (s *stubGCPClient) UID() (string, error) {
return s.retrieveUIDValue, s.retrieveUIDErr
}
func (s *stubGCPClient) SetInstanceMetadata(ctx context.Context, project, zone, instanceName, key, value string) error {
s.instanceMetadataProjects = append(s.instanceMetadataProjects, project)
s.instanceMetadataZones = append(s.instanceMetadataZones, zone)

View File

@ -70,6 +70,13 @@ func (m Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
panic("function *Metadata.GetLoadBalancerIP not implemented")
}
// UID returns the UID of the constellation.
func (m Metadata) UID(ctx context.Context) (string, error) {
// We expect only one constellation to be deployed in the same QEMU / libvirt environment.
// the UID can be an empty string.
return "", nil
}
// GetSubnetworkCIDR retrieves the subnetwork CIDR from cloud provider metadata.
func (m Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
return "10.244.0.0/16", nil

View File

@ -11,6 +11,8 @@ import (
// ProviderMetadata implementers read/write cloud provider metadata.
type ProviderMetadata interface {
// UID returns the unique identifier for the constellation.
UID(ctx context.Context) (string, error)
// List retrieves all instances belonging to the current Constellation.
List(ctx context.Context) ([]metadata.InstanceMetadata, error)
// Self retrieves the current instance.
@ -100,6 +102,9 @@ type stubProviderMetadata struct {
SupportedResp bool
SupportsLoadBalancerResp bool
UIDErr error
UIDResp string
}
func (m *stubProviderMetadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
@ -130,6 +135,10 @@ func (m *stubProviderMetadata) SupportsLoadBalancer() bool {
return m.SupportsLoadBalancerResp
}
func (m *stubProviderMetadata) UID(ctx context.Context) (string, error) {
return m.UIDResp, m.UIDErr
}
type stubCloudControllerManager struct {
SupportedResp bool
}