Separate shared azure code

This commit is contained in:
katexochen 2022-06-07 16:27:55 +02:00
parent 6cd93e4179
commit 180d7872dd
9 changed files with 103 additions and 94 deletions

View File

@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"net/url"
"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"
@ -15,6 +14,7 @@ import (
"github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/date" "github.com/Azure/go-autorest/autorest/date"
"github.com/Azure/go-autorest/autorest/to" "github.com/Azure/go-autorest/autorest/to"
"github.com/edgelesssys/constellation/internal/azureshared"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -47,12 +47,12 @@ func (c *Client) CreateServicePrincipal(ctx context.Context) (string, error) {
return "", err return "", err
} }
return ApplicationCredentials{ return azureshared.ApplicationCredentials{
TenantID: c.tenantID, TenantID: c.tenantID,
ClientID: createAppRes.AppID, ClientID: createAppRes.AppID,
ClientSecret: clientSecret, ClientSecret: clientSecret,
Location: c.location, Location: c.location,
}.ConvertToCloudServiceAccountURI(), nil }.ToCloudServiceAccountURI(), nil
} }
// TerminateServicePrincipal terminates an Azure AD app together with the service principal. // TerminateServicePrincipal terminates an Azure AD app together with the service principal.
@ -164,30 +164,6 @@ func (c *Client) assignResourceGroupRole(ctx context.Context, principalID, roleD
return err return err
} }
// ApplicationCredentials is a set of Azure AD application credentials.
// It is the equivalent of a service account key in other cloud providers.
type ApplicationCredentials struct {
TenantID string
ClientID string
ClientSecret string
Location string
}
// ConvertToCloudServiceAccountURI converts the ApplicationCredentials into a cloud service account URI.
func (c ApplicationCredentials) ConvertToCloudServiceAccountURI() string {
query := url.Values{}
query.Add("tenant_id", c.TenantID)
query.Add("client_id", c.ClientID)
query.Add("client_secret", c.ClientSecret)
query.Add("location", c.Location)
uri := url.URL{
Scheme: "serviceaccount",
Host: "azure",
RawQuery: query.Encode(),
}
return uri.String()
}
type createADApplicationOutput struct { type createADApplicationOutput struct {
AppID string AppID string
ObjectID string ObjectID string

View File

@ -3,7 +3,6 @@ package client
import ( import (
"context" "context"
"errors" "errors"
"net/url"
"testing" "testing"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
@ -12,7 +11,6 @@ import (
"github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/azure"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
@ -358,27 +356,3 @@ func TestAssignOwnerOfResourceGroup(t *testing.T) {
}) })
} }
} }
func TestConvertToCloudServiceAccountURI(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
key := ApplicationCredentials{
TenantID: "tenant-id",
ClientID: "client-id",
ClientSecret: "client-secret",
Location: "location",
}
cloudServiceAccountURI := key.ConvertToCloudServiceAccountURI()
uri, err := url.Parse(cloudServiceAccountURI)
require.NoError(err)
query := uri.Query()
assert.Equal("serviceaccount", uri.Scheme)
assert.Equal("azure", uri.Host)
assert.Equal(url.Values{
"tenant_id": []string{"tenant-id"},
"client_id": []string{"client-id"},
"client_secret": []string{"client-secret"},
"location": []string{"location"},
}, query)
}

View File

@ -8,6 +8,7 @@ import (
azurecl "github.com/edgelesssys/constellation/cli/azure/client" azurecl "github.com/edgelesssys/constellation/cli/azure/client"
"github.com/edgelesssys/constellation/cli/cloud/cloudtypes" "github.com/edgelesssys/constellation/cli/cloud/cloudtypes"
gcpcl "github.com/edgelesssys/constellation/cli/internal/gcp/client" gcpcl "github.com/edgelesssys/constellation/cli/internal/gcp/client"
"github.com/edgelesssys/constellation/internal/azureshared"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/gcpshared" "github.com/edgelesssys/constellation/internal/gcpshared"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
@ -121,10 +122,10 @@ func (c *fakeAzureClient) CreateInstancesVMs(ctx context.Context, input azurecl.
func (c *fakeAzureClient) CreateServicePrincipal(ctx context.Context) (string, error) { func (c *fakeAzureClient) CreateServicePrincipal(ctx context.Context) (string, error) {
c.adAppObjectID = "00000000-0000-0000-0000-000000000001" c.adAppObjectID = "00000000-0000-0000-0000-000000000001"
return azurecl.ApplicationCredentials{ return azureshared.ApplicationCredentials{
ClientID: "client-id", ClientID: "client-id",
ClientSecret: "client-secret", ClientSecret: "client-secret",
}.ConvertToCloudServiceAccountURI(), nil }.ToCloudServiceAccountURI(), nil
} }
func (c *fakeAzureClient) TerminateResourceGroup(ctx context.Context) error { func (c *fakeAzureClient) TerminateResourceGroup(ctx context.Context) error {
@ -199,10 +200,10 @@ func (c *stubAzureClient) CreateInstancesVMs(ctx context.Context, input azurecl.
} }
func (c *stubAzureClient) CreateServicePrincipal(ctx context.Context) (string, error) { func (c *stubAzureClient) CreateServicePrincipal(ctx context.Context) (string, error) {
return azurecl.ApplicationCredentials{ return azureshared.ApplicationCredentials{
ClientID: "00000000-0000-0000-0000-000000000000", ClientID: "00000000-0000-0000-0000-000000000000",
ClientSecret: "secret", ClientSecret: "secret",
}.ConvertToCloudServiceAccountURI(), c.createServicePrincipalErr }.ToCloudServiceAccountURI(), c.createServicePrincipalErr
} }
func (c *stubAzureClient) TerminateResourceGroup(ctx context.Context) error { func (c *stubAzureClient) TerminateResourceGroup(ctx context.Context) error {

View File

@ -3,6 +3,7 @@ package azure
import ( import (
"github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes" "github.com/edgelesssys/constellation/coordinator/cloudprovider/cloudtypes"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/internal/azureshared"
k8s "k8s.io/api/core/v1" k8s "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
@ -21,7 +22,7 @@ func (a *Autoscaler) Secrets(instance cloudtypes.Instance, cloudServiceAccountUR
if err != nil { if err != nil {
return resources.Secrets{}, err return resources.Secrets{}, err
} }
creds, err := getApplicationCredentials(cloudServiceAccountURI) creds, err := azureshared.ApplicationCredentialsFromURI(cloudServiceAccountURI)
if err != nil { if err != nil {
return resources.Secrets{}, err return resources.Secrets{}, err
} }

View File

@ -7,6 +7,7 @@ import (
"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/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/internal/azureshared"
k8s "k8s.io/api/core/v1" k8s "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
@ -68,7 +69,7 @@ func (c *CloudControllerManager) Secrets(ctx context.Context, instance cloudtype
if err != nil { if err != nil {
return resources.Secrets{}, err return resources.Secrets{}, err
} }
creds, err := getApplicationCredentials(cloudServiceAccountURI) creds, err := azureshared.ApplicationCredentialsFromURI(cloudServiceAccountURI)
if err != nil { if err != nil {
return resources.Secrets{}, err return resources.Secrets{}, err
} }

View File

@ -1,29 +0,0 @@
package azure
import (
"fmt"
"net/url"
"github.com/edgelesssys/constellation/cli/azure/client"
)
// getApplicationCredentials converts a cloudServiceAccountURI into Azure ApplicationCredentials.
func getApplicationCredentials(cloudServiceAccountURI string) (client.ApplicationCredentials, error) {
uri, err := url.Parse(cloudServiceAccountURI)
if err != nil {
return client.ApplicationCredentials{}, err
}
if uri.Scheme != "serviceaccount" {
return client.ApplicationCredentials{}, fmt.Errorf("invalid service account URI: invalid scheme: %s", uri.Scheme)
}
if uri.Host != "azure" {
return client.ApplicationCredentials{}, fmt.Errorf("invalid service account URI: invalid host: %s", uri.Host)
}
query := uri.Query()
return client.ApplicationCredentials{
TenantID: query.Get("tenant_id"),
ClientID: query.Get("client_id"),
ClientSecret: query.Get("client_secret"),
Location: query.Get("location"),
}, nil
}

View File

@ -0,0 +1,51 @@
package azureshared
import (
"fmt"
"net/url"
)
// ApplicationCredentials is a set of Azure AD application credentials.
// It is the equivalent of a service account key in other cloud providers.
type ApplicationCredentials struct {
TenantID string
ClientID string
ClientSecret string
Location string
}
// ApplicationCredentialsFromURI converts a cloudServiceAccountURI into Azure ApplicationCredentials.
func ApplicationCredentialsFromURI(cloudServiceAccountURI string) (ApplicationCredentials, error) {
uri, err := url.Parse(cloudServiceAccountURI)
if err != nil {
return ApplicationCredentials{}, err
}
if uri.Scheme != "serviceaccount" {
return ApplicationCredentials{}, fmt.Errorf("invalid service account URI: invalid scheme: %s", uri.Scheme)
}
if uri.Host != "azure" {
return ApplicationCredentials{}, fmt.Errorf("invalid service account URI: invalid host: %s", uri.Host)
}
query := uri.Query()
return ApplicationCredentials{
TenantID: query.Get("tenant_id"),
ClientID: query.Get("client_id"),
ClientSecret: query.Get("client_secret"),
Location: query.Get("location"),
}, nil
}
// ToCloudServiceAccountURI converts the ApplicationCredentials into a cloud service account URI.
func (c ApplicationCredentials) ToCloudServiceAccountURI() string {
query := url.Values{}
query.Add("tenant_id", c.TenantID)
query.Add("client_id", c.ClientID)
query.Add("client_secret", c.ClientSecret)
query.Add("location", c.Location)
uri := url.URL{
Scheme: "serviceaccount",
Host: "azure",
RawQuery: query.Encode(),
}
return uri.String()
}

View File

@ -1,15 +1,15 @@
package azure package azureshared
import ( import (
"net/url"
"testing" "testing"
"github.com/edgelesssys/constellation/cli/azure/client"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestGetApplicationCredentials(t *testing.T) { func TestApplicationCredentialsFromURI(t *testing.T) {
creds := client.ApplicationCredentials{ creds := ApplicationCredentials{
TenantID: "tenant-id", TenantID: "tenant-id",
ClientID: "client-id", ClientID: "client-id",
ClientSecret: "client-secret", ClientSecret: "client-secret",
@ -17,7 +17,7 @@ func TestGetApplicationCredentials(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
cloudServiceAccountURI string cloudServiceAccountURI string
wantCreds client.ApplicationCredentials wantCreds ApplicationCredentials
wantErr bool wantErr bool
}{ }{
"getApplicationCredentials works": { "getApplicationCredentials works": {
@ -43,7 +43,7 @@ func TestGetApplicationCredentials(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
creds, err := getApplicationCredentials(tc.cloudServiceAccountURI) creds, err := ApplicationCredentialsFromURI(tc.cloudServiceAccountURI)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
return return
@ -53,3 +53,27 @@ func TestGetApplicationCredentials(t *testing.T) {
}) })
} }
} }
func TestToCloudServiceAccountURI(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
key := ApplicationCredentials{
TenantID: "tenant-id",
ClientID: "client-id",
ClientSecret: "client-secret",
Location: "location",
}
cloudServiceAccountURI := key.ToCloudServiceAccountURI()
uri, err := url.Parse(cloudServiceAccountURI)
require.NoError(err)
query := uri.Query()
assert.Equal("serviceaccount", uri.Scheme)
assert.Equal("azure", uri.Host)
assert.Equal(url.Values{
"tenant_id": []string{"tenant-id"},
"client_id": []string{"client-id"},
"client_secret": []string{"client-secret"},
"location": []string{"location"},
}, query)
}

View File

@ -0,0 +1,10 @@
package azureshared
/*
Package azureshared contains code that is related to Microsoft Azure
and is used by multiple microservices.
This package is intended to have a minimal size and surface. If you
have Azure related code that is not shared by multiple microservices,
please keep the code in the microservice's internal package.
*/