AB#2104 Feat/azure logging (#198)

implementation for azure early boot logging
This commit is contained in:
Fabian Kammel 2022-06-10 13:18:30 +02:00 committed by GitHub
parent 963c6f98e5
commit 84552ca8f7
33 changed files with 526 additions and 212 deletions

View file

@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added ### Added
- Early boot logging for Cloud Provider: GCP & Azure
### Changed ### Changed

View file

@ -5,6 +5,7 @@ import (
"time" "time"
"github.com/Azure/azure-sdk-for-go/profiles/latest/authorization/mgmt/authorization" "github.com/Azure/azure-sdk-for-go/profiles/latest/authorization/mgmt/authorization"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "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/network/armnetwork"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
@ -139,3 +140,8 @@ type virtualMachinesAPI interface {
BeginCreateOrUpdate(ctx context.Context, resourceGroupName string, vmName string, parameters armcompute.VirtualMachine, BeginCreateOrUpdate(ctx context.Context, resourceGroupName string, vmName string, parameters armcompute.VirtualMachine,
options *armcompute.VirtualMachinesClientBeginCreateOrUpdateOptions) (virtualMachinesClientCreateOrUpdatePollerResponse, error) options *armcompute.VirtualMachinesClientBeginCreateOrUpdateOptions) (virtualMachinesClientCreateOrUpdatePollerResponse, error)
} }
type applicationInsightsAPI interface {
CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, insightProperties armapplicationinsights.Component,
options *armapplicationinsights.ComponentsClientCreateOrUpdateOptions) (armapplicationinsights.ComponentsClientCreateOrUpdateResponse, error)
}

View file

@ -6,6 +6,7 @@ import (
"github.com/Azure/azure-sdk-for-go/profiles/latest/authorization/mgmt/authorization" "github.com/Azure/azure-sdk-for-go/profiles/latest/authorization/mgmt/authorization"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "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/network/armnetwork"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
@ -402,3 +403,12 @@ func (a *stubRoleAssignmentsAPI) Create(ctx context.Context, scope string, roleA
} }
return authorization.RoleAssignment{}, a.createErrors[(a.createCounter-1)%len(a.createErrors)] return authorization.RoleAssignment{}, a.createErrors[(a.createCounter-1)%len(a.createErrors)]
} }
type stubApplicationInsightsAPI struct {
err error
}
func (a *stubApplicationInsightsAPI) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, insightProperties armapplicationinsights.Component, options *armapplicationinsights.ComponentsClientCreateOrUpdateOptions) (armapplicationinsights.ComponentsClientCreateOrUpdateResponse, error) {
resp := armapplicationinsights.ComponentsClientCreateOrUpdateResponse{}
return resp, a.err
}

View file

@ -0,0 +1,26 @@
package client
import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"golang.org/x/net/context"
)
func (c *Client) CreateApplicationInsight(ctx context.Context) error {
properties := armapplicationinsights.Component{
Kind: to.StringPtr("web"),
Location: to.StringPtr(c.location),
Properties: &armapplicationinsights.ComponentProperties{
ApplicationType: armapplicationinsights.ApplicationTypeWeb.ToPtr(),
},
}
_, err := c.applicationInsightsAPI.CreateOrUpdate(
ctx,
c.resourceGroup,
"constellation-insights-"+c.uid,
properties,
nil,
)
return err
}

View file

@ -0,0 +1,46 @@
package client
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCreateApplicationInsight(t *testing.T) {
testCases := map[string]struct {
applicationInsightsAPI applicationInsightsAPI
wantErr bool
}{
"successful create": {
applicationInsightsAPI: &stubApplicationInsightsAPI{
err: nil,
},
},
"failed create": {
applicationInsightsAPI: &stubApplicationInsightsAPI{
err: errors.New("some error"),
},
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
client := Client{
applicationInsightsAPI: tc.applicationInsightsAPI,
}
err := client.CreateApplicationInsight(context.Background())
if tc.wantErr {
assert.Error(err)
return
}
assert.NoError(err)
})
}
}

View file

@ -9,6 +9,7 @@ import (
"github.com/Azure/azure-sdk-for-go/profiles/latest/authorization/mgmt/authorization" "github.com/Azure/azure-sdk-for-go/profiles/latest/authorization/mgmt/authorization"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "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/network/armnetwork"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
@ -38,6 +39,7 @@ type Client struct {
applicationsAPI applicationsAPI
servicePrincipalsAPI servicePrincipalsAPI
roleAssignmentsAPI roleAssignmentsAPI
applicationInsightsAPI
adReplicationLagCheckInterval time.Duration adReplicationLagCheckInterval time.Duration
adReplicationLagCheckMaxRetries int adReplicationLagCheckMaxRetries int
@ -82,6 +84,7 @@ func NewFromDefault(subscriptionID, tenantID string) (*Client, error) {
networkInterfacesAPI := armnetwork.NewInterfacesClient(subscriptionID, cred, nil) networkInterfacesAPI := armnetwork.NewInterfacesClient(subscriptionID, cred, nil)
loadBalancersAPI := armnetwork.NewLoadBalancersClient(subscriptionID, cred, nil) loadBalancersAPI := armnetwork.NewLoadBalancersClient(subscriptionID, cred, nil)
virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil) virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil)
applicationInsightsAPI := armapplicationinsights.NewComponentsClient(subscriptionID, cred, nil)
applicationsAPI := graphrbac.NewApplicationsClient(tenantID) applicationsAPI := graphrbac.NewApplicationsClient(tenantID)
applicationsAPI.Authorizer = graphAuthorizer applicationsAPI.Authorizer = graphAuthorizer
servicePrincipalsAPI := graphrbac.NewServicePrincipalsClient(tenantID) servicePrincipalsAPI := graphrbac.NewServicePrincipalsClient(tenantID)
@ -101,6 +104,7 @@ func NewFromDefault(subscriptionID, tenantID string) (*Client, error) {
servicePrincipalsAPI: &servicePrincipalsClient{&servicePrincipalsAPI}, servicePrincipalsAPI: &servicePrincipalsClient{&servicePrincipalsAPI},
roleAssignmentsAPI: &roleAssignmentsClient{&roleAssignmentsAPI}, roleAssignmentsAPI: &roleAssignmentsClient{&roleAssignmentsAPI},
virtualMachinesAPI: &virtualMachinesClient{virtualMachinesAPI}, virtualMachinesAPI: &virtualMachinesClient{virtualMachinesAPI},
applicationInsightsAPI: applicationInsightsAPI,
subscriptionID: subscriptionID, subscriptionID: subscriptionID,
tenantID: tenantID, tenantID: tenantID,
nodes: cloudtypes.Instances{}, nodes: cloudtypes.Instances{},

View file

@ -25,6 +25,7 @@ type gcpclient interface {
type azureclient interface { type azureclient interface {
GetState() (state.ConstellationState, error) GetState() (state.ConstellationState, error)
SetState(state.ConstellationState) error SetState(state.ConstellationState) error
CreateApplicationInsight(ctx context.Context) error
CreateResourceGroup(ctx context.Context) error CreateResourceGroup(ctx context.Context) error
CreateExternalLoadBalancer(ctx context.Context) error CreateExternalLoadBalancer(ctx context.Context) error
CreateVirtualNetwork(ctx context.Context) error CreateVirtualNetwork(ctx context.Context) error

View file

@ -69,6 +69,10 @@ func (c *fakeAzureClient) SetState(stat state.ConstellationState) error {
return nil return nil
} }
func (c *fakeAzureClient) CreateApplicationInsight(ctx context.Context) error {
return nil
}
func (c *fakeAzureClient) CreateResourceGroup(ctx context.Context) error { func (c *fakeAzureClient) CreateResourceGroup(ctx context.Context) error {
c.resourceGroup = "resource-group" c.resourceGroup = "resource-group"
return nil return nil
@ -156,6 +160,7 @@ type stubAzureClient struct {
getStateErr error getStateErr error
setStateErr error setStateErr error
createApplicationInsightErr error
createResourceGroupErr error createResourceGroupErr error
createVirtualNetworkErr error createVirtualNetworkErr error
createSecurityGroupErr error createSecurityGroupErr error
@ -178,6 +183,10 @@ func (c *stubAzureClient) CreateExternalLoadBalancer(ctx context.Context) error
return c.createLoadBalancerErr return c.createLoadBalancerErr
} }
func (c *stubAzureClient) CreateApplicationInsight(ctx context.Context) error {
return c.createApplicationInsightErr
}
func (c *stubAzureClient) CreateResourceGroup(ctx context.Context) error { func (c *stubAzureClient) CreateResourceGroup(ctx context.Context) error {
return c.createResourceGroupErr return c.createResourceGroupErr
} }

View file

@ -166,6 +166,9 @@ func (c *Creator) createAzure(ctx context.Context, cl azureclient, config *confi
if err := cl.CreateInstances(ctx, createInput); err != nil { if err := cl.CreateInstances(ctx, createInput); err != nil {
return state.ConstellationState{}, err return state.ConstellationState{}, err
} }
if err := cl.CreateApplicationInsight(ctx); err != nil {
return state.ConstellationState{}, err
}
return cl.GetState() return cl.GetState()
} }

View file

@ -3,6 +3,7 @@ package azure
import ( import (
"context" "context"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "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/network/armnetwork"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
@ -91,3 +92,7 @@ type tagsAPI interface {
CreateOrUpdateAtScope(ctx context.Context, scope string, parameters armresources.TagsResource, options *armresources.TagsClientCreateOrUpdateAtScopeOptions) (armresources.TagsClientCreateOrUpdateAtScopeResponse, error) CreateOrUpdateAtScope(ctx context.Context, scope string, parameters armresources.TagsResource, options *armresources.TagsClientCreateOrUpdateAtScopeOptions) (armresources.TagsClientCreateOrUpdateAtScopeResponse, error)
UpdateAtScope(ctx context.Context, scope string, parameters armresources.TagsPatchResource, options *armresources.TagsClientUpdateAtScopeOptions) (armresources.TagsClientUpdateAtScopeResponse, error) UpdateAtScope(ctx context.Context, scope string, parameters armresources.TagsPatchResource, options *armresources.TagsClientUpdateAtScopeOptions) (armresources.TagsClientUpdateAtScopeResponse, error)
} }
type applicationInsightsAPI interface {
Get(ctx context.Context, resourceGroupName string, resourceName string, options *armapplicationinsights.ComponentsClientGetOptions) (armapplicationinsights.ComponentsClientGetResponse, error)
}

View file

@ -18,7 +18,7 @@ func (a *Autoscaler) Name() string {
// Secrets returns a list of secrets to deploy together with the k8s cluster-autoscaler. // 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 cloudtypes.Instance, cloudServiceAccountURI string) (resources.Secrets, error) {
subscriptionID, resourceGroup, err := extractBasicsFromProviderID(instance.ProviderID) subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(instance.ProviderID)
if err != nil { if err != nil {
return resources.Secrets{}, err return resources.Secrets{}, err
} }

View file

@ -65,7 +65,7 @@ func (c *CloudControllerManager) Secrets(ctx context.Context, instance cloudtype
// Azure CCM expects cloud provider config to contain cluster configuration and service principal client secrets // 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/ // reference: https://kubernetes-sigs.github.io/cloud-provider-azure/install/configs/
subscriptionID, resourceGroup, err := extractBasicsFromProviderID(instance.ProviderID) subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(instance.ProviderID)
if err != nil { if err != nil {
return resources.Secrets{}, err return resources.Secrets{}, err
} }
@ -75,7 +75,7 @@ func (c *CloudControllerManager) Secrets(ctx context.Context, instance cloudtype
} }
vmType := "standard" vmType := "standard"
if _, _, _, _, err := splitScaleSetProviderID(instance.ProviderID); err == nil { if _, _, _, _, err := azureshared.ScaleSetInformationFromProviderID(instance.ProviderID); err == nil {
vmType = "vmss" vmType = "vmss"
} }

View file

@ -1,6 +1,11 @@
package azure package azure
import ( import (
"context"
"errors"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/edgelesssys/constellation/internal/azureshared"
"github.com/microsoft/ApplicationInsights-Go/appinsights" "github.com/microsoft/ApplicationInsights-Go/appinsights"
) )
@ -10,10 +15,34 @@ type Logger struct {
// NewLogger creates a new client to store information in Azure Application Insights // NewLogger creates a new client to store information in Azure Application Insights
// https://github.com/Microsoft/ApplicationInsights-go // https://github.com/Microsoft/ApplicationInsights-go
func NewLogger(instrumentationKey string) *Logger { func NewLogger(ctx context.Context, metadata *Metadata) (*Logger, error) {
return &Logger{ providerID, err := metadata.providerID(ctx)
client: appinsights.NewTelemetryClient(instrumentationKey), if err != nil {
return nil, err
} }
_, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
if err != nil {
return nil, err
}
uid, err := azureshared.UIDFromProviderID(providerID)
if err != nil {
return nil, err
}
resourceName := "constellation-insights-" + uid
resp, err := metadata.applicationInsightsAPI.Get(ctx, resourceGroup, resourceName, &armapplicationinsights.ComponentsClientGetOptions{})
if err != nil {
return nil, err
}
if resp.Properties == nil || resp.Properties.InstrumentationKey == nil {
return nil, errors.New("unable to get instrumentation key")
}
return &Logger{
client: appinsights.NewTelemetryClient(*resp.Properties.InstrumentationKey),
}, nil
} }
// Disclose stores log information in Azure Application Insights! // Disclose stores log information in Azure Application Insights!

View file

@ -7,12 +7,14 @@ import (
"regexp" "regexp"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "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/network/armnetwork"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes" "github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
"github.com/edgelesssys/constellation/coordinator/core" "github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/azureshared"
) )
var ( var (
@ -32,6 +34,7 @@ type Metadata struct {
virtualMachinesAPI virtualMachinesAPI
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
tagsAPI tagsAPI
applicationInsightsAPI
} }
// NewMetadata creates a new Metadata. // NewMetadata creates a new Metadata.
@ -50,7 +53,7 @@ func NewMetadata(ctx context.Context) (*Metadata, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
subscriptionID, _, err := extractBasicsFromProviderID("azure://" + instanceMetadata.Compute.ResourceID) subscriptionID, _, err := azureshared.BasicsFromProviderID("azure://" + instanceMetadata.Compute.ResourceID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -63,6 +66,7 @@ func NewMetadata(ctx context.Context) (*Metadata, error) {
virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil) virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil)
virtualMachineScaleSetVMsAPI := armcompute.NewVirtualMachineScaleSetVMsClient(subscriptionID, cred, nil) virtualMachineScaleSetVMsAPI := armcompute.NewVirtualMachineScaleSetVMsClient(subscriptionID, cred, nil)
tagsAPI := armresources.NewTagsClient(subscriptionID, cred, nil) tagsAPI := armresources.NewTagsClient(subscriptionID, cred, nil)
applicationInsightsAPI := armapplicationinsights.NewComponentsClient(subscriptionID, cred, nil)
return &Metadata{ return &Metadata{
imdsAPI: &imdsAPI, imdsAPI: &imdsAPI,
@ -75,6 +79,7 @@ func NewMetadata(ctx context.Context) (*Metadata, error) {
virtualMachinesAPI: &virtualMachinesClient{virtualMachinesAPI}, virtualMachinesAPI: &virtualMachinesClient{virtualMachinesAPI},
virtualMachineScaleSetVMsAPI: &virtualMachineScaleSetVMsClient{virtualMachineScaleSetVMsAPI}, virtualMachineScaleSetVMsAPI: &virtualMachineScaleSetVMsClient{virtualMachineScaleSetVMsAPI},
tagsAPI: &tagsClient{tagsAPI}, tagsAPI: &tagsClient{tagsAPI},
applicationInsightsAPI: &applicationInsightsClient{applicationInsightsAPI},
}, nil }, nil
} }
@ -84,7 +89,7 @@ func (m *Metadata) List(ctx context.Context) ([]cloudtypes.Instance, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, resourceGroup, err := extractBasicsFromProviderID(providerID) _, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -131,7 +136,7 @@ func (m *Metadata) SignalRole(ctx context.Context, role role.Role) error {
if err != nil { if err != nil {
return err return err
} }
if _, _, _, _, err := splitScaleSetProviderID(providerID); err == nil { if _, _, _, _, err := azureshared.ScaleSetInformationFromProviderID(providerID); err == nil {
// scale set instances cannot store tags and role can be inferred from scale set name. // scale set instances cannot store tags and role can be inferred from scale set name.
return nil return nil
} }
@ -144,7 +149,7 @@ func (m *Metadata) GetNetworkSecurityGroupName(ctx context.Context) (string, err
if err != nil { if err != nil {
return "", err return "", err
} }
_, resourceGroup, err := extractBasicsFromProviderID(providerID) _, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -165,7 +170,7 @@ func (m *Metadata) GetSubnetworkCIDR(ctx context.Context) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
_, resourceGroup, err := extractBasicsFromProviderID(providerID) _, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -187,7 +192,7 @@ func (m *Metadata) getLoadBalancer(ctx context.Context) (*armnetwork.LoadBalance
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, resourceGroup, err := extractBasicsFromProviderID(providerID) _, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -253,7 +258,7 @@ func (m *Metadata) GetLoadBalancerIP(ctx context.Context) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
_, resourceGroup, err := extractBasicsFromProviderID(providerID) _, resourceGroup, err := azureshared.BasicsFromProviderID(providerID)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -286,19 +291,6 @@ func (m *Metadata) providerID(ctx context.Context) (string, error) {
return "azure://" + instanceMetadata.Compute.ResourceID, nil return "azure://" + instanceMetadata.Compute.ResourceID, nil
} }
// extractBasicsFromProviderID extracts subscriptionID and resourceGroup from both types of valid azure providerID.
func extractBasicsFromProviderID(providerID string) (subscriptionID, resourceGroup string, err error) {
subscriptionID, resourceGroup, _, err = splitVMProviderID(providerID)
if err == nil {
return subscriptionID, resourceGroup, nil
}
subscriptionID, resourceGroup, _, _, err = splitScaleSetProviderID(providerID)
if err == nil {
return subscriptionID, resourceGroup, nil
}
return "", "", fmt.Errorf("providerID %v is malformatted", providerID)
}
// extractInstanceTags converts azure tags into metadata key-value pairs. // extractInstanceTags converts azure tags into metadata key-value pairs.
func extractInstanceTags(tags map[string]*string) map[string]string { func extractInstanceTags(tags map[string]*string) map[string]string {
metadataMap := map[string]string{} metadataMap := map[string]string{}

View file

@ -605,47 +605,6 @@ func TestProviderID(t *testing.T) {
} }
} }
func TestExtractBasicsFromProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantErr bool
wantSubscriptionID string
wantResourceGroup string
}{
"providerID for individual instance works": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
},
"providerID for scale set instance works": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
},
"providerID is malformed": {
providerID: "malformed-provider-id",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
subscriptionID, resourceGroup, err := extractBasicsFromProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantSubscriptionID, subscriptionID)
assert.Equal(tc.wantResourceGroup, resourceGroup)
})
}
}
func TestExtractInstanceTags(t *testing.T) { func TestExtractInstanceTags(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
in map[string]*string in map[string]*string

View file

@ -9,17 +9,17 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes" "github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/azureshared"
) )
var ( var (
azureVMSSProviderIDRegexp = regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachineScaleSets/([^/]+)/virtualMachines/([^/]+)$`)
coordinatorScaleSetRegexp = regexp.MustCompile(`constellation-scale-set-coordinators-[0-9a-zA-Z]+$`) coordinatorScaleSetRegexp = regexp.MustCompile(`constellation-scale-set-coordinators-[0-9a-zA-Z]+$`)
nodeScaleSetRegexp = regexp.MustCompile(`constellation-scale-set-nodes-[0-9a-zA-Z]+$`) nodeScaleSetRegexp = regexp.MustCompile(`constellation-scale-set-nodes-[0-9a-zA-Z]+$`)
) )
// getScaleSetVM tries to get an azure vm belonging to a scale set. // 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) (cloudtypes.Instance, error) {
_, resourceGroup, scaleSet, instanceID, err := splitScaleSetProviderID(providerID) _, resourceGroup, scaleSet, instanceID, err := azureshared.ScaleSetInformationFromProviderID(providerID)
if err != nil { if err != nil {
return cloudtypes.Instance{}, err return cloudtypes.Instance{}, err
} }
@ -70,17 +70,6 @@ func (m *Metadata) listScaleSetVMs(ctx context.Context, resourceGroup string) ([
return instances, nil return instances, nil
} }
// splitScaleSetProviderID splits a provider's id belonging to an azure scaleset into core components.
// A providerID for scale set VMs is build after the following schema:
// - 'azure:///subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachineScaleSets/<scale-set-name>/virtualMachines/<instance-id>'
func splitScaleSetProviderID(providerID string) (subscriptionID, resourceGroup, scaleSet, instanceID string, err error) {
matches := azureVMSSProviderIDRegexp.FindStringSubmatch(providerID)
if len(matches) != 5 {
return "", "", "", "", errors.New("error splitting providerID")
}
return matches[1], matches[2], matches[3], matches[4], nil
}
// convertScaleSetVMToCoreInstance converts an azure scale set virtual machine with interface configurations into a core.Instance. // 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) (cloudtypes.Instance, error) {
if vm.ID == nil { if vm.ID == nil {

View file

@ -162,52 +162,6 @@ func TestListScaleSetVMs(t *testing.T) {
} }
} }
func TestSplitScaleSetProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantErr bool
wantSubscriptionID string
wantResourceGroup string
wantScaleSet string
wantInstanceID string
}{
"providerID for scale set instance works": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
wantScaleSet: "scale-set-name",
wantInstanceID: "instance-id",
},
"providerID for individual instance must fail": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
wantErr: true,
},
"providerID is malformed": {
providerID: "malformed-provider-id",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
subscriptionID, resourceGroup, scaleSet, instanceID, err := splitScaleSetProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantSubscriptionID, subscriptionID)
assert.Equal(tc.wantResourceGroup, resourceGroup)
assert.Equal(tc.wantScaleSet, scaleSet)
assert.Equal(tc.wantInstanceID, instanceID)
})
}
}
func TestConvertScaleSetVMToCoreInstance(t *testing.T) { func TestConvertScaleSetVMToCoreInstance(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
inVM armcompute.VirtualMachineScaleSetVM inVM armcompute.VirtualMachineScaleSetVM

View file

@ -2,9 +2,7 @@ package azure
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"regexp"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "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/network/armnetwork"
@ -12,13 +10,11 @@ import (
"github.com/Azure/go-autorest/autorest/to" "github.com/Azure/go-autorest/autorest/to"
"github.com/edgelesssys/constellation/coordinator/cloudprovider" "github.com/edgelesssys/constellation/coordinator/cloudprovider"
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes" "github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
"github.com/edgelesssys/constellation/internal/azureshared"
) )
var azureVMProviderIDRegexp = regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachines/([^/]+)$`)
// getVM tries to get a single azure vm.
func (m *Metadata) getVM(ctx context.Context, providerID string) (cloudtypes.Instance, error) { func (m *Metadata) getVM(ctx context.Context, providerID string) (cloudtypes.Instance, error) {
_, resourceGroup, instanceName, err := splitVMProviderID(providerID) _, resourceGroup, instanceName, err := azureshared.VMInformationFromProviderID(providerID)
if err != nil { if err != nil {
return cloudtypes.Instance{}, err return cloudtypes.Instance{}, err
} }
@ -73,17 +69,6 @@ func (m *Metadata) setTag(ctx context.Context, key, value string) error {
return err return err
} }
// splitVMProviderID splits a provider's id belonging to a single azure instance into core components.
// A providerID for individual VMs is build after the following schema:
// - 'azure:///subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachines/<instance-name>'
func splitVMProviderID(providerID string) (subscriptionID, resourceGroup, instanceName string, err error) {
matches := azureVMProviderIDRegexp.FindStringSubmatch(providerID)
if len(matches) != 4 {
return "", "", "", errors.New("error splitting providerID")
}
return matches[1], matches[2], matches[3], nil
}
// convertVMToCoreInstance converts an azure virtual machine with interface configurations into a cloudtypes.Instance. // convertVMToCoreInstance converts an azure virtual machine with interface configurations into a cloudtypes.Instance.
func convertVMToCoreInstance(vm armcompute.VirtualMachine, networkInterfaces []armnetwork.Interface) (cloudtypes.Instance, error) { func convertVMToCoreInstance(vm armcompute.VirtualMachine, networkInterfaces []armnetwork.Interface) (cloudtypes.Instance, error) {
if vm.Name == nil || vm.ID == nil { if vm.Name == nil || vm.ID == nil {

View file

@ -189,49 +189,6 @@ func TestSetTag(t *testing.T) {
} }
} }
func TestSplitVMProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantErr bool
wantSubscriptionID string
wantResourceGroup string
wantInstanceName string
}{
"providerID for individual instance works": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
wantInstanceName: "instance-name",
},
"providerID 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,
},
"providerID is malformed": {
providerID: "malformed-provider-id",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
subscriptionID, resourceGroup, instanceName, err := splitVMProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantSubscriptionID, subscriptionID)
assert.Equal(tc.wantResourceGroup, resourceGroup)
assert.Equal(tc.wantInstanceName, instanceName)
})
}
}
func TestConvertVMToCoreInstance(t *testing.T) { func TestConvertVMToCoreInstance(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
inVM armcompute.VirtualMachine inVM armcompute.VirtualMachine

View file

@ -3,6 +3,7 @@ package azure
import ( import (
"context" "context"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" "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/network/armnetwork"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
@ -110,3 +111,11 @@ type scaleSetsClient struct {
func (c *scaleSetsClient) List(resourceGroupName string, options *armcompute.VirtualMachineScaleSetsClientListOptions) virtualMachineScaleSetsClientListPager { func (c *scaleSetsClient) List(resourceGroupName string, options *armcompute.VirtualMachineScaleSetsClientListOptions) virtualMachineScaleSetsClientListPager {
return c.VirtualMachineScaleSetsClient.List(resourceGroupName, options) return c.VirtualMachineScaleSetsClient.List(resourceGroupName, options)
} }
type applicationInsightsClient struct {
*armapplicationinsights.ComponentsClient
}
func (c *applicationInsightsClient) Get(ctx context.Context, resourceGroupName string, resourceName string, options *armapplicationinsights.ComponentsClientGetOptions) (armapplicationinsights.ComponentsClientGetResponse, error) {
return c.ComponentsClient.Get(ctx, resourceGroupName, resourceName, options)
}

View file

@ -51,7 +51,7 @@ func (c *CloudControllerManager) ConfigMaps(instance cloudtypes.Instance) (resou
// reference: https://github.com/kubernetes/cloud-provider-gcp/blob/master/cluster/gce/gci/configure-helper.sh#L791-L892 // reference: https://github.com/kubernetes/cloud-provider-gcp/blob/master/cluster/gce/gci/configure-helper.sh#L791-L892
var config strings.Builder var config strings.Builder
config.WriteString("[global]\n") config.WriteString("[global]\n")
projectID, _, _, err := splitProviderID(instance.ProviderID) projectID, _, _, err := gcpshared.SplitProviderID(instance.ProviderID)
if err != nil { if err != nil {
return resources.ConfigMaps{}, err return resources.ConfigMaps{}, err
} }

View file

@ -3,13 +3,13 @@ package gcp
import ( import (
"context" "context"
"fmt" "fmt"
"regexp"
"strings" "strings"
compute "cloud.google.com/go/compute/apiv1" compute "cloud.google.com/go/compute/apiv1"
"github.com/edgelesssys/constellation/coordinator/cloudprovider" "github.com/edgelesssys/constellation/coordinator/cloudprovider"
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes" "github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
"github.com/edgelesssys/constellation/coordinator/core" "github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/internal/gcpshared"
"google.golang.org/api/iterator" "google.golang.org/api/iterator"
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1" computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@ -17,8 +17,6 @@ import (
const gcpSSHMetadataKey = "ssh-keys" const gcpSSHMetadataKey = "ssh-keys"
var providerIDRegex = regexp.MustCompile(`^gce://([^/]+)/([^/]+)/([^/]+)$`)
// Client implements the gcp.API interface. // Client implements the gcp.API interface.
type Client struct { type Client struct {
instanceAPI instanceAPI
@ -325,7 +323,7 @@ func convertToCoreInstance(in *computepb.Instance, project string, zone string)
metadata := extractInstanceMetadata(in.Metadata, "", false) metadata := extractInstanceMetadata(in.Metadata, "", false)
return cloudtypes.Instance{ return cloudtypes.Instance{
Name: *in.Name, Name: *in.Name,
ProviderID: joinProviderID(project, zone, *in.Name), ProviderID: gcpshared.JoinProviderID(project, zone, *in.Name),
Role: cloudprovider.ExtractRole(metadata), Role: cloudprovider.ExtractRole(metadata),
PrivateIPs: extractPrivateIPs(in.NetworkInterfaces), PrivateIPs: extractPrivateIPs(in.NetworkInterfaces),
PublicIPs: extractPublicIPs(in.NetworkInterfaces), PublicIPs: extractPublicIPs(in.NetworkInterfaces),
@ -334,22 +332,6 @@ func convertToCoreInstance(in *computepb.Instance, project string, zone string)
}, nil }, nil
} }
// joinProviderID builds a k8s provider ID for GCP instances.
// A providerID is build after the schema 'gce://<project-id>/<zone>/<instance-name>'
func joinProviderID(project, zone, instanceName string) string {
return fmt.Sprintf("gce://%v/%v/%v", project, zone, instanceName)
}
// splitProviderID splits a provider's id into core components.
// A providerID is build after the schema 'gce://<project-id>/<zone>/<instance-name>'
func splitProviderID(providerID string) (project, zone, instance string, err error) {
matches := providerIDRegex.FindStringSubmatch(providerID)
if len(matches) != 4 {
return "", "", "", fmt.Errorf("error splitting providerID: %v", providerID)
}
return matches[1], matches[2], matches[3], nil
}
// extractInstanceMetadata will extract the list of instance metadata key-value pairs into a map. // extractInstanceMetadata will extract the list of instance metadata key-value pairs into a map.
// If "skipKey" is true, "key" will be skipped. // If "skipKey" is true, "key" will be skipped.
func extractInstanceMetadata(in *computepb.Metadata, key string, skipKey bool) map[string]string { func extractInstanceMetadata(in *computepb.Metadata, key string, skipKey bool) map[string]string {

View file

@ -5,6 +5,7 @@ import (
"log" "log"
"cloud.google.com/go/logging" "cloud.google.com/go/logging"
"github.com/edgelesssys/constellation/internal/gcpshared"
) )
type Logger struct { type Logger struct {
@ -15,7 +16,7 @@ type Logger struct {
// NewLogger creates a new Cloud Logger for GCP. // NewLogger creates a new Cloud Logger for GCP.
// https://cloud.google.com/logging/docs/setup/go // https://cloud.google.com/logging/docs/setup/go
func NewLogger(ctx context.Context, providerID string, logName string) (*Logger, error) { func NewLogger(ctx context.Context, providerID string, logName string) (*Logger, error) {
projectID, _, _, err := splitProviderID(providerID) projectID, _, _, err := gcpshared.SplitProviderID(providerID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -7,6 +7,7 @@ import (
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes" "github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
"github.com/edgelesssys/constellation/coordinator/core" "github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/gcpshared"
) )
// API handles all GCP API requests. // API handles all GCP API requests.
@ -79,7 +80,7 @@ func (m *Metadata) Self(ctx context.Context) (cloudtypes.Instance, error) {
// GetInstance retrieves an instance using its providerID. // 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) (cloudtypes.Instance, error) {
project, zone, instanceName, err := splitProviderID(providerID) project, zone, instanceName, err := gcpshared.SplitProviderID(providerID)
if err != nil { if err != nil {
return cloudtypes.Instance{}, fmt.Errorf("invalid providerID: %w", err) return cloudtypes.Instance{}, fmt.Errorf("invalid providerID: %w", err)
} }

View file

@ -122,8 +122,10 @@ func main() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
// TODO: Implement cloud logging for Azure cloudLogger, err = azurecloud.NewLogger(context.Background(), metadata)
cloudLogger = &logging.NopLogger{} if err != nil {
log.Fatal(err)
}
coreMetadata = metadata coreMetadata = metadata
kube = kubernetes.New("azure", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), azurecloud.NewCloudControllerManager(metadata), &azurecloud.CloudNodeManager{}, &azurecloud.Autoscaler{}, metadata) kube = kubernetes.New("azure", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), azurecloud.NewCloudControllerManager(metadata), &azurecloud.CloudNodeManager{}, &azurecloud.Autoscaler{}, metadata)

3
go.mod
View file

@ -50,6 +50,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.13.2 github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.13.2
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.3.0 github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.3.0
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0 github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v0.2.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.3.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.3.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v0.3.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v0.3.1
@ -65,7 +66,6 @@ require (
github.com/aws/aws-sdk-go-v2/service/ec2 v1.32.0 github.com/aws/aws-sdk-go-v2/service/ec2 v1.32.0
github.com/aws/aws-sdk-go-v2/service/kms v1.16.0 github.com/aws/aws-sdk-go-v2/service/kms v1.16.0
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.2 github.com/aws/aws-sdk-go-v2/service/s3 v1.26.2
github.com/aws/smithy-go v1.11.2
github.com/coreos/go-systemd/v22 v22.3.2 github.com/coreos/go-systemd/v22 v22.3.2
github.com/docker/docker v20.10.13+incompatible github.com/docker/docker v20.10.13+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
@ -118,6 +118,7 @@ require (
require ( require (
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
github.com/aws/smithy-go v1.11.2 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // indirect github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
) )

2
go.sum
View file

@ -124,6 +124,8 @@ github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0 h1:8OgHKRX8uTyIi
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0/go.mod h1:uQSVRwN3dRA6hguqKpgzwonvQtpxaWo7/t5cbz3iHbE= github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0/go.mod h1:uQSVRwN3dRA6hguqKpgzwonvQtpxaWo7/t5cbz3iHbE=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1 h1:lirjIOHv5RrmDbZXw9lUz/fY68uU05qR4uIef58WMvQ= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1 h1:lirjIOHv5RrmDbZXw9lUz/fY68uU05qR4uIef58WMvQ=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1/go.mod h1:j1J9XXIo/eXD7YSrr73sYZTEY/AQ0+/Q6Aa96z1e2j8= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1/go.mod h1:j1J9XXIo/eXD7YSrr73sYZTEY/AQ0+/Q6Aa96z1e2j8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v0.2.1 h1:H+YaV8IY4sVFxrkSmyvfvXZv+wxUq/qTQOa9TkqUqPE=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v0.2.1/go.mod h1:IwvRqY+EcaQzfAGUdIZpzWELUdsZzuItWRP6cNTzgr0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0 h1:kqRtiAe9aH0WzzQm3Mq7N6mzcdZHGJZrdteepKT7ymU= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0 h1:kqRtiAe9aH0WzzQm3Mq7N6mzcdZHGJZrdteepKT7ymU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0/go.mod h1:isx+19QmRnAX0Ls0Adm/8SL3b8bIaZiSPbhpoyZX5Mw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0/go.mod h1:isx+19QmRnAX0Ls0Adm/8SL3b8bIaZiSPbhpoyZX5Mw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.3.1 h1:CyGPbnjITjA63agVN1nNznge7Tip0g7OiAvFPiT0btU= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.3.1 h1:CyGPbnjITjA63agVN1nNznge7Tip0g7OiAvFPiT0btU=

View file

@ -60,6 +60,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v0.2.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.3.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.3.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v0.3.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v0.3.1 // indirect

View file

@ -113,6 +113,8 @@ github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0 h1:8OgHKRX8uTyIi
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0/go.mod h1:uQSVRwN3dRA6hguqKpgzwonvQtpxaWo7/t5cbz3iHbE= github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.5.0/go.mod h1:uQSVRwN3dRA6hguqKpgzwonvQtpxaWo7/t5cbz3iHbE=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1 h1:lirjIOHv5RrmDbZXw9lUz/fY68uU05qR4uIef58WMvQ= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1 h1:lirjIOHv5RrmDbZXw9lUz/fY68uU05qR4uIef58WMvQ=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1/go.mod h1:j1J9XXIo/eXD7YSrr73sYZTEY/AQ0+/Q6Aa96z1e2j8= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.2.1/go.mod h1:j1J9XXIo/eXD7YSrr73sYZTEY/AQ0+/Q6Aa96z1e2j8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v0.2.1 h1:H+YaV8IY4sVFxrkSmyvfvXZv+wxUq/qTQOa9TkqUqPE=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v0.2.1/go.mod h1:IwvRqY+EcaQzfAGUdIZpzWELUdsZzuItWRP6cNTzgr0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0 h1:kqRtiAe9aH0WzzQm3Mq7N6mzcdZHGJZrdteepKT7ymU= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0 h1:kqRtiAe9aH0WzzQm3Mq7N6mzcdZHGJZrdteepKT7ymU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0/go.mod h1:isx+19QmRnAX0Ls0Adm/8SL3b8bIaZiSPbhpoyZX5Mw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v0.5.0/go.mod h1:isx+19QmRnAX0Ls0Adm/8SL3b8bIaZiSPbhpoyZX5Mw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.3.1 h1:CyGPbnjITjA63agVN1nNznge7Tip0g7OiAvFPiT0btU= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v0.3.1 h1:CyGPbnjITjA63agVN1nNznge7Tip0g7OiAvFPiT0btU=

View file

@ -0,0 +1,61 @@
package azureshared
import (
"errors"
"fmt"
"regexp"
"strings"
)
var (
azureVMProviderIDRegexp = regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachines/([^/]+)$`)
azureVMSSProviderIDRegexp = regexp.MustCompile(`^azure:///subscriptions/([^/]+)/resourceGroups/([^/]+)/providers/Microsoft.Compute/virtualMachineScaleSets/([^/]+)/virtualMachines/([^/]+)$`)
)
// BasicsFromProviderID extracts subscriptionID and resourceGroup from both types of valid azure providerID.
func BasicsFromProviderID(providerID string) (subscriptionID, resourceGroup string, err error) {
subscriptionID, resourceGroup, _, err = VMInformationFromProviderID(providerID)
if err == nil {
return subscriptionID, resourceGroup, nil
}
subscriptionID, resourceGroup, _, _, err = ScaleSetInformationFromProviderID(providerID)
if err == nil {
return subscriptionID, resourceGroup, nil
}
return "", "", fmt.Errorf("providerID %v is malformatted", providerID)
}
// UIDFromProviderID extracts our own generated unique ID, which is the
// suffix at the resource group, e.g., resource-group-J18dB
// J18dB is the UID.
func UIDFromProviderID(providerID string) (string, error) {
_, resourceGroup, err := BasicsFromProviderID(providerID)
if err != nil {
return "", err
}
parts := strings.Split(resourceGroup, "-")
return parts[len(parts)-1], nil
}
// VMInformationFromProviderID splits a provider's id belonging to a single azure instance into core components.
// A providerID for individual VMs is build after the following schema:
// - 'azure:///subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachines/<instance-name>'
func VMInformationFromProviderID(providerID string) (subscriptionID, resourceGroup, instanceName string, err error) {
matches := azureVMProviderIDRegexp.FindStringSubmatch(providerID)
if len(matches) != 4 {
return "", "", "", errors.New("error splitting providerID")
}
return matches[1], matches[2], matches[3], nil
}
// ScaleSetInformationFromProviderID splits a provider's id belonging to an azure scaleset into core components.
// A providerID for scale set VMs is build after the following schema:
// - 'azure:///subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Compute/virtualMachineScaleSets/<scale-set-name>/virtualMachines/<instance-id>'
func ScaleSetInformationFromProviderID(providerID string) (subscriptionID, resourceGroup, scaleSet, instanceID string, err error) {
matches := azureVMSSProviderIDRegexp.FindStringSubmatch(providerID)
if len(matches) != 5 {
return "", "", "", "", errors.New("error splitting providerID")
}
return matches[1], matches[2], matches[3], matches[4], nil
}

View file

@ -0,0 +1,177 @@
package azureshared
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBasicsFromProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantErr bool
wantSubscriptionID string
wantResourceGroup string
}{
"providerID for individual instance works": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
},
"providerID for scale set instance works": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
},
"providerID is malformed": {
providerID: "malformed-provider-id",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
subscriptionID, resourceGroup, err := BasicsFromProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantSubscriptionID, subscriptionID)
assert.Equal(tc.wantResourceGroup, resourceGroup)
})
}
}
func TestUIDFromProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantUID string
wantErr bool
}{
"UID from virtual machine works": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group-ABC123/providers/Microsoft.Compute/virtualMachines/instance-name",
wantUID: "ABC123",
},
"providerID is malformed": {
providerID: "malformed-provider-id",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
uid, err := UIDFromProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.wantUID, uid)
})
}
}
func TestVMInformationFromProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantSubscriptionID string
wantResourceGroup string
wantInstanceName string
wantErr bool
}{
"simple id": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-id",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
wantInstanceName: "instance-id",
},
"missing instance": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines",
wantErr: true,
},
"providerID 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,
},
"wrong provider": {
providerID: "gcp:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-id",
wantErr: true,
},
"providerID is malformed": {
providerID: "malformed-provider-id",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
subscriptionID, resourceGroup, instanceName, err := VMInformationFromProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
assert.NoError(err)
assert.Equal(tc.wantSubscriptionID, subscriptionID)
assert.Equal(tc.wantResourceGroup, resourceGroup)
assert.Equal(tc.wantInstanceName, instanceName)
})
}
}
func TestScaleSetInformationFromProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantSubscriptionID string
wantResourceGroup string
wantScaleSet string
wantInstanceID string
wantErr bool
}{
"providerID for scale set instance works": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachineScaleSets/scale-set-name/virtualMachines/instance-id",
wantSubscriptionID: "subscription-id",
wantResourceGroup: "resource-group",
wantScaleSet: "scale-set-name",
wantInstanceID: "instance-id",
},
"providerID for individual instance must fail": {
providerID: "azure:///subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Compute/virtualMachines/instance-name",
wantErr: true,
},
"providerID is malformed": {
providerID: "malformed-provider-id",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
subscriptionID, resourceGroup, scaleSet, instanceName, err := ScaleSetInformationFromProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
assert.NoError(err)
assert.Equal(tc.wantSubscriptionID, subscriptionID)
assert.Equal(tc.wantResourceGroup, resourceGroup)
assert.Equal(tc.wantScaleSet, scaleSet)
assert.Equal(tc.wantInstanceID, instanceName)
})
}
}

View file

@ -0,0 +1,24 @@
package gcpshared
import (
"fmt"
"regexp"
)
var providerIDRegex = regexp.MustCompile(`^gce://([^/]+)/([^/]+)/([^/]+)$`)
// SplitProviderID splits a provider's id into core components.
// A providerID is build after the schema 'gce://<project-id>/<zone>/<instance-name>'
func SplitProviderID(providerID string) (project, zone, instance string, err error) {
matches := providerIDRegex.FindStringSubmatch(providerID)
if len(matches) != 4 {
return "", "", "", fmt.Errorf("error splitting providerID: %v", providerID)
}
return matches[1], matches[2], matches[3], nil
}
// JoinProviderID builds a k8s provider ID for GCP instances.
// A providerID is build after the schema 'gce://<project-id>/<zone>/<instance-name>'
func JoinProviderID(project, zone, instanceName string) string {
return fmt.Sprintf("gce://%v/%v/%v", project, zone, instanceName)
}

View file

@ -0,0 +1,75 @@
package gcpshared
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSplitProviderID(t *testing.T) {
testCases := map[string]struct {
providerID string
wantProjectID string
wantZone string
wantInstance string
wantErr bool
}{
"simple id": {
providerID: "gce://someProject/someZone/someInstance",
wantProjectID: "someProject",
wantZone: "someZone",
wantInstance: "someInstance",
},
"incomplete id": {
providerID: "gce://someProject/someZone",
wantErr: true,
},
"wrong provider": {
providerID: "azure://someProject/someZone/someInstance",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
projectID, zone, instance, err := SplitProviderID(tc.providerID)
if tc.wantErr {
assert.Error(err)
return
}
assert.NoError(err)
assert.Equal(tc.wantProjectID, projectID)
assert.Equal(tc.wantZone, zone)
assert.Equal(tc.wantInstance, instance)
})
}
}
func TestJoinProviderID(t *testing.T) {
testCases := map[string]struct {
projectID string
zone string
instance string
wantProviderID string
}{
"simple id": {
projectID: "someProject",
zone: "someZone",
instance: "someInstance",
wantProviderID: "gce://someProject/someZone/someInstance",
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
providerID := JoinProviderID(tc.projectID, tc.zone, tc.instance)
assert.Equal(tc.wantProviderID, providerID)
})
}
}