mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-08-24 06:29:19 -04:00
Refactor provider metadata
This commit is contained in:
parent
32f1f5fd3e
commit
09e86e6c5d
36 changed files with 198 additions and 1340 deletions
|
@ -1,8 +1,8 @@
|
|||
package gcp
|
||||
|
||||
import (
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
k8s "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
|
@ -15,7 +15,7 @@ func (a *Autoscaler) Name() string {
|
|||
}
|
||||
|
||||
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler.
|
||||
func (a *Autoscaler) Secrets(instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||
func (a *Autoscaler) Secrets(instance metadata.InstanceMetadata, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||
return resources.Secrets{}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package gcp
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -12,7 +12,7 @@ func TestTrivialAutoscalerFunctions(t *testing.T) {
|
|||
autoscaler := Autoscaler{}
|
||||
|
||||
assert.NotEmpty(autoscaler.Name())
|
||||
assert.Empty(autoscaler.Secrets(cloudtypes.Instance{}, ""))
|
||||
assert.Empty(autoscaler.Secrets(metadata.InstanceMetadata{}, ""))
|
||||
assert.NotEmpty(autoscaler.Volumes())
|
||||
assert.NotEmpty(autoscaler.VolumeMounts())
|
||||
assert.NotEmpty(autoscaler.Env())
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
"github.com/edgelesssys/constellation/internal/gcpshared"
|
||||
k8s "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -46,7 +46,7 @@ func (c *CloudControllerManager) ExtraArgs() []string {
|
|||
|
||||
// ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager
|
||||
// Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ .
|
||||
func (c *CloudControllerManager) ConfigMaps(instance cloudtypes.Instance) (resources.ConfigMaps, error) {
|
||||
func (c *CloudControllerManager) ConfigMaps(instance metadata.InstanceMetadata) (resources.ConfigMaps, error) {
|
||||
// GCP CCM expects cloud config to contain the GCP project-id and other configuration.
|
||||
// reference: https://github.com/kubernetes/cloud-provider-gcp/blob/master/cluster/gce/gci/configure-helper.sh#L791-L892
|
||||
var config strings.Builder
|
||||
|
@ -80,7 +80,7 @@ func (c *CloudControllerManager) ConfigMaps(instance cloudtypes.Instance) (resou
|
|||
|
||||
// Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager.
|
||||
// Reference: https://kubernetes.io/docs/concepts/configuration/secret/ .
|
||||
func (c *CloudControllerManager) Secrets(ctx context.Context, instance cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||
func (c *CloudControllerManager) Secrets(ctx context.Context, _ string, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||
serviceAccountKey, err := gcpshared.ServiceAccountKeyFromURI(cloudServiceAccountURI)
|
||||
if err != nil {
|
||||
return resources.Secrets{}, err
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
"github.com/edgelesssys/constellation/internal/gcpshared"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -16,12 +16,12 @@ import (
|
|||
|
||||
func TestConfigMaps(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
instance cloudtypes.Instance
|
||||
instance metadata.InstanceMetadata
|
||||
wantConfigMaps resources.ConfigMaps
|
||||
wantErr bool
|
||||
}{
|
||||
"ConfigMaps works": {
|
||||
instance: cloudtypes.Instance{ProviderID: "gce://project-id/zone/instanceName-UID-0", Name: "instanceName-UID-0"},
|
||||
instance: metadata.InstanceMetadata{ProviderID: "gce://project-id/zone/instanceName-UID-0", Name: "instanceName-UID-0"},
|
||||
wantConfigMaps: resources.ConfigMaps{
|
||||
&k8s.ConfigMap{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
|
@ -43,7 +43,7 @@ node-tags = constellation-UID
|
|||
},
|
||||
},
|
||||
"invalid providerID fails": {
|
||||
instance: cloudtypes.Instance{ProviderID: "invalid"},
|
||||
instance: metadata.InstanceMetadata{ProviderID: "invalid"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func TestSecrets(t *testing.T) {
|
|||
rawKey, err := json.Marshal(serviceAccountKey)
|
||||
require.NoError(t, err)
|
||||
testCases := map[string]struct {
|
||||
instance cloudtypes.Instance
|
||||
instance metadata.InstanceMetadata
|
||||
cloudServiceAccountURI string
|
||||
wantSecrets resources.Secrets
|
||||
wantErr bool
|
||||
|
@ -117,7 +117,7 @@ func TestSecrets(t *testing.T) {
|
|||
require := require.New(t)
|
||||
|
||||
cloud := CloudControllerManager{}
|
||||
secrets, err := cloud.Secrets(context.Background(), tc.instance, tc.cloudServiceAccountURI)
|
||||
secrets, err := cloud.Secrets(context.Background(), tc.instance.ProviderID, tc.cloudServiceAccountURI)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
|
|
|
@ -7,16 +7,17 @@ import (
|
|||
"strings"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/core"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
"github.com/edgelesssys/constellation/internal/gcpshared"
|
||||
"google.golang.org/api/iterator"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const gcpSSHMetadataKey = "ssh-keys"
|
||||
const (
|
||||
gcpSSHMetadataKey = "ssh-keys"
|
||||
constellationUIDMetadataKey = "constellation-uid"
|
||||
)
|
||||
|
||||
var zoneFromRegionRegex = regexp.MustCompile("([a-z]*-[a-z]*[0-9])")
|
||||
|
||||
|
@ -51,7 +52,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) ([]cloudtypes.Instance, error) {
|
||||
func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([]metadata.InstanceMetadata, error) {
|
||||
uid, err := c.uid()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -62,7 +63,7 @@ func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([
|
|||
}
|
||||
instanceIterator := c.instanceAPI.List(ctx, req)
|
||||
|
||||
instances := []cloudtypes.Instance{}
|
||||
instances := []metadata.InstanceMetadata{}
|
||||
for {
|
||||
resp, err := instanceIterator.Next()
|
||||
if err == iterator.Done {
|
||||
|
@ -73,7 +74,7 @@ func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([
|
|||
}
|
||||
metadata := extractInstanceMetadata(resp.Metadata, "", false)
|
||||
// skip instances not belonging to the current constellation
|
||||
if instanceUID, ok := metadata[core.ConstellationUIDMetadataKey]; !ok || instanceUID != uid {
|
||||
if instanceUID, ok := metadata[constellationUIDMetadataKey]; !ok || instanceUID != uid {
|
||||
continue
|
||||
}
|
||||
instance, err := convertToCoreInstance(resp, project, zone)
|
||||
|
@ -87,10 +88,10 @@ func (c *Client) RetrieveInstances(ctx context.Context, project, zone string) ([
|
|||
}
|
||||
|
||||
// RetrieveInstance returns a an instance including ips and metadata.
|
||||
func (c *Client) RetrieveInstance(ctx context.Context, project, zone, instanceName string) (cloudtypes.Instance, error) {
|
||||
func (c *Client) RetrieveInstance(ctx context.Context, project, zone, instanceName string) (metadata.InstanceMetadata, error) {
|
||||
instance, err := c.getComputeInstance(ctx, project, zone, instanceName)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
|
||||
return convertToCoreInstance(instance, project, zone)
|
||||
|
@ -286,7 +287,7 @@ func (c *Client) updateInstanceMetadata(ctx context.Context, project, zone, inst
|
|||
// 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(core.ConstellationUIDMetadataKey)
|
||||
uid, err := c.RetrieveInstanceMetadata(constellationUIDMetadataKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("retrieving constellation uid: %w", err)
|
||||
}
|
||||
|
@ -367,19 +368,19 @@ func extractSSHKeys(metadata map[string]string) map[string][]string {
|
|||
}
|
||||
|
||||
// convertToCoreInstance converts a *computepb.Instance to a core.Instance.
|
||||
func convertToCoreInstance(in *computepb.Instance, project string, zone string) (cloudtypes.Instance, error) {
|
||||
func convertToCoreInstance(in *computepb.Instance, project string, zone string) (metadata.InstanceMetadata, error) {
|
||||
if in.Name == nil {
|
||||
return cloudtypes.Instance{}, fmt.Errorf("retrieving instance from compute API client returned invalid instance Name: %v", in.Name)
|
||||
return metadata.InstanceMetadata{}, fmt.Errorf("retrieving instance from compute API client returned invalid instance Name: %v", in.Name)
|
||||
}
|
||||
metadata := extractInstanceMetadata(in.Metadata, "", false)
|
||||
return cloudtypes.Instance{
|
||||
mdata := extractInstanceMetadata(in.Metadata, "", false)
|
||||
return metadata.InstanceMetadata{
|
||||
Name: *in.Name,
|
||||
ProviderID: gcpshared.JoinProviderID(project, zone, *in.Name),
|
||||
Role: cloudprovider.ExtractRole(metadata),
|
||||
Role: extractRole(mdata),
|
||||
PrivateIPs: extractPrivateIPs(in.NetworkInterfaces),
|
||||
PublicIPs: extractPublicIPs(in.NetworkInterfaces),
|
||||
AliasIPRanges: extractAliasIPRanges(in.NetworkInterfaces),
|
||||
SSHKeys: extractSSHKeys(metadata),
|
||||
SSHKeys: extractSSHKeys(mdata),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,8 @@ import (
|
|||
"testing"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/core"
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
gax "github.com/googleapis/gax-go/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -44,11 +43,11 @@ func TestRetrieveInstances(t *testing.T) {
|
|||
Value: proto.String("value-2"),
|
||||
},
|
||||
{
|
||||
Key: proto.String(core.ConstellationUIDMetadataKey),
|
||||
Key: proto.String(constellationUIDMetadataKey),
|
||||
Value: proto.String(uid),
|
||||
},
|
||||
{
|
||||
Key: proto.String(core.RoleMetadataKey),
|
||||
Key: proto.String(roleMetadataKey),
|
||||
Value: proto.String(role.Coordinator.String()),
|
||||
},
|
||||
},
|
||||
|
@ -70,14 +69,14 @@ func TestRetrieveInstances(t *testing.T) {
|
|||
metadata stubMetadataClient
|
||||
instanceIter *stubInstanceIterator
|
||||
instanceIterMutator func(*stubInstanceIterator)
|
||||
wantInstances []cloudtypes.Instance
|
||||
wantInstances []metadata.InstanceMetadata
|
||||
wantErr bool
|
||||
}{
|
||||
"retrieve works": {
|
||||
client: stubInstancesClient{},
|
||||
metadata: stubMetadataClient{InstanceValue: uid},
|
||||
instanceIter: newTestIter(),
|
||||
wantInstances: []cloudtypes.Instance{
|
||||
wantInstances: []metadata.InstanceMetadata{
|
||||
{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
|
@ -101,7 +100,7 @@ func TestRetrieveInstances(t *testing.T) {
|
|||
metadata: stubMetadataClient{InstanceValue: uid},
|
||||
instanceIter: newTestIter(),
|
||||
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].NetworkInterfaces = nil },
|
||||
wantInstances: []cloudtypes.Instance{
|
||||
wantInstances: []metadata.InstanceMetadata{
|
||||
{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
|
@ -118,7 +117,7 @@ func TestRetrieveInstances(t *testing.T) {
|
|||
metadata: stubMetadataClient{InstanceValue: uid},
|
||||
instanceIter: newTestIter(),
|
||||
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].NetworkInterfaces[0].NetworkIP = nil },
|
||||
wantInstances: []cloudtypes.Instance{
|
||||
wantInstances: []metadata.InstanceMetadata{
|
||||
{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
|
@ -135,7 +134,7 @@ func TestRetrieveInstances(t *testing.T) {
|
|||
metadata: stubMetadataClient{InstanceValue: uid},
|
||||
instanceIter: newTestIter(),
|
||||
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].Metadata.Items[2].Key = proto.String("") },
|
||||
wantInstances: []cloudtypes.Instance{},
|
||||
wantInstances: []metadata.InstanceMetadata{},
|
||||
},
|
||||
"constellation retrieval fails": {
|
||||
client: stubInstancesClient{},
|
||||
|
@ -148,7 +147,7 @@ func TestRetrieveInstances(t *testing.T) {
|
|||
metadata: stubMetadataClient{InstanceValue: uid},
|
||||
instanceIter: newTestIter(),
|
||||
instanceIterMutator: func(sii *stubInstanceIterator) { sii.instances[0].Metadata.Items[3].Key = proto.String("") },
|
||||
wantInstances: []cloudtypes.Instance{
|
||||
wantInstances: []metadata.InstanceMetadata{
|
||||
{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
|
@ -224,13 +223,13 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client stubInstancesClient
|
||||
clientInstance *computepb.Instance
|
||||
clientInstanceMutator func(*computepb.Instance)
|
||||
wantInstance cloudtypes.Instance
|
||||
wantInstance metadata.InstanceMetadata
|
||||
wantErr bool
|
||||
}{
|
||||
"retrieve works": {
|
||||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||
|
@ -246,7 +245,7 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
i.Metadata.Items[0].Key = proto.String("ssh-keys")
|
||||
i.Metadata.Items[0].Value = proto.String("bob:ssh-rsa bobskey")
|
||||
},
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||
|
@ -259,10 +258,10 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
clientInstanceMutator: func(i *computepb.Instance) {
|
||||
i.Metadata.Items[0].Key = proto.String(core.RoleMetadataKey)
|
||||
i.Metadata.Items[0].Key = proto.String(roleMetadataKey)
|
||||
i.Metadata.Items[0].Value = proto.String(role.Coordinator.String())
|
||||
},
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||
|
@ -283,7 +282,7 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0] = nil },
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||
|
@ -296,7 +295,7 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0].Key = nil },
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||
|
@ -309,7 +308,7 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
clientInstanceMutator: func(i *computepb.Instance) { i.Metadata.Items[0].Value = nil },
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||
|
@ -322,7 +321,7 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0] = nil },
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{},
|
||||
|
@ -335,7 +334,7 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0].NetworkIP = nil },
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||
|
@ -348,7 +347,7 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0].AliasIpRanges[0].IpCidrRange = nil },
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{},
|
||||
|
@ -361,7 +360,7 @@ func TestRetrieveInstance(t *testing.T) {
|
|||
client: stubInstancesClient{},
|
||||
clientInstance: newTestInstance(),
|
||||
clientInstanceMutator: func(i *computepb.Instance) { i.NetworkInterfaces[0].AccessConfigs[0].NatIP = nil },
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
AliasIPRanges: []string{"192.0.2.0/16"},
|
||||
|
|
|
@ -4,18 +4,16 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/core"
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
"github.com/edgelesssys/constellation/internal/gcpshared"
|
||||
)
|
||||
|
||||
// API handles all GCP API requests.
|
||||
type API interface {
|
||||
// RetrieveInstances retrieves a list of all accessible GCP instances with their metadata.
|
||||
RetrieveInstances(ctx context.Context, project, zone string) ([]cloudtypes.Instance, error)
|
||||
RetrieveInstances(ctx context.Context, project, zone string) ([]metadata.InstanceMetadata, error)
|
||||
// RetrieveInstances retrieves a single GCP instances with its metadata.
|
||||
RetrieveInstance(ctx context.Context, project, zone, instanceName string) (cloudtypes.Instance, error)
|
||||
RetrieveInstance(ctx context.Context, project, zone, instanceName string) (metadata.InstanceMetadata, error)
|
||||
// RetrieveInstanceMetadata retrieves the GCP instance metadata of the current instance.
|
||||
RetrieveInstanceMetadata(attr string) (string, error)
|
||||
// RetrieveProjectID retrieves the GCP projectID containing the current instance.
|
||||
|
@ -47,7 +45,7 @@ func New(api API) *Metadata {
|
|||
}
|
||||
|
||||
// List retrieves all instances belonging to the current constellation.
|
||||
func (m *Metadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
||||
func (m *Metadata) List(ctx context.Context) ([]metadata.InstanceMetadata, error) {
|
||||
project, err := m.api.RetrieveProjectID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -64,65 +62,31 @@ func (m *Metadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
|||
}
|
||||
|
||||
// Self retrieves the current instance.
|
||||
func (m *Metadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
|
||||
func (m *Metadata) Self(ctx context.Context) (metadata.InstanceMetadata, error) {
|
||||
project, err := m.api.RetrieveProjectID()
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
zone, err := m.api.RetrieveZone()
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
instanceName, err := m.api.RetrieveInstanceName()
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
return m.api.RetrieveInstance(ctx, project, zone, instanceName)
|
||||
}
|
||||
|
||||
// GetInstance retrieves an instance using its providerID.
|
||||
func (m *Metadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||
func (m *Metadata) GetInstance(ctx context.Context, providerID string) (metadata.InstanceMetadata, error) {
|
||||
project, zone, instanceName, err := gcpshared.SplitProviderID(providerID)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, fmt.Errorf("invalid providerID: %w", err)
|
||||
return metadata.InstanceMetadata{}, fmt.Errorf("invalid providerID: %w", err)
|
||||
}
|
||||
return m.api.RetrieveInstance(ctx, project, zone, instanceName)
|
||||
}
|
||||
|
||||
// SignalRole signals the constellation role via cloud provider metadata.
|
||||
func (m *Metadata) SignalRole(ctx context.Context, role role.Role) error {
|
||||
project, err := m.api.RetrieveProjectID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zone, err := m.api.RetrieveZone()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
instanceName, err := m.api.RetrieveInstanceName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.api.SetInstanceMetadata(ctx, project, zone, instanceName, core.RoleMetadataKey, role.String())
|
||||
}
|
||||
|
||||
// SetVPNIP stores the internally used VPN IP in cloud provider metadata.
|
||||
func (m *Metadata) SetVPNIP(ctx context.Context, vpnIP string) error {
|
||||
project, err := m.api.RetrieveProjectID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zone, err := m.api.RetrieveZone()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
instanceName, err := m.api.RetrieveInstanceName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.api.SetInstanceMetadata(ctx, project, zone, instanceName, core.VPNIPMetadataKey, vpnIP)
|
||||
}
|
||||
|
||||
// GetSubnetworkCIDR returns the subnetwork CIDR of the current instance.
|
||||
func (m *Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
|
||||
project, err := m.api.RetrieveProjectID()
|
||||
|
@ -140,11 +104,6 @@ func (m *Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
|
|||
return m.api.RetrieveSubnetworkAliasCIDR(ctx, project, zone, instanceName)
|
||||
}
|
||||
|
||||
// SupportsLoadBalancer returns true if the cloud provider supports load balancers.
|
||||
func (m *Metadata) SupportsLoadBalancer() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetLoadBalancerIP returns the IP of the load balancer.
|
||||
func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
|
||||
project, err := m.api.RetrieveProjectID()
|
||||
|
@ -157,8 +116,3 @@ func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
|
|||
}
|
||||
return m.api.RetrieveLoadBalancerIP(ctx, project, zone)
|
||||
}
|
||||
|
||||
// Supported is used to determine if metadata API is implemented for this cloud provider.
|
||||
func (m *Metadata) Supported() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -5,9 +5,7 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/core"
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -15,8 +13,8 @@ import (
|
|||
func TestList(t *testing.T) {
|
||||
err := errors.New("some err")
|
||||
uid := "1234"
|
||||
instancesGenerator := func() *[]cloudtypes.Instance {
|
||||
return &[]cloudtypes.Instance{
|
||||
instancesGenerator := func() *[]metadata.InstanceMetadata {
|
||||
return &[]metadata.InstanceMetadata{
|
||||
{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
|
@ -27,10 +25,10 @@ func TestList(t *testing.T) {
|
|||
|
||||
testCases := map[string]struct {
|
||||
client stubGCPClient
|
||||
instancesGenerator func() *[]cloudtypes.Instance
|
||||
instancesMutator func(*[]cloudtypes.Instance)
|
||||
instancesGenerator func() *[]metadata.InstanceMetadata
|
||||
instancesMutator func(*[]metadata.InstanceMetadata)
|
||||
wantErr bool
|
||||
wantInstances []cloudtypes.Instance
|
||||
wantInstances []metadata.InstanceMetadata
|
||||
}{
|
||||
"retrieve works": {
|
||||
client: stubGCPClient{
|
||||
|
@ -41,7 +39,7 @@ func TestList(t *testing.T) {
|
|||
},
|
||||
},
|
||||
instancesGenerator: instancesGenerator,
|
||||
wantInstances: []cloudtypes.Instance{
|
||||
wantInstances: []metadata.InstanceMetadata{
|
||||
{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
|
@ -106,19 +104,19 @@ func TestSelf(t *testing.T) {
|
|||
testCases := map[string]struct {
|
||||
client stubGCPClient
|
||||
wantErr bool
|
||||
wantInstance cloudtypes.Instance
|
||||
wantInstance metadata.InstanceMetadata
|
||||
}{
|
||||
"retrieve works": {
|
||||
client: stubGCPClient{
|
||||
projectID: "someProjectID",
|
||||
zone: "someZone",
|
||||
retrieveInstanceValue: cloudtypes.Instance{
|
||||
retrieveInstanceValue: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
},
|
||||
},
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
|
@ -180,18 +178,18 @@ func TestGetInstance(t *testing.T) {
|
|||
providerID string
|
||||
client stubGCPClient
|
||||
wantErr bool
|
||||
wantInstance cloudtypes.Instance
|
||||
wantInstance metadata.InstanceMetadata
|
||||
}{
|
||||
"retrieve works": {
|
||||
providerID: "gce://someProject/someZone/someInstance",
|
||||
client: stubGCPClient{
|
||||
retrieveInstanceValue: cloudtypes.Instance{
|
||||
retrieveInstanceValue: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
},
|
||||
},
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "someInstance",
|
||||
ProviderID: "gce://someProject/someZone/someInstance",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
|
@ -232,135 +230,10 @@ func TestGetInstance(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSignalRole(t *testing.T) {
|
||||
err := errors.New("some err")
|
||||
|
||||
testCases := map[string]struct {
|
||||
client stubGCPClient
|
||||
wantErr bool
|
||||
wantRole role.Role
|
||||
}{
|
||||
"signaling role works": {
|
||||
client: stubGCPClient{
|
||||
projectID: "someProjectID",
|
||||
zone: "someZone",
|
||||
instanceName: "someName",
|
||||
},
|
||||
wantRole: role.Coordinator,
|
||||
},
|
||||
"project metadata retrieval error is detected": {
|
||||
client: stubGCPClient{
|
||||
retrieveProjectIDErr: err,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"instance zone retrieval error is detected": {
|
||||
client: stubGCPClient{
|
||||
retrieveZoneErr: err,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"instance name retrieval error is detected": {
|
||||
client: stubGCPClient{
|
||||
retrieveInstanceNameErr: err,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
cloud := New(&tc.client)
|
||||
err := cloud.SignalRole(context.Background(), tc.wantRole)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
assert.ElementsMatch([]string{"someProjectID"}, tc.client.instanceMetadataProjects)
|
||||
assert.ElementsMatch([]string{"someZone"}, tc.client.instanceMetadataZones)
|
||||
assert.ElementsMatch([]string{"someName"}, tc.client.instanceMetadataInstanceNames)
|
||||
assert.ElementsMatch([]string{core.RoleMetadataKey}, tc.client.instanceMetadataKeys)
|
||||
assert.ElementsMatch([]string{tc.wantRole.String()}, tc.client.instanceMetadataValues)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetVPNIP(t *testing.T) {
|
||||
err := errors.New("some err")
|
||||
|
||||
testCases := map[string]struct {
|
||||
client stubGCPClient
|
||||
wantErr bool
|
||||
wantVPNIP string
|
||||
}{
|
||||
"signaling role works": {
|
||||
client: stubGCPClient{
|
||||
projectID: "someProjectID",
|
||||
zone: "someZone",
|
||||
instanceName: "someName",
|
||||
},
|
||||
wantVPNIP: "192.0.2.0",
|
||||
},
|
||||
"project metadata retrieval error is detected": {
|
||||
client: stubGCPClient{
|
||||
retrieveProjectIDErr: err,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"instance zone retrieval error is detected": {
|
||||
client: stubGCPClient{
|
||||
retrieveZoneErr: err,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"instance name retrieval error is detected": {
|
||||
client: stubGCPClient{
|
||||
retrieveInstanceNameErr: err,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
cloud := New(&tc.client)
|
||||
err := cloud.SetVPNIP(context.Background(), tc.wantVPNIP)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
assert.ElementsMatch([]string{"someProjectID"}, tc.client.instanceMetadataProjects)
|
||||
assert.ElementsMatch([]string{"someZone"}, tc.client.instanceMetadataZones)
|
||||
assert.ElementsMatch([]string{"someName"}, tc.client.instanceMetadataInstanceNames)
|
||||
assert.ElementsMatch([]string{core.VPNIPMetadataKey}, tc.client.instanceMetadataKeys)
|
||||
assert.ElementsMatch([]string{tc.wantVPNIP}, tc.client.instanceMetadataValues)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrivialMetadataFunctions(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
metadata := Metadata{}
|
||||
|
||||
assert.True(metadata.Supported())
|
||||
}
|
||||
|
||||
type stubGCPClient struct {
|
||||
retrieveInstanceValue cloudtypes.Instance
|
||||
retrieveInstanceValue metadata.InstanceMetadata
|
||||
retrieveInstanceErr error
|
||||
retrieveInstancesValues []cloudtypes.Instance
|
||||
retrieveInstancesValues []metadata.InstanceMetadata
|
||||
retrieveInstancesErr error
|
||||
retrieveInstanceMetadaValues map[string]string
|
||||
retrieveInstanceMetadataErr error
|
||||
|
@ -388,11 +261,11 @@ type stubGCPClient struct {
|
|||
unsetMetadataKeys []string
|
||||
}
|
||||
|
||||
func (s *stubGCPClient) RetrieveInstances(ctx context.Context, project, zone string) ([]cloudtypes.Instance, error) {
|
||||
func (s *stubGCPClient) RetrieveInstances(ctx context.Context, project, zone string) ([]metadata.InstanceMetadata, error) {
|
||||
return s.retrieveInstancesValues, s.retrieveInstancesErr
|
||||
}
|
||||
|
||||
func (s *stubGCPClient) RetrieveInstance(ctx context.Context, project, zone string, instanceName string) (cloudtypes.Instance, error) {
|
||||
func (s *stubGCPClient) RetrieveInstance(ctx context.Context, project, zone string, instanceName string) (metadata.InstanceMetadata, error) {
|
||||
return s.retrieveInstanceValue, s.retrieveInstanceErr
|
||||
}
|
||||
|
||||
|
|
19
coordinator/cloudprovider/gcp/role.go
Normal file
19
coordinator/cloudprovider/gcp/role.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package gcp
|
||||
|
||||
import (
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
)
|
||||
|
||||
const roleMetadataKey = "constellation-role"
|
||||
|
||||
// extractRole extracts role from cloud provider metadata.
|
||||
func extractRole(metadata map[string]string) role.Role {
|
||||
switch metadata[roleMetadataKey] {
|
||||
case role.Coordinator.String():
|
||||
return role.Coordinator
|
||||
case role.Node.String():
|
||||
return role.Node
|
||||
default:
|
||||
return role.Unknown
|
||||
}
|
||||
}
|
55
coordinator/cloudprovider/gcp/role_test.go
Normal file
55
coordinator/cloudprovider/gcp/role_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package gcp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
goleak.VerifyTestMain(m,
|
||||
// https://github.com/census-instrumentation/opencensus-go/issues/1262
|
||||
goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestExtractRole(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
metadata map[string]string
|
||||
wantRole role.Role
|
||||
}{
|
||||
"coordinator role": {
|
||||
metadata: map[string]string{
|
||||
roleMetadataKey: role.Coordinator.String(),
|
||||
},
|
||||
wantRole: role.Coordinator,
|
||||
},
|
||||
"node role": {
|
||||
metadata: map[string]string{
|
||||
roleMetadataKey: role.Node.String(),
|
||||
},
|
||||
wantRole: role.Node,
|
||||
},
|
||||
"unknown role": {
|
||||
metadata: map[string]string{
|
||||
roleMetadataKey: "some-unknown-role",
|
||||
},
|
||||
wantRole: role.Unknown,
|
||||
},
|
||||
"no role": {
|
||||
wantRole: role.Unknown,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
role := extractRole(tc.metadata)
|
||||
|
||||
assert.Equal(tc.wantRole, role)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue