mirror of
				https://github.com/edgelesssys/constellation.git
				synced 2025-10-31 11:49:02 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			275 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package client
 | |
| 
 | |
| import (
 | |
| 	"crypto/rand"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"math/big"
 | |
| 	"time"
 | |
| 
 | |
| 	"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/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/azure-sdk-for-go/services/graphrbac/1.6/graphrbac"
 | |
| 	"github.com/Azure/go-autorest/autorest"
 | |
| 	"github.com/Azure/go-autorest/autorest/azure/auth"
 | |
| 	"github.com/edgelesssys/constellation/cli/azure"
 | |
| 	"github.com/edgelesssys/constellation/cli/cloudprovider"
 | |
| 	"github.com/edgelesssys/constellation/internal/state"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	graphAPIResource      = "https://graph.windows.net"
 | |
| 	managementAPIResource = "https://management.azure.com"
 | |
| )
 | |
| 
 | |
| // Client is a client for Azure.
 | |
| type Client struct {
 | |
| 	networksAPI
 | |
| 	networkSecurityGroupsAPI
 | |
| 	resourceGroupAPI
 | |
| 	scaleSetsAPI
 | |
| 	publicIPAddressesAPI
 | |
| 	networkInterfacesAPI
 | |
| 	virtualMachinesAPI
 | |
| 	applicationsAPI
 | |
| 	servicePrincipalsAPI
 | |
| 	roleAssignmentsAPI
 | |
| 
 | |
| 	adReplicationLagCheckInterval   time.Duration
 | |
| 	adReplicationLagCheckMaxRetries int
 | |
| 
 | |
| 	nodes        azure.Instances
 | |
| 	coordinators azure.Instances
 | |
| 
 | |
| 	name                 string
 | |
| 	uid                  string
 | |
| 	resourceGroup        string
 | |
| 	location             string
 | |
| 	subscriptionID       string
 | |
| 	tenantID             string
 | |
| 	subnetID             string
 | |
| 	coordinatorsScaleSet string
 | |
| 	nodesScaleSet        string
 | |
| 	networkSecurityGroup string
 | |
| 	adAppObjectID        string
 | |
| }
 | |
| 
 | |
| // NewFromDefault creates a client with initialized clients.
 | |
| func NewFromDefault(subscriptionID, tenantID string) (*Client, error) {
 | |
| 	cred, err := azidentity.NewDefaultAzureCredential(nil)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	graphAuthorizer, err := getAuthorizer(graphAPIResource)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	managementAuthorizer, err := getAuthorizer(managementAPIResource)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	netAPI := armnetwork.NewVirtualNetworksClient(subscriptionID, cred, nil)
 | |
| 	netSecGrpAPI := armnetwork.NewSecurityGroupsClient(subscriptionID, cred, nil)
 | |
| 	resGroupAPI := armresources.NewResourceGroupsClient(subscriptionID, cred, nil)
 | |
| 	scaleSetAPI := armcompute.NewVirtualMachineScaleSetsClient(subscriptionID, cred, nil)
 | |
| 	publicIPAddressesAPI := armnetwork.NewPublicIPAddressesClient(subscriptionID, cred, nil)
 | |
| 	networkInterfacesAPI := armnetwork.NewInterfacesClient(subscriptionID, cred, nil)
 | |
| 	virtualMachinesAPI := armcompute.NewVirtualMachinesClient(subscriptionID, cred, nil)
 | |
| 	applicationsAPI := graphrbac.NewApplicationsClient(tenantID)
 | |
| 	applicationsAPI.Authorizer = graphAuthorizer
 | |
| 	servicePrincipalsAPI := graphrbac.NewServicePrincipalsClient(tenantID)
 | |
| 	servicePrincipalsAPI.Authorizer = graphAuthorizer
 | |
| 	roleAssignmentsAPI := authorization.NewRoleAssignmentsClient(subscriptionID)
 | |
| 	roleAssignmentsAPI.Authorizer = managementAuthorizer
 | |
| 
 | |
| 	return &Client{
 | |
| 		networksAPI:                     &networksClient{netAPI},
 | |
| 		networkSecurityGroupsAPI:        &networkSecurityGroupsClient{netSecGrpAPI},
 | |
| 		resourceGroupAPI:                &resourceGroupsClient{resGroupAPI},
 | |
| 		scaleSetsAPI:                    &virtualMachineScaleSetsClient{scaleSetAPI},
 | |
| 		publicIPAddressesAPI:            &publicIPAddressesClient{publicIPAddressesAPI},
 | |
| 		networkInterfacesAPI:            &networkInterfacesClient{networkInterfacesAPI},
 | |
| 		applicationsAPI:                 &applicationsClient{&applicationsAPI},
 | |
| 		servicePrincipalsAPI:            &servicePrincipalsClient{&servicePrincipalsAPI},
 | |
| 		roleAssignmentsAPI:              &roleAssignmentsClient{&roleAssignmentsAPI},
 | |
| 		virtualMachinesAPI:              &virtualMachinesClient{virtualMachinesAPI},
 | |
| 		subscriptionID:                  subscriptionID,
 | |
| 		tenantID:                        tenantID,
 | |
| 		nodes:                           azure.Instances{},
 | |
| 		coordinators:                    azure.Instances{},
 | |
| 		adReplicationLagCheckInterval:   adReplicationLagCheckInterval,
 | |
| 		adReplicationLagCheckMaxRetries: adReplicationLagCheckMaxRetries,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // NewInitialized creates and initializes client by setting the subscriptionID, location and name
 | |
| // of the Constellation.
 | |
| func NewInitialized(subscriptionID, tenantID, name, location string) (*Client, error) {
 | |
| 	client, err := NewFromDefault(subscriptionID, tenantID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	err = client.init(location, name)
 | |
| 	return client, err
 | |
| }
 | |
| 
 | |
| // init initializes the client.
 | |
| func (c *Client) init(location, name string) error {
 | |
| 	c.location = location
 | |
| 	c.name = name
 | |
| 	uid, err := c.generateUID()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	c.uid = uid
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // GetState returns the state of the client as ConstellationState.
 | |
| func (c *Client) GetState() (state.ConstellationState, error) {
 | |
| 	var stat state.ConstellationState
 | |
| 	stat.CloudProvider = cloudprovider.Azure.String()
 | |
| 	if len(c.resourceGroup) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no resource group")
 | |
| 	}
 | |
| 	stat.AzureResourceGroup = c.resourceGroup
 | |
| 	if c.name == "" {
 | |
| 		return state.ConstellationState{}, errors.New("client has no name")
 | |
| 	}
 | |
| 	stat.Name = c.name
 | |
| 	if len(c.uid) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no uid")
 | |
| 	}
 | |
| 	stat.UID = c.uid
 | |
| 	if len(c.location) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no location")
 | |
| 	}
 | |
| 	stat.AzureLocation = c.location
 | |
| 	if len(c.subscriptionID) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no subscription")
 | |
| 	}
 | |
| 	stat.AzureSubscription = c.subscriptionID
 | |
| 	if len(c.tenantID) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no tenant")
 | |
| 	}
 | |
| 	stat.AzureTenant = c.tenantID
 | |
| 	if len(c.subnetID) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no subnet")
 | |
| 	}
 | |
| 	stat.AzureSubnet = c.subnetID
 | |
| 	if len(c.networkSecurityGroup) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no network security group")
 | |
| 	}
 | |
| 	stat.AzureNetworkSecurityGroup = c.networkSecurityGroup
 | |
| 	if len(c.nodesScaleSet) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no nodes scale set")
 | |
| 	}
 | |
| 	stat.AzureNodesScaleSet = c.nodesScaleSet
 | |
| 	if len(c.coordinatorsScaleSet) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no coordinators scale set")
 | |
| 	}
 | |
| 	stat.AzureCoordinatorsScaleSet = c.coordinatorsScaleSet
 | |
| 	if len(c.nodes) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no nodes")
 | |
| 	}
 | |
| 	stat.AzureNodes = c.nodes
 | |
| 	if len(c.coordinators) == 0 {
 | |
| 		return state.ConstellationState{}, errors.New("client has no coordinators")
 | |
| 	}
 | |
| 	stat.AzureCoordinators = c.coordinators
 | |
| 	// AD App Object ID does not have to be set at all times
 | |
| 	stat.AzureADAppObjectID = c.adAppObjectID
 | |
| 
 | |
| 	return stat, nil
 | |
| }
 | |
| 
 | |
| // SetState sets the state of the client to the handed ConstellationState.
 | |
| func (c *Client) SetState(stat state.ConstellationState) error {
 | |
| 	if stat.CloudProvider != cloudprovider.Azure.String() {
 | |
| 		return errors.New("state is not azure state")
 | |
| 	}
 | |
| 	if len(stat.AzureResourceGroup) == 0 {
 | |
| 		return errors.New("state has no resource group")
 | |
| 	}
 | |
| 	c.resourceGroup = stat.AzureResourceGroup
 | |
| 	if stat.Name == "" {
 | |
| 		return errors.New("state has no name")
 | |
| 	}
 | |
| 	c.name = stat.Name
 | |
| 	if len(stat.UID) == 0 {
 | |
| 		return errors.New("state has no uuid")
 | |
| 	}
 | |
| 	c.uid = stat.UID
 | |
| 	if len(stat.AzureLocation) == 0 {
 | |
| 		return errors.New("state has no location")
 | |
| 	}
 | |
| 	c.location = stat.AzureLocation
 | |
| 	if len(stat.AzureSubscription) == 0 {
 | |
| 		return errors.New("state has no subscription")
 | |
| 	}
 | |
| 	c.subscriptionID = stat.AzureSubscription
 | |
| 	if len(stat.AzureTenant) == 0 {
 | |
| 		return errors.New("state has no tenant")
 | |
| 	}
 | |
| 	c.tenantID = stat.AzureTenant
 | |
| 	if len(stat.AzureSubnet) == 0 {
 | |
| 		return errors.New("state has no subnet")
 | |
| 	}
 | |
| 	c.subnetID = stat.AzureSubnet
 | |
| 	if len(stat.AzureNetworkSecurityGroup) == 0 {
 | |
| 		return errors.New("state has no subnet")
 | |
| 	}
 | |
| 	c.networkSecurityGroup = stat.AzureNetworkSecurityGroup
 | |
| 	if len(stat.AzureNodesScaleSet) == 0 {
 | |
| 		return errors.New("state has no nodes scale set")
 | |
| 	}
 | |
| 	c.nodesScaleSet = stat.AzureNodesScaleSet
 | |
| 	if len(stat.AzureCoordinatorsScaleSet) == 0 {
 | |
| 		return errors.New("state has no nodes scale set")
 | |
| 	}
 | |
| 	c.coordinatorsScaleSet = stat.AzureCoordinatorsScaleSet
 | |
| 	if len(stat.AzureNodes) == 0 {
 | |
| 		return errors.New("state has no coordinator scale set")
 | |
| 	}
 | |
| 	c.nodes = stat.AzureNodes
 | |
| 	if len(stat.AzureCoordinators) == 0 {
 | |
| 		return errors.New("state has no coordinators")
 | |
| 	}
 | |
| 	c.coordinators = stat.AzureCoordinators
 | |
| 	// AD App Object ID does not have to be set at all times
 | |
| 	c.adAppObjectID = stat.AzureADAppObjectID
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *Client) generateUID() (string, error) {
 | |
| 	letters := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
 | |
| 
 | |
| 	const uidLen = 5
 | |
| 	uid := make([]byte, uidLen)
 | |
| 	for i := 0; i < uidLen; i++ {
 | |
| 		n, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
 | |
| 		if err != nil {
 | |
| 			return "", err
 | |
| 		}
 | |
| 		uid[i] = letters[n.Int64()]
 | |
| 	}
 | |
| 	return string(uid), nil
 | |
| }
 | |
| 
 | |
| // getAuthorizer creates an autorest.Authorizer for different Azure AD APIs using either environment variables or azure cli credentials.
 | |
| func getAuthorizer(resource string) (autorest.Authorizer, error) {
 | |
| 	authorizer, cliErr := auth.NewAuthorizerFromCLIWithResource(resource)
 | |
| 	if cliErr == nil {
 | |
| 		return authorizer, nil
 | |
| 	}
 | |
| 	authorizer, envErr := auth.NewAuthorizerFromEnvironmentWithResource(resource)
 | |
| 	if envErr == nil {
 | |
| 		return authorizer, nil
 | |
| 	}
 | |
| 	return nil, fmt.Errorf("unable to create authorizer from env or cli: %v %v", envErr, cliErr)
 | |
| }
 | 
