mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-25 07:29:38 -05:00
Use Terraform for create on GCP
This commit is contained in:
parent
f990c4d692
commit
d973740b03
@ -29,6 +29,8 @@ 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. -->
|
||||||
- Autoscaling is now directly managed inside Kubernetes, by the Constellation node operator.
|
- Autoscaling is now directly managed inside Kubernetes, by the Constellation node operator.
|
||||||
|
- The `constellation create` on GCP now uses Terraform to create and destroy cloud resources.
|
||||||
|
- GCP instances are now created without public IPs by default.
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
<!-- For soon-to-be removed features. -->
|
<!-- For soon-to-be removed features. -->
|
||||||
|
@ -10,23 +10,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
||||||
gcpcl "github.com/edgelesssys/constellation/v2/cli/internal/gcp/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"
|
||||||
)
|
)
|
||||||
|
|
||||||
type gcpclient interface {
|
type terraformClient interface {
|
||||||
GetState() state.ConstellationState
|
GetState() state.ConstellationState
|
||||||
SetState(state.ConstellationState)
|
CreateCluster(ctx context.Context, name string, input terraform.Variables) error
|
||||||
CreateVPCs(ctx context.Context) error
|
DestroyCluster(ctx context.Context) error
|
||||||
CreateFirewall(ctx context.Context, input gcpcl.FirewallInput) error
|
CleanUpWorkspace() error
|
||||||
CreateInstances(ctx context.Context, input gcpcl.CreateInstancesInput) error
|
RemoveInstaller()
|
||||||
CreateLoadBalancers(ctx context.Context, isDebugCluster bool) error
|
|
||||||
TerminateFirewall(ctx context.Context) error
|
|
||||||
TerminateVPCs(context.Context) error
|
|
||||||
TerminateLoadBalancers(context.Context) error
|
|
||||||
TerminateInstances(context.Context) error
|
|
||||||
Close() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type azureclient interface {
|
type azureclient interface {
|
||||||
@ -39,11 +32,3 @@ type azureclient interface {
|
|||||||
CreateInstances(ctx context.Context, input azurecl.CreateInstancesInput) error
|
CreateInstances(ctx context.Context, input azurecl.CreateInstancesInput) error
|
||||||
TerminateResourceGroupResources(ctx context.Context) error
|
TerminateResourceGroupResources(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type qemuclient interface {
|
|
||||||
GetState() state.ConstellationState
|
|
||||||
CreateCluster(ctx context.Context, name string, input terraform.CreateClusterInput) error
|
|
||||||
DestroyCluster(ctx context.Context) error
|
|
||||||
CleanUpWorkspace() error
|
|
||||||
RemoveInstaller()
|
|
||||||
}
|
|
||||||
|
@ -8,12 +8,11 @@ package cloudcmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
||||||
gcpcl "github.com/edgelesssys/constellation/v2/cli/internal/gcp/client"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/azureshared"
|
"github.com/edgelesssys/constellation/v2/internal/azureshared"
|
||||||
"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/cloud/cloudtypes"
|
||||||
@ -195,196 +194,34 @@ func (c *stubAzureClient) TerminateServicePrincipal(ctx context.Context) error {
|
|||||||
return c.terminateServicePrincipalErr
|
return c.terminateServicePrincipalErr
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeGcpClient struct {
|
type stubTerraformClient struct {
|
||||||
workers cloudtypes.Instances
|
state state.ConstellationState
|
||||||
controlPlanes cloudtypes.Instances
|
cleanUpWorkspaceCalled bool
|
||||||
|
removeInstallerCalled bool
|
||||||
workerInstanceGroup string
|
destroyClusterCalled bool
|
||||||
controlPlaneInstanceGroup string
|
createClusterErr error
|
||||||
controlPlaneTemplate string
|
destroyClusterErr error
|
||||||
workerTemplate string
|
cleanUpWorkspaceErr error
|
||||||
network string
|
|
||||||
subnetwork string
|
|
||||||
firewalls []string
|
|
||||||
project string
|
|
||||||
uid string
|
|
||||||
name string
|
|
||||||
zone string
|
|
||||||
loadbalancers []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeGcpClient) GetState() state.ConstellationState {
|
func (c *stubTerraformClient) GetState() state.ConstellationState {
|
||||||
return state.ConstellationState{
|
return c.state
|
||||||
CloudProvider: cloudprovider.GCP.String(),
|
|
||||||
GCPWorkerInstances: c.workers,
|
|
||||||
GCPControlPlaneInstances: c.controlPlanes,
|
|
||||||
GCPWorkerInstanceGroup: c.workerInstanceGroup,
|
|
||||||
GCPControlPlaneInstanceGroup: c.controlPlaneInstanceGroup,
|
|
||||||
GCPWorkerInstanceTemplate: c.workerTemplate,
|
|
||||||
GCPControlPlaneInstanceTemplate: c.controlPlaneTemplate,
|
|
||||||
GCPNetwork: c.network,
|
|
||||||
GCPSubnetwork: c.subnetwork,
|
|
||||||
GCPFirewalls: c.firewalls,
|
|
||||||
GCPProject: c.project,
|
|
||||||
Name: c.name,
|
|
||||||
UID: c.uid,
|
|
||||||
GCPZone: c.zone,
|
|
||||||
GCPLoadbalancers: c.loadbalancers,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeGcpClient) SetState(stat state.ConstellationState) {
|
func (c *stubTerraformClient) CreateCluster(ctx context.Context, name string, input terraform.Variables) error {
|
||||||
c.workers = stat.GCPWorkerInstances
|
return c.createClusterErr
|
||||||
c.controlPlanes = stat.GCPControlPlaneInstances
|
|
||||||
c.workerInstanceGroup = stat.GCPWorkerInstanceGroup
|
|
||||||
c.controlPlaneInstanceGroup = stat.GCPControlPlaneInstanceGroup
|
|
||||||
c.workerTemplate = stat.GCPWorkerInstanceTemplate
|
|
||||||
c.controlPlaneTemplate = stat.GCPControlPlaneInstanceTemplate
|
|
||||||
c.network = stat.GCPNetwork
|
|
||||||
c.subnetwork = stat.GCPSubnetwork
|
|
||||||
c.firewalls = stat.GCPFirewalls
|
|
||||||
c.project = stat.GCPProject
|
|
||||||
c.name = stat.Name
|
|
||||||
c.uid = stat.UID
|
|
||||||
c.zone = stat.GCPZone
|
|
||||||
c.loadbalancers = stat.GCPLoadbalancers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeGcpClient) CreateVPCs(ctx context.Context) error {
|
func (c *stubTerraformClient) DestroyCluster(ctx context.Context) error {
|
||||||
c.network = "network"
|
c.destroyClusterCalled = true
|
||||||
c.subnetwork = "subnetwork"
|
return c.destroyClusterErr
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeGcpClient) CreateFirewall(ctx context.Context, input gcpcl.FirewallInput) error {
|
func (c *stubTerraformClient) CleanUpWorkspace() error {
|
||||||
if c.network == "" {
|
c.cleanUpWorkspaceCalled = true
|
||||||
return errors.New("client has not network")
|
return c.cleanUpWorkspaceErr
|
||||||
}
|
|
||||||
for _, rule := range input.Ingress {
|
|
||||||
c.firewalls = append(c.firewalls, rule.Name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeGcpClient) CreateInstances(ctx context.Context, input gcpcl.CreateInstancesInput) error {
|
func (c *stubTerraformClient) RemoveInstaller() {
|
||||||
c.controlPlaneInstanceGroup = "controlplane-group"
|
c.removeInstallerCalled = true
|
||||||
c.workerInstanceGroup = "workers-group"
|
|
||||||
c.workerTemplate = "worker-template"
|
|
||||||
c.controlPlaneTemplate = "controlplane-template"
|
|
||||||
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 *fakeGcpClient) CreateLoadBalancers(ctx context.Context, isDebugCluster bool) error {
|
|
||||||
c.loadbalancers = []string{"kube-lb", "boot-lb", "verify-lb"}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeGcpClient) TerminateFirewall(ctx context.Context) error {
|
|
||||||
if len(c.firewalls) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
c.firewalls = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeGcpClient) TerminateVPCs(context.Context) error {
|
|
||||||
if len(c.firewalls) != 0 {
|
|
||||||
return errors.New("client has firewalls, which must be deleted first")
|
|
||||||
}
|
|
||||||
c.network = ""
|
|
||||||
c.subnetwork = ""
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeGcpClient) TerminateInstances(context.Context) error {
|
|
||||||
c.workerTemplate = ""
|
|
||||||
c.controlPlaneTemplate = ""
|
|
||||||
c.workerInstanceGroup = ""
|
|
||||||
c.controlPlaneInstanceGroup = ""
|
|
||||||
c.workers = nil
|
|
||||||
c.controlPlanes = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeGcpClient) TerminateLoadBalancers(context.Context) error {
|
|
||||||
c.loadbalancers = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *fakeGcpClient) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type stubGcpClient struct {
|
|
||||||
terminateFirewallCalled bool
|
|
||||||
terminateInstancesCalled bool
|
|
||||||
terminateVPCsCalled bool
|
|
||||||
closeCalled bool
|
|
||||||
|
|
||||||
createVPCsErr error
|
|
||||||
createFirewallErr error
|
|
||||||
createInstancesErr error
|
|
||||||
createLoadBalancerErr error
|
|
||||||
terminateFirewallErr error
|
|
||||||
terminateVPCsErr error
|
|
||||||
terminateInstancesErr error
|
|
||||||
terminateLoadBalancerErr error
|
|
||||||
closeErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) GetState() state.ConstellationState {
|
|
||||||
return state.ConstellationState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) SetState(state.ConstellationState) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) CreateVPCs(ctx context.Context) error {
|
|
||||||
return c.createVPCsErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) CreateFirewall(ctx context.Context, input gcpcl.FirewallInput) error {
|
|
||||||
return c.createFirewallErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) CreateInstances(ctx context.Context, input gcpcl.CreateInstancesInput) error {
|
|
||||||
return c.createInstancesErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) CreateLoadBalancers(ctx context.Context, isDebugClient bool) error {
|
|
||||||
return c.createLoadBalancerErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) TerminateFirewall(ctx context.Context) error {
|
|
||||||
c.terminateFirewallCalled = true
|
|
||||||
return c.terminateFirewallErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) TerminateVPCs(context.Context) error {
|
|
||||||
c.terminateVPCsCalled = true
|
|
||||||
return c.terminateVPCsErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) TerminateInstances(context.Context) error {
|
|
||||||
c.terminateInstancesCalled = true
|
|
||||||
return c.terminateInstancesErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) TerminateLoadBalancers(context.Context) error {
|
|
||||||
return c.terminateLoadBalancerErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubGcpClient) Close() error {
|
|
||||||
c.closeCalled = true
|
|
||||||
return c.closeErr
|
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/gcp"
|
|
||||||
gcpcl "github.com/edgelesssys/constellation/v2/cli/internal/gcp/client"
|
|
||||||
"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/cloud/cloudtypes"
|
||||||
@ -26,27 +24,20 @@ import (
|
|||||||
// Creator creates cloud resources.
|
// Creator creates cloud resources.
|
||||||
type Creator struct {
|
type Creator struct {
|
||||||
out io.Writer
|
out io.Writer
|
||||||
newGCPClient func(ctx context.Context, project, zone, region, name string) (gcpclient, error)
|
newTerraformClient func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error)
|
||||||
newAzureClient func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error)
|
newAzureClient func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error)
|
||||||
newQEMUClient func(ctx context.Context) (qemuclient, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCreator creates a new creator.
|
// NewCreator creates a new creator.
|
||||||
func NewCreator(out io.Writer) *Creator {
|
func NewCreator(out io.Writer) *Creator {
|
||||||
return &Creator{
|
return &Creator{
|
||||||
out: out,
|
out: out,
|
||||||
newGCPClient: func(ctx context.Context, project, zone, region, name string) (gcpclient, error) {
|
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||||
return gcpcl.NewInitialized(ctx, project, zone, region, name)
|
return terraform.New(ctx, provider)
|
||||||
},
|
},
|
||||||
newAzureClient: func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error) {
|
newAzureClient: func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error) {
|
||||||
return azurecl.NewInitialized(subscriptionID, tenantID, name, location, resourceGroup)
|
return azurecl.NewInitialized(subscriptionID, tenantID, name, location, resourceGroup)
|
||||||
},
|
},
|
||||||
newQEMUClient: func(ctx context.Context) (qemuclient, error) {
|
|
||||||
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
|
|
||||||
return nil, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
||||||
}
|
|
||||||
return terraform.New(ctx, cloudprovider.QEMU)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,18 +54,12 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
|
|||||||
|
|
||||||
switch provider {
|
switch provider {
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
cl, err := c.newGCPClient(
|
cl, err := c.newTerraformClient(ctx, provider)
|
||||||
ctx,
|
|
||||||
config.Provider.GCP.Project,
|
|
||||||
config.Provider.GCP.Zone,
|
|
||||||
config.Provider.GCP.Region,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
defer cl.Close()
|
defer cl.RemoveInstaller()
|
||||||
return c.createGCP(ctx, cl, config, insType, controlPlaneCount, workerCount, ingressRules)
|
return c.createGCP(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl, err := c.newAzureClient(
|
cl, err := c.newAzureClient(
|
||||||
config.Provider.Azure.SubscriptionID,
|
config.Provider.Azure.SubscriptionID,
|
||||||
@ -88,7 +73,10 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
|
|||||||
}
|
}
|
||||||
return c.createAzure(ctx, cl, config, insType, controlPlaneCount, workerCount, ingressRules)
|
return c.createAzure(ctx, cl, config, insType, controlPlaneCount, workerCount, ingressRules)
|
||||||
case cloudprovider.QEMU:
|
case cloudprovider.QEMU:
|
||||||
cl, err := c.newQEMUClient(ctx)
|
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)
|
||||||
|
}
|
||||||
|
cl, err := c.newTerraformClient(ctx, provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
@ -99,75 +87,29 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createGCP(ctx context.Context, cl gcpclient, config *config.Config, insType string, controlPlaneCount, workerCount int, ingressRules cloudtypes.Firewall,
|
func (c *Creator) createGCP(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, &rollbackerGCP{client: cl})
|
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||||
|
|
||||||
if err := cl.CreateVPCs(ctx); err != nil {
|
vars := &terraform.GCPVariables{
|
||||||
return state.ConstellationState{}, err
|
CommonVariables: terraform.CommonVariables{
|
||||||
}
|
Name: name,
|
||||||
|
|
||||||
if err := cl.CreateFirewall(ctx, gcpcl.FirewallInput{
|
|
||||||
Ingress: ingressRules,
|
|
||||||
Egress: constants.EgressRules,
|
|
||||||
}); err != nil {
|
|
||||||
return state.ConstellationState{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// additionally create allow-internal rules
|
|
||||||
internalFirewallInput := gcpcl.FirewallInput{
|
|
||||||
Ingress: cloudtypes.Firewall{
|
|
||||||
{
|
|
||||||
Name: "allow-cluster-internal-tcp",
|
|
||||||
Protocol: "tcp",
|
|
||||||
IPRange: gcpcl.SubnetExtCIDR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "allow-cluster-internal-udp",
|
|
||||||
Protocol: "udp",
|
|
||||||
IPRange: gcpcl.SubnetExtCIDR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "allow-cluster-internal-icmp",
|
|
||||||
Protocol: "icmp",
|
|
||||||
IPRange: gcpcl.SubnetExtCIDR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "allow-node-internal-tcp",
|
|
||||||
Protocol: "tcp",
|
|
||||||
IPRange: gcpcl.SubnetCIDR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "allow-node-internal-udp",
|
|
||||||
Protocol: "udp",
|
|
||||||
IPRange: gcpcl.SubnetCIDR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "allow-node-internal-icmp",
|
|
||||||
Protocol: "icmp",
|
|
||||||
IPRange: gcpcl.SubnetCIDR,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := cl.CreateFirewall(ctx, internalFirewallInput); err != nil {
|
|
||||||
return state.ConstellationState{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
createInput := gcpcl.CreateInstancesInput{
|
|
||||||
EnableSerialConsole: config.IsDebugCluster(),
|
|
||||||
CountControlPlanes: controlPlaneCount,
|
CountControlPlanes: controlPlaneCount,
|
||||||
CountWorkers: workerCount,
|
CountWorkers: workerCount,
|
||||||
ImageID: config.Provider.GCP.Image,
|
|
||||||
InstanceType: insType,
|
|
||||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||||
|
},
|
||||||
|
Project: config.Provider.GCP.Project,
|
||||||
|
Region: config.Provider.GCP.Region,
|
||||||
|
Zone: config.Provider.GCP.Zone,
|
||||||
|
CredentialsFile: config.Provider.GCP.ServiceAccountKeyPath,
|
||||||
|
InstanceType: insType,
|
||||||
StateDiskType: config.Provider.GCP.StateDiskType,
|
StateDiskType: config.Provider.GCP.StateDiskType,
|
||||||
KubeEnv: gcp.KubeEnv,
|
ImageID: config.Provider.GCP.Image,
|
||||||
}
|
Debug: config.IsDebugCluster(),
|
||||||
if err := cl.CreateInstances(ctx, createInput); err != nil {
|
|
||||||
return state.ConstellationState{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.CreateLoadBalancers(ctx, config.IsDebugCluster()); err != nil {
|
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,25 +153,27 @@ func (c *Creator) createAzure(ctx context.Context, cl azureclient, config *confi
|
|||||||
return cl.GetState(), nil
|
return cl.GetState(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createQEMU(ctx context.Context, cl qemuclient, name string, config *config.Config, controlPlaneCount, workerCount int,
|
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, name string, config *config.Config,
|
||||||
|
controlPlaneCount, workerCount int,
|
||||||
) (stat state.ConstellationState, retErr error) {
|
) (stat state.ConstellationState, retErr error) {
|
||||||
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerQEMU{client: cl})
|
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||||
|
|
||||||
input := terraform.CreateClusterInput{
|
vars := &terraform.QEMUVariables{
|
||||||
|
CommonVariables: terraform.CommonVariables{
|
||||||
|
Name: name,
|
||||||
CountControlPlanes: controlPlaneCount,
|
CountControlPlanes: controlPlaneCount,
|
||||||
CountWorkers: workerCount,
|
CountWorkers: workerCount,
|
||||||
QEMU: terraform.QEMUInput{
|
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||||
|
},
|
||||||
ImagePath: config.Provider.QEMU.Image,
|
ImagePath: config.Provider.QEMU.Image,
|
||||||
ImageFormat: config.Provider.QEMU.ImageFormat,
|
ImageFormat: config.Provider.QEMU.ImageFormat,
|
||||||
CPUCount: config.Provider.QEMU.VCPUs,
|
CPUCount: config.Provider.QEMU.VCPUs,
|
||||||
MemorySizeMiB: config.Provider.QEMU.Memory,
|
MemorySizeMiB: config.Provider.QEMU.Memory,
|
||||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
|
||||||
IPRangeStart: config.Provider.QEMU.IPRangeStart,
|
IPRangeStart: config.Provider.QEMU.IPRangeStart,
|
||||||
MetadataAPIImage: config.Provider.QEMU.MetadataAPIImage,
|
MetadataAPIImage: config.Provider.QEMU.MetadataAPIImage,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.CreateCluster(ctx, name, input); err != nil {
|
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
||||||
return state.ConstellationState{}, err
|
return state.ConstellationState{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,28 +22,7 @@ import (
|
|||||||
func TestCreator(t *testing.T) {
|
func TestCreator(t *testing.T) {
|
||||||
wantGCPState := state.ConstellationState{
|
wantGCPState := state.ConstellationState{
|
||||||
CloudProvider: cloudprovider.GCP.String(),
|
CloudProvider: cloudprovider.GCP.String(),
|
||||||
GCPProject: "project",
|
LoadBalancerIP: "192.0.2.1",
|
||||||
GCPControlPlaneInstances: 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"},
|
|
||||||
},
|
|
||||||
GCPWorkerInstances: 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"},
|
|
||||||
},
|
|
||||||
GCPWorkerInstanceGroup: "workers-group",
|
|
||||||
GCPControlPlaneInstanceGroup: "controlplane-group",
|
|
||||||
GCPWorkerInstanceTemplate: "worker-template",
|
|
||||||
GCPControlPlaneInstanceTemplate: "controlplane-template",
|
|
||||||
GCPNetwork: "network",
|
|
||||||
GCPSubnetwork: "subnetwork",
|
|
||||||
GCPLoadbalancers: []string{"kube-lb", "boot-lb", "verify-lb"},
|
|
||||||
GCPFirewalls: []string{
|
|
||||||
"bootstrapper", "ssh", "nodeport", "kubernetes", "konnectivity", "recovery",
|
|
||||||
"allow-cluster-internal-tcp", "allow-cluster-internal-udp", "allow-cluster-internal-icmp",
|
|
||||||
"allow-node-internal-tcp", "allow-node-internal-udp", "allow-node-internal-icmp",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wantAzureState := state.ConstellationState{
|
wantAzureState := state.ConstellationState{
|
||||||
@ -66,8 +45,8 @@ func TestCreator(t *testing.T) {
|
|||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
gcpclient gcpclient
|
tfClient terraformClient
|
||||||
newGCPClientErr error
|
newTfClientErr error
|
||||||
azureclient azureclient
|
azureclient azureclient
|
||||||
newAzureClientErr error
|
newAzureClientErr error
|
||||||
provider cloudprovider.Provider
|
provider cloudprovider.Provider
|
||||||
@ -77,40 +56,19 @@ func TestCreator(t *testing.T) {
|
|||||||
wantRollback bool // Use only together with stubClients.
|
wantRollback bool // Use only together with stubClients.
|
||||||
}{
|
}{
|
||||||
"gcp": {
|
"gcp": {
|
||||||
gcpclient: &fakeGcpClient{project: "project"},
|
tfClient: &stubTerraformClient{state: wantGCPState},
|
||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
config: config.Default(),
|
config: config.Default(),
|
||||||
wantState: wantGCPState,
|
wantState: wantGCPState,
|
||||||
},
|
},
|
||||||
"gcp newGCPClient error": {
|
"gcp newGCPClient error": {
|
||||||
newGCPClientErr: someErr,
|
newTfClientErr: someErr,
|
||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
config: config.Default(),
|
config: config.Default(),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"gcp CreateVPCs error": {
|
"gcp create cluster error": {
|
||||||
gcpclient: &stubGcpClient{createVPCsErr: someErr},
|
tfClient: &stubTerraformClient{createClusterErr: someErr},
|
||||||
provider: cloudprovider.GCP,
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
wantRollback: true,
|
|
||||||
},
|
|
||||||
"gcp CreateFirewall error": {
|
|
||||||
gcpclient: &stubGcpClient{createFirewallErr: someErr},
|
|
||||||
provider: cloudprovider.GCP,
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
wantRollback: true,
|
|
||||||
},
|
|
||||||
"gcp CreateInstances error": {
|
|
||||||
gcpclient: &stubGcpClient{createInstancesErr: someErr},
|
|
||||||
provider: cloudprovider.GCP,
|
|
||||||
config: config.Default(),
|
|
||||||
wantErr: true,
|
|
||||||
wantRollback: true,
|
|
||||||
},
|
|
||||||
"gcp CreateLoadBalancer error": {
|
|
||||||
gcpclient: &stubGcpClient{createLoadBalancerErr: someErr},
|
|
||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
config: config.Default(),
|
config: config.Default(),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
@ -162,8 +120,8 @@ func TestCreator(t *testing.T) {
|
|||||||
|
|
||||||
creator := &Creator{
|
creator := &Creator{
|
||||||
out: &bytes.Buffer{},
|
out: &bytes.Buffer{},
|
||||||
newGCPClient: func(ctx context.Context, project, zone, region, name string) (gcpclient, error) {
|
newTerraformClient: func(ctx context.Context, provider cloudprovider.Provider) (terraformClient, error) {
|
||||||
return tc.gcpclient, tc.newGCPClientErr
|
return tc.tfClient, tc.newTfClientErr
|
||||||
},
|
},
|
||||||
newAzureClient: func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error) {
|
newAzureClient: func(subscriptionID, tenantID, name, location, resourceGroup string) (azureclient, error) {
|
||||||
return tc.azureclient, tc.newAzureClientErr
|
return tc.azureclient, tc.newAzureClientErr
|
||||||
@ -176,12 +134,12 @@ func TestCreator(t *testing.T) {
|
|||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
if tc.wantRollback {
|
if tc.wantRollback {
|
||||||
switch tc.provider {
|
switch tc.provider {
|
||||||
|
case cloudprovider.QEMU:
|
||||||
|
fallthrough
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
cl := tc.gcpclient.(*stubGcpClient)
|
cl := tc.tfClient.(*stubTerraformClient)
|
||||||
assert.True(cl.terminateFirewallCalled)
|
assert.True(cl.destroyClusterCalled)
|
||||||
assert.True(cl.terminateInstancesCalled)
|
assert.True(cl.cleanUpWorkspaceCalled)
|
||||||
assert.True(cl.terminateVPCsCalled)
|
|
||||||
assert.True(cl.closeCalled)
|
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl := tc.azureclient.(*stubAzureClient)
|
cl := tc.azureclient.(*stubAzureClient)
|
||||||
assert.True(cl.terminateResourceGroupResourcesCalled)
|
assert.True(cl.terminateResourceGroupResourcesCalled)
|
||||||
|
@ -34,19 +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 rollbackerGCP struct {
|
|
||||||
client gcpclient
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rollbackerGCP) rollback(ctx context.Context) error {
|
|
||||||
var err error
|
|
||||||
err = multierr.Append(err, r.client.TerminateLoadBalancers(ctx))
|
|
||||||
err = multierr.Append(err, r.client.TerminateInstances(ctx))
|
|
||||||
err = multierr.Append(err, r.client.TerminateFirewall(ctx))
|
|
||||||
err = multierr.Append(err, r.client.TerminateVPCs(ctx))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type rollbackerAzure struct {
|
type rollbackerAzure struct {
|
||||||
client azureclient
|
client azureclient
|
||||||
}
|
}
|
||||||
@ -55,11 +42,11 @@ func (r *rollbackerAzure) rollback(ctx context.Context) error {
|
|||||||
return r.client.TerminateResourceGroupResources(ctx)
|
return r.client.TerminateResourceGroupResources(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type rollbackerQEMU struct {
|
type rollbackerTerraform struct {
|
||||||
client qemuclient
|
client terraformClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rollbackerQEMU) rollback(ctx context.Context) error {
|
func (r *rollbackerTerraform) rollback(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
err = multierr.Append(err, r.client.DestroyCluster(ctx))
|
err = multierr.Append(err, r.client.DestroyCluster(ctx))
|
||||||
err = multierr.Append(err, r.client.CleanUpWorkspace())
|
err = multierr.Append(err, r.client.CleanUpWorkspace())
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
azurecl "github.com/edgelesssys/constellation/v2/cli/internal/azure/client"
|
||||||
gcpcl "github.com/edgelesssys/constellation/v2/cli/internal/gcp/client"
|
|
||||||
"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/state"
|
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||||
@ -19,23 +18,19 @@ import (
|
|||||||
|
|
||||||
// Terminator deletes cloud provider resources.
|
// Terminator deletes cloud provider resources.
|
||||||
type Terminator struct {
|
type Terminator struct {
|
||||||
newGCPClient func(ctx context.Context) (gcpclient, error)
|
newTerraformClient func(ctx context.Context) (terraformClient, error)
|
||||||
newAzureClient func(subscriptionID, tenantID string) (azureclient, error)
|
newAzureClient func(subscriptionID, tenantID string) (azureclient, error)
|
||||||
newQEMUClient func(ctx context.Context) (qemuclient, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTerminator create a new cloud terminator.
|
// NewTerminator create a new cloud terminator.
|
||||||
func NewTerminator() *Terminator {
|
func NewTerminator() *Terminator {
|
||||||
return &Terminator{
|
return &Terminator{
|
||||||
newGCPClient: func(ctx context.Context) (gcpclient, error) {
|
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
||||||
return gcpcl.NewFromDefault(ctx)
|
return terraform.New(ctx, cloudprovider.GCP)
|
||||||
},
|
},
|
||||||
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
||||||
return azurecl.NewFromDefault(subscriptionID, tenantID)
|
return azurecl.NewFromDefault(subscriptionID, tenantID)
|
||||||
},
|
},
|
||||||
newQEMUClient: func(ctx context.Context) (qemuclient, error) {
|
|
||||||
return terraform.New(ctx, cloudprovider.QEMU)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,57 +38,33 @@ func NewTerminator() *Terminator {
|
|||||||
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 {
|
switch provider {
|
||||||
case cloudprovider.GCP:
|
|
||||||
cl, err := t.newGCPClient(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer cl.Close()
|
|
||||||
return t.terminateGCP(ctx, cl, state)
|
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl, err := t.newAzureClient(state.AzureSubscription, state.AzureTenant)
|
cl, err := t.newAzureClient(state.AzureSubscription, state.AzureTenant)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return t.terminateAzure(ctx, cl, state)
|
return t.terminateAzure(ctx, cl, state)
|
||||||
|
case cloudprovider.GCP:
|
||||||
|
fallthrough
|
||||||
case cloudprovider.QEMU:
|
case cloudprovider.QEMU:
|
||||||
cl, err := t.newQEMUClient(ctx)
|
cl, err := t.newTerraformClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return t.terminateQEMU(ctx, cl)
|
return t.terminateTerraform(ctx, cl)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported provider: %s", provider)
|
return fmt.Errorf("unsupported provider: %s", provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminator) terminateGCP(ctx context.Context, cl gcpclient, state state.ConstellationState) error {
|
|
||||||
cl.SetState(state)
|
|
||||||
|
|
||||||
if err := cl.TerminateLoadBalancers(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := cl.TerminateInstances(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := cl.TerminateFirewall(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := cl.TerminateVPCs(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminator) terminateAzure(ctx context.Context, cl azureclient, state state.ConstellationState) error {
|
func (t *Terminator) terminateAzure(ctx context.Context, cl azureclient, state state.ConstellationState) error {
|
||||||
cl.SetState(state)
|
cl.SetState(state)
|
||||||
|
|
||||||
return cl.TerminateResourceGroupResources(ctx)
|
return cl.TerminateResourceGroupResources(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminator) terminateQEMU(ctx context.Context, cl qemuclient) error {
|
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient) error {
|
||||||
if err := cl.DestroyCluster(ctx); err != nil {
|
if err := cl.DestroyCluster(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,25 +18,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestTerminator(t *testing.T) {
|
func TestTerminator(t *testing.T) {
|
||||||
someGCPState := func() state.ConstellationState {
|
|
||||||
return state.ConstellationState{
|
|
||||||
CloudProvider: cloudprovider.GCP.String(),
|
|
||||||
GCPProject: "project",
|
|
||||||
GCPWorkerInstances: 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"},
|
|
||||||
},
|
|
||||||
GCPControlPlaneInstances: cloudtypes.Instances{
|
|
||||||
"id-c": {PrivateIP: "192.0.2.1", PublicIP: "192.0.2.1"},
|
|
||||||
},
|
|
||||||
GCPWorkerInstanceGroup: "worker-group",
|
|
||||||
GCPControlPlaneInstanceGroup: "controlplane-group",
|
|
||||||
GCPWorkerInstanceTemplate: "template",
|
|
||||||
GCPControlPlaneInstanceTemplate: "template",
|
|
||||||
GCPNetwork: "network",
|
|
||||||
GCPFirewalls: []string{"a", "b", "c"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
someAzureState := func() state.ConstellationState {
|
someAzureState := func() state.ConstellationState {
|
||||||
return state.ConstellationState{
|
return state.ConstellationState{
|
||||||
CloudProvider: cloudprovider.Azure.String(),
|
CloudProvider: cloudprovider.Azure.String(),
|
||||||
@ -53,35 +34,44 @@ func TestTerminator(t *testing.T) {
|
|||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
gcpclient gcpclient
|
tfClient terraformClient
|
||||||
newGCPClientErr error
|
newTfClientErr error
|
||||||
azureclient azureclient
|
azureclient azureclient
|
||||||
newAzureClientErr error
|
newAzureClientErr error
|
||||||
state state.ConstellationState
|
state state.ConstellationState
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"gcp": {
|
"gcp": {
|
||||||
gcpclient: &stubGcpClient{},
|
tfClient: &stubTerraformClient{},
|
||||||
state: someGCPState(),
|
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
||||||
},
|
},
|
||||||
"gcp newGCPClient error": {
|
"gcp newTfClientErr": {
|
||||||
newGCPClientErr: someErr,
|
newTfClientErr: someErr,
|
||||||
state: someGCPState(),
|
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"gcp terminateInstances error": {
|
"gcp destroy cluster error": {
|
||||||
gcpclient: &stubGcpClient{terminateInstancesErr: someErr},
|
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
|
||||||
state: someGCPState(),
|
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"gcp terminateFirewall error": {
|
"gcp clean up workspace error": {
|
||||||
gcpclient: &stubGcpClient{terminateFirewallErr: someErr},
|
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: someErr},
|
||||||
state: someGCPState(),
|
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"gcp terminateVPCs error": {
|
"qemu": {
|
||||||
gcpclient: &stubGcpClient{terminateVPCsErr: someErr},
|
tfClient: &stubTerraformClient{},
|
||||||
state: someGCPState(),
|
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||||
|
},
|
||||||
|
"qemu destroy cluster error": {
|
||||||
|
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
|
||||||
|
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"qemu clean up workspace error": {
|
||||||
|
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: someErr},
|
||||||
|
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"azure": {
|
"azure": {
|
||||||
@ -109,8 +99,8 @@ func TestTerminator(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
terminator := &Terminator{
|
terminator := &Terminator{
|
||||||
newGCPClient: func(ctx context.Context) (gcpclient, error) {
|
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
||||||
return tc.gcpclient, tc.newGCPClientErr
|
return tc.tfClient, tc.newTfClientErr
|
||||||
},
|
},
|
||||||
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
newAzureClient: func(subscriptionID, tenantID string) (azureclient, error) {
|
||||||
return tc.azureclient, tc.newAzureClientErr
|
return tc.azureclient, tc.newAzureClientErr
|
||||||
@ -125,11 +115,11 @@ func TestTerminator(t *testing.T) {
|
|||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
switch cloudprovider.FromString(tc.state.CloudProvider) {
|
switch cloudprovider.FromString(tc.state.CloudProvider) {
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
cl := tc.gcpclient.(*stubGcpClient)
|
fallthrough
|
||||||
assert.True(cl.terminateFirewallCalled)
|
case cloudprovider.QEMU:
|
||||||
assert.True(cl.terminateInstancesCalled)
|
cl := tc.tfClient.(*stubTerraformClient)
|
||||||
assert.True(cl.terminateVPCsCalled)
|
assert.True(cl.destroyClusterCalled)
|
||||||
assert.True(cl.closeCalled)
|
assert.True(cl.removeInstallerCalled)
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl := tc.azureclient.(*stubAzureClient)
|
cl := tc.azureclient.(*stubAzureClient)
|
||||||
assert.True(cl.terminateResourceGroupResourcesCalled)
|
assert.True(cl.terminateResourceGroupResourcesCalled)
|
||||||
|
@ -50,7 +50,6 @@ func TestInitArgumentValidation(t *testing.T) {
|
|||||||
func TestInitialize(t *testing.T) {
|
func TestInitialize(t *testing.T) {
|
||||||
testGcpState := &state.ConstellationState{
|
testGcpState := &state.ConstellationState{
|
||||||
CloudProvider: "GCP",
|
CloudProvider: "GCP",
|
||||||
GCPWorkerInstances: cloudtypes.Instances{"id-0": {}, "id-1": {}},
|
|
||||||
}
|
}
|
||||||
gcpServiceAccKey := &gcpshared.ServiceAccountKey{
|
gcpServiceAccKey := &gcpshared.ServiceAccountKey{
|
||||||
Type: "service_account",
|
Type: "service_account",
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) Edgeless Systems GmbH
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package terraform
|
|
||||||
|
|
||||||
// CreateClusterInput is user configuration for creating a cluster with Terraform.
|
|
||||||
type CreateClusterInput struct {
|
|
||||||
// CountControlPlanes is the number of control-plane nodes to create.
|
|
||||||
CountControlPlanes int
|
|
||||||
// CountWorkers is the number of worker nodes to create.
|
|
||||||
CountWorkers int
|
|
||||||
// QEMU is the configuration for QEMU clusters.
|
|
||||||
QEMU QEMUInput
|
|
||||||
}
|
|
||||||
|
|
||||||
// QEMUInput is user configuration for creating a QEMU cluster with Terraform.
|
|
||||||
type QEMUInput struct {
|
|
||||||
// CPUCount is the number of CPUs to allocate to each node.
|
|
||||||
CPUCount int
|
|
||||||
// MemorySizeMiB is the amount of memory to allocate to each node, in MiB.
|
|
||||||
MemorySizeMiB int
|
|
||||||
// StateDiskSizeGB is the size of the state disk to allocate to each node, in GB.
|
|
||||||
StateDiskSizeGB int
|
|
||||||
// IPRangeStart is the first IP address in the IP range to allocate to the cluster.
|
|
||||||
IPRangeStart int
|
|
||||||
// ImagePath is the path to the image to use for the nodes.
|
|
||||||
ImagePath string
|
|
||||||
// ImageFormat is the format of the image from ImagePath.
|
|
||||||
ImageFormat string
|
|
||||||
// MetadataAPIImage is the container image to use for the metadata API.
|
|
||||||
MetadataAPIImage string
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ package terraform
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
@ -59,7 +58,7 @@ func New(ctx context.Context, provider cloudprovider.Provider) (*Client, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateCluster creates a Constellation cluster using Terraform.
|
// CreateCluster creates a Constellation cluster using Terraform.
|
||||||
func (c *Client) CreateCluster(ctx context.Context, name string, input CreateClusterInput) error {
|
func (c *Client) CreateCluster(ctx context.Context, name string, vars Variables) error {
|
||||||
if err := prepareWorkspace(c.file, c.provider); err != nil {
|
if err := prepareWorkspace(c.file, c.provider); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -68,7 +67,7 @@ func (c *Client) CreateCluster(ctx context.Context, name string, input CreateClu
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := writeUserConfig(c.file, c.provider, name, input); err != nil {
|
if err := c.file.Write(terraformVarsFile, []byte(vars.String())); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,35 +136,6 @@ func (c *Client) GetState() state.ConstellationState {
|
|||||||
return c.state
|
return c.state
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeUserConfig writes the user config file for Terraform.
|
|
||||||
func writeUserConfig(file file.Handler, provider cloudprovider.Provider, name string, input CreateClusterInput) error {
|
|
||||||
var userConfig string
|
|
||||||
switch provider {
|
|
||||||
case cloudprovider.QEMU:
|
|
||||||
userConfig = fmt.Sprintf(`
|
|
||||||
constellation_coreos_image = "%s"
|
|
||||||
image_format = "%s"
|
|
||||||
control_plane_count = %d
|
|
||||||
worker_count = %d
|
|
||||||
vcpus = %d
|
|
||||||
memory = %d
|
|
||||||
state_disk_size = %d
|
|
||||||
ip_range_start = %d
|
|
||||||
metadata_api_image = "%s"
|
|
||||||
name = "%s"
|
|
||||||
`,
|
|
||||||
input.QEMU.ImagePath, input.QEMU.ImageFormat,
|
|
||||||
input.CountControlPlanes, input.CountWorkers,
|
|
||||||
input.QEMU.CPUCount, input.QEMU.MemorySizeMiB, input.QEMU.StateDiskSizeGB,
|
|
||||||
input.QEMU.IPRangeStart,
|
|
||||||
input.QEMU.MetadataAPIImage,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return file.Write(terraformVarsFile, []byte(userConfig))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExecutable returns a Terraform executable either from the local filesystem,
|
// GetExecutable returns a Terraform executable either from the local filesystem,
|
||||||
// or downloads the latest version fulfilling the version constraint.
|
// or downloads the latest version fulfilling the version constraint.
|
||||||
func GetExecutable(ctx context.Context, workingDir string) (terraform *tfexec.Terraform, remove func(), err error) {
|
func GetExecutable(ctx context.Context, workingDir string) (terraform *tfexec.Terraform, remove func(), err error) {
|
||||||
|
3
cli/internal/terraform/terraform/gcp/outputs.tf
Normal file
3
cli/internal/terraform/terraform/gcp/outputs.tf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
output "ip" {
|
||||||
|
value = google_compute_global_address.loadbalancer_ip.address
|
||||||
|
}
|
@ -4,6 +4,22 @@ variable "name" {
|
|||||||
description = "Base name of the cluster."
|
description = "Base name of the cluster."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "control_plane_count" {
|
||||||
|
type = number
|
||||||
|
description = "The number of control plane nodes to deploy."
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_count" {
|
||||||
|
type = number
|
||||||
|
description = "The number of worker nodes to deploy."
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "state_disk_size" {
|
||||||
|
type = number
|
||||||
|
default = 30
|
||||||
|
description = "The size of the state disk in GB."
|
||||||
|
}
|
||||||
|
|
||||||
variable "project" {
|
variable "project" {
|
||||||
type = string
|
type = string
|
||||||
description = "The GCP project to deploy the cluster in."
|
description = "The GCP project to deploy the cluster in."
|
||||||
@ -24,27 +40,11 @@ variable "credentials_file" {
|
|||||||
description = "The path to the GCP credentials file."
|
description = "The path to the GCP credentials file."
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "control_plane_count" {
|
|
||||||
type = number
|
|
||||||
description = "The number of control plane nodes to deploy."
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "worker_count" {
|
|
||||||
type = number
|
|
||||||
description = "The number of worker nodes to deploy."
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "instance_type" {
|
variable "instance_type" {
|
||||||
type = string
|
type = string
|
||||||
description = "The GCP instance type to deploy."
|
description = "The GCP instance type to deploy."
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "state_disk_size" {
|
|
||||||
type = number
|
|
||||||
default = 30
|
|
||||||
description = "The size of the state disk in GB."
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "state_disk_type" {
|
variable "state_disk_type" {
|
||||||
type = string
|
type = string
|
||||||
default = "pd-ssd"
|
default = "pd-ssd"
|
@ -22,9 +22,9 @@ import (
|
|||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateInstances(t *testing.T) {
|
func TestCreateCluster(t *testing.T) {
|
||||||
someErr := errors.New("error")
|
someErr := errors.New("failed")
|
||||||
getState := func() *tfjson.State {
|
newTestState := func() *tfjson.State {
|
||||||
workingState := tfjson.State{
|
workingState := tfjson.State{
|
||||||
Values: &tfjson.StateValues{
|
Values: &tfjson.StateValues{
|
||||||
Outputs: map[string]*tfjson.StateOutput{
|
Outputs: map[string]*tfjson.StateOutput{
|
||||||
@ -34,52 +34,59 @@ func TestCreateInstances(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &workingState
|
return &workingState
|
||||||
}
|
}
|
||||||
|
qemuVars := &QEMUVariables{
|
||||||
|
CommonVariables: CommonVariables{
|
||||||
|
Name: "name",
|
||||||
|
CountControlPlanes: 1,
|
||||||
|
CountWorkers: 2,
|
||||||
|
StateDiskSizeGB: 11,
|
||||||
|
},
|
||||||
|
CPUCount: 1,
|
||||||
|
MemorySizeMiB: 1024,
|
||||||
|
IPRangeStart: 100,
|
||||||
|
ImagePath: "path",
|
||||||
|
ImageFormat: "format",
|
||||||
|
MetadataAPIImage: "api",
|
||||||
|
}
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
provider cloudprovider.Provider
|
provider cloudprovider.Provider
|
||||||
input CreateClusterInput
|
vars Variables
|
||||||
tf *stubTerraform
|
tf *stubTerraform
|
||||||
fs afero.Fs
|
fs afero.Fs
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"works": {
|
"works": {
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
tf: &stubTerraform{
|
vars: qemuVars,
|
||||||
showState: getState(),
|
tf: &stubTerraform{showState: newTestState()},
|
||||||
},
|
|
||||||
fs: afero.NewMemMapFs(),
|
fs: afero.NewMemMapFs(),
|
||||||
},
|
},
|
||||||
"init fails": {
|
"init fails": {
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
tf: &stubTerraform{
|
tf: &stubTerraform{initErr: someErr},
|
||||||
initErr: someErr,
|
|
||||||
showState: getState(),
|
|
||||||
},
|
|
||||||
fs: afero.NewMemMapFs(),
|
fs: afero.NewMemMapFs(),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"apply fails": {
|
"apply fails": {
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
tf: &stubTerraform{
|
vars: qemuVars,
|
||||||
applyErr: someErr,
|
tf: &stubTerraform{applyErr: someErr},
|
||||||
showState: getState(),
|
|
||||||
},
|
|
||||||
fs: afero.NewMemMapFs(),
|
fs: afero.NewMemMapFs(),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"show fails": {
|
"show fails": {
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
tf: &stubTerraform{
|
vars: qemuVars,
|
||||||
showErr: someErr,
|
tf: &stubTerraform{showErr: someErr},
|
||||||
},
|
|
||||||
fs: afero.NewMemMapFs(),
|
fs: afero.NewMemMapFs(),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"no ip": {
|
"no ip": {
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
|
vars: qemuVars,
|
||||||
tf: &stubTerraform{
|
tf: &stubTerraform{
|
||||||
showState: &tfjson.State{
|
showState: &tfjson.State{
|
||||||
Values: &tfjson.StateValues{
|
Values: &tfjson.StateValues{
|
||||||
@ -90,11 +97,22 @@ func TestCreateInstances(t *testing.T) {
|
|||||||
fs: afero.NewMemMapFs(),
|
fs: afero.NewMemMapFs(),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
"ip has wrong type": {
|
||||||
|
provider: cloudprovider.QEMU,
|
||||||
|
vars: qemuVars,
|
||||||
|
tf: &stubTerraform{
|
||||||
|
showState: &tfjson.State{
|
||||||
|
Values: &tfjson.StateValues{
|
||||||
|
Outputs: map[string]*tfjson.StateOutput{"ip": {Value: 42}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fs: afero.NewMemMapFs(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
"prepare workspace fails": {
|
"prepare workspace fails": {
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
tf: &stubTerraform{
|
tf: &stubTerraform{showState: newTestState()},
|
||||||
showState: getState(),
|
|
||||||
},
|
|
||||||
fs: afero.NewReadOnlyFs(afero.NewMemMapFs()),
|
fs: afero.NewReadOnlyFs(afero.NewMemMapFs()),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@ -110,12 +128,12 @@ func TestCreateInstances(t *testing.T) {
|
|||||||
file: file.NewHandler(tc.fs),
|
file: file.NewHandler(tc.fs),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.CreateCluster(context.Background(), "test", tc.input)
|
err := c.CreateCluster(context.Background(), "test", tc.vars)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
117
cli/internal/terraform/variables.go
Normal file
117
cli/internal/terraform/variables.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Variables is a struct that holds all variables that are passed to Terraform.
|
||||||
|
type Variables interface {
|
||||||
|
fmt.Stringer
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonVariables is user configuration for creating a cluster with Terraform.
|
||||||
|
type CommonVariables struct {
|
||||||
|
// Name of the cluster.
|
||||||
|
Name string
|
||||||
|
// CountControlPlanes is the number of control-plane nodes to create.
|
||||||
|
CountControlPlanes int
|
||||||
|
// CountWorkers is the number of worker nodes to create.
|
||||||
|
CountWorkers int
|
||||||
|
// StateDiskSizeGB is the size of the state disk to allocate to each node, in GB.
|
||||||
|
StateDiskSizeGB int
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the variables, formatted as Terraform variables.
|
||||||
|
func (v *CommonVariables) String() string {
|
||||||
|
b := &strings.Builder{}
|
||||||
|
writeLinef(b, "name = %q", v.Name)
|
||||||
|
writeLinef(b, "control_plane_count = %d", v.CountControlPlanes)
|
||||||
|
writeLinef(b, "worker_count = %d", v.CountWorkers)
|
||||||
|
writeLinef(b, "state_disk_size = %d", v.StateDiskSizeGB)
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GCPVariables is user configuration for creating a cluster with Terraform on GCP.
|
||||||
|
type GCPVariables struct {
|
||||||
|
// CommonVariables contains common variables.
|
||||||
|
CommonVariables
|
||||||
|
|
||||||
|
// Project is the ID of the GCP project to use.
|
||||||
|
Project string
|
||||||
|
// Region is the GCP region to use.
|
||||||
|
Region string
|
||||||
|
// Zone is the GCP zone to use.
|
||||||
|
Zone string
|
||||||
|
// CredentialsFile is the path to the GCP credentials file.
|
||||||
|
CredentialsFile string
|
||||||
|
// InstanceType is the GCP instance type to use.
|
||||||
|
InstanceType string
|
||||||
|
// StateDiskType is the GCP disk type to use for the state disk.
|
||||||
|
StateDiskType string
|
||||||
|
// ImageID is the ID of the GCP image to use.
|
||||||
|
ImageID string
|
||||||
|
// Debug is true if debug mode is enabled.
|
||||||
|
Debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the variables, formatted as Terraform variables.
|
||||||
|
func (v *GCPVariables) String() string {
|
||||||
|
b := &strings.Builder{}
|
||||||
|
b.WriteString(v.CommonVariables.String())
|
||||||
|
writeLinef(b, "project = %q", v.Project)
|
||||||
|
writeLinef(b, "region = %q", v.Region)
|
||||||
|
writeLinef(b, "zone = %q", v.Zone)
|
||||||
|
writeLinef(b, "credentials_file = %q", v.CredentialsFile)
|
||||||
|
writeLinef(b, "instance_type = %q", v.InstanceType)
|
||||||
|
writeLinef(b, "state_disk_type = %q", v.StateDiskType)
|
||||||
|
writeLinef(b, "image_id = %q", v.ImageID)
|
||||||
|
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.
|
||||||
|
CommonVariables
|
||||||
|
|
||||||
|
// CPUCount is the number of CPUs to allocate to each node.
|
||||||
|
CPUCount int
|
||||||
|
// MemorySizeMiB is the amount of memory to allocate to each node, in MiB.
|
||||||
|
MemorySizeMiB int
|
||||||
|
// IPRangeStart is the first IP address in the IP range to allocate to the cluster.
|
||||||
|
IPRangeStart int
|
||||||
|
// ImagePath is the path to the image to use for the nodes.
|
||||||
|
ImagePath string
|
||||||
|
// ImageFormat is the format of the image from ImagePath.
|
||||||
|
ImageFormat string
|
||||||
|
// MetadataAPIImage is the container image to use for the metadata API.
|
||||||
|
MetadataAPIImage string
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the variables, formatted as Terraform variables.
|
||||||
|
func (v *QEMUVariables) String() string {
|
||||||
|
b := &strings.Builder{}
|
||||||
|
b.WriteString(v.CommonVariables.String())
|
||||||
|
writeLinef(b, "constellation_coreos_image = %q", v.ImagePath)
|
||||||
|
writeLinef(b, "image_format = %q", v.ImageFormat)
|
||||||
|
writeLinef(b, "vcpus = %d", v.CPUCount)
|
||||||
|
writeLinef(b, "memory = %d", v.MemorySizeMiB)
|
||||||
|
writeLinef(b, "ip_range_start = %d", v.IPRangeStart)
|
||||||
|
writeLinef(b, "metadata_api_image = %q", v.MetadataAPIImage)
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLinef(builder *strings.Builder, format string, a ...interface{}) {
|
||||||
|
builder.WriteString(fmt.Sprintf(format, a...))
|
||||||
|
builder.WriteByte('\n')
|
||||||
|
}
|
10
go.mod
10
go.mod
@ -34,10 +34,8 @@ replace github.com/google/go-attestation => github.com/malt3/go-attestation v0.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute v1.7.0
|
cloud.google.com/go/compute v1.7.0
|
||||||
cloud.google.com/go/iam v0.3.0
|
|
||||||
cloud.google.com/go/kms v1.4.0
|
cloud.google.com/go/kms v1.4.0
|
||||||
cloud.google.com/go/logging v1.4.2
|
cloud.google.com/go/logging v1.4.2
|
||||||
cloud.google.com/go/resourcemanager v1.2.0
|
|
||||||
cloud.google.com/go/storage v1.22.1
|
cloud.google.com/go/storage v1.22.1
|
||||||
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible
|
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.1
|
||||||
@ -106,13 +104,9 @@ require (
|
|||||||
k8s.io/utils v0.0.0-20220812165043-ad590609e2e5
|
k8s.io/utils v0.0.0-20220812165043-ad590609e2e5
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
|
||||||
github.com/zclconf/go-cty v1.11.0 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.102.0 // indirect
|
cloud.google.com/go v0.102.0 // indirect
|
||||||
|
cloud.google.com/go/iam v0.3.0 // indirect
|
||||||
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
|
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 // indirect
|
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.5.0 // indirect
|
||||||
@ -198,6 +192,7 @@ require (
|
|||||||
github.com/gosuri/uitable v0.0.4 // indirect
|
github.com/gosuri/uitable v0.0.4 // indirect
|
||||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/huandu/xstrings v1.3.2 // indirect
|
github.com/huandu/xstrings v1.3.2 // indirect
|
||||||
github.com/icholy/replace v0.5.0
|
github.com/icholy/replace v0.5.0
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
@ -257,6 +252,7 @@ require (
|
|||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
github.com/xlab/treeprint v1.1.0 // indirect
|
github.com/xlab/treeprint v1.1.0 // indirect
|
||||||
|
github.com/zclconf/go-cty v1.11.0 // indirect
|
||||||
go.opencensus.io v0.23.0 // indirect
|
go.opencensus.io v0.23.0 // indirect
|
||||||
go.starlark.net v0.0.0-20220223235035-243c74974e97 // indirect
|
go.starlark.net v0.0.0-20220223235035-243c74974e97 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -71,8 +71,6 @@ cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+
|
|||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w=
|
cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w=
|
||||||
cloud.google.com/go/resourcemanager v1.2.0 h1:Oyt8+J80B51HgIPNk3p1ezTamu1wVj2bj7rBwL5Qd6k=
|
|
||||||
cloud.google.com/go/resourcemanager v1.2.0/go.mod h1:hFYbG0p7E8vVfQO3yfeaqEQVFO6n9gg9W2czYIdSEy4=
|
|
||||||
cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk=
|
cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk=
|
||||||
cloud.google.com/go/spanner v1.17.0/go.mod h1:+17t2ixFwRG4lWRwE+5kipDR9Ef07Jkmc8z0IbMDKUs=
|
cloud.google.com/go/spanner v1.17.0/go.mod h1:+17t2ixFwRG4lWRwE+5kipDR9Ef07Jkmc8z0IbMDKUs=
|
||||||
cloud.google.com/go/spanner v1.18.0/go.mod h1:LvAjUXPeJRGNuGpikMULjhLj/t9cRvdc+fxRoLiugXA=
|
cloud.google.com/go/spanner v1.18.0/go.mod h1:LvAjUXPeJRGNuGpikMULjhLj/t9cRvdc+fxRoLiugXA=
|
||||||
|
20
hack/go.mod
20
hack/go.mod
@ -56,22 +56,10 @@ require (
|
|||||||
libvirt.org/go/libvirtxml v1.8007.0
|
libvirt.org/go/libvirtxml v1.8007.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
|
||||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
|
||||||
github.com/hashicorp/hc-install v0.4.0 // indirect
|
|
||||||
github.com/hashicorp/terraform-exec v0.17.3 // indirect
|
|
||||||
github.com/hashicorp/terraform-json v0.14.0 // indirect
|
|
||||||
github.com/zclconf/go-cty v1.11.0 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.102.0 // indirect
|
cloud.google.com/go v0.102.0 // indirect
|
||||||
cloud.google.com/go/iam v0.3.0 // indirect
|
cloud.google.com/go/iam v0.3.0 // indirect
|
||||||
cloud.google.com/go/kms v1.4.0 // indirect
|
cloud.google.com/go/kms v1.4.0 // indirect
|
||||||
cloud.google.com/go/resourcemanager v1.2.0 // indirect
|
|
||||||
cloud.google.com/go/storage v1.22.1 // indirect
|
cloud.google.com/go/storage v1.22.1 // indirect
|
||||||
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
|
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
|
||||||
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
|
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
|
||||||
@ -155,6 +143,13 @@ require (
|
|||||||
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
|
||||||
github.com/googleapis/go-type-adapters v1.0.0 // indirect
|
github.com/googleapis/go-type-adapters v1.0.0 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||||
|
github.com/hashicorp/hc-install v0.4.0 // indirect
|
||||||
|
github.com/hashicorp/terraform-exec v0.17.3 // indirect
|
||||||
|
github.com/hashicorp/terraform-json v0.14.0 // indirect
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
@ -184,6 +179,7 @@ require (
|
|||||||
github.com/theupdateframework/go-tuf v0.3.2 // indirect
|
github.com/theupdateframework/go-tuf v0.3.2 // indirect
|
||||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||||
|
github.com/zclconf/go-cty v1.11.0 // indirect
|
||||||
go.opencensus.io v0.23.0 // indirect
|
go.opencensus.io v0.23.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.3.0 // indirect
|
go.opentelemetry.io/otel/trace v1.3.0 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
@ -65,8 +65,6 @@ cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+
|
|||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w=
|
cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w=
|
||||||
cloud.google.com/go/resourcemanager v1.2.0 h1:Oyt8+J80B51HgIPNk3p1ezTamu1wVj2bj7rBwL5Qd6k=
|
|
||||||
cloud.google.com/go/resourcemanager v1.2.0/go.mod h1:hFYbG0p7E8vVfQO3yfeaqEQVFO6n9gg9W2czYIdSEy4=
|
|
||||||
cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk=
|
cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk=
|
||||||
cloud.google.com/go/spanner v1.17.0/go.mod h1:+17t2ixFwRG4lWRwE+5kipDR9Ef07Jkmc8z0IbMDKUs=
|
cloud.google.com/go/spanner v1.17.0/go.mod h1:+17t2ixFwRG4lWRwE+5kipDR9Ef07Jkmc8z0IbMDKUs=
|
||||||
cloud.google.com/go/spanner v1.18.0/go.mod h1:LvAjUXPeJRGNuGpikMULjhLj/t9cRvdc+fxRoLiugXA=
|
cloud.google.com/go/spanner v1.18.0/go.mod h1:LvAjUXPeJRGNuGpikMULjhLj/t9cRvdc+fxRoLiugXA=
|
||||||
|
Loading…
Reference in New Issue
Block a user