mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-06-18 03:09:32 -04:00
cli: add Terraform log support (#1620)
* add Terraform logging * add TF logging to CLI * fix path * only create file if logging is enabled * update bazel files * register persistent flags manually * clidocgen * move logging code to separate file * reword yes flag parsing error * update bazel buildfile * factor out log level setting
This commit is contained in:
parent
ca1400819d
commit
1d0ee796e8
30 changed files with 688 additions and 238 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -46,6 +46,7 @@ image/config.mk
|
||||||
.terraform
|
.terraform
|
||||||
.terraform.tfstate.lock.info
|
.terraform.tfstate.lock.info
|
||||||
*.tfvars
|
*.tfvars
|
||||||
|
terraform.log
|
||||||
|
|
||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
@ -48,6 +48,7 @@ func NewRootCmd() *cobra.Command {
|
||||||
|
|
||||||
rootCmd.PersistentFlags().Bool("debug", false, "enable debug logging")
|
rootCmd.PersistentFlags().Bool("debug", false, "enable debug logging")
|
||||||
rootCmd.PersistentFlags().Bool("force", false, "disable version compatibility checks - might result in corrupted clusters")
|
rootCmd.PersistentFlags().Bool("force", false, "disable version compatibility checks - might result in corrupted clusters")
|
||||||
|
rootCmd.PersistentFlags().String("tf-log", "NONE", "sets the Terraform log level (default \"NONE\" - no logs)")
|
||||||
|
|
||||||
rootCmd.AddCommand(cmd.NewConfigCmd())
|
rootCmd.AddCommand(cmd.NewConfigCmd())
|
||||||
rootCmd.AddCommand(cmd.NewCreateCmd())
|
rootCmd.AddCommand(cmd.NewCreateCmd())
|
||||||
|
|
|
@ -23,9 +23,9 @@ type imageFetcher interface {
|
||||||
|
|
||||||
type terraformClient interface {
|
type terraformClient interface {
|
||||||
PrepareWorkspace(path string, input terraform.Variables) error
|
PrepareWorkspace(path string, input terraform.Variables) error
|
||||||
CreateCluster(ctx context.Context) (terraform.CreateOutput, error)
|
CreateCluster(ctx context.Context, logLevel terraform.LogLevel) (terraform.CreateOutput, error)
|
||||||
CreateIAMConfig(ctx context.Context, provider cloudprovider.Provider) (terraform.IAMOutput, error)
|
CreateIAMConfig(ctx context.Context, provider cloudprovider.Provider, logLevel terraform.LogLevel) (terraform.IAMOutput, error)
|
||||||
Destroy(ctx context.Context) error
|
Destroy(ctx context.Context, logLevel terraform.LogLevel) error
|
||||||
CleanUpWorkspace() error
|
CleanUpWorkspace() error
|
||||||
RemoveInstaller()
|
RemoveInstaller()
|
||||||
Show(ctx context.Context) (*tfjson.State, error)
|
Show(ctx context.Context) (*tfjson.State, error)
|
||||||
|
|
|
@ -45,7 +45,7 @@ type stubTerraformClient struct {
|
||||||
showErr error
|
showErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubTerraformClient) CreateCluster(_ context.Context) (terraform.CreateOutput, error) {
|
func (c *stubTerraformClient) CreateCluster(_ context.Context, _ terraform.LogLevel) (terraform.CreateOutput, error) {
|
||||||
return terraform.CreateOutput{
|
return terraform.CreateOutput{
|
||||||
IP: c.ip,
|
IP: c.ip,
|
||||||
Secret: c.initSecret,
|
Secret: c.initSecret,
|
||||||
|
@ -54,7 +54,7 @@ func (c *stubTerraformClient) CreateCluster(_ context.Context) (terraform.Create
|
||||||
}, c.createClusterErr
|
}, c.createClusterErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubTerraformClient) CreateIAMConfig(_ context.Context, _ cloudprovider.Provider) (terraform.IAMOutput, error) {
|
func (c *stubTerraformClient) CreateIAMConfig(_ context.Context, _ cloudprovider.Provider, _ terraform.LogLevel) (terraform.IAMOutput, error) {
|
||||||
return c.iamOutput, c.iamOutputErr
|
return c.iamOutput, c.iamOutputErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ func (c *stubTerraformClient) PrepareWorkspace(_ string, _ terraform.Variables)
|
||||||
return c.prepareWorkspaceErr
|
return c.prepareWorkspaceErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubTerraformClient) Destroy(_ context.Context) error {
|
func (c *stubTerraformClient) Destroy(_ context.Context, _ terraform.LogLevel) error {
|
||||||
c.destroyCalled = true
|
c.destroyCalled = true
|
||||||
return c.destroyErr
|
return c.destroyErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,43 +63,54 @@ func NewCreator(out io.Writer) *Creator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateOptions are the options for creating a Constellation cluster.
|
||||||
|
type CreateOptions struct {
|
||||||
|
Provider cloudprovider.Provider
|
||||||
|
Config *config.Config
|
||||||
|
InsType string
|
||||||
|
ControlPlaneCount int
|
||||||
|
WorkerCount int
|
||||||
|
image string
|
||||||
|
TFLogLevel terraform.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
// Create creates the handed amount of instances and all the needed resources.
|
// Create creates the handed amount of instances and all the needed resources.
|
||||||
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, insType string, controlPlaneCount, workerCount int,
|
func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.File, error) {
|
||||||
) (clusterid.File, error) {
|
image, err := c.image.FetchReference(ctx, opts.Config)
|
||||||
image, err := c.image.FetchReference(ctx, config)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, fmt.Errorf("fetching image reference: %w", err)
|
return clusterid.File{}, fmt.Errorf("fetching image reference: %w", err)
|
||||||
}
|
}
|
||||||
|
opts.image = image
|
||||||
|
|
||||||
switch provider {
|
switch opts.Provider {
|
||||||
case cloudprovider.AWS:
|
case cloudprovider.AWS:
|
||||||
cl, err := c.newTerraformClient(ctx)
|
cl, err := c.newTerraformClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createAWS(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
|
return c.createAWS(ctx, cl, opts)
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
cl, err := c.newTerraformClient(ctx)
|
cl, err := c.newTerraformClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createGCP(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
|
return c.createGCP(ctx, cl, opts)
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl, err := c.newTerraformClient(ctx)
|
cl, err := c.newTerraformClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createAzure(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
|
return c.createAzure(ctx, cl, opts)
|
||||||
case cloudprovider.OpenStack:
|
case cloudprovider.OpenStack:
|
||||||
cl, err := c.newTerraformClient(ctx)
|
cl, err := c.newTerraformClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createOpenStack(ctx, cl, config, controlPlaneCount, workerCount, image)
|
return c.createOpenStack(ctx, cl, opts)
|
||||||
case cloudprovider.QEMU:
|
case cloudprovider.QEMU:
|
||||||
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
|
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
|
||||||
return clusterid.File{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
|
return clusterid.File{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
@ -110,38 +121,40 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
lv := c.newLibvirtRunner()
|
lv := c.newLibvirtRunner()
|
||||||
return c.createQEMU(ctx, cl, lv, config, controlPlaneCount, workerCount, image)
|
qemuOpts := qemuCreateOptions{
|
||||||
|
source: image,
|
||||||
|
CreateOptions: opts,
|
||||||
|
}
|
||||||
|
return c.createQEMU(ctx, cl, lv, qemuOpts)
|
||||||
default:
|
default:
|
||||||
return clusterid.File{}, fmt.Errorf("unsupported cloud provider: %s", provider)
|
return clusterid.File{}, fmt.Errorf("unsupported cloud provider: %s", opts.Provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createAWS(ctx context.Context, cl terraformClient, config *config.Config,
|
func (c *Creator) createAWS(ctx context.Context, cl terraformClient, opts CreateOptions) (idFile clusterid.File, retErr error) {
|
||||||
insType string, controlPlaneCount, workerCount int, image string,
|
|
||||||
) (idFile clusterid.File, retErr error) {
|
|
||||||
vars := terraform.AWSClusterVariables{
|
vars := terraform.AWSClusterVariables{
|
||||||
CommonVariables: terraform.CommonVariables{
|
CommonVariables: terraform.CommonVariables{
|
||||||
Name: config.Name,
|
Name: opts.Config.Name,
|
||||||
CountControlPlanes: controlPlaneCount,
|
CountControlPlanes: opts.ControlPlaneCount,
|
||||||
CountWorkers: workerCount,
|
CountWorkers: opts.WorkerCount,
|
||||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||||
},
|
},
|
||||||
StateDiskType: config.Provider.AWS.StateDiskType,
|
StateDiskType: opts.Config.Provider.AWS.StateDiskType,
|
||||||
Region: config.Provider.AWS.Region,
|
Region: opts.Config.Provider.AWS.Region,
|
||||||
Zone: config.Provider.AWS.Zone,
|
Zone: opts.Config.Provider.AWS.Zone,
|
||||||
InstanceType: insType,
|
InstanceType: opts.InsType,
|
||||||
AMIImageID: image,
|
AMIImageID: opts.image,
|
||||||
IAMProfileControlPlane: config.Provider.AWS.IAMProfileControlPlane,
|
IAMProfileControlPlane: opts.Config.Provider.AWS.IAMProfileControlPlane,
|
||||||
IAMProfileWorkerNodes: config.Provider.AWS.IAMProfileWorkerNodes,
|
IAMProfileWorkerNodes: opts.Config.Provider.AWS.IAMProfileWorkerNodes,
|
||||||
Debug: config.IsDebugCluster(),
|
Debug: opts.Config.IsDebugCluster(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.AWS.String())), &vars); err != nil {
|
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.AWS.String())), &vars); err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||||
tfOutput, err := cl.CreateCluster(ctx)
|
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
@ -154,32 +167,30 @@ func (c *Creator) createAWS(ctx context.Context, cl terraformClient, config *con
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *config.Config,
|
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, opts CreateOptions) (idFile clusterid.File, retErr error) {
|
||||||
insType string, controlPlaneCount, workerCount int, image string,
|
|
||||||
) (idFile clusterid.File, retErr error) {
|
|
||||||
vars := terraform.GCPClusterVariables{
|
vars := terraform.GCPClusterVariables{
|
||||||
CommonVariables: terraform.CommonVariables{
|
CommonVariables: terraform.CommonVariables{
|
||||||
Name: config.Name,
|
Name: opts.Config.Name,
|
||||||
CountControlPlanes: controlPlaneCount,
|
CountControlPlanes: opts.ControlPlaneCount,
|
||||||
CountWorkers: workerCount,
|
CountWorkers: opts.WorkerCount,
|
||||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||||
},
|
},
|
||||||
Project: config.Provider.GCP.Project,
|
Project: opts.Config.Provider.GCP.Project,
|
||||||
Region: config.Provider.GCP.Region,
|
Region: opts.Config.Provider.GCP.Region,
|
||||||
Zone: config.Provider.GCP.Zone,
|
Zone: opts.Config.Provider.GCP.Zone,
|
||||||
CredentialsFile: config.Provider.GCP.ServiceAccountKeyPath,
|
CredentialsFile: opts.Config.Provider.GCP.ServiceAccountKeyPath,
|
||||||
InstanceType: insType,
|
InstanceType: opts.InsType,
|
||||||
StateDiskType: config.Provider.GCP.StateDiskType,
|
StateDiskType: opts.Config.Provider.GCP.StateDiskType,
|
||||||
ImageID: image,
|
ImageID: opts.image,
|
||||||
Debug: config.IsDebugCluster(),
|
Debug: opts.Config.IsDebugCluster(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.GCP.String())), &vars); err != nil {
|
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.GCP.String())), &vars); err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||||
tfOutput, err := cl.CreateCluster(ctx)
|
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
@ -192,27 +203,26 @@ func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *con
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *config.Config, insType string, controlPlaneCount, workerCount int, image string,
|
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, opts CreateOptions) (idFile clusterid.File, retErr error) {
|
||||||
) (idFile clusterid.File, retErr error) {
|
|
||||||
vars := terraform.AzureClusterVariables{
|
vars := terraform.AzureClusterVariables{
|
||||||
CommonVariables: terraform.CommonVariables{
|
CommonVariables: terraform.CommonVariables{
|
||||||
Name: config.Name,
|
Name: opts.Config.Name,
|
||||||
CountControlPlanes: controlPlaneCount,
|
CountControlPlanes: opts.ControlPlaneCount,
|
||||||
CountWorkers: workerCount,
|
CountWorkers: opts.WorkerCount,
|
||||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||||
},
|
},
|
||||||
Location: config.Provider.Azure.Location,
|
Location: opts.Config.Provider.Azure.Location,
|
||||||
ResourceGroup: config.Provider.Azure.ResourceGroup,
|
ResourceGroup: opts.Config.Provider.Azure.ResourceGroup,
|
||||||
UserAssignedIdentity: config.Provider.Azure.UserAssignedIdentity,
|
UserAssignedIdentity: opts.Config.Provider.Azure.UserAssignedIdentity,
|
||||||
InstanceType: insType,
|
InstanceType: opts.InsType,
|
||||||
StateDiskType: config.Provider.Azure.StateDiskType,
|
StateDiskType: opts.Config.Provider.Azure.StateDiskType,
|
||||||
ImageID: image,
|
ImageID: opts.image,
|
||||||
SecureBoot: *config.Provider.Azure.SecureBoot,
|
SecureBoot: *opts.Config.Provider.Azure.SecureBoot,
|
||||||
CreateMAA: config.Provider.Azure.EnforceIDKeyDigest == idkeydigest.MAAFallback,
|
CreateMAA: opts.Config.Provider.Azure.EnforceIDKeyDigest == idkeydigest.MAAFallback,
|
||||||
Debug: config.IsDebugCluster(),
|
Debug: opts.Config.IsDebugCluster(),
|
||||||
}
|
}
|
||||||
|
|
||||||
attestVariant, err := variant.FromString(config.AttestationVariant)
|
attestVariant, err := variant.FromString(opts.Config.AttestationVariant)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, fmt.Errorf("parsing attestation variant: %w", err)
|
return clusterid.File{}, fmt.Errorf("parsing attestation variant: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -224,8 +234,8 @@ func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *c
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||||
tfOutput, err := cl.CreateCluster(ctx)
|
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
@ -348,14 +358,12 @@ func normalizeAzureURIs(vars terraform.AzureClusterVariables) terraform.AzureClu
|
||||||
return vars
|
return vars
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, config *config.Config,
|
func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, opts CreateOptions) (idFile clusterid.File, retErr error) {
|
||||||
controlPlaneCount, workerCount int, image string,
|
|
||||||
) (idFile clusterid.File, retErr error) {
|
|
||||||
// TODO: Remove this once OpenStack is supported.
|
// TODO: Remove this once OpenStack is supported.
|
||||||
if os.Getenv("CONSTELLATION_OPENSTACK_DEV") != "1" {
|
if os.Getenv("CONSTELLATION_OPENSTACK_DEV") != "1" {
|
||||||
return clusterid.File{}, errors.New("OpenStack isn't supported yet")
|
return clusterid.File{}, errors.New("OpenStack isn't supported yet")
|
||||||
}
|
}
|
||||||
if _, hasOSAuthURL := os.LookupEnv("OS_AUTH_URL"); !hasOSAuthURL && config.Provider.OpenStack.Cloud == "" {
|
if _, hasOSAuthURL := os.LookupEnv("OS_AUTH_URL"); !hasOSAuthURL && opts.Config.Provider.OpenStack.Cloud == "" {
|
||||||
return clusterid.File{}, errors.New(
|
return clusterid.File{}, errors.New(
|
||||||
"neither environment variable OS_AUTH_URL nor cloud name for \"clouds.yaml\" is set. OpenStack authentication requires a set of " +
|
"neither environment variable OS_AUTH_URL nor cloud name for \"clouds.yaml\" is set. OpenStack authentication requires a set of " +
|
||||||
"OS_* environment variables that are typically sourced into the current shell with an openrc file " +
|
"OS_* environment variables that are typically sourced into the current shell with an openrc file " +
|
||||||
|
@ -366,29 +374,29 @@ func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, confi
|
||||||
|
|
||||||
vars := terraform.OpenStackClusterVariables{
|
vars := terraform.OpenStackClusterVariables{
|
||||||
CommonVariables: terraform.CommonVariables{
|
CommonVariables: terraform.CommonVariables{
|
||||||
Name: config.Name,
|
Name: opts.Config.Name,
|
||||||
CountControlPlanes: controlPlaneCount,
|
CountControlPlanes: opts.ControlPlaneCount,
|
||||||
CountWorkers: workerCount,
|
CountWorkers: opts.WorkerCount,
|
||||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||||
},
|
},
|
||||||
Cloud: config.Provider.OpenStack.Cloud,
|
Cloud: opts.Config.Provider.OpenStack.Cloud,
|
||||||
AvailabilityZone: config.Provider.OpenStack.AvailabilityZone,
|
AvailabilityZone: opts.Config.Provider.OpenStack.AvailabilityZone,
|
||||||
FloatingIPPoolID: config.Provider.OpenStack.FloatingIPPoolID,
|
FloatingIPPoolID: opts.Config.Provider.OpenStack.FloatingIPPoolID,
|
||||||
FlavorID: config.Provider.OpenStack.FlavorID,
|
FlavorID: opts.Config.Provider.OpenStack.FlavorID,
|
||||||
ImageURL: image,
|
ImageURL: opts.image,
|
||||||
DirectDownload: *config.Provider.OpenStack.DirectDownload,
|
DirectDownload: *opts.Config.Provider.OpenStack.DirectDownload,
|
||||||
OpenstackUserDomainName: config.Provider.OpenStack.UserDomainName,
|
OpenstackUserDomainName: opts.Config.Provider.OpenStack.UserDomainName,
|
||||||
OpenstackUsername: config.Provider.OpenStack.Username,
|
OpenstackUsername: opts.Config.Provider.OpenStack.Username,
|
||||||
OpenstackPassword: config.Provider.OpenStack.Password,
|
OpenstackPassword: opts.Config.Provider.OpenStack.Password,
|
||||||
Debug: config.IsDebugCluster(),
|
Debug: opts.Config.IsDebugCluster(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.OpenStack.String())), &vars); err != nil {
|
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.OpenStack.String())), &vars); err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||||
tfOutput, err := cl.CreateCluster(ctx)
|
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
@ -401,26 +409,29 @@ func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, confi
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, config *config.Config,
|
type qemuCreateOptions struct {
|
||||||
controlPlaneCount, workerCount int, source string,
|
source string
|
||||||
) (idFile clusterid.File, retErr error) {
|
CreateOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, opts qemuCreateOptions) (idFile clusterid.File, retErr error) {
|
||||||
qemuRollbacker := &rollbackerQEMU{client: cl, libvirt: lv, createdWorkspace: false}
|
qemuRollbacker := &rollbackerQEMU{client: cl, libvirt: lv, createdWorkspace: false}
|
||||||
defer rollbackOnError(c.out, &retErr, qemuRollbacker)
|
defer rollbackOnError(c.out, &retErr, qemuRollbacker, opts.TFLogLevel)
|
||||||
|
|
||||||
// TODO: render progress bar
|
// TODO: render progress bar
|
||||||
downloader := c.newRawDownloader()
|
downloader := c.newRawDownloader()
|
||||||
imagePath, err := downloader.Download(ctx, c.out, false, source, config.Image)
|
imagePath, err := downloader.Download(ctx, c.out, false, opts.source, opts.Config.Image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, fmt.Errorf("download raw image: %w", err)
|
return clusterid.File{}, fmt.Errorf("download raw image: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
libvirtURI := config.Provider.QEMU.LibvirtURI
|
libvirtURI := opts.Config.Provider.QEMU.LibvirtURI
|
||||||
libvirtSocketPath := "."
|
libvirtSocketPath := "."
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
// if no libvirt URI is specified, start a libvirt container
|
// if no libvirt URI is specified, start a libvirt container
|
||||||
case libvirtURI == "":
|
case libvirtURI == "":
|
||||||
if err := lv.Start(ctx, config.Name, config.Provider.QEMU.LibvirtContainerImage); err != nil {
|
if err := lv.Start(ctx, opts.Config.Name, opts.Config.Provider.QEMU.LibvirtContainerImage); err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
libvirtURI = libvirt.LibvirtTCPConnectURI
|
libvirtURI = libvirt.LibvirtTCPConnectURI
|
||||||
|
@ -452,21 +463,21 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
||||||
|
|
||||||
vars := terraform.QEMUVariables{
|
vars := terraform.QEMUVariables{
|
||||||
CommonVariables: terraform.CommonVariables{
|
CommonVariables: terraform.CommonVariables{
|
||||||
Name: config.Name,
|
Name: opts.Config.Name,
|
||||||
CountControlPlanes: controlPlaneCount,
|
CountControlPlanes: opts.ControlPlaneCount,
|
||||||
CountWorkers: workerCount,
|
CountWorkers: opts.WorkerCount,
|
||||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||||
},
|
},
|
||||||
LibvirtURI: libvirtURI,
|
LibvirtURI: libvirtURI,
|
||||||
LibvirtSocketPath: libvirtSocketPath,
|
LibvirtSocketPath: libvirtSocketPath,
|
||||||
ImagePath: imagePath,
|
ImagePath: imagePath,
|
||||||
ImageFormat: config.Provider.QEMU.ImageFormat,
|
ImageFormat: opts.Config.Provider.QEMU.ImageFormat,
|
||||||
CPUCount: config.Provider.QEMU.VCPUs,
|
CPUCount: opts.Config.Provider.QEMU.VCPUs,
|
||||||
MemorySizeMiB: config.Provider.QEMU.Memory,
|
MemorySizeMiB: opts.Config.Provider.QEMU.Memory,
|
||||||
MetadataAPIImage: config.Provider.QEMU.MetadataAPIImage,
|
MetadataAPIImage: opts.Config.Provider.QEMU.MetadataAPIImage,
|
||||||
MetadataLibvirtURI: metadataLibvirtURI,
|
MetadataLibvirtURI: metadataLibvirtURI,
|
||||||
NVRAM: config.Provider.QEMU.NVRAM,
|
NVRAM: opts.Config.Provider.QEMU.NVRAM,
|
||||||
Firmware: config.Provider.QEMU.Firmware,
|
Firmware: opts.Config.Provider.QEMU.Firmware,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.QEMU.String())), &vars); err != nil {
|
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.QEMU.String())), &vars); err != nil {
|
||||||
|
@ -476,7 +487,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
||||||
// Allow rollback of QEMU Terraform workspace from this point on
|
// Allow rollback of QEMU Terraform workspace from this point on
|
||||||
qemuRollbacker.createdWorkspace = true
|
qemuRollbacker.createdWorkspace = true
|
||||||
|
|
||||||
tfOutput, err := cl.CreateCluster(ctx)
|
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterid.File{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,15 @@ func TestCreator(t *testing.T) {
|
||||||
policyPatcher: tc.policyPatcher,
|
policyPatcher: tc.policyPatcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
idFile, err := creator.Create(context.Background(), tc.provider, tc.config, "type", 2, 3)
|
opts := CreateOptions{
|
||||||
|
Provider: tc.provider,
|
||||||
|
Config: tc.config,
|
||||||
|
InsType: "type",
|
||||||
|
ControlPlaneCount: 2,
|
||||||
|
WorkerCount: 3,
|
||||||
|
TFLogLevel: terraform.LogLevelNone,
|
||||||
|
}
|
||||||
|
idFile, err := creator.Create(context.Background(), opts)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
|
|
@ -70,8 +70,8 @@ func (d *IAMDestroyer) GetTfstateServiceAccountKey(ctx context.Context) (gcpshar
|
||||||
}
|
}
|
||||||
|
|
||||||
// DestroyIAMConfiguration destroys the previously created IAM configuration and deletes the local IAM terraform files.
|
// DestroyIAMConfiguration destroys the previously created IAM configuration and deletes the local IAM terraform files.
|
||||||
func (d *IAMDestroyer) DestroyIAMConfiguration(ctx context.Context) error {
|
func (d *IAMDestroyer) DestroyIAMConfiguration(ctx context.Context, logLevel terraform.LogLevel) error {
|
||||||
if err := d.client.Destroy(ctx); err != nil {
|
if err := d.client.Destroy(ctx, logLevel); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return d.client.CleanUpWorkspace()
|
return d.client.CleanUpWorkspace()
|
||||||
|
@ -83,11 +83,12 @@ type IAMCreator struct {
|
||||||
newTerraformClient func(ctx context.Context) (terraformClient, error)
|
newTerraformClient func(ctx context.Context) (terraformClient, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IAMConfig holds the necessary values for IAM configuration.
|
// IAMConfigOptions holds the necessary values for IAM configuration.
|
||||||
type IAMConfig struct {
|
type IAMConfigOptions struct {
|
||||||
GCP GCPIAMConfig
|
GCP GCPIAMConfig
|
||||||
Azure AzureIAMConfig
|
Azure AzureIAMConfig
|
||||||
AWS AWSIAMConfig
|
AWS AWSIAMConfig
|
||||||
|
TFLogLevel terraform.LogLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
// GCPIAMConfig holds the necessary values for GCP IAM configuration.
|
// GCPIAMConfig holds the necessary values for GCP IAM configuration.
|
||||||
|
@ -122,7 +123,7 @@ func NewIAMCreator(out io.Writer) *IAMCreator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create prepares and hands over the corresponding providers IAM creator.
|
// Create prepares and hands over the corresponding providers IAM creator.
|
||||||
func (c *IAMCreator) Create(ctx context.Context, provider cloudprovider.Provider, iamConfig *IAMConfig) (iamid.File, error) {
|
func (c *IAMCreator) Create(ctx context.Context, provider cloudprovider.Provider, opts *IAMConfigOptions) (iamid.File, error) {
|
||||||
switch provider {
|
switch provider {
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
cl, err := c.newTerraformClient(ctx)
|
cl, err := c.newTerraformClient(ctx)
|
||||||
|
@ -130,42 +131,42 @@ func (c *IAMCreator) Create(ctx context.Context, provider cloudprovider.Provider
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createGCP(ctx, cl, iamConfig)
|
return c.createGCP(ctx, cl, opts)
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl, err := c.newTerraformClient(ctx)
|
cl, err := c.newTerraformClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createAzure(ctx, cl, iamConfig)
|
return c.createAzure(ctx, cl, opts)
|
||||||
case cloudprovider.AWS:
|
case cloudprovider.AWS:
|
||||||
cl, err := c.newTerraformClient(ctx)
|
cl, err := c.newTerraformClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createAWS(ctx, cl, iamConfig)
|
return c.createAWS(ctx, cl, opts)
|
||||||
default:
|
default:
|
||||||
return iamid.File{}, fmt.Errorf("unsupported cloud provider: %s", provider)
|
return iamid.File{}, fmt.Errorf("unsupported cloud provider: %s", provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createGCP creates the IAM configuration on GCP.
|
// createGCP creates the IAM configuration on GCP.
|
||||||
func (c *IAMCreator) createGCP(ctx context.Context, cl terraformClient, iamConfig *IAMConfig) (retFile iamid.File, retErr error) {
|
func (c *IAMCreator) createGCP(ctx context.Context, cl terraformClient, opts *IAMConfigOptions) (retFile iamid.File, retErr error) {
|
||||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||||
|
|
||||||
vars := terraform.GCPIAMVariables{
|
vars := terraform.GCPIAMVariables{
|
||||||
ServiceAccountID: iamConfig.GCP.ServiceAccountID,
|
ServiceAccountID: opts.GCP.ServiceAccountID,
|
||||||
Project: iamConfig.GCP.ProjectID,
|
Project: opts.GCP.ProjectID,
|
||||||
Region: iamConfig.GCP.Region,
|
Region: opts.GCP.Region,
|
||||||
Zone: iamConfig.GCP.Zone,
|
Zone: opts.GCP.Zone,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.GCP.String())), &vars); err != nil {
|
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.GCP.String())), &vars); err != nil {
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.GCP)
|
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.GCP, opts.TFLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
|
@ -179,20 +180,20 @@ func (c *IAMCreator) createGCP(ctx context.Context, cl terraformClient, iamConfi
|
||||||
}
|
}
|
||||||
|
|
||||||
// createAzure creates the IAM configuration on Azure.
|
// createAzure creates the IAM configuration on Azure.
|
||||||
func (c *IAMCreator) createAzure(ctx context.Context, cl terraformClient, iamConfig *IAMConfig) (retFile iamid.File, retErr error) {
|
func (c *IAMCreator) createAzure(ctx context.Context, cl terraformClient, opts *IAMConfigOptions) (retFile iamid.File, retErr error) {
|
||||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||||
|
|
||||||
vars := terraform.AzureIAMVariables{
|
vars := terraform.AzureIAMVariables{
|
||||||
Region: iamConfig.Azure.Region,
|
Region: opts.Azure.Region,
|
||||||
ResourceGroup: iamConfig.Azure.ResourceGroup,
|
ResourceGroup: opts.Azure.ResourceGroup,
|
||||||
ServicePrincipal: iamConfig.Azure.ServicePrincipal,
|
ServicePrincipal: opts.Azure.ServicePrincipal,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.Azure.String())), &vars); err != nil {
|
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.Azure.String())), &vars); err != nil {
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.Azure)
|
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.Azure, opts.TFLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
|
@ -210,19 +211,19 @@ func (c *IAMCreator) createAzure(ctx context.Context, cl terraformClient, iamCon
|
||||||
}
|
}
|
||||||
|
|
||||||
// createAWS creates the IAM configuration on AWS.
|
// createAWS creates the IAM configuration on AWS.
|
||||||
func (c *IAMCreator) createAWS(ctx context.Context, cl terraformClient, iamConfig *IAMConfig) (retFile iamid.File, retErr error) {
|
func (c *IAMCreator) createAWS(ctx context.Context, cl terraformClient, opts *IAMConfigOptions) (retFile iamid.File, retErr error) {
|
||||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||||
|
|
||||||
vars := terraform.AWSIAMVariables{
|
vars := terraform.AWSIAMVariables{
|
||||||
Region: iamConfig.AWS.Region,
|
Region: opts.AWS.Region,
|
||||||
Prefix: iamConfig.AWS.Prefix,
|
Prefix: opts.AWS.Prefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.AWS.String())), &vars); err != nil {
|
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.AWS.String())), &vars); err != nil {
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.AWS)
|
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.AWS, opts.TFLogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iamid.File{}, err
|
return iamid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ func TestIAMCreator(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
tfClient terraformClient
|
tfClient terraformClient
|
||||||
newTfClientErr error
|
newTfClientErr error
|
||||||
config *IAMConfig
|
config *IAMConfigOptions
|
||||||
provider cloudprovider.Provider
|
provider cloudprovider.Provider
|
||||||
wantIAMIDFile iamid.File
|
wantIAMIDFile iamid.File
|
||||||
wantErr bool
|
wantErr bool
|
||||||
|
@ -107,19 +107,19 @@ func TestIAMCreator(t *testing.T) {
|
||||||
tfClient: &stubTerraformClient{iamOutput: validGCPIAMOutput},
|
tfClient: &stubTerraformClient{iamOutput: validGCPIAMOutput},
|
||||||
wantIAMIDFile: validGCPIAMIDFile,
|
wantIAMIDFile: validGCPIAMIDFile,
|
||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
config: &IAMConfig{GCP: validGCPIAMConfig},
|
config: &IAMConfigOptions{GCP: validGCPIAMConfig},
|
||||||
},
|
},
|
||||||
"azure": {
|
"azure": {
|
||||||
tfClient: &stubTerraformClient{iamOutput: validAzureIAMOutput},
|
tfClient: &stubTerraformClient{iamOutput: validAzureIAMOutput},
|
||||||
wantIAMIDFile: validAzureIAMIDFile,
|
wantIAMIDFile: validAzureIAMIDFile,
|
||||||
provider: cloudprovider.Azure,
|
provider: cloudprovider.Azure,
|
||||||
config: &IAMConfig{Azure: validAzureIAMConfig},
|
config: &IAMConfigOptions{Azure: validAzureIAMConfig},
|
||||||
},
|
},
|
||||||
"aws": {
|
"aws": {
|
||||||
tfClient: &stubTerraformClient{iamOutput: validAWSIAMOutput},
|
tfClient: &stubTerraformClient{iamOutput: validAWSIAMOutput},
|
||||||
wantIAMIDFile: validAWSIAMIDFile,
|
wantIAMIDFile: validAWSIAMIDFile,
|
||||||
provider: cloudprovider.AWS,
|
provider: cloudprovider.AWS,
|
||||||
config: &IAMConfig{AWS: validAWSIAMConfig},
|
config: &IAMConfigOptions{AWS: validAWSIAMConfig},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ func TestDestroyIAMConfiguration(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
destroyer := &IAMDestroyer{client: tc.tfClient}
|
destroyer := &IAMDestroyer{client: tc.tfClient}
|
||||||
|
|
||||||
err := destroyer.DestroyIAMConfiguration(context.Background())
|
err := destroyer.DestroyIAMConfiguration(context.Background(), terraform.LogLevelNone)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
|
|
@ -11,22 +11,24 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
// rollbacker does a rollback.
|
// rollbacker does a rollback.
|
||||||
type rollbacker interface {
|
type rollbacker interface {
|
||||||
rollback(ctx context.Context) error
|
rollback(ctx context.Context, logLevel terraform.LogLevel) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// rollbackOnError calls rollback on the rollbacker if the handed error is not nil,
|
// rollbackOnError calls rollback on the rollbacker if the handed error is not nil,
|
||||||
// and writes logs to the writer w.
|
// and writes logs to the writer w.
|
||||||
func rollbackOnError(w io.Writer, onErr *error, roll rollbacker) {
|
func rollbackOnError(w io.Writer, onErr *error, roll rollbacker, logLevel terraform.LogLevel) {
|
||||||
if *onErr == nil {
|
if *onErr == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "An error occurred: %s\n", *onErr)
|
fmt.Fprintf(w, "An error occurred: %s\n", *onErr)
|
||||||
fmt.Fprintln(w, "Attempting to roll back.")
|
fmt.Fprintln(w, "Attempting to roll back.")
|
||||||
if err := roll.rollback(context.Background()); err != nil {
|
if err := roll.rollback(context.Background(), logLevel); err != nil {
|
||||||
*onErr = errors.Join(*onErr, fmt.Errorf("on rollback: %w", err)) // TODO: print the error, or return it?
|
*onErr = errors.Join(*onErr, fmt.Errorf("on rollback: %w", err)) // TODO: print the error, or return it?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -37,8 +39,8 @@ type rollbackerTerraform struct {
|
||||||
client terraformClient
|
client terraformClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rollbackerTerraform) rollback(ctx context.Context) error {
|
func (r *rollbackerTerraform) rollback(ctx context.Context, logLevel terraform.LogLevel) error {
|
||||||
if err := r.client.Destroy(ctx); err != nil {
|
if err := r.client.Destroy(ctx, logLevel); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return r.client.CleanUpWorkspace()
|
return r.client.CleanUpWorkspace()
|
||||||
|
@ -50,9 +52,9 @@ type rollbackerQEMU struct {
|
||||||
createdWorkspace bool
|
createdWorkspace bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rollbackerQEMU) rollback(ctx context.Context) (retErr error) {
|
func (r *rollbackerQEMU) rollback(ctx context.Context, logLevel terraform.LogLevel) (retErr error) {
|
||||||
if r.createdWorkspace {
|
if r.createdWorkspace {
|
||||||
retErr = r.client.Destroy(ctx)
|
retErr = r.client.Destroy(ctx, logLevel)
|
||||||
}
|
}
|
||||||
if retErr := errors.Join(retErr, r.libvirt.Stop(ctx)); retErr != nil {
|
if retErr := errors.Join(retErr, r.libvirt.Stop(ctx)); retErr != nil {
|
||||||
return retErr
|
return retErr
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ func TestRollbackTerraform(t *testing.T) {
|
||||||
client: tc.tfClient,
|
client: tc.tfClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := rollbacker.rollback(context.Background())
|
err := rollbacker.rollback(context.Background(), terraform.LogLevelNone)
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
if tc.tfClient.cleanUpWorkspaceErr == nil {
|
if tc.tfClient.cleanUpWorkspaceErr == nil {
|
||||||
|
@ -98,7 +99,7 @@ func TestRollbackQEMU(t *testing.T) {
|
||||||
createdWorkspace: tc.createdWorkspace,
|
createdWorkspace: tc.createdWorkspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := rollbacker.rollback(context.Background())
|
err := rollbacker.rollback(context.Background(), terraform.LogLevelNone)
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
if tc.tfClient.cleanUpWorkspaceErr == nil {
|
if tc.tfClient.cleanUpWorkspaceErr == nil {
|
||||||
|
|
|
@ -33,7 +33,7 @@ func NewTerminator() *Terminator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Terminate deletes the could provider resources.
|
// Terminate deletes the could provider resources.
|
||||||
func (t *Terminator) Terminate(ctx context.Context) (retErr error) {
|
func (t *Terminator) Terminate(ctx context.Context, logLevel terraform.LogLevel) (retErr error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if retErr == nil {
|
if retErr == nil {
|
||||||
retErr = t.newLibvirtRunner().Stop(ctx)
|
retErr = t.newLibvirtRunner().Stop(ctx)
|
||||||
|
@ -46,11 +46,11 @@ func (t *Terminator) Terminate(ctx context.Context) (retErr error) {
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
|
|
||||||
return t.terminateTerraform(ctx, cl)
|
return t.terminateTerraform(ctx, cl, logLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient) error {
|
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient, logLevel terraform.LogLevel) error {
|
||||||
if err := cl.Destroy(ctx); err != nil {
|
if err := cl.Destroy(ctx, logLevel); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return cl.CleanUpWorkspace()
|
return cl.CleanUpWorkspace()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ func TestTerminator(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := terminator.Terminate(context.Background())
|
err := terminator.Terminate(context.Background(), terraform.LogLevelNone)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
|
|
@ -120,6 +120,7 @@ go_test(
|
||||||
"//cli/internal/helm",
|
"//cli/internal/helm",
|
||||||
"//cli/internal/iamid",
|
"//cli/internal/iamid",
|
||||||
"//cli/internal/kubernetes",
|
"//cli/internal/kubernetes",
|
||||||
|
"//cli/internal/terraform",
|
||||||
"//disk-mapper/recoverproto",
|
"//disk-mapper/recoverproto",
|
||||||
"//internal/atls",
|
"//internal/atls",
|
||||||
"//internal/attestation/measurements",
|
"//internal/attestation/measurements",
|
||||||
|
|
|
@ -12,18 +12,15 @@ import (
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/iamid"
|
"github.com/edgelesssys/constellation/v2/cli/internal/iamid"
|
||||||
|
"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/gcpshared"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type cloudCreator interface {
|
type cloudCreator interface {
|
||||||
Create(
|
Create(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
provider cloudprovider.Provider,
|
opts cloudcmd.CreateOptions,
|
||||||
config *config.Config,
|
|
||||||
insType string,
|
|
||||||
coordCount, nodeCount int,
|
|
||||||
) (clusterid.File, error)
|
) (clusterid.File, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,15 +28,15 @@ type cloudIAMCreator interface {
|
||||||
Create(
|
Create(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
provider cloudprovider.Provider,
|
provider cloudprovider.Provider,
|
||||||
iamConfig *cloudcmd.IAMConfig,
|
opts *cloudcmd.IAMConfigOptions,
|
||||||
) (iamid.File, error)
|
) (iamid.File, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type iamDestroyer interface {
|
type iamDestroyer interface {
|
||||||
DestroyIAMConfiguration(ctx context.Context) error
|
DestroyIAMConfiguration(ctx context.Context, logLevel terraform.LogLevel) error
|
||||||
GetTfstateServiceAccountKey(ctx context.Context) (gcpshared.ServiceAccountKey, error)
|
GetTfstateServiceAccountKey(ctx context.Context) (gcpshared.ServiceAccountKey, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type cloudTerminator interface {
|
type cloudTerminator interface {
|
||||||
Terminate(context.Context) error
|
Terminate(ctx context.Context, logLevel terraform.LogLevel) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ import (
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/iamid"
|
"github.com/edgelesssys/constellation/v2/cli/internal/iamid"
|
||||||
|
"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/gcpshared"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,13 +34,10 @@ type stubCloudCreator struct {
|
||||||
|
|
||||||
func (c *stubCloudCreator) Create(
|
func (c *stubCloudCreator) Create(
|
||||||
_ context.Context,
|
_ context.Context,
|
||||||
provider cloudprovider.Provider,
|
opts cloudcmd.CreateOptions,
|
||||||
_ *config.Config,
|
|
||||||
_ string,
|
|
||||||
_, _ int,
|
|
||||||
) (clusterid.File, error) {
|
) (clusterid.File, error) {
|
||||||
c.createCalled = true
|
c.createCalled = true
|
||||||
c.id.CloudProvider = provider
|
c.id.CloudProvider = opts.Provider
|
||||||
return c.id, c.createErr
|
return c.id, c.createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +46,7 @@ type stubCloudTerminator struct {
|
||||||
terminateErr error
|
terminateErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubCloudTerminator) Terminate(context.Context) error {
|
func (c *stubCloudTerminator) Terminate(_ context.Context, _ terraform.LogLevel) error {
|
||||||
c.called = true
|
c.called = true
|
||||||
return c.terminateErr
|
return c.terminateErr
|
||||||
}
|
}
|
||||||
|
@ -67,7 +64,7 @@ type stubIAMCreator struct {
|
||||||
func (c *stubIAMCreator) Create(
|
func (c *stubIAMCreator) Create(
|
||||||
_ context.Context,
|
_ context.Context,
|
||||||
provider cloudprovider.Provider,
|
provider cloudprovider.Provider,
|
||||||
_ *cloudcmd.IAMConfig,
|
_ *cloudcmd.IAMConfigOptions,
|
||||||
) (iamid.File, error) {
|
) (iamid.File, error) {
|
||||||
c.createCalled = true
|
c.createCalled = true
|
||||||
c.id.CloudProvider = provider
|
c.id.CloudProvider = provider
|
||||||
|
@ -82,7 +79,7 @@ type stubIAMDestroyer struct {
|
||||||
getTfstateKeyErr error
|
getTfstateKeyErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *stubIAMDestroyer) DestroyIAMConfiguration(_ context.Context) error {
|
func (d *stubIAMDestroyer) DestroyIAMConfiguration(_ context.Context, _ terraform.LogLevel) error {
|
||||||
d.destroyCalled = true
|
d.destroyCalled = true
|
||||||
return d.destroyErr
|
return d.destroyErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,15 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner.Start("Creating", false)
|
spinner.Start("Creating", false)
|
||||||
idFile, err := creator.Create(cmd.Context(), provider, conf, instanceType, flags.controllerCount, flags.workerCount)
|
opts := cloudcmd.CreateOptions{
|
||||||
|
Provider: provider,
|
||||||
|
Config: conf,
|
||||||
|
InsType: instanceType,
|
||||||
|
ControlPlaneCount: flags.controllerCount,
|
||||||
|
WorkerCount: flags.workerCount,
|
||||||
|
TFLogLevel: flags.tfLogLevel,
|
||||||
|
}
|
||||||
|
idFile, err := creator.Create(cmd.Context(), opts)
|
||||||
spinner.Stop()
|
spinner.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return translateCreateErrors(cmd, err)
|
return translateCreateErrors(cmd, err)
|
||||||
|
@ -190,7 +198,7 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
|
||||||
|
|
||||||
yes, err := cmd.Flags().GetBool("yes")
|
yes, err := cmd.Flags().GetBool("yes")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return createFlags{}, fmt.Errorf("%w; Set '-yes' without a value to automatically confirm", err)
|
return createFlags{}, fmt.Errorf("parsing yes bool: %w", err)
|
||||||
}
|
}
|
||||||
c.log.Debugf("Yes flag is %t", yes)
|
c.log.Debugf("Yes flag is %t", yes)
|
||||||
|
|
||||||
|
@ -206,10 +214,21 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
|
||||||
}
|
}
|
||||||
c.log.Debugf("force flag is %t", force)
|
c.log.Debugf("force flag is %t", force)
|
||||||
|
|
||||||
|
logLevelString, err := cmd.Flags().GetString("tf-log")
|
||||||
|
if err != nil {
|
||||||
|
return createFlags{}, fmt.Errorf("parsing tf-log string: %w", err)
|
||||||
|
}
|
||||||
|
logLevel, err := terraform.ParseLogLevel(logLevelString)
|
||||||
|
if err != nil {
|
||||||
|
return createFlags{}, fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err)
|
||||||
|
}
|
||||||
|
c.log.Debugf("Terraform logs will be written into %s at level %s", constants.TerraformLogFile, logLevel.String())
|
||||||
|
|
||||||
return createFlags{
|
return createFlags{
|
||||||
controllerCount: controllerCount,
|
controllerCount: controllerCount,
|
||||||
workerCount: workerCount,
|
workerCount: workerCount,
|
||||||
configPath: configPath,
|
configPath: configPath,
|
||||||
|
tfLogLevel: logLevel,
|
||||||
force: force,
|
force: force,
|
||||||
yes: yes,
|
yes: yes,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -220,6 +239,7 @@ type createFlags struct {
|
||||||
controllerCount int
|
controllerCount int
|
||||||
workerCount int
|
workerCount int
|
||||||
configPath string
|
configPath string
|
||||||
|
tfLogLevel terraform.LogLevel
|
||||||
force bool
|
force bool
|
||||||
yes bool
|
yes bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,6 +185,7 @@ func TestCreate(t *testing.T) {
|
||||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||||
|
cmd.Flags().String("tf-log", "NONE", "") // register persistent flag manually
|
||||||
|
|
||||||
if tc.yesFlag {
|
if tc.yesFlag {
|
||||||
require.NoError(cmd.Flags().Set("yes", "true"))
|
require.NoError(cmd.Flags().Set("yes", "true"))
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/iamid"
|
"github.com/edgelesssys/constellation/v2/cli/internal/iamid"
|
||||||
|
"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/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
|
@ -147,8 +148,18 @@ func createRunIAMFunc(provider cloudprovider.Provider) func(cmd *cobra.Command,
|
||||||
return fmt.Errorf("unknown provider %s", provider)
|
return fmt.Errorf("unknown provider %s", provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(cmd *cobra.Command, args []string) error {
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
iamCreator, err := newIAMCreator(cmd)
|
logLevelString, err := cmd.Flags().GetString("tf-log")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing tf-log string: %w", err)
|
||||||
|
}
|
||||||
|
logLevel, err := terraform.ParseLogLevel(logLevelString)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
iamCreator, err := newIAMCreator(cmd, logLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating iamCreator: %w", err)
|
return fmt.Errorf("creating iamCreator: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -161,7 +172,7 @@ func createRunIAMFunc(provider cloudprovider.Provider) func(cmd *cobra.Command,
|
||||||
}
|
}
|
||||||
|
|
||||||
// newIAMCreator creates a new iamiamCreator.
|
// newIAMCreator creates a new iamiamCreator.
|
||||||
func newIAMCreator(cmd *cobra.Command) (*iamCreator, error) {
|
func newIAMCreator(cmd *cobra.Command, logLevel terraform.LogLevel) (*iamCreator, error) {
|
||||||
spinner, err := newSpinnerOrStderr(cmd)
|
spinner, err := newSpinnerOrStderr(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("creating spinner: %w", err)
|
return nil, fmt.Errorf("creating spinner: %w", err)
|
||||||
|
@ -170,13 +181,17 @@ func newIAMCreator(cmd *cobra.Command) (*iamCreator, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("creating logger: %w", err)
|
return nil, fmt.Errorf("creating logger: %w", err)
|
||||||
}
|
}
|
||||||
|
log.Debugf("Terraform logs will be written into %s at level %s", constants.TerraformLogFile, logLevel.String())
|
||||||
|
|
||||||
return &iamCreator{
|
return &iamCreator{
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
spinner: spinner,
|
spinner: spinner,
|
||||||
log: log,
|
log: log,
|
||||||
creator: cloudcmd.NewIAMCreator(spinner),
|
creator: cloudcmd.NewIAMCreator(spinner),
|
||||||
fileHandler: file.NewHandler(afero.NewOsFs()),
|
fileHandler: file.NewHandler(afero.NewOsFs()),
|
||||||
iamConfig: &cloudcmd.IAMConfig{},
|
iamConfig: &cloudcmd.IAMConfigOptions{
|
||||||
|
TFLogLevel: logLevel,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +203,7 @@ type iamCreator struct {
|
||||||
fileHandler file.Handler
|
fileHandler file.Handler
|
||||||
provider cloudprovider.Provider
|
provider cloudprovider.Provider
|
||||||
providerCreator providerIAMCreator
|
providerCreator providerIAMCreator
|
||||||
iamConfig *cloudcmd.IAMConfig
|
iamConfig *cloudcmd.IAMConfigOptions
|
||||||
log debugLog
|
log debugLog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,7 +376,7 @@ type providerIAMCreator interface {
|
||||||
// writeOutputValuesToConfig writes the output values of the IAM creation to the constellation config file.
|
// writeOutputValuesToConfig writes the output values of the IAM creation to the constellation config file.
|
||||||
writeOutputValuesToConfig(conf *config.Config, flags iamFlags, iamFile iamid.File)
|
writeOutputValuesToConfig(conf *config.Config, flags iamFlags, iamFile iamid.File)
|
||||||
// parseFlagsAndSetupConfig parses the provider-specific flags and fills the values into the IAM config (output values of the command).
|
// parseFlagsAndSetupConfig parses the provider-specific flags and fills the values into the IAM config (output values of the command).
|
||||||
parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfig) (iamFlags, error)
|
parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfigOptions) (iamFlags, error)
|
||||||
// parseAndWriteIDFile parses the GCP service account key and writes it to a keyfile. It is only implemented for GCP.
|
// parseAndWriteIDFile parses the GCP service account key and writes it to a keyfile. It is only implemented for GCP.
|
||||||
parseAndWriteIDFile(iamFile iamid.File, fileHandler file.Handler) error
|
parseAndWriteIDFile(iamFile iamid.File, fileHandler file.Handler) error
|
||||||
}
|
}
|
||||||
|
@ -369,7 +384,7 @@ type providerIAMCreator interface {
|
||||||
// awsIAMCreator implements the providerIAMCreator interface for AWS.
|
// awsIAMCreator implements the providerIAMCreator interface for AWS.
|
||||||
type awsIAMCreator struct{}
|
type awsIAMCreator struct{}
|
||||||
|
|
||||||
func (c *awsIAMCreator) parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfig) (iamFlags, error) {
|
func (c *awsIAMCreator) parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfigOptions) (iamFlags, error) {
|
||||||
prefix, err := cmd.Flags().GetString("prefix")
|
prefix, err := cmd.Flags().GetString("prefix")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iamFlags{}, fmt.Errorf("parsing prefix string: %w", err)
|
return iamFlags{}, fmt.Errorf("parsing prefix string: %w", err)
|
||||||
|
@ -429,7 +444,7 @@ func (c *awsIAMCreator) parseAndWriteIDFile(_ iamid.File, _ file.Handler) error
|
||||||
// azureIAMCreator implements the providerIAMCreator interface for Azure.
|
// azureIAMCreator implements the providerIAMCreator interface for Azure.
|
||||||
type azureIAMCreator struct{}
|
type azureIAMCreator struct{}
|
||||||
|
|
||||||
func (c *azureIAMCreator) parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfig) (iamFlags, error) {
|
func (c *azureIAMCreator) parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfigOptions) (iamFlags, error) {
|
||||||
region, err := cmd.Flags().GetString("region")
|
region, err := cmd.Flags().GetString("region")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iamFlags{}, fmt.Errorf("parsing region string: %w", err)
|
return iamFlags{}, fmt.Errorf("parsing region string: %w", err)
|
||||||
|
@ -494,7 +509,7 @@ func (c *azureIAMCreator) parseAndWriteIDFile(_ iamid.File, _ file.Handler) erro
|
||||||
// gcpIAMCreator implements the providerIAMCreator interface for GCP.
|
// gcpIAMCreator implements the providerIAMCreator interface for GCP.
|
||||||
type gcpIAMCreator struct{}
|
type gcpIAMCreator struct{}
|
||||||
|
|
||||||
func (c *gcpIAMCreator) parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfig) (iamFlags, error) {
|
func (c *gcpIAMCreator) parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfigOptions) (iamFlags, error) {
|
||||||
zone, err := cmd.Flags().GetString("zone")
|
zone, err := cmd.Flags().GetString("zone")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return iamFlags{}, fmt.Errorf("parsing zone string: %w", err)
|
return iamFlags{}, fmt.Errorf("parsing zone string: %w", err)
|
||||||
|
|
|
@ -278,6 +278,7 @@ func TestIAMCreateAWS(t *testing.T) {
|
||||||
cmd.Flags().String("kubernetes", semver.MajorMinor(config.Default().KubernetesVersion), "")
|
cmd.Flags().String("kubernetes", semver.MajorMinor(config.Default().KubernetesVersion), "")
|
||||||
cmd.Flags().Bool("yes", false, "")
|
cmd.Flags().Bool("yes", false, "")
|
||||||
cmd.Flags().String("name", "constell", "")
|
cmd.Flags().String("name", "constell", "")
|
||||||
|
cmd.Flags().String("tf-log", "NONE", "")
|
||||||
|
|
||||||
if tc.zoneFlag != "" {
|
if tc.zoneFlag != "" {
|
||||||
require.NoError(cmd.Flags().Set("zone", tc.zoneFlag))
|
require.NoError(cmd.Flags().Set("zone", tc.zoneFlag))
|
||||||
|
@ -306,7 +307,7 @@ func TestIAMCreateAWS(t *testing.T) {
|
||||||
spinner: &nopSpinner{},
|
spinner: &nopSpinner{},
|
||||||
creator: tc.creator,
|
creator: tc.creator,
|
||||||
fileHandler: fileHandler,
|
fileHandler: fileHandler,
|
||||||
iamConfig: &cloudcmd.IAMConfig{},
|
iamConfig: &cloudcmd.IAMConfigOptions{},
|
||||||
provider: tc.provider,
|
provider: tc.provider,
|
||||||
providerCreator: &awsIAMCreator{},
|
providerCreator: &awsIAMCreator{},
|
||||||
}
|
}
|
||||||
|
@ -550,12 +551,13 @@ func TestIAMCreateAzure(t *testing.T) {
|
||||||
cmd.SetErr(&bytes.Buffer{})
|
cmd.SetErr(&bytes.Buffer{})
|
||||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||||
|
|
||||||
// register persistent flag manually
|
// register persistent flags manually
|
||||||
cmd.Flags().String("config", constants.ConfigFilename, "")
|
cmd.Flags().String("config", constants.ConfigFilename, "")
|
||||||
cmd.Flags().Bool("generate-config", false, "")
|
cmd.Flags().Bool("generate-config", false, "")
|
||||||
cmd.Flags().String("kubernetes", semver.MajorMinor(config.Default().KubernetesVersion), "")
|
cmd.Flags().String("kubernetes", semver.MajorMinor(config.Default().KubernetesVersion), "")
|
||||||
cmd.Flags().Bool("yes", false, "")
|
cmd.Flags().Bool("yes", false, "")
|
||||||
cmd.Flags().String("name", "constell", "")
|
cmd.Flags().String("name", "constell", "")
|
||||||
|
cmd.Flags().String("tf-log", "NONE", "")
|
||||||
|
|
||||||
if tc.regionFlag != "" {
|
if tc.regionFlag != "" {
|
||||||
require.NoError(cmd.Flags().Set("region", tc.regionFlag))
|
require.NoError(cmd.Flags().Set("region", tc.regionFlag))
|
||||||
|
@ -587,7 +589,7 @@ func TestIAMCreateAzure(t *testing.T) {
|
||||||
spinner: &nopSpinner{},
|
spinner: &nopSpinner{},
|
||||||
creator: tc.creator,
|
creator: tc.creator,
|
||||||
fileHandler: fileHandler,
|
fileHandler: fileHandler,
|
||||||
iamConfig: &cloudcmd.IAMConfig{},
|
iamConfig: &cloudcmd.IAMConfigOptions{},
|
||||||
provider: tc.provider,
|
provider: tc.provider,
|
||||||
providerCreator: &azureIAMCreator{},
|
providerCreator: &azureIAMCreator{},
|
||||||
}
|
}
|
||||||
|
@ -862,6 +864,7 @@ func TestIAMCreateGCP(t *testing.T) {
|
||||||
cmd.Flags().String("kubernetes", semver.MajorMinor(config.Default().KubernetesVersion), "")
|
cmd.Flags().String("kubernetes", semver.MajorMinor(config.Default().KubernetesVersion), "")
|
||||||
cmd.Flags().Bool("yes", false, "")
|
cmd.Flags().Bool("yes", false, "")
|
||||||
cmd.Flags().String("name", "constell", "")
|
cmd.Flags().String("name", "constell", "")
|
||||||
|
cmd.Flags().String("tf-log", "NONE", "")
|
||||||
|
|
||||||
if tc.zoneFlag != "" {
|
if tc.zoneFlag != "" {
|
||||||
require.NoError(cmd.Flags().Set("zone", tc.zoneFlag))
|
require.NoError(cmd.Flags().Set("zone", tc.zoneFlag))
|
||||||
|
@ -893,7 +896,7 @@ func TestIAMCreateGCP(t *testing.T) {
|
||||||
spinner: &nopSpinner{},
|
spinner: &nopSpinner{},
|
||||||
creator: tc.creator,
|
creator: tc.creator,
|
||||||
fileHandler: fileHandler,
|
fileHandler: fileHandler,
|
||||||
iamConfig: &cloudcmd.IAMConfig{},
|
iamConfig: &cloudcmd.IAMConfigOptions{},
|
||||||
provider: tc.provider,
|
provider: tc.provider,
|
||||||
providerCreator: &gcpIAMCreator{},
|
providerCreator: &gcpIAMCreator{},
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
|
@ -56,9 +57,14 @@ type destroyCmd struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destroyer iamDestroyer, fsHandler file.Handler) error {
|
func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destroyer iamDestroyer, fsHandler file.Handler) error {
|
||||||
|
flags, err := c.parseDestroyFlags(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing flags: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// check if there is a possibility that the cluster is still running by looking out for specific files
|
// check if there is a possibility that the cluster is still running by looking out for specific files
|
||||||
c.log.Debugf("Checking if %q exists", constants.AdminConfFilename)
|
c.log.Debugf("Checking if %q exists", constants.AdminConfFilename)
|
||||||
_, err := fsHandler.Stat(constants.AdminConfFilename)
|
_, err = fsHandler.Stat(constants.AdminConfFilename)
|
||||||
if !errors.Is(err, os.ErrNotExist) {
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
return fmt.Errorf("file %q still exists, please make sure to terminate your cluster before destroying your IAM configuration", constants.AdminConfFilename)
|
return fmt.Errorf("file %q still exists, please make sure to terminate your cluster before destroying your IAM configuration", constants.AdminConfFilename)
|
||||||
}
|
}
|
||||||
|
@ -68,12 +74,6 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr
|
||||||
return fmt.Errorf("file %q still exists, please make sure to terminate your cluster before destroying your IAM configuration", constants.ClusterIDsFileName)
|
return fmt.Errorf("file %q still exists, please make sure to terminate your cluster before destroying your IAM configuration", constants.ClusterIDsFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
yes, err := cmd.Flags().GetBool("yes")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.log.Debugf("\"yes\" flag is set to %t", yes)
|
|
||||||
|
|
||||||
gcpFileExists := false
|
gcpFileExists := false
|
||||||
|
|
||||||
c.log.Debugf("Checking if %q exists", constants.GCPServiceAccountKeyFile)
|
c.log.Debugf("Checking if %q exists", constants.GCPServiceAccountKeyFile)
|
||||||
|
@ -87,7 +87,7 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr
|
||||||
gcpFileExists = true
|
gcpFileExists = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !yes {
|
if !flags.yes {
|
||||||
// Confirmation
|
// Confirmation
|
||||||
confirmString := "Do you really want to destroy your IAM configuration?"
|
confirmString := "Do you really want to destroy your IAM configuration?"
|
||||||
if gcpFileExists {
|
if gcpFileExists {
|
||||||
|
@ -119,7 +119,7 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr
|
||||||
|
|
||||||
spinner.Start("Destroying IAM configuration", false)
|
spinner.Start("Destroying IAM configuration", false)
|
||||||
defer spinner.Stop()
|
defer spinner.Stop()
|
||||||
if err := destroyer.DestroyIAMConfiguration(cmd.Context()); err != nil {
|
if err := destroyer.DestroyIAMConfiguration(cmd.Context(), flags.tfLogLevel); err != nil {
|
||||||
return fmt.Errorf("destroying IAM configuration: %w", err)
|
return fmt.Errorf("destroying IAM configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,3 +155,32 @@ func (c *destroyCmd) deleteGCPServiceAccountKeyFile(cmd *cobra.Command, destroye
|
||||||
c.log.Debugf("Successfully deleted %q", constants.GCPServiceAccountKeyFile)
|
c.log.Debugf("Successfully deleted %q", constants.GCPServiceAccountKeyFile)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type destroyFlags struct {
|
||||||
|
yes bool
|
||||||
|
tfLogLevel terraform.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDestroyFlags parses the flags of the create command.
|
||||||
|
func (c *destroyCmd) parseDestroyFlags(cmd *cobra.Command) (destroyFlags, error) {
|
||||||
|
yes, err := cmd.Flags().GetBool("yes")
|
||||||
|
if err != nil {
|
||||||
|
return destroyFlags{}, fmt.Errorf("parsing yes bool: %w", err)
|
||||||
|
}
|
||||||
|
c.log.Debugf("Yes flag is %t", yes)
|
||||||
|
|
||||||
|
logLevelString, err := cmd.Flags().GetString("tf-log")
|
||||||
|
if err != nil {
|
||||||
|
return destroyFlags{}, fmt.Errorf("parsing tf-log string: %w", err)
|
||||||
|
}
|
||||||
|
logLevel, err := terraform.ParseLogLevel(logLevelString)
|
||||||
|
if err != nil {
|
||||||
|
return destroyFlags{}, fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err)
|
||||||
|
}
|
||||||
|
c.log.Debugf("Terraform logs will be written into %s at level %s", constants.TerraformLogFile, logLevel.String())
|
||||||
|
|
||||||
|
return destroyFlags{
|
||||||
|
tfLogLevel: logLevel,
|
||||||
|
yes: yes,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -105,6 +105,10 @@ func TestIAMDestroy(t *testing.T) {
|
||||||
cmd.SetOut(&bytes.Buffer{})
|
cmd.SetOut(&bytes.Buffer{})
|
||||||
cmd.SetErr(&bytes.Buffer{})
|
cmd.SetErr(&bytes.Buffer{})
|
||||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||||
|
|
||||||
|
// register persistent flags manually
|
||||||
|
cmd.Flags().String("tf-log", "NONE", "")
|
||||||
|
|
||||||
assert.NoError(cmd.Flags().Set("yes", tc.yesFlag))
|
assert.NoError(cmd.Flags().Set("yes", tc.yesFlag))
|
||||||
|
|
||||||
c := &destroyCmd{log: logger.NewTest(t)}
|
c := &destroyCmd{log: logger.NewTest(t)}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
|
@ -73,17 +74,22 @@ func (m *miniUpCmd) up(cmd *cobra.Command, creator cloudCreator, spinner spinner
|
||||||
return fmt.Errorf("system requirements not met: %w", err)
|
return fmt.Errorf("system requirements not met: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flags, err := m.parseUpFlags(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing flags: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
fileHandler := file.NewHandler(afero.NewOsFs())
|
fileHandler := file.NewHandler(afero.NewOsFs())
|
||||||
|
|
||||||
// create config if not passed as flag and set default values
|
// create config if not passed as flag and set default values
|
||||||
config, err := m.prepareConfig(cmd, fileHandler)
|
config, err := m.prepareConfig(cmd, fileHandler, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("preparing config: %w", err)
|
return fmt.Errorf("preparing config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create cluster
|
// create cluster
|
||||||
spinner.Start("Creating cluster in QEMU ", false)
|
spinner.Start("Creating cluster in QEMU ", false)
|
||||||
err = m.createMiniCluster(cmd.Context(), fileHandler, creator, config)
|
err = m.createMiniCluster(cmd.Context(), fileHandler, creator, config, flags.tfLogLevel)
|
||||||
spinner.Stop()
|
spinner.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating cluster: %w", err)
|
return fmt.Errorf("creating cluster: %w", err)
|
||||||
|
@ -173,20 +179,10 @@ func (m *miniUpCmd) checkSystemRequirements(out io.Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareConfig reads a given config, or creates a new minimal QEMU config.
|
// prepareConfig reads a given config, or creates a new minimal QEMU config.
|
||||||
func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config, error) {
|
func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler, flags upFlags) (*config.Config, error) {
|
||||||
m.log.Debugf("Preparing configuration")
|
|
||||||
configPath, err := cmd.Flags().GetString("config")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
force, err := cmd.Flags().GetBool("force")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parsing force argument: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for existing config
|
// check for existing config
|
||||||
if configPath != "" {
|
if flags.configPath != "" {
|
||||||
conf, err := config.New(fileHandler, configPath, force)
|
conf, err := config.New(fileHandler, flags.configPath, flags.force)
|
||||||
var configValidationErr *config.ValidationError
|
var configValidationErr *config.ValidationError
|
||||||
if errors.As(err, &configValidationErr) {
|
if errors.As(err, &configValidationErr) {
|
||||||
cmd.PrintErrln(configValidationErr.LongMessage())
|
cmd.PrintErrln(configValidationErr.LongMessage())
|
||||||
|
@ -199,11 +195,11 @@ func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler)
|
||||||
}
|
}
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
m.log.Debugf("Configuration path is %q", configPath)
|
m.log.Debugf("Configuration path is %q", flags.configPath)
|
||||||
if err := cmd.Flags().Set("config", constants.ConfigFilename); err != nil {
|
if err := cmd.Flags().Set("config", constants.ConfigFilename); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = fileHandler.Stat(constants.ConfigFilename)
|
_, err := fileHandler.Stat(constants.ConfigFilename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// config already exists, prompt user to overwrite
|
// config already exists, prompt user to overwrite
|
||||||
cmd.PrintErrln("A config file already exists in the current workspace. Use --config to use an existing config file.")
|
cmd.PrintErrln("A config file already exists in the current workspace. Use --config to use an existing config file.")
|
||||||
|
@ -227,9 +223,17 @@ func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createMiniCluster creates a new cluster using the given config.
|
// createMiniCluster creates a new cluster using the given config.
|
||||||
func (m *miniUpCmd) createMiniCluster(ctx context.Context, fileHandler file.Handler, creator cloudCreator, config *config.Config) error {
|
func (m *miniUpCmd) createMiniCluster(ctx context.Context, fileHandler file.Handler, creator cloudCreator, config *config.Config, tfLogLevel terraform.LogLevel) error {
|
||||||
m.log.Debugf("Creating mini cluster")
|
m.log.Debugf("Creating mini cluster")
|
||||||
idFile, err := creator.Create(ctx, cloudprovider.QEMU, config, "", 1, 1)
|
opts := cloudcmd.CreateOptions{
|
||||||
|
Provider: cloudprovider.QEMU,
|
||||||
|
Config: config,
|
||||||
|
InsType: "",
|
||||||
|
ControlPlaneCount: 1,
|
||||||
|
WorkerCount: 1,
|
||||||
|
TFLogLevel: tfLogLevel,
|
||||||
|
}
|
||||||
|
idFile, err := creator.Create(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -271,3 +275,39 @@ func (m *miniUpCmd) initializeMiniCluster(cmd *cobra.Command, fileHandler file.H
|
||||||
m.log.Debugf("Initialized mini cluster")
|
m.log.Debugf("Initialized mini cluster")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type upFlags struct {
|
||||||
|
configPath string
|
||||||
|
force bool
|
||||||
|
tfLogLevel terraform.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *miniUpCmd) parseUpFlags(cmd *cobra.Command) (upFlags, error) {
|
||||||
|
m.log.Debugf("Preparing configuration")
|
||||||
|
configPath, err := cmd.Flags().GetString("config")
|
||||||
|
if err != nil {
|
||||||
|
return upFlags{}, fmt.Errorf("parsing config string: %w", err)
|
||||||
|
}
|
||||||
|
m.log.Debugf("Configuration path is %q", configPath)
|
||||||
|
force, err := cmd.Flags().GetBool("force")
|
||||||
|
if err != nil {
|
||||||
|
return upFlags{}, fmt.Errorf("parsing force bool: %w", err)
|
||||||
|
}
|
||||||
|
m.log.Debugf("force flag is %q", configPath)
|
||||||
|
|
||||||
|
logLevelString, err := cmd.Flags().GetString("tf-log")
|
||||||
|
if err != nil {
|
||||||
|
return upFlags{}, fmt.Errorf("parsing tf-log string: %w", err)
|
||||||
|
}
|
||||||
|
logLevel, err := terraform.ParseLogLevel(logLevelString)
|
||||||
|
if err != nil {
|
||||||
|
return upFlags{}, fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err)
|
||||||
|
}
|
||||||
|
m.log.Debugf("Terraform logs will be written into %s at level %s", constants.TerraformLogFile, logLevel.String())
|
||||||
|
|
||||||
|
return upFlags{
|
||||||
|
configPath: configPath,
|
||||||
|
force: force,
|
||||||
|
tfLogLevel: logLevel,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
)
|
)
|
||||||
|
@ -48,12 +49,12 @@ func runTerminate(cmd *cobra.Command, _ []string) error {
|
||||||
|
|
||||||
func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.Handler, spinner spinnerInterf,
|
func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.Handler, spinner spinnerInterf,
|
||||||
) error {
|
) error {
|
||||||
yesFlag, err := cmd.Flags().GetBool("yes")
|
flags, err := parseTerminateFlags(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("parsing flags: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !yesFlag {
|
if !flags.yes {
|
||||||
cmd.Println("You are about to terminate a Constellation cluster.")
|
cmd.Println("You are about to terminate a Constellation cluster.")
|
||||||
cmd.Println("All of its associated resources will be DESTROYED.")
|
cmd.Println("All of its associated resources will be DESTROYED.")
|
||||||
cmd.Println("This action is irreversible and ALL DATA WILL BE LOST.")
|
cmd.Println("This action is irreversible and ALL DATA WILL BE LOST.")
|
||||||
|
@ -68,7 +69,7 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner.Start("Terminating", false)
|
spinner.Start("Terminating", false)
|
||||||
err = terminator.Terminate(cmd.Context())
|
err = terminator.Terminate(cmd.Context(), flags.logLevel)
|
||||||
spinner.Stop()
|
spinner.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("terminating Constellation cluster: %w", err)
|
return fmt.Errorf("terminating Constellation cluster: %w", err)
|
||||||
|
@ -87,3 +88,28 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.
|
||||||
|
|
||||||
return removeErr
|
return removeErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type terminateFlags struct {
|
||||||
|
yes bool
|
||||||
|
logLevel terraform.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTerminateFlags(cmd *cobra.Command) (terminateFlags, error) {
|
||||||
|
yes, err := cmd.Flags().GetBool("yes")
|
||||||
|
if err != nil {
|
||||||
|
return terminateFlags{}, fmt.Errorf("parsing yes bool: %w", err)
|
||||||
|
}
|
||||||
|
logLevelString, err := cmd.Flags().GetString("tf-log")
|
||||||
|
if err != nil {
|
||||||
|
return terminateFlags{}, fmt.Errorf("parsing tf-log string: %w", err)
|
||||||
|
}
|
||||||
|
logLevel, err := terraform.ParseLogLevel(logLevelString)
|
||||||
|
if err != nil {
|
||||||
|
return terminateFlags{}, fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return terminateFlags{
|
||||||
|
yes: yes,
|
||||||
|
logLevel: logLevel,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -135,6 +135,9 @@ func TestTerminate(t *testing.T) {
|
||||||
cmd.SetErr(&bytes.Buffer{})
|
cmd.SetErr(&bytes.Buffer{})
|
||||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||||
|
|
||||||
|
// register persistent flags manually
|
||||||
|
cmd.Flags().String("tf-log", "NONE", "")
|
||||||
|
|
||||||
require.NotNil(tc.setupFs)
|
require.NotNil(tc.setupFs)
|
||||||
fileHandler := file.NewHandler(tc.setupFs(require, tc.idFile))
|
fileHandler := file.NewHandler(tc.setupFs(require, tc.idFile))
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ go_library(
|
||||||
name = "terraform",
|
name = "terraform",
|
||||||
srcs = [
|
srcs = [
|
||||||
"loader.go",
|
"loader.go",
|
||||||
|
"logging.go",
|
||||||
"terraform.go",
|
"terraform.go",
|
||||||
"variables.go",
|
"variables.go",
|
||||||
],
|
],
|
||||||
|
@ -73,6 +74,7 @@ go_library(
|
||||||
visibility = ["//cli:__subpackages__"],
|
visibility = ["//cli:__subpackages__"],
|
||||||
deps = [
|
deps = [
|
||||||
"//internal/cloud/cloudprovider",
|
"//internal/cloud/cloudprovider",
|
||||||
|
"//internal/constants",
|
||||||
"//internal/file",
|
"//internal/file",
|
||||||
"@com_github_hashicorp_go_version//:go-version",
|
"@com_github_hashicorp_go_version//:go-version",
|
||||||
"@com_github_hashicorp_hc_install//:hc-install",
|
"@com_github_hashicorp_hc_install//:hc-install",
|
||||||
|
|
75
cli/internal/terraform/logging.go
Normal file
75
cli/internal/terraform/logging.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LogLevelNone represents a log level that does not produce any output.
|
||||||
|
LogLevelNone LogLevel = iota
|
||||||
|
// LogLevelError enables log output at ERROR level.
|
||||||
|
LogLevelError
|
||||||
|
// LogLevelWarn enables log output at WARN level.
|
||||||
|
LogLevelWarn
|
||||||
|
// LogLevelInfo enables log output at INFO level.
|
||||||
|
LogLevelInfo
|
||||||
|
// LogLevelDebug enables log output at DEBUG level.
|
||||||
|
LogLevelDebug
|
||||||
|
// LogLevelTrace enables log output at TRACE level.
|
||||||
|
LogLevelTrace
|
||||||
|
// LogLevelJSON enables log output at TRACE level in JSON format.
|
||||||
|
LogLevelJSON
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogLevel is a Terraform log level.
|
||||||
|
// As per https://developer.hashicorp.com/terraform/internals/debugging
|
||||||
|
type LogLevel int
|
||||||
|
|
||||||
|
// ParseLogLevel parses a log level string into a Terraform log level.
|
||||||
|
func ParseLogLevel(level string) (LogLevel, error) {
|
||||||
|
switch strings.ToUpper(level) {
|
||||||
|
case "NONE":
|
||||||
|
return LogLevelNone, nil
|
||||||
|
case "ERROR":
|
||||||
|
return LogLevelError, nil
|
||||||
|
case "WARN":
|
||||||
|
return LogLevelWarn, nil
|
||||||
|
case "INFO":
|
||||||
|
return LogLevelInfo, nil
|
||||||
|
case "DEBUG":
|
||||||
|
return LogLevelDebug, nil
|
||||||
|
case "TRACE":
|
||||||
|
return LogLevelTrace, nil
|
||||||
|
case "JSON":
|
||||||
|
return LogLevelJSON, nil
|
||||||
|
default:
|
||||||
|
return LogLevelNone, fmt.Errorf("invalid log level %s", level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of a Terraform log level.
|
||||||
|
func (l LogLevel) String() string {
|
||||||
|
switch l {
|
||||||
|
case LogLevelError:
|
||||||
|
return "ERROR"
|
||||||
|
case LogLevelWarn:
|
||||||
|
return "WARN"
|
||||||
|
case LogLevelInfo:
|
||||||
|
return "INFO"
|
||||||
|
case LogLevelDebug:
|
||||||
|
return "DEBUG"
|
||||||
|
case LogLevelTrace:
|
||||||
|
return "TRACE"
|
||||||
|
case LogLevelJSON:
|
||||||
|
return "JSON"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,9 +17,11 @@ package terraform
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/hashicorp/go-version"
|
"github.com/hashicorp/go-version"
|
||||||
install "github.com/hashicorp/hc-install"
|
install "github.com/hashicorp/hc-install"
|
||||||
|
@ -83,18 +85,22 @@ func (c *Client) PrepareWorkspace(path string, vars Variables) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCluster creates a Constellation cluster using Terraform.
|
// CreateCluster creates a Constellation cluster using Terraform.
|
||||||
func (c *Client) CreateCluster(ctx context.Context) (CreateOutput, error) {
|
func (c *Client) CreateCluster(ctx context.Context, logLevel LogLevel) (CreateOutput, error) {
|
||||||
|
if err := c.setLogLevel(logLevel); err != nil {
|
||||||
|
return CreateOutput{}, fmt.Errorf("set terraform log level %s: %w", logLevel.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.tf.Init(ctx); err != nil {
|
if err := c.tf.Init(ctx); err != nil {
|
||||||
return CreateOutput{}, err
|
return CreateOutput{}, fmt.Errorf("terraform init: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.tf.Apply(ctx); err != nil {
|
if err := c.tf.Apply(ctx); err != nil {
|
||||||
return CreateOutput{}, err
|
return CreateOutput{}, fmt.Errorf("terraform apply: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tfState, err := c.tf.Show(ctx)
|
tfState, err := c.tf.Show(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CreateOutput{}, err
|
return CreateOutput{}, fmt.Errorf("terraform show: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ipOutput, ok := tfState.Values.Outputs["ip"]
|
ipOutput, ok := tfState.Values.Outputs["ip"]
|
||||||
|
@ -177,7 +183,11 @@ type AWSIAMOutput struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateIAMConfig creates an IAM configuration using Terraform.
|
// CreateIAMConfig creates an IAM configuration using Terraform.
|
||||||
func (c *Client) CreateIAMConfig(ctx context.Context, provider cloudprovider.Provider) (IAMOutput, error) {
|
func (c *Client) CreateIAMConfig(ctx context.Context, provider cloudprovider.Provider, logLevel LogLevel) (IAMOutput, error) {
|
||||||
|
if err := c.setLogLevel(logLevel); err != nil {
|
||||||
|
return IAMOutput{}, fmt.Errorf("set terraform log level %s: %w", logLevel.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.tf.Init(ctx); err != nil {
|
if err := c.tf.Init(ctx); err != nil {
|
||||||
return IAMOutput{}, err
|
return IAMOutput{}, err
|
||||||
}
|
}
|
||||||
|
@ -285,9 +295,13 @@ func (c *Client) CreateIAMConfig(ctx context.Context, provider cloudprovider.Pro
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy destroys Terraform-created cloud resources.
|
// Destroy destroys Terraform-created cloud resources.
|
||||||
func (c *Client) Destroy(ctx context.Context) error {
|
func (c *Client) Destroy(ctx context.Context, logLevel LogLevel) error {
|
||||||
|
if err := c.setLogLevel(logLevel); err != nil {
|
||||||
|
return fmt.Errorf("set terraform log level %s: %w", logLevel.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.tf.Init(ctx); err != nil {
|
if err := c.tf.Init(ctx); err != nil {
|
||||||
return err
|
return fmt.Errorf("terraform init: %w", err)
|
||||||
}
|
}
|
||||||
return c.tf.Destroy(ctx)
|
return c.tf.Destroy(ctx)
|
||||||
}
|
}
|
||||||
|
@ -354,9 +368,24 @@ func (c *Client) writeVars(vars Variables) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setLogLevel sets the log level for Terraform.
|
||||||
|
func (c *Client) setLogLevel(logLevel LogLevel) error {
|
||||||
|
if logLevel.String() != "" {
|
||||||
|
if err := c.tf.SetLog(logLevel.String()); err != nil {
|
||||||
|
return fmt.Errorf("set log level %s: %w", logLevel.String(), err)
|
||||||
|
}
|
||||||
|
if err := c.tf.SetLogPath(filepath.Join("..", constants.TerraformLogFile)); err != nil {
|
||||||
|
return fmt.Errorf("set log path: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type tfInterface interface {
|
type tfInterface interface {
|
||||||
Apply(context.Context, ...tfexec.ApplyOption) error
|
Apply(context.Context, ...tfexec.ApplyOption) error
|
||||||
Destroy(context.Context, ...tfexec.DestroyOption) error
|
Destroy(context.Context, ...tfexec.DestroyOption) error
|
||||||
Init(context.Context, ...tfexec.InitOption) error
|
Init(context.Context, ...tfexec.InitOption) error
|
||||||
Show(context.Context, ...tfexec.ShowOption) (*tfjson.State, error)
|
Show(context.Context, ...tfexec.ShowOption) (*tfjson.State, error)
|
||||||
|
SetLog(level string) error
|
||||||
|
SetLogPath(path string) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,6 +299,22 @@ func TestCreateCluster(t *testing.T) {
|
||||||
fs: afero.NewMemMapFs(),
|
fs: afero.NewMemMapFs(),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
"set log fails": {
|
||||||
|
pathBase: "terraform",
|
||||||
|
provider: cloudprovider.QEMU,
|
||||||
|
vars: qemuVars,
|
||||||
|
tf: &stubTerraform{setLogErr: someErr},
|
||||||
|
fs: afero.NewMemMapFs(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"set log path fails": {
|
||||||
|
pathBase: "terraform",
|
||||||
|
provider: cloudprovider.QEMU,
|
||||||
|
vars: qemuVars,
|
||||||
|
tf: &stubTerraform{setLogPathErr: someErr},
|
||||||
|
fs: afero.NewMemMapFs(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
"no ip": {
|
"no ip": {
|
||||||
pathBase: "terraform",
|
pathBase: "terraform",
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
|
@ -406,7 +422,7 @@ func TestCreateCluster(t *testing.T) {
|
||||||
|
|
||||||
path := path.Join(tc.pathBase, strings.ToLower(tc.provider.String()))
|
path := path.Join(tc.pathBase, strings.ToLower(tc.provider.String()))
|
||||||
require.NoError(c.PrepareWorkspace(path, tc.vars))
|
require.NoError(c.PrepareWorkspace(path, tc.vars))
|
||||||
tfOutput, err := c.CreateCluster(context.Background())
|
tfOutput, err := c.CreateCluster(context.Background(), LogLevelDebug)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
@ -481,6 +497,22 @@ func TestCreateIAM(t *testing.T) {
|
||||||
wantErr bool
|
wantErr bool
|
||||||
want IAMOutput
|
want IAMOutput
|
||||||
}{
|
}{
|
||||||
|
"set log fails": {
|
||||||
|
pathBase: path.Join("terraform", "iam"),
|
||||||
|
provider: cloudprovider.GCP,
|
||||||
|
vars: gcpVars,
|
||||||
|
tf: &stubTerraform{setLogErr: someErr},
|
||||||
|
fs: afero.NewMemMapFs(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"set log path fails": {
|
||||||
|
pathBase: path.Join("terraform", "iam"),
|
||||||
|
provider: cloudprovider.GCP,
|
||||||
|
vars: gcpVars,
|
||||||
|
tf: &stubTerraform{setLogPathErr: someErr},
|
||||||
|
fs: afero.NewMemMapFs(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
"gcp works": {
|
"gcp works": {
|
||||||
pathBase: path.Join("terraform", "iam"),
|
pathBase: path.Join("terraform", "iam"),
|
||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
|
@ -685,7 +717,7 @@ func TestCreateIAM(t *testing.T) {
|
||||||
|
|
||||||
path := path.Join(tc.pathBase, strings.ToLower(tc.provider.String()))
|
path := path.Join(tc.pathBase, strings.ToLower(tc.provider.String()))
|
||||||
require.NoError(c.PrepareWorkspace(path, tc.vars))
|
require.NoError(c.PrepareWorkspace(path, tc.vars))
|
||||||
IAMoutput, err := c.CreateIAMConfig(context.Background(), tc.provider)
|
IAMoutput, err := c.CreateIAMConfig(context.Background(), tc.provider, LogLevelDebug)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
@ -698,6 +730,7 @@ func TestCreateIAM(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDestroyInstances(t *testing.T) {
|
func TestDestroyInstances(t *testing.T) {
|
||||||
|
someErr := errors.New("some error")
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
tf *stubTerraform
|
tf *stubTerraform
|
||||||
wantErr bool
|
wantErr bool
|
||||||
|
@ -706,9 +739,15 @@ func TestDestroyInstances(t *testing.T) {
|
||||||
tf: &stubTerraform{},
|
tf: &stubTerraform{},
|
||||||
},
|
},
|
||||||
"destroy fails": {
|
"destroy fails": {
|
||||||
tf: &stubTerraform{
|
tf: &stubTerraform{destroyErr: someErr},
|
||||||
destroyErr: errors.New("error"),
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
"setLog fails": {
|
||||||
|
tf: &stubTerraform{setLogErr: someErr},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"setLogPath fails": {
|
||||||
|
tf: &stubTerraform{setLogPathErr: someErr},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -721,7 +760,7 @@ func TestDestroyInstances(t *testing.T) {
|
||||||
tf: tc.tf,
|
tf: tc.tf,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.Destroy(context.Background())
|
err := c.Destroy(context.Background(), LogLevelDebug)
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -788,12 +827,121 @@ func TestCleanupWorkspace(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseLogLevel(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
level string
|
||||||
|
want LogLevel
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"json": {
|
||||||
|
level: "json",
|
||||||
|
want: LogLevelJSON,
|
||||||
|
},
|
||||||
|
"trace": {
|
||||||
|
level: "trace",
|
||||||
|
want: LogLevelTrace,
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
level: "debug",
|
||||||
|
want: LogLevelDebug,
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
level: "info",
|
||||||
|
want: LogLevelInfo,
|
||||||
|
},
|
||||||
|
"warn": {
|
||||||
|
level: "warn",
|
||||||
|
want: LogLevelWarn,
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
level: "error",
|
||||||
|
want: LogLevelError,
|
||||||
|
},
|
||||||
|
"none": {
|
||||||
|
level: "none",
|
||||||
|
want: LogLevelNone,
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
level: "unknown",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"empty": {
|
||||||
|
level: "",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
level, err := ParseLogLevel(tc.level)
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(tc.want, level)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogLevelString(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
level LogLevel
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
"json": {
|
||||||
|
level: LogLevelJSON,
|
||||||
|
want: "JSON",
|
||||||
|
},
|
||||||
|
"trace": {
|
||||||
|
level: LogLevelTrace,
|
||||||
|
want: "TRACE",
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
level: LogLevelDebug,
|
||||||
|
want: "DEBUG",
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
level: LogLevelInfo,
|
||||||
|
want: "INFO",
|
||||||
|
},
|
||||||
|
"warn": {
|
||||||
|
level: LogLevelWarn,
|
||||||
|
want: "WARN",
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
level: LogLevelError,
|
||||||
|
want: "ERROR",
|
||||||
|
},
|
||||||
|
"none": {
|
||||||
|
level: LogLevelNone,
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
"invalid int": {
|
||||||
|
level: LogLevel(-1),
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
assert.Equal(tc.want, tc.level.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type stubTerraform struct {
|
type stubTerraform struct {
|
||||||
applyErr error
|
applyErr error
|
||||||
destroyErr error
|
destroyErr error
|
||||||
initErr error
|
initErr error
|
||||||
showErr error
|
showErr error
|
||||||
showState *tfjson.State
|
setLogErr error
|
||||||
|
setLogPathErr error
|
||||||
|
showState *tfjson.State
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubTerraform) Apply(context.Context, ...tfexec.ApplyOption) error {
|
func (s *stubTerraform) Apply(context.Context, ...tfexec.ApplyOption) error {
|
||||||
|
@ -811,3 +959,11 @@ func (s *stubTerraform) Init(context.Context, ...tfexec.InitOption) error {
|
||||||
func (s *stubTerraform) Show(context.Context, ...tfexec.ShowOption) (*tfjson.State, error) {
|
func (s *stubTerraform) Show(context.Context, ...tfexec.ShowOption) (*tfjson.State, error) {
|
||||||
return s.showState, s.showErr
|
return s.showState, s.showErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stubTerraform) SetLog(_ string) error {
|
||||||
|
return s.setLogErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stubTerraform) SetLogPath(_ string) error {
|
||||||
|
return s.setLogPathErr
|
||||||
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ Work with the Constellation configuration file.
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation config generate
|
## constellation config generate
|
||||||
|
@ -84,6 +85,7 @@ constellation config generate {aws|azure|gcp|openstack|qemu} [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation config fetch-measurements
|
## constellation config fetch-measurements
|
||||||
|
@ -114,6 +116,7 @@ constellation config fetch-measurements [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation config instance-types
|
## constellation config instance-types
|
||||||
|
@ -140,6 +143,7 @@ constellation config instance-types [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation config kubernetes-versions
|
## constellation config kubernetes-versions
|
||||||
|
@ -166,6 +170,7 @@ constellation config kubernetes-versions [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation create
|
## constellation create
|
||||||
|
@ -195,6 +200,7 @@ constellation create [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation init
|
## constellation init
|
||||||
|
@ -226,6 +232,7 @@ constellation init [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation mini
|
## constellation mini
|
||||||
|
@ -248,6 +255,7 @@ Manage MiniConstellation clusters.
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation mini up
|
## constellation mini up
|
||||||
|
@ -275,8 +283,9 @@ constellation mini up [flags]
|
||||||
### Options inherited from parent commands
|
### Options inherited from parent commands
|
||||||
|
|
||||||
```
|
```
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation mini down
|
## constellation mini down
|
||||||
|
@ -304,6 +313,7 @@ constellation mini down [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation verify
|
## constellation verify
|
||||||
|
@ -333,6 +343,7 @@ constellation verify [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation upgrade
|
## constellation upgrade
|
||||||
|
@ -355,6 +366,7 @@ Find and apply upgrades to your Constellation cluster.
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation upgrade check
|
## constellation upgrade check
|
||||||
|
@ -384,6 +396,7 @@ constellation upgrade check [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation upgrade apply
|
## constellation upgrade apply
|
||||||
|
@ -413,6 +426,7 @@ constellation upgrade apply [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation recover
|
## constellation recover
|
||||||
|
@ -443,6 +457,7 @@ constellation recover [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation terminate
|
## constellation terminate
|
||||||
|
@ -472,6 +487,7 @@ constellation terminate [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation version
|
## constellation version
|
||||||
|
@ -498,6 +514,7 @@ constellation version [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation iam
|
## constellation iam
|
||||||
|
@ -520,6 +537,7 @@ Work with the IAM configuration on your cloud provider.
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation iam create
|
## constellation iam create
|
||||||
|
@ -545,6 +563,7 @@ Create IAM configuration on a cloud platform for your Constellation cluster.
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation iam create aws
|
## constellation iam create aws
|
||||||
|
@ -576,6 +595,7 @@ constellation iam create aws [flags]
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
--generate-config automatically generate a configuration file and fill in the required fields
|
--generate-config automatically generate a configuration file and fill in the required fields
|
||||||
-k, --kubernetes string Kubernetes version to use in format MAJOR.MINOR - only usable in combination with --generate-config (default "v1.25")
|
-k, --kubernetes string Kubernetes version to use in format MAJOR.MINOR - only usable in combination with --generate-config (default "v1.25")
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
-y, --yes create the IAM configuration without further confirmation
|
-y, --yes create the IAM configuration without further confirmation
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -608,6 +628,7 @@ constellation iam create azure [flags]
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
--generate-config automatically generate a configuration file and fill in the required fields
|
--generate-config automatically generate a configuration file and fill in the required fields
|
||||||
-k, --kubernetes string Kubernetes version to use in format MAJOR.MINOR - only usable in combination with --generate-config (default "v1.25")
|
-k, --kubernetes string Kubernetes version to use in format MAJOR.MINOR - only usable in combination with --generate-config (default "v1.25")
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
-y, --yes create the IAM configuration without further confirmation
|
-y, --yes create the IAM configuration without further confirmation
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -643,6 +664,7 @@ constellation iam create gcp [flags]
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
--generate-config automatically generate a configuration file and fill in the required fields
|
--generate-config automatically generate a configuration file and fill in the required fields
|
||||||
-k, --kubernetes string Kubernetes version to use in format MAJOR.MINOR - only usable in combination with --generate-config (default "v1.25")
|
-k, --kubernetes string Kubernetes version to use in format MAJOR.MINOR - only usable in combination with --generate-config (default "v1.25")
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
-y, --yes create the IAM configuration without further confirmation
|
-y, --yes create the IAM configuration without further confirmation
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -671,6 +693,7 @@ constellation iam destroy [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
## constellation status
|
## constellation status
|
||||||
|
@ -699,5 +722,6 @@ constellation status [flags]
|
||||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||||
--debug enable debug logging
|
--debug enable debug logging
|
||||||
--force disable version compatibility checks - might result in corrupted clusters
|
--force disable version compatibility checks - might result in corrupted clusters
|
||||||
|
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,8 @@ const (
|
||||||
EnvVarNoSpinner = EnvVarPrefix + "NO_SPINNER"
|
EnvVarNoSpinner = EnvVarPrefix + "NO_SPINNER"
|
||||||
// MiniConstellationUID is a sentinel value for the UID of a mini constellation.
|
// MiniConstellationUID is a sentinel value for the UID of a mini constellation.
|
||||||
MiniConstellationUID = "mini"
|
MiniConstellationUID = "mini"
|
||||||
|
// TerraformLogFile is the file name of the Terraform log file.
|
||||||
|
TerraformLogFile = "terraform.log"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Kubernetes.
|
// Kubernetes.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue