mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-06-19 03:39:32 -04:00
Use Terraform for create Azure
This commit is contained in:
parent
98a16b2b47
commit
f4af9c56f5
9 changed files with 97 additions and 400 deletions
|
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Changed
|
### Changed
|
||||||
<!-- For changes in existing functionality. -->
|
<!-- For changes in existing functionality. -->
|
||||||
- Verify measurements using [Rekor](https://github.com/sigstore/rekor) transparency log.
|
- Verify measurements using [Rekor](https://github.com/sigstore/rekor) transparency log.
|
||||||
|
- The `constellation create` on Azure now uses Terraform to create and destroy cloud resources.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
<!-- For soon-to-be removed features. -->
|
<!-- For soon-to-be removed features. -->
|
||||||
|
|
|
@ -9,7 +9,6 @@ package cloudcmd
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||||
)
|
)
|
||||||
|
@ -22,17 +21,6 @@ type terraformClient interface {
|
||||||
RemoveInstaller()
|
RemoveInstaller()
|
||||||
}
|
}
|
||||||
|
|
||||||
type azureclient interface {
|
|
||||||
GetState() state.ConstellationState
|
|
||||||
SetState(state.ConstellationState)
|
|
||||||
CreateApplicationInsight(ctx context.Context) error
|
|
||||||
CreateExternalLoadBalancer(ctx context.Context, isDebugCluster bool) error
|
|
||||||
CreateVirtualNetwork(ctx context.Context) error
|
|
||||||
CreateSecurityGroup(ctx context.Context, input azurecl.NetworkSecurityGroupInput) error
|
|
||||||
CreateInstances(ctx context.Context, input azurecl.CreateInstancesInput) error
|
|
||||||
TerminateResourceGroupResources(ctx context.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type libvirtRunner interface {
|
type libvirtRunner interface {
|
||||||
Start(ctx context.Context, containerName, imageName string) error
|
Start(ctx context.Context, containerName, imageName string) error
|
||||||
Stop(ctx context.Context) error
|
Stop(ctx context.Context) error
|
||||||
|
|
|
@ -8,14 +8,9 @@ package cloudcmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/azureshared"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
)
|
)
|
||||||
|
@ -27,173 +22,6 @@ func TestMain(m *testing.M) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeAzureClient struct {
|
|
||||||
workers cloudtypes.Instances
|
|
||||||
controlPlanes cloudtypes.Instances
|
|
||||||
|
|
||||||
resourceGroup string
|
|
||||||
name string
|
|
||||||
uid string
|
|
||||||
location string
|
|
||||||
subscriptionID string
|
|
||||||
tenantID string
|
|
||||||
subnetID string
|
|
||||||
loadBalancerName string
|
|
||||||
controlPlaneScaleSet string
|
|
||||||
workerScaleSet string
|
|
||||||
networkSecurityGroup string
|
|
||||||
adAppObjectID string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) GetState() state.ConstellationState {
|
|
||||||
return state.ConstellationState{
|
|
||||||
CloudProvider: cloudprovider.Azure.String(),
|
|
||||||
AzureWorkerInstances: c.workers,
|
|
||||||
AzureControlPlaneInstances: c.controlPlanes,
|
|
||||||
Name: c.name,
|
|
||||||
UID: c.uid,
|
|
||||||
AzureResourceGroup: c.resourceGroup,
|
|
||||||
AzureLocation: c.location,
|
|
||||||
AzureSubscription: c.subscriptionID,
|
|
||||||
AzureTenant: c.tenantID,
|
|
||||||
AzureSubnet: c.subnetID,
|
|
||||||
AzureNetworkSecurityGroup: c.networkSecurityGroup,
|
|
||||||
AzureWorkerScaleSet: c.workerScaleSet,
|
|
||||||
AzureControlPlaneScaleSet: c.controlPlaneScaleSet,
|
|
||||||
AzureADAppObjectID: c.adAppObjectID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) SetState(stat state.ConstellationState) {
|
|
||||||
c.workers = stat.AzureWorkerInstances
|
|
||||||
c.controlPlanes = stat.AzureControlPlaneInstances
|
|
||||||
c.name = stat.Name
|
|
||||||
c.uid = stat.UID
|
|
||||||
c.resourceGroup = stat.AzureResourceGroup
|
|
||||||
c.location = stat.AzureLocation
|
|
||||||
c.subscriptionID = stat.AzureSubscription
|
|
||||||
c.tenantID = stat.AzureTenant
|
|
||||||
c.subnetID = stat.AzureSubnet
|
|
||||||
c.networkSecurityGroup = stat.AzureNetworkSecurityGroup
|
|
||||||
c.workerScaleSet = stat.AzureWorkerScaleSet
|
|
||||||
c.controlPlaneScaleSet = stat.AzureControlPlaneScaleSet
|
|
||||||
c.adAppObjectID = stat.AzureADAppObjectID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) CreateApplicationInsight(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) CreateVirtualNetwork(ctx context.Context) error {
|
|
||||||
c.subnetID = "subnet"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) CreateExternalLoadBalancer(ctx context.Context, isDebugCluster bool) error {
|
|
||||||
c.loadBalancerName = "loadBalancer"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) CreateSecurityGroup(ctx context.Context, input azurecl.NetworkSecurityGroupInput) error {
|
|
||||||
c.networkSecurityGroup = "network-security-group"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) CreateInstances(ctx context.Context, input azurecl.CreateInstancesInput) error {
|
|
||||||
c.controlPlaneScaleSet = "controlplanes-scale-set"
|
|
||||||
c.workerScaleSet = "workers-scale-set"
|
|
||||||
c.workers = make(cloudtypes.Instances)
|
|
||||||
for i := 0; i < input.CountWorkers; i++ {
|
|
||||||
id := "id-" + strconv.Itoa(i)
|
|
||||||
c.workers[id] = cloudtypes.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
|
|
||||||
}
|
|
||||||
c.controlPlanes = make(cloudtypes.Instances)
|
|
||||||
for i := 0; i < input.CountControlPlanes; i++ {
|
|
||||||
id := "id-" + strconv.Itoa(i)
|
|
||||||
c.controlPlanes[id] = cloudtypes.Instance{PublicIP: "192.0.2.1", PrivateIP: "192.0.2.1"}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) CreateServicePrincipal(ctx context.Context) (string, error) {
|
|
||||||
c.adAppObjectID = "00000000-0000-0000-0000-000000000001"
|
|
||||||
return azureshared.ApplicationCredentials{
|
|
||||||
AppClientID: "client-id",
|
|
||||||
ClientSecretValue: "client-secret",
|
|
||||||
}.ToCloudServiceAccountURI(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) TerminateResourceGroupResources(ctx context.Context) error {
|
|
||||||
// TODO(katexochen)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeAzureClient) TerminateServicePrincipal(ctx context.Context) error {
|
|
||||||
if c.adAppObjectID == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
c.adAppObjectID = ""
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type stubAzureClient struct {
|
|
||||||
terminateResourceGroupResourcesCalled bool
|
|
||||||
terminateServicePrincipalCalled bool
|
|
||||||
|
|
||||||
createApplicationInsightErr error
|
|
||||||
createVirtualNetworkErr error
|
|
||||||
createSecurityGroupErr error
|
|
||||||
createLoadBalancerErr error
|
|
||||||
createInstancesErr error
|
|
||||||
createServicePrincipalErr error
|
|
||||||
terminateResourceGroupResourcesErr error
|
|
||||||
terminateServicePrincipalErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) GetState() state.ConstellationState {
|
|
||||||
return state.ConstellationState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) SetState(state.ConstellationState) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) CreateExternalLoadBalancer(ctx context.Context, isDebugCluster bool) error {
|
|
||||||
return c.createLoadBalancerErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) CreateApplicationInsight(ctx context.Context) error {
|
|
||||||
return c.createApplicationInsightErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) CreateVirtualNetwork(ctx context.Context) error {
|
|
||||||
return c.createVirtualNetworkErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) CreateSecurityGroup(ctx context.Context, input azurecl.NetworkSecurityGroupInput) error {
|
|
||||||
return c.createSecurityGroupErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) CreateInstances(ctx context.Context, input azurecl.CreateInstancesInput) error {
|
|
||||||
return c.createInstancesErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) CreateServicePrincipal(ctx context.Context) (string, error) {
|
|
||||||
return azureshared.ApplicationCredentials{
|
|
||||||
AppClientID: "00000000-0000-0000-0000-000000000000",
|
|
||||||
ClientSecretValue: "secret",
|
|
||||||
}.ToCloudServiceAccountURI(), c.createServicePrincipalErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) TerminateResourceGroupResources(ctx context.Context) error {
|
|
||||||
c.terminateResourceGroupResourcesCalled = true
|
|
||||||
return c.terminateResourceGroupResourcesErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubAzureClient) TerminateServicePrincipal(ctx context.Context) error {
|
|
||||||
c.terminateServicePrincipalCalled = true
|
|
||||||
return c.terminateServicePrincipalErr
|
|
||||||
}
|
|
||||||
|
|
||||||
type stubTerraformClient struct {
|
type stubTerraformClient struct {
|
||||||
state state.ConstellationState
|
state state.ConstellationState
|
||||||
cleanUpWorkspaceCalled bool
|
cleanUpWorkspaceCalled bool
|
||||||
|
|
|
@ -15,13 +15,10 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,7 +26,6 @@ import (
|
||||||
type Creator struct {
|
type Creator struct {
|
||||||
out io.Writer
|
out io.Writer
|
||||||
newTerraformClient func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error)
|
newTerraformClient func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error)
|
||||||
newAzureClient func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error)
|
|
||||||
newLibvirtRunner func() libvirtRunner
|
newLibvirtRunner func() libvirtRunner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,9 +36,6 @@ func NewCreator(out io.Writer) *Creator {
|
||||||
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||||
return terraform.New(ctx, provider)
|
return terraform.New(ctx, provider)
|
||||||
},
|
},
|
||||||
newAzureClient: func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error) {
|
|
||||||
return azurecl.NewInitialized(subscriptionID, tenantID, name, location, resourceGroup)
|
|
||||||
},
|
|
||||||
newLibvirtRunner: func() libvirtRunner {
|
newLibvirtRunner: func() libvirtRunner {
|
||||||
return libvirt.New()
|
return libvirt.New()
|
||||||
},
|
},
|
||||||
|
@ -52,14 +45,6 @@ func NewCreator(out io.Writer) *Creator {
|
||||||
// Create creates the handed amount of instances and all the needed resources.
|
// Create creates the handed amount of instances and all the needed resources.
|
||||||
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, name, insType string, controlPlaneCount, workerCount int,
|
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, name, insType string, controlPlaneCount, workerCount int,
|
||||||
) (state.ConstellationState, error) {
|
) (state.ConstellationState, error) {
|
||||||
// Use debug ingress firewall rules when debug mode / image is enabled
|
|
||||||
var ingressRules cloudtypes.Firewall
|
|
||||||
if config.IsDebugCluster() {
|
|
||||||
ingressRules = constants.IngressRulesDebug
|
|
||||||
} else {
|
|
||||||
ingressRules = constants.IngressRulesNoDebug
|
|
||||||
}
|
|
||||||
|
|
||||||
switch provider {
|
switch provider {
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
cl, err := c.newTerraformClient(ctx, provider)
|
cl, err := c.newTerraformClient(ctx, provider)
|
||||||
|
@ -69,17 +54,12 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createGCP(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
return c.createGCP(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl, err := c.newAzureClient(
|
cl, err := c.newTerraformClient(ctx, provider)
|
||||||
config.Provider.Azure.SubscriptionID,
|
|
||||||
config.Provider.Azure.TenantID,
|
|
||||||
name,
|
|
||||||
config.Provider.Azure.Location,
|
|
||||||
config.Provider.Azure.ResourceGroup,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
return c.createAzure(ctx, cl, config, insType, controlPlaneCount, workerCount, ingressRules)
|
defer cl.RemoveInstaller()
|
||||||
|
return c.createAzure(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
||||||
case cloudprovider.QEMU:
|
case cloudprovider.QEMU:
|
||||||
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
|
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
|
||||||
return state.ConstellationState{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
|
return state.ConstellationState{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
@ -125,37 +105,29 @@ func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *con
|
||||||
return cl.GetState(), nil
|
return cl.GetState(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createAzure(ctx context.Context, cl azureclient, config *config.Config, insType string, controlPlaneCount, workerCount int, ingressRules cloudtypes.Firewall,
|
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *config.Config,
|
||||||
|
name, insType string, controlPlaneCount, workerCount int,
|
||||||
) (stat state.ConstellationState, retErr error) {
|
) (stat state.ConstellationState, retErr error) {
|
||||||
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerAzure{client: cl})
|
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||||
|
|
||||||
if err := cl.CreateApplicationInsight(ctx); err != nil {
|
vars := &terraform.AzureVariables{
|
||||||
return state.ConstellationState{}, err
|
CommonVariables: terraform.CommonVariables{
|
||||||
}
|
Name: name,
|
||||||
if err := cl.CreateExternalLoadBalancer(ctx, config.IsDebugCluster()); err != nil {
|
|
||||||
return state.ConstellationState{}, err
|
|
||||||
}
|
|
||||||
if err := cl.CreateVirtualNetwork(ctx); err != nil {
|
|
||||||
return state.ConstellationState{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cl.CreateSecurityGroup(ctx, azurecl.NetworkSecurityGroupInput{
|
|
||||||
Ingress: ingressRules,
|
|
||||||
Egress: constants.EgressRules,
|
|
||||||
}); err != nil {
|
|
||||||
return state.ConstellationState{}, err
|
|
||||||
}
|
|
||||||
createInput := azurecl.CreateInstancesInput{
|
|
||||||
CountControlPlanes: controlPlaneCount,
|
CountControlPlanes: controlPlaneCount,
|
||||||
CountWorkers: workerCount,
|
CountWorkers: workerCount,
|
||||||
InstanceType: insType,
|
|
||||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||||
|
},
|
||||||
|
Location: config.Provider.Azure.Location,
|
||||||
|
ResourceGroup: config.Provider.Azure.ResourceGroup,
|
||||||
|
UserAssignedIdentity: config.Provider.Azure.UserAssignedIdentity,
|
||||||
|
InstanceType: insType,
|
||||||
StateDiskType: config.Provider.Azure.StateDiskType,
|
StateDiskType: config.Provider.Azure.StateDiskType,
|
||||||
Image: config.Provider.Azure.Image,
|
ImageID: config.Provider.Azure.Image,
|
||||||
UserAssingedIdentity: config.Provider.Azure.UserAssignedIdentity,
|
|
||||||
ConfidentialVM: *config.Provider.Azure.ConfidentialVM,
|
ConfidentialVM: *config.Provider.Azure.ConfidentialVM,
|
||||||
|
Debug: config.IsDebugCluster(),
|
||||||
}
|
}
|
||||||
if err := cl.CreateInstances(ctx, createInput); err != nil {
|
|
||||||
|
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -33,30 +32,11 @@ func TestCreator(t *testing.T) {
|
||||||
LoadBalancerIP: "192.0.2.1",
|
LoadBalancerIP: "192.0.2.1",
|
||||||
}
|
}
|
||||||
|
|
||||||
wantAzureState := state.ConstellationState{
|
|
||||||
CloudProvider: cloudprovider.Azure.String(),
|
|
||||||
AzureControlPlaneInstances: cloudtypes.Instances{
|
|
||||||
"id-0": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
"id-1": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
},
|
|
||||||
AzureWorkerInstances: cloudtypes.Instances{
|
|
||||||
"id-0": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
"id-1": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
"id-2": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
},
|
|
||||||
AzureSubnet: "subnet",
|
|
||||||
AzureNetworkSecurityGroup: "network-security-group",
|
|
||||||
AzureWorkerScaleSet: "workers-scale-set",
|
|
||||||
AzureControlPlaneScaleSet: "controlplanes-scale-set",
|
|
||||||
}
|
|
||||||
|
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
tfClient terraformClient
|
tfClient terraformClient
|
||||||
newTfClientErr error
|
newTfClientErr error
|
||||||
azureclient azureclient
|
|
||||||
newAzureClientErr error
|
|
||||||
libvirt *stubLibvirtRunner
|
libvirt *stubLibvirtRunner
|
||||||
provider cloudprovider.Provider
|
provider cloudprovider.Provider
|
||||||
config *config.Config
|
config *config.Config
|
||||||
|
@ -114,39 +94,6 @@ func TestCreator(t *testing.T) {
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
wantRollback: !failOnNonAMD64,
|
wantRollback: !failOnNonAMD64,
|
||||||
},
|
},
|
||||||
"azure": {
|
|
||||||
azureclient: &fakeAzureClient{},
|
|
||||||
provider: cloudprovider.Azure,
|
|
||||||
config: config.Default(),
|
|
||||||
wantState: wantAzureState,
|
|
||||||
},
|
|
||||||
"azure newAzureClient error": {
|
|
||||||
newAzureClientErr: someErr,
|
|
||||||
provider: cloudprovider.Azure,
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"azure CreateVirtualNetwork error": {
|
|
||||||
azureclient: &stubAzureClient{createVirtualNetworkErr: someErr},
|
|
||||||
provider: cloudprovider.Azure,
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
wantRollback: true,
|
|
||||||
},
|
|
||||||
"azure CreateSecurityGroup error": {
|
|
||||||
azureclient: &stubAzureClient{createSecurityGroupErr: someErr},
|
|
||||||
provider: cloudprovider.Azure,
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
wantRollback: true,
|
|
||||||
},
|
|
||||||
"azure CreateInstances error": {
|
|
||||||
azureclient: &stubAzureClient{createInstancesErr: someErr},
|
|
||||||
provider: cloudprovider.Azure,
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
wantRollback: true,
|
|
||||||
},
|
|
||||||
"unknown provider": {
|
"unknown provider": {
|
||||||
provider: cloudprovider.Unknown,
|
provider: cloudprovider.Unknown,
|
||||||
config: config.Default(),
|
config: config.Default(),
|
||||||
|
@ -163,9 +110,6 @@ func TestCreator(t *testing.T) {
|
||||||
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||||
return tc.tfClient, tc.newTfClientErr
|
return tc.tfClient, tc.newTfClientErr
|
||||||
},
|
},
|
||||||
newAzureClient: func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error) {
|
|
||||||
return tc.azureclient, tc.newAzureClientErr
|
|
||||||
},
|
|
||||||
newLibvirtRunner: func() libvirtRunner {
|
newLibvirtRunner: func() libvirtRunner {
|
||||||
return tc.libvirt
|
return tc.libvirt
|
||||||
},
|
},
|
||||||
|
@ -176,19 +120,11 @@ func TestCreator(t *testing.T) {
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
if tc.wantRollback {
|
if tc.wantRollback {
|
||||||
switch tc.provider {
|
|
||||||
case cloudprovider.QEMU:
|
|
||||||
cl := tc.tfClient.(*stubTerraformClient)
|
cl := tc.tfClient.(*stubTerraformClient)
|
||||||
assert.True(cl.destroyClusterCalled)
|
assert.True(cl.destroyClusterCalled)
|
||||||
assert.True(cl.cleanUpWorkspaceCalled)
|
assert.True(cl.cleanUpWorkspaceCalled)
|
||||||
|
if tc.provider == cloudprovider.QEMU {
|
||||||
assert.True(tc.libvirt.stopCalled)
|
assert.True(tc.libvirt.stopCalled)
|
||||||
case cloudprovider.GCP:
|
|
||||||
cl := tc.tfClient.(*stubTerraformClient)
|
|
||||||
assert.True(cl.destroyClusterCalled)
|
|
||||||
assert.True(cl.cleanUpWorkspaceCalled)
|
|
||||||
case cloudprovider.Azure:
|
|
||||||
cl := tc.azureclient.(*stubAzureClient)
|
|
||||||
assert.True(cl.terminateResourceGroupResourcesCalled)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -34,14 +34,6 @@ func rollbackOnError(ctx context.Context, w io.Writer, onErr *error, roll rollba
|
||||||
fmt.Fprintln(w, "Rollback succeeded.")
|
fmt.Fprintln(w, "Rollback succeeded.")
|
||||||
}
|
}
|
||||||
|
|
||||||
type rollbackerAzure struct {
|
|
||||||
client azureclient
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rollbackerAzure) rollback(ctx context.Context) error {
|
|
||||||
return r.client.TerminateResourceGroupResources(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
type rollbackerTerraform struct {
|
type rollbackerTerraform struct {
|
||||||
client terraformClient
|
client terraformClient
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
|
@ -19,19 +18,15 @@ import (
|
||||||
|
|
||||||
// Terminator deletes cloud provider resources.
|
// Terminator deletes cloud provider resources.
|
||||||
type Terminator struct {
|
type Terminator struct {
|
||||||
newTerraformClient func(ctx context.Context) (terraformClient, error)
|
newTerraformClient func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error)
|
||||||
newAzureClient func(subscriptionID, tenantID string) (azureclient, error)
|
|
||||||
newLibvirtRunner func() libvirtRunner
|
newLibvirtRunner func() libvirtRunner
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTerminator create a new cloud terminator.
|
// NewTerminator create a new cloud terminator.
|
||||||
func NewTerminator() *Terminator {
|
func NewTerminator() *Terminator {
|
||||||
return &Terminator{
|
return &Terminator{
|
||||||
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||||
return terraform.New(ctx, cloudprovider.GCP)
|
return terraform.New(ctx, provider)
|
||||||
},
|
|
||||||
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
|
||||||
return azurecl.NewFromDefault(subscriptionID, tenantID)
|
|
||||||
},
|
},
|
||||||
newLibvirtRunner: func() libvirtRunner {
|
newLibvirtRunner: func() libvirtRunner {
|
||||||
return libvirt.New()
|
return libvirt.New()
|
||||||
|
@ -42,37 +37,22 @@ func NewTerminator() *Terminator {
|
||||||
// Terminate deletes the could provider resources defined in the constellation state.
|
// Terminate deletes the could provider resources defined in the constellation state.
|
||||||
func (t *Terminator) Terminate(ctx context.Context, state state.ConstellationState) error {
|
func (t *Terminator) Terminate(ctx context.Context, state state.ConstellationState) error {
|
||||||
provider := cloudprovider.FromString(state.CloudProvider)
|
provider := cloudprovider.FromString(state.CloudProvider)
|
||||||
switch provider {
|
if provider == cloudprovider.Unknown {
|
||||||
case cloudprovider.Azure:
|
return fmt.Errorf("unknown cloud provider %s", state.CloudProvider)
|
||||||
cl, err := t.newAzureClient(state.AzureSubscription, state.AzureTenant)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return t.terminateAzure(ctx, cl, state)
|
|
||||||
case cloudprovider.GCP:
|
cl, err := t.newTerraformClient(ctx, provider)
|
||||||
cl, err := t.newTerraformClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer cl.RemoveInstaller()
|
|
||||||
return t.terminateTerraform(ctx, cl)
|
|
||||||
case cloudprovider.QEMU:
|
|
||||||
cl, err := t.newTerraformClient(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
|
|
||||||
|
if provider == cloudprovider.QEMU {
|
||||||
libvirt := t.newLibvirtRunner()
|
libvirt := t.newLibvirtRunner()
|
||||||
return t.terminateQEMU(ctx, cl, libvirt)
|
return t.terminateQEMU(ctx, cl, libvirt)
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported provider: %s", provider)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminator) terminateAzure(ctx context.Context, cl azureclient, state state.ConstellationState) error {
|
return t.terminateTerraform(ctx, cl)
|
||||||
cl.SetState(state)
|
|
||||||
|
|
||||||
return cl.TerminateResourceGroupResources(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient) error {
|
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient) error {
|
||||||
|
|
|
@ -12,32 +12,16 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTerminator(t *testing.T) {
|
func TestTerminator(t *testing.T) {
|
||||||
someAzureState := func() state.ConstellationState {
|
|
||||||
return state.ConstellationState{
|
|
||||||
CloudProvider: cloudprovider.Azure.String(),
|
|
||||||
AzureWorkerInstances: cloudtypes.Instances{
|
|
||||||
"id-0": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
"id-1": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
},
|
|
||||||
AzureControlPlaneInstances: cloudtypes.Instances{
|
|
||||||
"id-c": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
},
|
|
||||||
AzureADAppObjectID: "00000000-0000-0000-0000-000000000001",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
tfClient terraformClient
|
tfClient terraformClient
|
||||||
newTfClientErr error
|
newTfClientErr error
|
||||||
azureclient azureclient
|
|
||||||
newAzureClientErr error
|
|
||||||
libvirt *stubLibvirtRunner
|
libvirt *stubLibvirtRunner
|
||||||
state state.ConstellationState
|
state state.ConstellationState
|
||||||
wantErr bool
|
wantErr bool
|
||||||
|
@ -84,20 +68,6 @@ func TestTerminator(t *testing.T) {
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"azure": {
|
|
||||||
azureclient: &stubAzureClient{},
|
|
||||||
state: someAzureState(),
|
|
||||||
},
|
|
||||||
"azure newAzureClient error": {
|
|
||||||
newAzureClientErr: someErr,
|
|
||||||
state: someAzureState(),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"azure terminateResourceGroupResources error": {
|
|
||||||
azureclient: &stubAzureClient{terminateResourceGroupResourcesErr: someErr},
|
|
||||||
state: someAzureState(),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"unknown cloud provider": {
|
"unknown cloud provider": {
|
||||||
state: state.ConstellationState{},
|
state: state.ConstellationState{},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
|
@ -109,12 +79,9 @@ func TestTerminator(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
terminator := &Terminator{
|
terminator := &Terminator{
|
||||||
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||||
return tc.tfClient, tc.newTfClientErr
|
return tc.tfClient, tc.newTfClientErr
|
||||||
},
|
},
|
||||||
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
|
||||||
return tc.azureclient, tc.newAzureClientErr
|
|
||||||
},
|
|
||||||
newLibvirtRunner: func() libvirtRunner {
|
newLibvirtRunner: func() libvirtRunner {
|
||||||
return tc.libvirt
|
return tc.libvirt
|
||||||
},
|
},
|
||||||
|
@ -126,17 +93,11 @@ func TestTerminator(t *testing.T) {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
} else {
|
} else {
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
switch cloudprovider.FromString(tc.state.CloudProvider) {
|
|
||||||
case cloudprovider.QEMU:
|
|
||||||
assert.True(tc.libvirt.stopCalled)
|
|
||||||
fallthrough
|
|
||||||
case cloudprovider.GCP:
|
|
||||||
cl := tc.tfClient.(*stubTerraformClient)
|
cl := tc.tfClient.(*stubTerraformClient)
|
||||||
assert.True(cl.destroyClusterCalled)
|
assert.True(cl.destroyClusterCalled)
|
||||||
assert.True(cl.removeInstallerCalled)
|
assert.True(cl.removeInstallerCalled)
|
||||||
case cloudprovider.Azure:
|
if cloudprovider.FromString(tc.state.CloudProvider) == cloudprovider.QEMU {
|
||||||
cl := tc.azureclient.(*stubAzureClient)
|
assert.True(tc.libvirt.stopCalled)
|
||||||
assert.True(cl.terminateResourceGroupResourcesCalled)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -78,6 +78,45 @@ func (v *GCPVariables) String() string {
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AzureVariables is user configuration for creating a cluster with Terraform on Azure.
|
||||||
|
type AzureVariables struct {
|
||||||
|
// CommonVariables contains common variables.
|
||||||
|
CommonVariables
|
||||||
|
|
||||||
|
// ResourceGroup is the name of the Azure resource group to use.
|
||||||
|
ResourceGroup string
|
||||||
|
// Location is the Azure location to use.
|
||||||
|
Location string
|
||||||
|
// UserAssignedIdentity is the name of the Azure user-assigned identity to use.
|
||||||
|
UserAssignedIdentity string
|
||||||
|
// InstanceType is the Azure instance type to use.
|
||||||
|
InstanceType string
|
||||||
|
// StateDiskType is the Azure disk type to use for the state disk.
|
||||||
|
StateDiskType string
|
||||||
|
// ImageID is the ID of the Azure image to use.
|
||||||
|
ImageID string
|
||||||
|
// ConfidentialVM sets the VM to be confidential.
|
||||||
|
ConfidentialVM bool
|
||||||
|
// Debug is true if debug mode is enabled.
|
||||||
|
Debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the variables, formatted as Terraform variables.
|
||||||
|
func (v *AzureVariables) String() string {
|
||||||
|
b := &strings.Builder{}
|
||||||
|
b.WriteString(v.CommonVariables.String())
|
||||||
|
writeLinef(b, "resource_group = %q", v.ResourceGroup)
|
||||||
|
writeLinef(b, "location = %q", v.Location)
|
||||||
|
writeLinef(b, "user_assigned_identity = %q", v.UserAssignedIdentity)
|
||||||
|
writeLinef(b, "instance_type = %q", v.InstanceType)
|
||||||
|
writeLinef(b, "state_disk_type = %q", v.StateDiskType)
|
||||||
|
writeLinef(b, "image_id = %q", v.ImageID)
|
||||||
|
writeLinef(b, "confidential_vm = %t", v.ConfidentialVM)
|
||||||
|
writeLinef(b, "debug = %t", v.Debug)
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
// QEMUVariables is user configuration for creating a QEMU cluster with Terraform.
|
// QEMUVariables is user configuration for creating a QEMU cluster with Terraform.
|
||||||
type QEMUVariables struct {
|
type QEMUVariables struct {
|
||||||
// CommonVariables contains common variables.
|
// CommonVariables contains common variables.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue