mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-12-15 08:05:19 -05:00
Refactor provider metadata
This commit is contained in:
parent
32f1f5fd3e
commit
09e86e6c5d
36 changed files with 198 additions and 1340 deletions
|
|
@ -50,16 +50,6 @@ type publicIPAddressesAPI interface {
|
|||
options *armnetwork.PublicIPAddressesClientGetOptions) (armnetwork.PublicIPAddressesClientGetResponse, error)
|
||||
}
|
||||
|
||||
type virtualMachinesAPI interface {
|
||||
Get(ctx context.Context, resourceGroupName string, vmName string, options *armcompute.VirtualMachinesClientGetOptions) (armcompute.VirtualMachinesClientGetResponse, error)
|
||||
List(resourceGroupName string, options *armcompute.VirtualMachinesClientListOptions) virtualMachinesClientListPager
|
||||
}
|
||||
|
||||
type virtualMachinesClientListPager interface {
|
||||
NextPage(ctx context.Context) bool
|
||||
PageResponse() armcompute.VirtualMachinesClientListResponse
|
||||
}
|
||||
|
||||
type virtualMachineScaleSetVMsAPI interface {
|
||||
Get(ctx context.Context, resourceGroupName string, vmScaleSetName string, instanceID string, options *armcompute.VirtualMachineScaleSetVMsClientGetOptions) (armcompute.VirtualMachineScaleSetVMsClientGetResponse, error)
|
||||
List(resourceGroupName string, virtualMachineScaleSetName string, options *armcompute.VirtualMachineScaleSetVMsClientListOptions) virtualMachineScaleSetVMsClientListPager
|
||||
|
|
|
|||
|
|
@ -52,49 +52,6 @@ func (a *stubNetworkInterfacesAPI) Get(ctx context.Context, resourceGroupName st
|
|||
}, a.getErr
|
||||
}
|
||||
|
||||
type stubVirtualMachinesClientListPager struct {
|
||||
pagesCounter int
|
||||
pages [][]*armcompute.VirtualMachine
|
||||
}
|
||||
|
||||
func (p *stubVirtualMachinesClientListPager) NextPage(ctx context.Context) bool {
|
||||
return p.pagesCounter < len(p.pages)
|
||||
}
|
||||
|
||||
func (p *stubVirtualMachinesClientListPager) PageResponse() armcompute.VirtualMachinesClientListResponse {
|
||||
if p.pagesCounter >= len(p.pages) {
|
||||
return armcompute.VirtualMachinesClientListResponse{}
|
||||
}
|
||||
p.pagesCounter = p.pagesCounter + 1
|
||||
return armcompute.VirtualMachinesClientListResponse{
|
||||
VirtualMachinesClientListResult: armcompute.VirtualMachinesClientListResult{
|
||||
VirtualMachineListResult: armcompute.VirtualMachineListResult{
|
||||
Value: p.pages[p.pagesCounter-1],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type stubVirtualMachinesAPI struct {
|
||||
getVM armcompute.VirtualMachine
|
||||
getErr error
|
||||
listPages [][]*armcompute.VirtualMachine
|
||||
}
|
||||
|
||||
func (a *stubVirtualMachinesAPI) Get(ctx context.Context, resourceGroupName string, vmName string, options *armcompute.VirtualMachinesClientGetOptions) (armcompute.VirtualMachinesClientGetResponse, error) {
|
||||
return armcompute.VirtualMachinesClientGetResponse{
|
||||
VirtualMachinesClientGetResult: armcompute.VirtualMachinesClientGetResult{
|
||||
VirtualMachine: a.getVM,
|
||||
},
|
||||
}, a.getErr
|
||||
}
|
||||
|
||||
func (a *stubVirtualMachinesAPI) List(resourceGroupName string, options *armcompute.VirtualMachinesClientListOptions) virtualMachinesClientListPager {
|
||||
return &stubVirtualMachinesClientListPager{
|
||||
pages: a.listPages,
|
||||
}
|
||||
}
|
||||
|
||||
type stubVirtualMachineScaleSetVMsClientListPager struct {
|
||||
pagesCounter int
|
||||
pages [][]*armcompute.VirtualMachineScaleSetVM
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package azure
|
||||
|
||||
import (
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||
"github.com/edgelesssys/constellation/internal/azureshared"
|
||||
k8s "k8s.io/api/core/v1"
|
||||
|
|
@ -17,8 +16,8 @@ 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) {
|
||||
subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(instance.ProviderID)
|
||||
func (a *Autoscaler) Secrets(providerID string, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||
subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
|
||||
if err != nil {
|
||||
return resources.Secrets{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package azure
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
|
@ -13,13 +12,13 @@ import (
|
|||
|
||||
func TestAutoscalerSecrets(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
instance cloudtypes.Instance
|
||||
providerID string
|
||||
cloudServiceAccountURI string
|
||||
wantSecrets resources.Secrets
|
||||
wantErr bool
|
||||
}{
|
||||
"Secrets works": {
|
||||
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret",
|
||||
wantSecrets: resources.Secrets{
|
||||
&k8s.Secret{
|
||||
|
|
@ -43,11 +42,11 @@ func TestAutoscalerSecrets(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"invalid providerID fails": {
|
||||
instance: cloudtypes.Instance{ProviderID: "invalid"},
|
||||
wantErr: true,
|
||||
providerID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"invalid cloudServiceAccountURI fails": {
|
||||
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
cloudServiceAccountURI: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
|
|
@ -59,7 +58,7 @@ func TestAutoscalerSecrets(t *testing.T) {
|
|||
require := require.New(t)
|
||||
|
||||
autoscaler := Autoscaler{}
|
||||
secrets, err := autoscaler.Secrets(tc.instance, tc.cloudServiceAccountURI)
|
||||
secrets, err := autoscaler.Secrets(tc.providerID, tc.cloudServiceAccountURI)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
"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/azureshared"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
k8s "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
|
@ -55,17 +55,17 @@ 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) {
|
||||
return resources.ConfigMaps{}, nil
|
||||
}
|
||||
|
||||
// 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, providerID string, cloudServiceAccountURI string) (resources.Secrets, error) {
|
||||
// Azure CCM expects cloud provider config to contain cluster configuration and service principal client secrets
|
||||
// reference: https://kubernetes-sigs.github.io/cloud-provider-azure/install/configs/
|
||||
|
||||
subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(instance.ProviderID)
|
||||
subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
|
||||
if err != nil {
|
||||
return resources.Secrets{}, err
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ func (c *CloudControllerManager) Secrets(ctx context.Context, instance cloudtype
|
|||
}
|
||||
|
||||
vmType := "standard"
|
||||
if _, _, _, _, err := azureshared.ScaleSetInformationFromProviderID(instance.ProviderID); err == nil {
|
||||
if _, _, _, _, err := azureshared.ScaleSetInformationFromProviderID(providerID); err == nil {
|
||||
vmType = "vmss"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import (
|
|||
"errors"
|
||||
"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/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
k8s "k8s.io/api/core/v1"
|
||||
|
|
@ -16,14 +16,14 @@ import (
|
|||
func TestSecrets(t *testing.T) {
|
||||
someErr := errors.New("some error")
|
||||
testCases := map[string]struct {
|
||||
instance cloudtypes.Instance
|
||||
providerID string
|
||||
metadata ccmMetadata
|
||||
cloudServiceAccountURI string
|
||||
wantSecrets resources.Secrets
|
||||
wantErr bool
|
||||
}{
|
||||
"Secrets works": {
|
||||
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
||||
metadata: &ccmMetadataStub{loadBalancerName: "load-balancer-name", networkSecurityGroupName: "network-security-group-name"},
|
||||
wantSecrets: resources.Secrets{
|
||||
|
|
@ -43,7 +43,7 @@ func TestSecrets(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"Secrets works for scale sets": {
|
||||
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"},
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
||||
metadata: &ccmMetadataStub{loadBalancerName: "load-balancer-name", networkSecurityGroupName: "network-security-group-name"},
|
||||
wantSecrets: resources.Secrets{
|
||||
|
|
@ -63,24 +63,24 @@ func TestSecrets(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"cannot get load balancer Name": {
|
||||
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"},
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
||||
metadata: &ccmMetadataStub{getLoadBalancerNameErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"cannot get network security group name": {
|
||||
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id"},
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
cloudServiceAccountURI: "serviceaccount://azure?tenant_id=tenant-id&client_id=client-id&client_secret=client-secret&location=location",
|
||||
metadata: &ccmMetadataStub{getNetworkSecurityGroupNameErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"invalid providerID fails": {
|
||||
instance: cloudtypes.Instance{ProviderID: "invalid"},
|
||||
metadata: &ccmMetadataStub{},
|
||||
wantErr: true,
|
||||
providerID: "invalid",
|
||||
metadata: &ccmMetadataStub{},
|
||||
wantErr: true,
|
||||
},
|
||||
"invalid cloudServiceAccountURI fails": {
|
||||
instance: cloudtypes.Instance{ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"},
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
metadata: &ccmMetadataStub{},
|
||||
cloudServiceAccountURI: "invalid",
|
||||
wantErr: true,
|
||||
|
|
@ -93,7 +93,7 @@ func TestSecrets(t *testing.T) {
|
|||
require := require.New(t)
|
||||
|
||||
cloud := NewCloudControllerManager(tc.metadata)
|
||||
secrets, err := cloud.Secrets(context.Background(), tc.instance, tc.cloudServiceAccountURI)
|
||||
secrets, err := cloud.Secrets(context.Background(), tc.providerID, tc.cloudServiceAccountURI)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
|
|
@ -112,7 +112,7 @@ func TestTrivialCCMFunctions(t *testing.T) {
|
|||
assert.NotEmpty(cloud.Path())
|
||||
assert.NotEmpty(cloud.Name())
|
||||
assert.NotEmpty(cloud.ExtraArgs())
|
||||
assert.Empty(cloud.ConfigMaps(cloudtypes.Instance{}))
|
||||
assert.Empty(cloud.ConfigMaps(metadata.InstanceMetadata{}))
|
||||
assert.NotEmpty(cloud.Volumes())
|
||||
assert.NotEmpty(cloud.VolumeMounts())
|
||||
assert.Empty(cloud.Env())
|
||||
|
|
|
|||
|
|
@ -11,10 +11,8 @@ import (
|
|||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/core"
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
"github.com/edgelesssys/constellation/internal/azureshared"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -31,7 +29,6 @@ type Metadata struct {
|
|||
publicIPAddressesAPI
|
||||
scaleSetsAPI
|
||||
loadBalancerAPI
|
||||
virtualMachinesAPI
|
||||
virtualMachineScaleSetVMsAPI
|
||||
tagsAPI
|
||||
applicationInsightsAPI
|
||||
|
|
@ -63,7 +60,6 @@ func NewMetadata(ctx context.Context) (*Metadata, error) {
|
|||
securityGroupsAPI := armnetwork.NewSecurityGroupsClient(subscriptionID, cred, nil)
|
||||
scaleSetsAPI := armcompute.NewVirtualMachineScaleSetsClient(subscriptionID, cred, nil)
|
||||
loadBalancerAPI := armnetwork.NewLoadBalancersClient(subscriptionID, cred, nil)
|
||||
virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil)
|
||||
virtualMachineScaleSetVMsAPI := armcompute.NewVirtualMachineScaleSetVMsClient(subscriptionID, cred, nil)
|
||||
tagsAPI := armresources.NewTagsClient(subscriptionID, cred, nil)
|
||||
applicationInsightsAPI := armapplicationinsights.NewComponentsClient(subscriptionID, cred, nil)
|
||||
|
|
@ -76,7 +72,6 @@ func NewMetadata(ctx context.Context) (*Metadata, error) {
|
|||
publicIPAddressesAPI: &publicIPAddressesClient{publicIPAddressesAPI},
|
||||
loadBalancerAPI: &loadBalancersClient{loadBalancerAPI},
|
||||
scaleSetsAPI: &scaleSetsClient{scaleSetsAPI},
|
||||
virtualMachinesAPI: &virtualMachinesClient{virtualMachinesAPI},
|
||||
virtualMachineScaleSetVMsAPI: &virtualMachineScaleSetVMsClient{virtualMachineScaleSetVMsAPI},
|
||||
tagsAPI: &tagsClient{tagsAPI},
|
||||
applicationInsightsAPI: &applicationInsightsClient{applicationInsightsAPI},
|
||||
|
|
@ -84,7 +79,7 @@ func NewMetadata(ctx context.Context) (*Metadata, error) {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
providerID, err := m.providerID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -93,54 +88,29 @@ func (m *Metadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
singleInstances, err := m.listVMs(ctx, resourceGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scaleSetInstances, err := m.listScaleSetVMs(ctx, resourceGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instances := make([]cloudtypes.Instance, 0, len(singleInstances)+len(scaleSetInstances))
|
||||
instances = append(instances, singleInstances...)
|
||||
instances = append(instances, scaleSetInstances...)
|
||||
return instances, nil
|
||||
return scaleSetInstances, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
providerID, err := m.providerID(ctx)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
return m.GetInstance(ctx, providerID)
|
||||
}
|
||||
|
||||
// GetInstance retrieves an instance using its providerID.
|
||||
func (m *Metadata) GetInstance(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||
instance, singleErr := m.getVM(ctx, providerID)
|
||||
if singleErr == nil {
|
||||
return instance, nil
|
||||
}
|
||||
func (m *Metadata) GetInstance(ctx context.Context, providerID string) (metadata.InstanceMetadata, error) {
|
||||
instance, scaleSetErr := m.getScaleSetVM(ctx, providerID)
|
||||
if scaleSetErr == nil {
|
||||
return instance, nil
|
||||
}
|
||||
return cloudtypes.Instance{}, fmt.Errorf("retrieving instance given providerID %v as either single VM or scale set VM: %v; %v", providerID, singleErr, scaleSetErr)
|
||||
}
|
||||
|
||||
// SignalRole signals the constellation role via cloud provider metadata.
|
||||
// On single VMs, the role is stored in tags, on scale set VMs, the role is inferred from the scale set and not signalied explicitly.
|
||||
func (m *Metadata) SignalRole(ctx context.Context, role role.Role) error {
|
||||
providerID, err := m.providerID(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, _, _, _, err := azureshared.ScaleSetInformationFromProviderID(providerID); err == nil {
|
||||
// scale set instances cannot store tags and role can be inferred from scale set name.
|
||||
return nil
|
||||
}
|
||||
return m.setTag(ctx, core.RoleMetadataKey, role.String())
|
||||
return metadata.InstanceMetadata{}, fmt.Errorf("retrieving instance given providerID %v: %w", providerID, scaleSetErr)
|
||||
}
|
||||
|
||||
// GetNetworkSecurityGroupName returns the security group name of the resource group.
|
||||
|
|
|
|||
|
|
@ -8,20 +8,13 @@ import (
|
|||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
wantInstances := []cloudtypes.Instance{
|
||||
{
|
||||
Name: "instance-name",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||
},
|
||||
wantInstances := []metadata.InstanceMetadata{
|
||||
{
|
||||
Name: "scale-set-name-instance-id",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
|
|
@ -33,17 +26,15 @@ func TestList(t *testing.T) {
|
|||
imdsAPI imdsAPI
|
||||
networkInterfacesAPI networkInterfacesAPI
|
||||
scaleSetsAPI scaleSetsAPI
|
||||
virtualMachinesAPI virtualMachinesAPI
|
||||
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||
tagsAPI tagsAPI
|
||||
wantErr bool
|
||||
wantInstances []cloudtypes.Instance
|
||||
wantInstances []metadata.InstanceMetadata
|
||||
}{
|
||||
"List works": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
scaleSetsAPI: newScaleSetsStub(),
|
||||
virtualMachinesAPI: newVirtualMachinesStub(),
|
||||
virtualMachineScaleSetVMsAPI: newVirtualMachineScaleSetsVMsStub(),
|
||||
tagsAPI: newTagsStub(),
|
||||
wantInstances: wantInstances,
|
||||
|
|
@ -56,16 +47,10 @@ func TestList(t *testing.T) {
|
|||
imdsAPI: newInvalidIMDSStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"listVMs fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
virtualMachinesAPI: newFailingListsVirtualMachinesStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"listScaleSetVMs fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
scaleSetsAPI: newScaleSetsStub(),
|
||||
virtualMachinesAPI: newVirtualMachinesStub(),
|
||||
virtualMachineScaleSetVMsAPI: newFailingListsVirtualMachineScaleSetsVMsStub(),
|
||||
tagsAPI: newTagsStub(),
|
||||
wantErr: true,
|
||||
|
|
@ -77,15 +62,14 @@ func TestList(t *testing.T) {
|
|||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
metadata := Metadata{
|
||||
azureMetadata := Metadata{
|
||||
imdsAPI: tc.imdsAPI,
|
||||
networkInterfacesAPI: tc.networkInterfacesAPI,
|
||||
scaleSetsAPI: tc.scaleSetsAPI,
|
||||
virtualMachinesAPI: tc.virtualMachinesAPI,
|
||||
virtualMachineScaleSetVMsAPI: tc.virtualMachineScaleSetVMsAPI,
|
||||
tagsAPI: tc.tagsAPI,
|
||||
}
|
||||
instances, err := metadata.List(context.Background())
|
||||
instances, err := azureMetadata.List(context.Background())
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
|
@ -98,13 +82,13 @@ func TestList(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSelf(t *testing.T) {
|
||||
wantVMInstance := cloudtypes.Instance{
|
||||
wantVMInstance := metadata.InstanceMetadata{
|
||||
Name: "instance-name",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||
}
|
||||
wantScaleSetInstance := cloudtypes.Instance{
|
||||
wantScaleSetInstance := metadata.InstanceMetadata{
|
||||
Name: "scale-set-name-instance-id",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
|
|
@ -113,22 +97,19 @@ func TestSelf(t *testing.T) {
|
|||
testCases := map[string]struct {
|
||||
imdsAPI imdsAPI
|
||||
networkInterfacesAPI networkInterfacesAPI
|
||||
virtualMachinesAPI virtualMachinesAPI
|
||||
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||
wantErr bool
|
||||
wantInstance cloudtypes.Instance
|
||||
wantInstance metadata.InstanceMetadata
|
||||
}{
|
||||
"self for individual instance works": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
virtualMachinesAPI: newVirtualMachinesStub(),
|
||||
virtualMachineScaleSetVMsAPI: newVirtualMachineScaleSetsVMsStub(),
|
||||
wantInstance: wantVMInstance,
|
||||
},
|
||||
"self for scale set instance works": {
|
||||
imdsAPI: newScaleSetIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
virtualMachinesAPI: newVirtualMachinesStub(),
|
||||
virtualMachineScaleSetVMsAPI: newVirtualMachineScaleSetsVMsStub(),
|
||||
wantInstance: wantScaleSetInstance,
|
||||
},
|
||||
|
|
@ -137,9 +118,8 @@ func TestSelf(t *testing.T) {
|
|||
wantErr: true,
|
||||
},
|
||||
"GetInstance fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
virtualMachinesAPI: newFailingGetVirtualMachinesStub(),
|
||||
wantErr: true,
|
||||
imdsAPI: newIMDSStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +131,6 @@ func TestSelf(t *testing.T) {
|
|||
metadata := Metadata{
|
||||
imdsAPI: tc.imdsAPI,
|
||||
networkInterfacesAPI: tc.networkInterfacesAPI,
|
||||
virtualMachinesAPI: tc.virtualMachinesAPI,
|
||||
virtualMachineScaleSetVMsAPI: tc.virtualMachineScaleSetVMsAPI,
|
||||
}
|
||||
instance, err := metadata.Self(context.Background())
|
||||
|
|
@ -166,50 +145,6 @@ func TestSelf(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSignalRole(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
imdsAPI imdsAPI
|
||||
tagsAPI tagsAPI
|
||||
wantErr bool
|
||||
}{
|
||||
"SignalRole works": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
tagsAPI: newTagsStub(),
|
||||
},
|
||||
"SignalRole is not attempted on scale set vm": {
|
||||
imdsAPI: newScaleSetIMDSStub(),
|
||||
},
|
||||
"providerID cannot be retrieved": {
|
||||
imdsAPI: &stubIMDSAPI{retrieveErr: errors.New("imds err")},
|
||||
wantErr: true,
|
||||
},
|
||||
"setting tag fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
tagsAPI: newFailingTagsStub(),
|
||||
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,
|
||||
tagsAPI: tc.tagsAPI,
|
||||
}
|
||||
err := metadata.SignalRole(context.Background(), role.Coordinator)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNetworkSecurityGroupName(t *testing.T) {
|
||||
name := "network-security-group-name"
|
||||
testCases := map[string]struct {
|
||||
|
|
@ -719,12 +654,6 @@ func newInvalidIMDSStub() *stubIMDSAPI {
|
|||
}
|
||||
}
|
||||
|
||||
func newFailingIMDSStub() *stubIMDSAPI {
|
||||
return &stubIMDSAPI{
|
||||
retrieveErr: errors.New("imds retrieve error"),
|
||||
}
|
||||
}
|
||||
|
||||
func newNetworkInterfacesStub() *stubNetworkInterfacesAPI {
|
||||
return &stubNetworkInterfacesAPI{
|
||||
getInterface: armnetwork.Interface{
|
||||
|
|
@ -754,81 +683,6 @@ func newScaleSetsStub() *stubScaleSetsAPI {
|
|||
}
|
||||
}
|
||||
|
||||
func newVirtualMachinesStub() *stubVirtualMachinesAPI {
|
||||
return &stubVirtualMachinesAPI{
|
||||
getVM: armcompute.VirtualMachine{
|
||||
Name: to.StringPtr("instance-name"),
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"),
|
||||
Properties: &armcompute.VirtualMachineProperties{
|
||||
NetworkProfile: &armcompute.NetworkProfile{
|
||||
NetworkInterfaces: []*armcompute.NetworkInterfaceReference{
|
||||
{
|
||||
ID: to.StringPtr("/subscriptions/subscription/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||
},
|
||||
},
|
||||
},
|
||||
OSProfile: &armcompute.OSProfile{
|
||||
LinuxConfiguration: &armcompute.LinuxConfiguration{
|
||||
SSH: &armcompute.SSHConfiguration{
|
||||
PublicKeys: []*armcompute.SSHPublicKey{
|
||||
{
|
||||
KeyData: to.StringPtr("key-data"),
|
||||
Path: to.StringPtr("/home/user/.ssh/authorized_keys"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
listPages: [][]*armcompute.VirtualMachine{
|
||||
{
|
||||
{
|
||||
Name: to.StringPtr("instance-name"),
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"),
|
||||
Properties: &armcompute.VirtualMachineProperties{
|
||||
NetworkProfile: &armcompute.NetworkProfile{
|
||||
NetworkInterfaces: []*armcompute.NetworkInterfaceReference{
|
||||
{
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||
},
|
||||
},
|
||||
},
|
||||
OSProfile: &armcompute.OSProfile{
|
||||
LinuxConfiguration: &armcompute.LinuxConfiguration{
|
||||
SSH: &armcompute.SSHConfiguration{
|
||||
PublicKeys: []*armcompute.SSHPublicKey{
|
||||
{
|
||||
KeyData: to.StringPtr("key-data"),
|
||||
Path: to.StringPtr("/home/user/.ssh/authorized_keys"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newFailingListsVirtualMachinesStub() *stubVirtualMachinesAPI {
|
||||
return &stubVirtualMachinesAPI{
|
||||
listPages: [][]*armcompute.VirtualMachine{
|
||||
{
|
||||
{},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newFailingGetVirtualMachinesStub() *stubVirtualMachinesAPI {
|
||||
return &stubVirtualMachinesAPI{
|
||||
getErr: errors.New("get err"),
|
||||
}
|
||||
}
|
||||
|
||||
func newVirtualMachineScaleSetsVMsStub() *stubVirtualMachineScaleSetVMsAPI {
|
||||
return &stubVirtualMachineScaleSetVMsAPI{
|
||||
getVM: armcompute.VirtualMachineScaleSetVM{
|
||||
|
|
@ -907,10 +761,3 @@ func newFailingListsVirtualMachineScaleSetsVMsStub() *stubVirtualMachineScaleSet
|
|||
func newTagsStub() *stubTagsAPI {
|
||||
return &stubTagsAPI{}
|
||||
}
|
||||
|
||||
func newFailingTagsStub() *stubTagsAPI {
|
||||
return &stubTagsAPI{
|
||||
createOrUpdateAtScopeErr: errors.New("createOrUpdateErr"),
|
||||
updateAtScopeErr: errors.New("updateErr"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
"github.com/edgelesssys/constellation/internal/azureshared"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -18,30 +18,30 @@ var (
|
|||
)
|
||||
|
||||
// getScaleSetVM tries to get an azure vm belonging to a scale set.
|
||||
func (m *Metadata) getScaleSetVM(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||
func (m *Metadata) getScaleSetVM(ctx context.Context, providerID string) (metadata.InstanceMetadata, error) {
|
||||
_, resourceGroup, scaleSet, instanceID, err := azureshared.ScaleSetInformationFromProviderID(providerID)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
vmResp, err := m.virtualMachineScaleSetVMsAPI.Get(ctx, resourceGroup, scaleSet, instanceID, nil)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
networkInterfaces, err := m.getScaleSetVMInterfaces(ctx, vmResp.VirtualMachineScaleSetVM, resourceGroup, scaleSet, instanceID)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
publicIPAddresses, err := m.getScaleSetVMPublicIPAddresses(ctx, resourceGroup, scaleSet, instanceID, networkInterfaces)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
return metadata.InstanceMetadata{}, err
|
||||
}
|
||||
|
||||
return convertScaleSetVMToCoreInstance(scaleSet, vmResp.VirtualMachineScaleSetVM, networkInterfaces, publicIPAddresses)
|
||||
}
|
||||
|
||||
// listScaleSetVMs lists all scale set VMs in the current resource group.
|
||||
func (m *Metadata) listScaleSetVMs(ctx context.Context, resourceGroup string) ([]cloudtypes.Instance, error) {
|
||||
instances := []cloudtypes.Instance{}
|
||||
func (m *Metadata) listScaleSetVMs(ctx context.Context, resourceGroup string) ([]metadata.InstanceMetadata, error) {
|
||||
instances := []metadata.InstanceMetadata{}
|
||||
scaleSetPager := m.scaleSetsAPI.List(resourceGroup, nil)
|
||||
for scaleSetPager.NextPage(ctx) {
|
||||
for _, scaleSet := range scaleSetPager.PageResponse().Value {
|
||||
|
|
@ -71,12 +71,12 @@ func (m *Metadata) listScaleSetVMs(ctx context.Context, resourceGroup string) ([
|
|||
}
|
||||
|
||||
// convertScaleSetVMToCoreInstance converts an azure scale set virtual machine with interface configurations into a core.Instance.
|
||||
func convertScaleSetVMToCoreInstance(scaleSet string, vm armcompute.VirtualMachineScaleSetVM, networkInterfaces []armnetwork.Interface, publicIPAddresses []string) (cloudtypes.Instance, error) {
|
||||
func convertScaleSetVMToCoreInstance(scaleSet string, vm armcompute.VirtualMachineScaleSetVM, networkInterfaces []armnetwork.Interface, publicIPAddresses []string) (metadata.InstanceMetadata, error) {
|
||||
if vm.ID == nil {
|
||||
return cloudtypes.Instance{}, errors.New("retrieving instance from armcompute API client returned no instance ID")
|
||||
return metadata.InstanceMetadata{}, errors.New("retrieving instance from armcompute API client returned no instance ID")
|
||||
}
|
||||
if vm.Properties == nil || vm.Properties.OSProfile == nil || vm.Properties.OSProfile.ComputerName == nil {
|
||||
return cloudtypes.Instance{}, errors.New("retrieving instance from armcompute API client returned no computer name")
|
||||
return metadata.InstanceMetadata{}, errors.New("retrieving instance from armcompute API client returned no computer name")
|
||||
}
|
||||
var sshKeys map[string][]string
|
||||
if vm.Properties.OSProfile.LinuxConfiguration == nil || vm.Properties.OSProfile.LinuxConfiguration.SSH == nil {
|
||||
|
|
@ -84,7 +84,7 @@ func convertScaleSetVMToCoreInstance(scaleSet string, vm armcompute.VirtualMachi
|
|||
} else {
|
||||
sshKeys = extractSSHKeys(*vm.Properties.OSProfile.LinuxConfiguration.SSH)
|
||||
}
|
||||
return cloudtypes.Instance{
|
||||
return metadata.InstanceMetadata{
|
||||
Name: *vm.Properties.OSProfile.ComputerName,
|
||||
ProviderID: "azure://" + *vm.ID,
|
||||
Role: extractScaleSetVMRole(scaleSet),
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import (
|
|||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/coordinator/role"
|
||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetScaleSetVM(t *testing.T) {
|
||||
wantInstance := cloudtypes.Instance{
|
||||
wantInstance := metadata.InstanceMetadata{
|
||||
Name: "scale-set-name-instance-id",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
|
|
@ -26,7 +26,7 @@ func TestGetScaleSetVM(t *testing.T) {
|
|||
networkInterfacesAPI networkInterfacesAPI
|
||||
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||
wantErr bool
|
||||
wantInstance cloudtypes.Instance
|
||||
wantInstance metadata.InstanceMetadata
|
||||
}{
|
||||
"getVM for scale set instance works": {
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
|
|
@ -43,12 +43,6 @@ func TestGetScaleSetVM(t *testing.T) {
|
|||
virtualMachineScaleSetVMsAPI: newFailingGetScaleSetVirtualMachinesStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"retrieving interfaces fails": {
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
virtualMachineScaleSetVMsAPI: newVirtualMachineScaleSetsVMsStub(),
|
||||
networkInterfacesAPI: newFailingNetworkInterfacesStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"conversion fails": {
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
virtualMachineScaleSetVMsAPI: newGetInvalidScaleSetVirtualMachinesStub(),
|
||||
|
|
@ -79,7 +73,7 @@ func TestGetScaleSetVM(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListScaleSetVMs(t *testing.T) {
|
||||
wantInstances := []cloudtypes.Instance{
|
||||
wantInstances := []metadata.InstanceMetadata{
|
||||
{
|
||||
Name: "scale-set-name-instance-id",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
|
|
@ -93,7 +87,7 @@ func TestListScaleSetVMs(t *testing.T) {
|
|||
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||
scaleSetsAPI scaleSetsAPI
|
||||
wantErr bool
|
||||
wantInstances []cloudtypes.Instance
|
||||
wantInstances []metadata.InstanceMetadata
|
||||
}{
|
||||
"listVMs works": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
|
|
@ -114,7 +108,7 @@ func TestListScaleSetVMs(t *testing.T) {
|
|||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
virtualMachineScaleSetVMsAPI: &stubVirtualMachineScaleSetVMsAPI{},
|
||||
scaleSetsAPI: newScaleSetsStub(),
|
||||
wantInstances: []cloudtypes.Instance{},
|
||||
wantInstances: []metadata.InstanceMetadata{},
|
||||
},
|
||||
"can skip nil in VM list": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
|
|
@ -123,13 +117,6 @@ func TestListScaleSetVMs(t *testing.T) {
|
|||
scaleSetsAPI: newScaleSetsStub(),
|
||||
wantInstances: wantInstances,
|
||||
},
|
||||
"retrieving network interfaces fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newFailingNetworkInterfacesStub(),
|
||||
virtualMachineScaleSetVMsAPI: newVirtualMachineScaleSetsVMsStub(),
|
||||
scaleSetsAPI: newScaleSetsStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"converting instance fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
|
|
@ -168,7 +155,7 @@ func TestConvertScaleSetVMToCoreInstance(t *testing.T) {
|
|||
inInterface []armnetwork.Interface
|
||||
inPublicIPs []string
|
||||
wantErr bool
|
||||
wantInstance cloudtypes.Instance
|
||||
wantInstance metadata.InstanceMetadata
|
||||
}{
|
||||
"conversion works": {
|
||||
inVM: armcompute.VirtualMachineScaleSetVM{
|
||||
|
|
@ -197,7 +184,7 @@ func TestConvertScaleSetVMToCoreInstance(t *testing.T) {
|
|||
},
|
||||
},
|
||||
inPublicIPs: []string{"192.0.2.100", "192.0.2.101"},
|
||||
wantInstance: cloudtypes.Instance{
|
||||
wantInstance: metadata.InstanceMetadata{
|
||||
Name: "scale-set-name-instance-id",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/internal/azureshared"
|
||||
)
|
||||
|
||||
func (m *Metadata) getVM(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
|
||||
_, resourceGroup, instanceName, err := azureshared.VMInformationFromProviderID(providerID)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
}
|
||||
vmResp, err := m.virtualMachinesAPI.Get(ctx, resourceGroup, instanceName, nil)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
}
|
||||
interfaceIPConfigurations, err := m.getVMInterfaces(ctx, vmResp.VirtualMachine, resourceGroup)
|
||||
if err != nil {
|
||||
return cloudtypes.Instance{}, err
|
||||
}
|
||||
return convertVMToCoreInstance(vmResp.VirtualMachine, interfaceIPConfigurations)
|
||||
}
|
||||
|
||||
// listVMs lists all individual VMs in the current resource group.
|
||||
func (m *Metadata) listVMs(ctx context.Context, resourceGroup string) ([]cloudtypes.Instance, error) {
|
||||
instances := []cloudtypes.Instance{}
|
||||
pager := m.virtualMachinesAPI.List(resourceGroup, nil)
|
||||
for pager.NextPage(ctx) {
|
||||
for _, vm := range pager.PageResponse().Value {
|
||||
if vm == nil {
|
||||
continue
|
||||
}
|
||||
interfaces, err := m.getVMInterfaces(ctx, *vm, resourceGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instance, err := convertVMToCoreInstance(*vm, interfaces)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instances = append(instances, instance)
|
||||
}
|
||||
}
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
// setTag merges key-value pair into VM tags.
|
||||
func (m *Metadata) setTag(ctx context.Context, key, value string) error {
|
||||
instanceMetadata, err := m.imdsAPI.Retrieve(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = m.tagsAPI.UpdateAtScope(ctx, instanceMetadata.Compute.ResourceID, armresources.TagsPatchResource{
|
||||
Operation: armresources.TagsPatchOperationMerge.ToPtr(),
|
||||
Properties: &armresources.Tags{
|
||||
Tags: map[string]*string{
|
||||
key: to.StringPtr(value),
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// convertVMToCoreInstance converts an azure virtual machine with interface configurations into a cloudtypes.Instance.
|
||||
func convertVMToCoreInstance(vm armcompute.VirtualMachine, networkInterfaces []armnetwork.Interface) (cloudtypes.Instance, error) {
|
||||
if vm.Name == nil || vm.ID == nil {
|
||||
return cloudtypes.Instance{}, fmt.Errorf("retrieving instance from armcompute API client returned invalid instance Name (%v) or ID (%v)", vm.Name, vm.ID)
|
||||
}
|
||||
var sshKeys map[string][]string
|
||||
if vm.Properties == nil || vm.Properties.OSProfile == nil || vm.Properties.OSProfile.LinuxConfiguration == nil || vm.Properties.OSProfile.LinuxConfiguration.SSH == nil {
|
||||
sshKeys = map[string][]string{}
|
||||
} else {
|
||||
sshKeys = extractSSHKeys(*vm.Properties.OSProfile.LinuxConfiguration.SSH)
|
||||
}
|
||||
metadata := extractInstanceTags(vm.Tags)
|
||||
return cloudtypes.Instance{
|
||||
Name: *vm.Name,
|
||||
ProviderID: "azure://" + *vm.ID,
|
||||
Role: cloudprovider.ExtractRole(metadata),
|
||||
PrivateIPs: extractPrivateIPs(networkInterfaces),
|
||||
SSHKeys: sshKeys,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -1,374 +0,0 @@
|
|||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
|
||||
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetVM(t *testing.T) {
|
||||
wantInstance := cloudtypes.Instance{
|
||||
Name: "instance-name",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||
}
|
||||
testCases := map[string]struct {
|
||||
providerID string
|
||||
networkInterfacesAPI networkInterfacesAPI
|
||||
virtualMachinesAPI virtualMachinesAPI
|
||||
wantErr bool
|
||||
wantInstance cloudtypes.Instance
|
||||
}{
|
||||
"getVM for individual instance works": {
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
virtualMachinesAPI: newVirtualMachinesStub(),
|
||||
wantInstance: wantInstance,
|
||||
},
|
||||
"getVM for scale set instance must fail": {
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
|
||||
wantErr: true,
|
||||
},
|
||||
"Get fails": {
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
virtualMachinesAPI: newFailingGetVirtualMachinesStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"retrieving interfaces fails": {
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
virtualMachinesAPI: newVirtualMachinesStub(),
|
||||
networkInterfacesAPI: newFailingNetworkInterfacesStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"conversion fails": {
|
||||
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
virtualMachinesAPI: newGetInvalidVirtualMachinesStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
metadata := Metadata{
|
||||
networkInterfacesAPI: tc.networkInterfacesAPI,
|
||||
virtualMachinesAPI: tc.virtualMachinesAPI,
|
||||
}
|
||||
instance, err := metadata.getVM(context.Background(), tc.providerID)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantInstance, instance)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListVMs(t *testing.T) {
|
||||
wantInstances := []cloudtypes.Instance{
|
||||
{
|
||||
Name: "instance-name",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||
},
|
||||
}
|
||||
testCases := map[string]struct {
|
||||
imdsAPI imdsAPI
|
||||
networkInterfacesAPI networkInterfacesAPI
|
||||
virtualMachinesAPI virtualMachinesAPI
|
||||
wantErr bool
|
||||
wantInstances []cloudtypes.Instance
|
||||
}{
|
||||
"listVMs works": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
virtualMachinesAPI: newVirtualMachinesStub(),
|
||||
wantInstances: wantInstances,
|
||||
},
|
||||
"listVMs can return 0 VMs": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
virtualMachinesAPI: &stubVirtualMachinesAPI{},
|
||||
wantInstances: []cloudtypes.Instance{},
|
||||
},
|
||||
"can skip nil in VM list": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
virtualMachinesAPI: newListContainingNilVirtualMachinesStub(),
|
||||
wantInstances: wantInstances,
|
||||
},
|
||||
"retrieving network interfaces fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newFailingNetworkInterfacesStub(),
|
||||
virtualMachinesAPI: newVirtualMachinesStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"converting instance fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
networkInterfacesAPI: newNetworkInterfacesStub(),
|
||||
virtualMachinesAPI: newListContainingInvalidVirtualMachinesStub(),
|
||||
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,
|
||||
networkInterfacesAPI: tc.networkInterfacesAPI,
|
||||
virtualMachinesAPI: tc.virtualMachinesAPI,
|
||||
}
|
||||
instances, err := metadata.listVMs(context.Background(), "resource-group")
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.ElementsMatch(tc.wantInstances, instances)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetTag(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
imdsAPI imdsAPI
|
||||
tagsAPI tagsAPI
|
||||
wantErr bool
|
||||
}{
|
||||
"setTag works": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
tagsAPI: newTagsStub(),
|
||||
},
|
||||
"retrieving resource ID fails": {
|
||||
imdsAPI: newFailingIMDSStub(),
|
||||
tagsAPI: newTagsStub(),
|
||||
wantErr: true,
|
||||
},
|
||||
"updating tags fails": {
|
||||
imdsAPI: newIMDSStub(),
|
||||
tagsAPI: newFailingTagsStub(),
|
||||
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,
|
||||
tagsAPI: tc.tagsAPI,
|
||||
}
|
||||
err := metadata.setTag(context.Background(), "key", "value")
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertVMToCoreInstance(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
inVM armcompute.VirtualMachine
|
||||
inInterface []armnetwork.Interface
|
||||
wantErr bool
|
||||
wantInstance cloudtypes.Instance
|
||||
}{
|
||||
"conversion works": {
|
||||
inVM: armcompute.VirtualMachine{
|
||||
Name: to.StringPtr("instance-name"),
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"),
|
||||
Tags: map[string]*string{"tag-key": to.StringPtr("tag-value")},
|
||||
Properties: &armcompute.VirtualMachineProperties{
|
||||
OSProfile: &armcompute.OSProfile{
|
||||
LinuxConfiguration: &armcompute.LinuxConfiguration{
|
||||
SSH: &armcompute.SSHConfiguration{
|
||||
PublicKeys: []*armcompute.SSHPublicKey{
|
||||
{
|
||||
Path: to.StringPtr("/home/user/.ssh/authorized_keys"),
|
||||
KeyData: to.StringPtr("key-data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
inInterface: []armnetwork.Interface{
|
||||
{
|
||||
Name: to.StringPtr("interface-name"),
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||
{
|
||||
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantInstance: cloudtypes.Instance{
|
||||
Name: "instance-name",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
SSHKeys: map[string][]string{"user": {"key-data"}},
|
||||
},
|
||||
},
|
||||
"conversion without SSH keys works": {
|
||||
inVM: armcompute.VirtualMachine{
|
||||
Name: to.StringPtr("instance-name"),
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"),
|
||||
Tags: map[string]*string{"tag-key": to.StringPtr("tag-value")},
|
||||
},
|
||||
inInterface: []armnetwork.Interface{
|
||||
{
|
||||
Name: to.StringPtr("interface-name"),
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||
Properties: &armnetwork.InterfacePropertiesFormat{
|
||||
IPConfigurations: []*armnetwork.InterfaceIPConfiguration{
|
||||
{
|
||||
Properties: &armnetwork.InterfaceIPConfigurationPropertiesFormat{
|
||||
PrivateIPAddress: to.StringPtr("192.0.2.0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantInstance: cloudtypes.Instance{
|
||||
Name: "instance-name",
|
||||
ProviderID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
|
||||
PrivateIPs: []string{"192.0.2.0"},
|
||||
SSHKeys: map[string][]string{},
|
||||
},
|
||||
},
|
||||
"invalid instance": {
|
||||
inVM: armcompute.VirtualMachine{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
instance, err := convertVMToCoreInstance(tc.inVM, tc.inInterface)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantInstance, instance)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newFailingNetworkInterfacesStub() *stubNetworkInterfacesAPI {
|
||||
return &stubNetworkInterfacesAPI{
|
||||
getErr: errors.New("get err"),
|
||||
}
|
||||
}
|
||||
|
||||
func newGetInvalidVirtualMachinesStub() *stubVirtualMachinesAPI {
|
||||
return &stubVirtualMachinesAPI{
|
||||
getVM: armcompute.VirtualMachine{},
|
||||
}
|
||||
}
|
||||
|
||||
func newListContainingNilVirtualMachinesStub() *stubVirtualMachinesAPI {
|
||||
return &stubVirtualMachinesAPI{
|
||||
listPages: [][]*armcompute.VirtualMachine{
|
||||
{
|
||||
nil,
|
||||
{
|
||||
Name: to.StringPtr("instance-name"),
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name"),
|
||||
Tags: map[string]*string{
|
||||
"tag-key": to.StringPtr("tag-value"),
|
||||
},
|
||||
Properties: &armcompute.VirtualMachineProperties{
|
||||
NetworkProfile: &armcompute.NetworkProfile{
|
||||
NetworkInterfaces: []*armcompute.NetworkInterfaceReference{
|
||||
{
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||
},
|
||||
},
|
||||
},
|
||||
OSProfile: &armcompute.OSProfile{
|
||||
LinuxConfiguration: &armcompute.LinuxConfiguration{
|
||||
SSH: &armcompute.SSHConfiguration{
|
||||
PublicKeys: []*armcompute.SSHPublicKey{
|
||||
{
|
||||
KeyData: to.StringPtr("key-data"),
|
||||
Path: to.StringPtr("/home/user/.ssh/authorized_keys"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newListContainingInvalidVirtualMachinesStub() *stubVirtualMachinesAPI {
|
||||
return &stubVirtualMachinesAPI{
|
||||
listPages: [][]*armcompute.VirtualMachine{
|
||||
{
|
||||
{
|
||||
Name: nil,
|
||||
ID: nil,
|
||||
Properties: &armcompute.VirtualMachineProperties{
|
||||
NetworkProfile: &armcompute.NetworkProfile{
|
||||
NetworkInterfaces: []*armcompute.NetworkInterfaceReference{
|
||||
{
|
||||
ID: to.StringPtr("/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Network/networkInterfaces/interface-name"),
|
||||
},
|
||||
},
|
||||
},
|
||||
OSProfile: &armcompute.OSProfile{
|
||||
LinuxConfiguration: &armcompute.LinuxConfiguration{
|
||||
SSH: &armcompute.SSHConfiguration{
|
||||
PublicKeys: []*armcompute.SSHPublicKey{
|
||||
{
|
||||
KeyData: to.StringPtr("key-data"),
|
||||
Path: to.StringPtr("/home/user/.ssh/authorized_keys"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -68,18 +68,6 @@ func (c *loadBalancersClient) List(resourceGroupName string, options *armnetwork
|
|||
return c.LoadBalancersClient.List(resourceGroupName, options)
|
||||
}
|
||||
|
||||
type virtualMachinesClient struct {
|
||||
*armcompute.VirtualMachinesClient
|
||||
}
|
||||
|
||||
func (c *virtualMachinesClient) Get(ctx context.Context, resourceGroupName, vmName string, options *armcompute.VirtualMachinesClientGetOptions) (armcompute.VirtualMachinesClientGetResponse, error) {
|
||||
return c.VirtualMachinesClient.Get(ctx, resourceGroupName, vmName, options)
|
||||
}
|
||||
|
||||
func (c *virtualMachinesClient) List(resourceGroupName string, options *armcompute.VirtualMachinesClientListOptions) virtualMachinesClientListPager {
|
||||
return c.VirtualMachinesClient.List(resourceGroupName, options)
|
||||
}
|
||||
|
||||
type virtualMachineScaleSetVMsClient struct {
|
||||
*armcompute.VirtualMachineScaleSetVMsClient
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue