mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
Use Terraform for create Azure
This commit is contained in:
parent
98a16b2b47
commit
f4af9c56f5
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
<!-- For changes in existing functionality. -->
|
||||
- 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
|
||||
<!-- For soon-to-be removed features. -->
|
||||
|
@ -9,7 +9,6 @@ package cloudcmd
|
||||
import (
|
||||
"context"
|
||||
|
||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
)
|
||||
@ -22,17 +21,6 @@ type terraformClient interface {
|
||||
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 {
|
||||
Start(ctx context.Context, containerName, imageName string) error
|
||||
Stop(ctx context.Context) error
|
||||
|
@ -8,14 +8,9 @@ package cloudcmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
||||
"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"
|
||||
"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 {
|
||||
state state.ConstellationState
|
||||
cleanUpWorkspaceCalled bool
|
||||
|
@ -15,13 +15,10 @@ import (
|
||||
"runtime"
|
||||
"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/terraform"
|
||||
"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/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
)
|
||||
|
||||
@ -29,7 +26,6 @@ import (
|
||||
type Creator struct {
|
||||
out io.Writer
|
||||
newTerraformClient func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error)
|
||||
newAzureClient func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error)
|
||||
newLibvirtRunner func() libvirtRunner
|
||||
}
|
||||
|
||||
@ -40,9 +36,6 @@ func NewCreator(out io.Writer) *Creator {
|
||||
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||
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 {
|
||||
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.
|
||||
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, name, insType string, controlPlaneCount, workerCount int,
|
||||
) (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 {
|
||||
case cloudprovider.GCP:
|
||||
cl, err := c.newTerraformClient(ctx, provider)
|
||||
@ -69,17 +54,12 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createGCP(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
||||
case cloudprovider.Azure:
|
||||
cl, err := c.newAzureClient(
|
||||
config.Provider.Azure.SubscriptionID,
|
||||
config.Provider.Azure.TenantID,
|
||||
name,
|
||||
config.Provider.Azure.Location,
|
||||
config.Provider.Azure.ResourceGroup,
|
||||
)
|
||||
cl, err := c.newTerraformClient(ctx, provider)
|
||||
if err != nil {
|
||||
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:
|
||||
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)
|
||||
@ -125,37 +105,29 @@ func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *con
|
||||
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) {
|
||||
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 {
|
||||
return state.ConstellationState{}, err
|
||||
}
|
||||
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,
|
||||
CountWorkers: workerCount,
|
||||
vars := &terraform.AzureVariables{
|
||||
CommonVariables: terraform.CommonVariables{
|
||||
Name: name,
|
||||
CountControlPlanes: controlPlaneCount,
|
||||
CountWorkers: workerCount,
|
||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||
},
|
||||
Location: config.Provider.Azure.Location,
|
||||
ResourceGroup: config.Provider.Azure.ResourceGroup,
|
||||
UserAssignedIdentity: config.Provider.Azure.UserAssignedIdentity,
|
||||
InstanceType: insType,
|
||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||
StateDiskType: config.Provider.Azure.StateDiskType,
|
||||
Image: config.Provider.Azure.Image,
|
||||
UserAssingedIdentity: config.Provider.Azure.UserAssignedIdentity,
|
||||
ImageID: config.Provider.Azure.Image,
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"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/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -33,36 +32,17 @@ func TestCreator(t *testing.T) {
|
||||
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")
|
||||
|
||||
testCases := map[string]struct {
|
||||
tfClient terraformClient
|
||||
newTfClientErr error
|
||||
azureclient azureclient
|
||||
newAzureClientErr error
|
||||
libvirt *stubLibvirtRunner
|
||||
provider cloudprovider.Provider
|
||||
config *config.Config
|
||||
wantState state.ConstellationState
|
||||
wantErr bool
|
||||
wantRollback bool // Use only together with stubClients.
|
||||
tfClient terraformClient
|
||||
newTfClientErr error
|
||||
libvirt *stubLibvirtRunner
|
||||
provider cloudprovider.Provider
|
||||
config *config.Config
|
||||
wantState state.ConstellationState
|
||||
wantErr bool
|
||||
wantRollback bool // Use only together with stubClients.
|
||||
}{
|
||||
"gcp": {
|
||||
tfClient: &stubTerraformClient{state: wantGCPState},
|
||||
@ -114,39 +94,6 @@ func TestCreator(t *testing.T) {
|
||||
wantErr: true,
|
||||
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": {
|
||||
provider: cloudprovider.Unknown,
|
||||
config: config.Default(),
|
||||
@ -163,9 +110,6 @@ func TestCreator(t *testing.T) {
|
||||
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||
return tc.tfClient, tc.newTfClientErr
|
||||
},
|
||||
newAzureClient: func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error) {
|
||||
return tc.azureclient, tc.newAzureClientErr
|
||||
},
|
||||
newLibvirtRunner: func() libvirtRunner {
|
||||
return tc.libvirt
|
||||
},
|
||||
@ -176,19 +120,11 @@ func TestCreator(t *testing.T) {
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
if tc.wantRollback {
|
||||
switch tc.provider {
|
||||
case cloudprovider.QEMU:
|
||||
cl := tc.tfClient.(*stubTerraformClient)
|
||||
assert.True(cl.destroyClusterCalled)
|
||||
assert.True(cl.cleanUpWorkspaceCalled)
|
||||
cl := tc.tfClient.(*stubTerraformClient)
|
||||
assert.True(cl.destroyClusterCalled)
|
||||
assert.True(cl.cleanUpWorkspaceCalled)
|
||||
if tc.provider == cloudprovider.QEMU {
|
||||
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 {
|
||||
|
@ -34,14 +34,6 @@ func rollbackOnError(ctx context.Context, w io.Writer, onErr *error, roll rollba
|
||||
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 {
|
||||
client terraformClient
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"context"
|
||||
"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/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
@ -19,19 +18,15 @@ import (
|
||||
|
||||
// Terminator deletes cloud provider resources.
|
||||
type Terminator struct {
|
||||
newTerraformClient func(ctx context.Context) (terraformClient, error)
|
||||
newAzureClient func(subscriptionID, tenantID string) (azureclient, error)
|
||||
newTerraformClient func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error)
|
||||
newLibvirtRunner func() libvirtRunner
|
||||
}
|
||||
|
||||
// NewTerminator create a new cloud terminator.
|
||||
func NewTerminator() *Terminator {
|
||||
return &Terminator{
|
||||
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
||||
return terraform.New(ctx, cloudprovider.GCP)
|
||||
},
|
||||
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
||||
return azurecl.NewFromDefault(subscriptionID, tenantID)
|
||||
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||
return terraform.New(ctx, provider)
|
||||
},
|
||||
newLibvirtRunner: func() libvirtRunner {
|
||||
return libvirt.New()
|
||||
@ -42,37 +37,22 @@ func NewTerminator() *Terminator {
|
||||
// Terminate deletes the could provider resources defined in the constellation state.
|
||||
func (t *Terminator) Terminate(ctx context.Context, state state.ConstellationState) error {
|
||||
provider := cloudprovider.FromString(state.CloudProvider)
|
||||
switch provider {
|
||||
case cloudprovider.Azure:
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return t.terminateTerraform(ctx, cl)
|
||||
case cloudprovider.QEMU:
|
||||
cl, err := t.newTerraformClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
if provider == cloudprovider.Unknown {
|
||||
return fmt.Errorf("unknown cloud provider %s", state.CloudProvider)
|
||||
}
|
||||
|
||||
cl, err := t.newTerraformClient(ctx, provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
|
||||
if provider == cloudprovider.QEMU {
|
||||
libvirt := t.newLibvirtRunner()
|
||||
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 {
|
||||
cl.SetState(state)
|
||||
|
||||
return cl.TerminateResourceGroupResources(ctx)
|
||||
return t.terminateTerraform(ctx, cl)
|
||||
}
|
||||
|
||||
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient) error {
|
||||
|
@ -12,35 +12,19 @@ import (
|
||||
"testing"
|
||||
|
||||
"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/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
testCases := map[string]struct {
|
||||
tfClient terraformClient
|
||||
newTfClientErr error
|
||||
azureclient azureclient
|
||||
newAzureClientErr error
|
||||
libvirt *stubLibvirtRunner
|
||||
state state.ConstellationState
|
||||
wantErr bool
|
||||
tfClient terraformClient
|
||||
newTfClientErr error
|
||||
libvirt *stubLibvirtRunner
|
||||
state state.ConstellationState
|
||||
wantErr bool
|
||||
}{
|
||||
"gcp": {
|
||||
tfClient: &stubTerraformClient{},
|
||||
@ -84,20 +68,6 @@ func TestTerminator(t *testing.T) {
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||
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": {
|
||||
state: state.ConstellationState{},
|
||||
wantErr: true,
|
||||
@ -109,12 +79,9 @@ func TestTerminator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
terminator := &Terminator{
|
||||
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
||||
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||
return tc.tfClient, tc.newTfClientErr
|
||||
},
|
||||
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
||||
return tc.azureclient, tc.newAzureClientErr
|
||||
},
|
||||
newLibvirtRunner: func() libvirtRunner {
|
||||
return tc.libvirt
|
||||
},
|
||||
@ -126,17 +93,11 @@ func TestTerminator(t *testing.T) {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
switch cloudprovider.FromString(tc.state.CloudProvider) {
|
||||
case cloudprovider.QEMU:
|
||||
cl := tc.tfClient.(*stubTerraformClient)
|
||||
assert.True(cl.destroyClusterCalled)
|
||||
assert.True(cl.removeInstallerCalled)
|
||||
if cloudprovider.FromString(tc.state.CloudProvider) == cloudprovider.QEMU {
|
||||
assert.True(tc.libvirt.stopCalled)
|
||||
fallthrough
|
||||
case cloudprovider.GCP:
|
||||
cl := tc.tfClient.(*stubTerraformClient)
|
||||
assert.True(cl.destroyClusterCalled)
|
||||
assert.True(cl.removeInstallerCalled)
|
||||
case cloudprovider.Azure:
|
||||
cl := tc.azureclient.(*stubAzureClient)
|
||||
assert.True(cl.terminateResourceGroupResourcesCalled)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -78,6 +78,45 @@ func (v *GCPVariables) String() 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.
|
||||
type QEMUVariables struct {
|
||||
// CommonVariables contains common variables.
|
||||
|
Loading…
Reference in New Issue
Block a user