mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -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
1
.gitignore
vendored
1
.gitignore
vendored
@ -46,6 +46,7 @@ image/config.mk
|
||||
.terraform
|
||||
.terraform.tfstate.lock.info
|
||||
*.tfvars
|
||||
terraform.log
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
@ -48,6 +48,7 @@ func NewRootCmd() *cobra.Command {
|
||||
|
||||
rootCmd.PersistentFlags().Bool("debug", false, "enable debug logging")
|
||||
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.NewCreateCmd())
|
||||
|
@ -23,9 +23,9 @@ type imageFetcher interface {
|
||||
|
||||
type terraformClient interface {
|
||||
PrepareWorkspace(path string, input terraform.Variables) error
|
||||
CreateCluster(ctx context.Context) (terraform.CreateOutput, error)
|
||||
CreateIAMConfig(ctx context.Context, provider cloudprovider.Provider) (terraform.IAMOutput, error)
|
||||
Destroy(ctx context.Context) error
|
||||
CreateCluster(ctx context.Context, logLevel terraform.LogLevel) (terraform.CreateOutput, error)
|
||||
CreateIAMConfig(ctx context.Context, provider cloudprovider.Provider, logLevel terraform.LogLevel) (terraform.IAMOutput, error)
|
||||
Destroy(ctx context.Context, logLevel terraform.LogLevel) error
|
||||
CleanUpWorkspace() error
|
||||
RemoveInstaller()
|
||||
Show(ctx context.Context) (*tfjson.State, error)
|
||||
|
@ -45,7 +45,7 @@ type stubTerraformClient struct {
|
||||
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{
|
||||
IP: c.ip,
|
||||
Secret: c.initSecret,
|
||||
@ -54,7 +54,7 @@ func (c *stubTerraformClient) CreateCluster(_ context.Context) (terraform.Create
|
||||
}, 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
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ func (c *stubTerraformClient) PrepareWorkspace(_ string, _ terraform.Variables)
|
||||
return c.prepareWorkspaceErr
|
||||
}
|
||||
|
||||
func (c *stubTerraformClient) Destroy(_ context.Context) error {
|
||||
func (c *stubTerraformClient) Destroy(_ context.Context, _ terraform.LogLevel) error {
|
||||
c.destroyCalled = true
|
||||
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.
|
||||
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, insType string, controlPlaneCount, workerCount int,
|
||||
) (clusterid.File, error) {
|
||||
image, err := c.image.FetchReference(ctx, config)
|
||||
func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.File, error) {
|
||||
image, err := c.image.FetchReference(ctx, opts.Config)
|
||||
if err != nil {
|
||||
return clusterid.File{}, fmt.Errorf("fetching image reference: %w", err)
|
||||
}
|
||||
opts.image = image
|
||||
|
||||
switch provider {
|
||||
switch opts.Provider {
|
||||
case cloudprovider.AWS:
|
||||
cl, err := c.newTerraformClient(ctx)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createAWS(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
|
||||
return c.createAWS(ctx, cl, opts)
|
||||
case cloudprovider.GCP:
|
||||
cl, err := c.newTerraformClient(ctx)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createGCP(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
|
||||
return c.createGCP(ctx, cl, opts)
|
||||
case cloudprovider.Azure:
|
||||
cl, err := c.newTerraformClient(ctx)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createAzure(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
|
||||
return c.createAzure(ctx, cl, opts)
|
||||
case cloudprovider.OpenStack:
|
||||
cl, err := c.newTerraformClient(ctx)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createOpenStack(ctx, cl, config, controlPlaneCount, workerCount, image)
|
||||
return c.createOpenStack(ctx, cl, opts)
|
||||
case cloudprovider.QEMU:
|
||||
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)
|
||||
@ -110,38 +121,40 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
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:
|
||||
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,
|
||||
insType string, controlPlaneCount, workerCount int, image string,
|
||||
) (idFile clusterid.File, retErr error) {
|
||||
func (c *Creator) createAWS(ctx context.Context, cl terraformClient, opts CreateOptions) (idFile clusterid.File, retErr error) {
|
||||
vars := terraform.AWSClusterVariables{
|
||||
CommonVariables: terraform.CommonVariables{
|
||||
Name: config.Name,
|
||||
CountControlPlanes: controlPlaneCount,
|
||||
CountWorkers: workerCount,
|
||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||
Name: opts.Config.Name,
|
||||
CountControlPlanes: opts.ControlPlaneCount,
|
||||
CountWorkers: opts.WorkerCount,
|
||||
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||
},
|
||||
StateDiskType: config.Provider.AWS.StateDiskType,
|
||||
Region: config.Provider.AWS.Region,
|
||||
Zone: config.Provider.AWS.Zone,
|
||||
InstanceType: insType,
|
||||
AMIImageID: image,
|
||||
IAMProfileControlPlane: config.Provider.AWS.IAMProfileControlPlane,
|
||||
IAMProfileWorkerNodes: config.Provider.AWS.IAMProfileWorkerNodes,
|
||||
Debug: config.IsDebugCluster(),
|
||||
StateDiskType: opts.Config.Provider.AWS.StateDiskType,
|
||||
Region: opts.Config.Provider.AWS.Region,
|
||||
Zone: opts.Config.Provider.AWS.Zone,
|
||||
InstanceType: opts.InsType,
|
||||
AMIImageID: opts.image,
|
||||
IAMProfileControlPlane: opts.Config.Provider.AWS.IAMProfileControlPlane,
|
||||
IAMProfileWorkerNodes: opts.Config.Provider.AWS.IAMProfileWorkerNodes,
|
||||
Debug: opts.Config.IsDebugCluster(),
|
||||
}
|
||||
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.AWS.String())), &vars); err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||
tfOutput, err := cl.CreateCluster(ctx)
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
@ -154,32 +167,30 @@ func (c *Creator) createAWS(ctx context.Context, cl terraformClient, config *con
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *config.Config,
|
||||
insType string, controlPlaneCount, workerCount int, image string,
|
||||
) (idFile clusterid.File, retErr error) {
|
||||
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, opts CreateOptions) (idFile clusterid.File, retErr error) {
|
||||
vars := terraform.GCPClusterVariables{
|
||||
CommonVariables: terraform.CommonVariables{
|
||||
Name: config.Name,
|
||||
CountControlPlanes: controlPlaneCount,
|
||||
CountWorkers: workerCount,
|
||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||
Name: opts.Config.Name,
|
||||
CountControlPlanes: opts.ControlPlaneCount,
|
||||
CountWorkers: opts.WorkerCount,
|
||||
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||
},
|
||||
Project: config.Provider.GCP.Project,
|
||||
Region: config.Provider.GCP.Region,
|
||||
Zone: config.Provider.GCP.Zone,
|
||||
CredentialsFile: config.Provider.GCP.ServiceAccountKeyPath,
|
||||
InstanceType: insType,
|
||||
StateDiskType: config.Provider.GCP.StateDiskType,
|
||||
ImageID: image,
|
||||
Debug: config.IsDebugCluster(),
|
||||
Project: opts.Config.Provider.GCP.Project,
|
||||
Region: opts.Config.Provider.GCP.Region,
|
||||
Zone: opts.Config.Provider.GCP.Zone,
|
||||
CredentialsFile: opts.Config.Provider.GCP.ServiceAccountKeyPath,
|
||||
InstanceType: opts.InsType,
|
||||
StateDiskType: opts.Config.Provider.GCP.StateDiskType,
|
||||
ImageID: opts.image,
|
||||
Debug: opts.Config.IsDebugCluster(),
|
||||
}
|
||||
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.GCP.String())), &vars); err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||
tfOutput, err := cl.CreateCluster(ctx)
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
@ -192,27 +203,26 @@ func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *con
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *config.Config, insType string, controlPlaneCount, workerCount int, image string,
|
||||
) (idFile clusterid.File, retErr error) {
|
||||
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, opts CreateOptions) (idFile clusterid.File, retErr error) {
|
||||
vars := terraform.AzureClusterVariables{
|
||||
CommonVariables: terraform.CommonVariables{
|
||||
Name: config.Name,
|
||||
CountControlPlanes: controlPlaneCount,
|
||||
CountWorkers: workerCount,
|
||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||
Name: opts.Config.Name,
|
||||
CountControlPlanes: opts.ControlPlaneCount,
|
||||
CountWorkers: opts.WorkerCount,
|
||||
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||
},
|
||||
Location: config.Provider.Azure.Location,
|
||||
ResourceGroup: config.Provider.Azure.ResourceGroup,
|
||||
UserAssignedIdentity: config.Provider.Azure.UserAssignedIdentity,
|
||||
InstanceType: insType,
|
||||
StateDiskType: config.Provider.Azure.StateDiskType,
|
||||
ImageID: image,
|
||||
SecureBoot: *config.Provider.Azure.SecureBoot,
|
||||
CreateMAA: config.Provider.Azure.EnforceIDKeyDigest == idkeydigest.MAAFallback,
|
||||
Debug: config.IsDebugCluster(),
|
||||
Location: opts.Config.Provider.Azure.Location,
|
||||
ResourceGroup: opts.Config.Provider.Azure.ResourceGroup,
|
||||
UserAssignedIdentity: opts.Config.Provider.Azure.UserAssignedIdentity,
|
||||
InstanceType: opts.InsType,
|
||||
StateDiskType: opts.Config.Provider.Azure.StateDiskType,
|
||||
ImageID: opts.image,
|
||||
SecureBoot: *opts.Config.Provider.Azure.SecureBoot,
|
||||
CreateMAA: opts.Config.Provider.Azure.EnforceIDKeyDigest == idkeydigest.MAAFallback,
|
||||
Debug: opts.Config.IsDebugCluster(),
|
||||
}
|
||||
|
||||
attestVariant, err := variant.FromString(config.AttestationVariant)
|
||||
attestVariant, err := variant.FromString(opts.Config.AttestationVariant)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||
tfOutput, err := cl.CreateCluster(ctx)
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
@ -348,14 +358,12 @@ func normalizeAzureURIs(vars terraform.AzureClusterVariables) terraform.AzureClu
|
||||
return vars
|
||||
}
|
||||
|
||||
func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, config *config.Config,
|
||||
controlPlaneCount, workerCount int, image string,
|
||||
) (idFile clusterid.File, retErr error) {
|
||||
func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, opts CreateOptions) (idFile clusterid.File, retErr error) {
|
||||
// TODO: Remove this once OpenStack is supported.
|
||||
if os.Getenv("CONSTELLATION_OPENSTACK_DEV") != "1" {
|
||||
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(
|
||||
"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 " +
|
||||
@ -366,29 +374,29 @@ func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, confi
|
||||
|
||||
vars := terraform.OpenStackClusterVariables{
|
||||
CommonVariables: terraform.CommonVariables{
|
||||
Name: config.Name,
|
||||
CountControlPlanes: controlPlaneCount,
|
||||
CountWorkers: workerCount,
|
||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||
Name: opts.Config.Name,
|
||||
CountControlPlanes: opts.ControlPlaneCount,
|
||||
CountWorkers: opts.WorkerCount,
|
||||
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||
},
|
||||
Cloud: config.Provider.OpenStack.Cloud,
|
||||
AvailabilityZone: config.Provider.OpenStack.AvailabilityZone,
|
||||
FloatingIPPoolID: config.Provider.OpenStack.FloatingIPPoolID,
|
||||
FlavorID: config.Provider.OpenStack.FlavorID,
|
||||
ImageURL: image,
|
||||
DirectDownload: *config.Provider.OpenStack.DirectDownload,
|
||||
OpenstackUserDomainName: config.Provider.OpenStack.UserDomainName,
|
||||
OpenstackUsername: config.Provider.OpenStack.Username,
|
||||
OpenstackPassword: config.Provider.OpenStack.Password,
|
||||
Debug: config.IsDebugCluster(),
|
||||
Cloud: opts.Config.Provider.OpenStack.Cloud,
|
||||
AvailabilityZone: opts.Config.Provider.OpenStack.AvailabilityZone,
|
||||
FloatingIPPoolID: opts.Config.Provider.OpenStack.FloatingIPPoolID,
|
||||
FlavorID: opts.Config.Provider.OpenStack.FlavorID,
|
||||
ImageURL: opts.image,
|
||||
DirectDownload: *opts.Config.Provider.OpenStack.DirectDownload,
|
||||
OpenstackUserDomainName: opts.Config.Provider.OpenStack.UserDomainName,
|
||||
OpenstackUsername: opts.Config.Provider.OpenStack.Username,
|
||||
OpenstackPassword: opts.Config.Provider.OpenStack.Password,
|
||||
Debug: opts.Config.IsDebugCluster(),
|
||||
}
|
||||
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.OpenStack.String())), &vars); err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||
tfOutput, err := cl.CreateCluster(ctx)
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
@ -401,26 +409,29 @@ func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, confi
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, config *config.Config,
|
||||
controlPlaneCount, workerCount int, source string,
|
||||
) (idFile clusterid.File, retErr error) {
|
||||
type qemuCreateOptions struct {
|
||||
source string
|
||||
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}
|
||||
defer rollbackOnError(c.out, &retErr, qemuRollbacker)
|
||||
defer rollbackOnError(c.out, &retErr, qemuRollbacker, opts.TFLogLevel)
|
||||
|
||||
// TODO: render progress bar
|
||||
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 {
|
||||
return clusterid.File{}, fmt.Errorf("download raw image: %w", err)
|
||||
}
|
||||
|
||||
libvirtURI := config.Provider.QEMU.LibvirtURI
|
||||
libvirtURI := opts.Config.Provider.QEMU.LibvirtURI
|
||||
libvirtSocketPath := "."
|
||||
|
||||
switch {
|
||||
// if no libvirt URI is specified, start a libvirt container
|
||||
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
|
||||
}
|
||||
libvirtURI = libvirt.LibvirtTCPConnectURI
|
||||
@ -452,21 +463,21 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
||||
|
||||
vars := terraform.QEMUVariables{
|
||||
CommonVariables: terraform.CommonVariables{
|
||||
Name: config.Name,
|
||||
CountControlPlanes: controlPlaneCount,
|
||||
CountWorkers: workerCount,
|
||||
StateDiskSizeGB: config.StateDiskSizeGB,
|
||||
Name: opts.Config.Name,
|
||||
CountControlPlanes: opts.ControlPlaneCount,
|
||||
CountWorkers: opts.WorkerCount,
|
||||
StateDiskSizeGB: opts.Config.StateDiskSizeGB,
|
||||
},
|
||||
LibvirtURI: libvirtURI,
|
||||
LibvirtSocketPath: libvirtSocketPath,
|
||||
ImagePath: imagePath,
|
||||
ImageFormat: config.Provider.QEMU.ImageFormat,
|
||||
CPUCount: config.Provider.QEMU.VCPUs,
|
||||
MemorySizeMiB: config.Provider.QEMU.Memory,
|
||||
MetadataAPIImage: config.Provider.QEMU.MetadataAPIImage,
|
||||
ImageFormat: opts.Config.Provider.QEMU.ImageFormat,
|
||||
CPUCount: opts.Config.Provider.QEMU.VCPUs,
|
||||
MemorySizeMiB: opts.Config.Provider.QEMU.Memory,
|
||||
MetadataAPIImage: opts.Config.Provider.QEMU.MetadataAPIImage,
|
||||
MetadataLibvirtURI: metadataLibvirtURI,
|
||||
NVRAM: config.Provider.QEMU.NVRAM,
|
||||
Firmware: config.Provider.QEMU.Firmware,
|
||||
NVRAM: opts.Config.Provider.QEMU.NVRAM,
|
||||
Firmware: opts.Config.Provider.QEMU.Firmware,
|
||||
}
|
||||
|
||||
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
|
||||
qemuRollbacker.createdWorkspace = true
|
||||
|
||||
tfOutput, err := cl.CreateCluster(ctx)
|
||||
tfOutput, err := cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
|
@ -215,7 +215,15 @@ func TestCreator(t *testing.T) {
|
||||
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 {
|
||||
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.
|
||||
func (d *IAMDestroyer) DestroyIAMConfiguration(ctx context.Context) error {
|
||||
if err := d.client.Destroy(ctx); err != nil {
|
||||
func (d *IAMDestroyer) DestroyIAMConfiguration(ctx context.Context, logLevel terraform.LogLevel) error {
|
||||
if err := d.client.Destroy(ctx, logLevel); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.client.CleanUpWorkspace()
|
||||
@ -83,11 +83,12 @@ type IAMCreator struct {
|
||||
newTerraformClient func(ctx context.Context) (terraformClient, error)
|
||||
}
|
||||
|
||||
// IAMConfig holds the necessary values for IAM configuration.
|
||||
type IAMConfig struct {
|
||||
GCP GCPIAMConfig
|
||||
Azure AzureIAMConfig
|
||||
AWS AWSIAMConfig
|
||||
// IAMConfigOptions holds the necessary values for IAM configuration.
|
||||
type IAMConfigOptions struct {
|
||||
GCP GCPIAMConfig
|
||||
Azure AzureIAMConfig
|
||||
AWS AWSIAMConfig
|
||||
TFLogLevel terraform.LogLevel
|
||||
}
|
||||
|
||||
// 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.
|
||||
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 {
|
||||
case cloudprovider.GCP:
|
||||
cl, err := c.newTerraformClient(ctx)
|
||||
@ -130,42 +131,42 @@ func (c *IAMCreator) Create(ctx context.Context, provider cloudprovider.Provider
|
||||
return iamid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createGCP(ctx, cl, iamConfig)
|
||||
return c.createGCP(ctx, cl, opts)
|
||||
case cloudprovider.Azure:
|
||||
cl, err := c.newTerraformClient(ctx)
|
||||
if err != nil {
|
||||
return iamid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createAzure(ctx, cl, iamConfig)
|
||||
return c.createAzure(ctx, cl, opts)
|
||||
case cloudprovider.AWS:
|
||||
cl, err := c.newTerraformClient(ctx)
|
||||
if err != nil {
|
||||
return iamid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createAWS(ctx, cl, iamConfig)
|
||||
return c.createAWS(ctx, cl, opts)
|
||||
default:
|
||||
return iamid.File{}, fmt.Errorf("unsupported cloud provider: %s", provider)
|
||||
}
|
||||
}
|
||||
|
||||
// createGCP creates the IAM configuration on GCP.
|
||||
func (c *IAMCreator) createGCP(ctx context.Context, cl terraformClient, iamConfig *IAMConfig) (retFile iamid.File, retErr error) {
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||
func (c *IAMCreator) createGCP(ctx context.Context, cl terraformClient, opts *IAMConfigOptions) (retFile iamid.File, retErr error) {
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||
|
||||
vars := terraform.GCPIAMVariables{
|
||||
ServiceAccountID: iamConfig.GCP.ServiceAccountID,
|
||||
Project: iamConfig.GCP.ProjectID,
|
||||
Region: iamConfig.GCP.Region,
|
||||
Zone: iamConfig.GCP.Zone,
|
||||
ServiceAccountID: opts.GCP.ServiceAccountID,
|
||||
Project: opts.GCP.ProjectID,
|
||||
Region: opts.GCP.Region,
|
||||
Zone: opts.GCP.Zone,
|
||||
}
|
||||
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.GCP.String())), &vars); err != nil {
|
||||
return iamid.File{}, err
|
||||
}
|
||||
|
||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.GCP)
|
||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.GCP, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
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.
|
||||
func (c *IAMCreator) createAzure(ctx context.Context, cl terraformClient, iamConfig *IAMConfig) (retFile iamid.File, retErr error) {
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||
func (c *IAMCreator) createAzure(ctx context.Context, cl terraformClient, opts *IAMConfigOptions) (retFile iamid.File, retErr error) {
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||
|
||||
vars := terraform.AzureIAMVariables{
|
||||
Region: iamConfig.Azure.Region,
|
||||
ResourceGroup: iamConfig.Azure.ResourceGroup,
|
||||
ServicePrincipal: iamConfig.Azure.ServicePrincipal,
|
||||
Region: opts.Azure.Region,
|
||||
ResourceGroup: opts.Azure.ResourceGroup,
|
||||
ServicePrincipal: opts.Azure.ServicePrincipal,
|
||||
}
|
||||
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.Azure.String())), &vars); err != nil {
|
||||
return iamid.File{}, err
|
||||
}
|
||||
|
||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.Azure)
|
||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.Azure, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
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.
|
||||
func (c *IAMCreator) createAWS(ctx context.Context, cl terraformClient, iamConfig *IAMConfig) (retFile iamid.File, retErr error) {
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||
func (c *IAMCreator) createAWS(ctx context.Context, cl terraformClient, opts *IAMConfigOptions) (retFile iamid.File, retErr error) {
|
||||
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
|
||||
|
||||
vars := terraform.AWSIAMVariables{
|
||||
Region: iamConfig.AWS.Region,
|
||||
Prefix: iamConfig.AWS.Prefix,
|
||||
Region: opts.AWS.Region,
|
||||
Prefix: opts.AWS.Prefix,
|
||||
}
|
||||
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", "iam", strings.ToLower(cloudprovider.AWS.String())), &vars); err != nil {
|
||||
return iamid.File{}, err
|
||||
}
|
||||
|
||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.AWS)
|
||||
iamOutput, err := cl.CreateIAMConfig(ctx, cloudprovider.AWS, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return iamid.File{}, err
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ func TestIAMCreator(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
tfClient terraformClient
|
||||
newTfClientErr error
|
||||
config *IAMConfig
|
||||
config *IAMConfigOptions
|
||||
provider cloudprovider.Provider
|
||||
wantIAMIDFile iamid.File
|
||||
wantErr bool
|
||||
@ -107,19 +107,19 @@ func TestIAMCreator(t *testing.T) {
|
||||
tfClient: &stubTerraformClient{iamOutput: validGCPIAMOutput},
|
||||
wantIAMIDFile: validGCPIAMIDFile,
|
||||
provider: cloudprovider.GCP,
|
||||
config: &IAMConfig{GCP: validGCPIAMConfig},
|
||||
config: &IAMConfigOptions{GCP: validGCPIAMConfig},
|
||||
},
|
||||
"azure": {
|
||||
tfClient: &stubTerraformClient{iamOutput: validAzureIAMOutput},
|
||||
wantIAMIDFile: validAzureIAMIDFile,
|
||||
provider: cloudprovider.Azure,
|
||||
config: &IAMConfig{Azure: validAzureIAMConfig},
|
||||
config: &IAMConfigOptions{Azure: validAzureIAMConfig},
|
||||
},
|
||||
"aws": {
|
||||
tfClient: &stubTerraformClient{iamOutput: validAWSIAMOutput},
|
||||
wantIAMIDFile: validAWSIAMIDFile,
|
||||
provider: cloudprovider.AWS,
|
||||
config: &IAMConfig{AWS: validAWSIAMConfig},
|
||||
config: &IAMConfigOptions{AWS: validAWSIAMConfig},
|
||||
},
|
||||
}
|
||||
|
||||
@ -188,7 +188,7 @@ func TestDestroyIAMConfiguration(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
destroyer := &IAMDestroyer{client: tc.tfClient}
|
||||
|
||||
err := destroyer.DestroyIAMConfiguration(context.Background())
|
||||
err := destroyer.DestroyIAMConfiguration(context.Background(), terraform.LogLevelNone)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
@ -11,22 +11,24 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
)
|
||||
|
||||
// rollbacker does a rollback.
|
||||
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,
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "An error occurred: %s\n", *onErr)
|
||||
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?
|
||||
return
|
||||
}
|
||||
@ -37,8 +39,8 @@ type rollbackerTerraform struct {
|
||||
client terraformClient
|
||||
}
|
||||
|
||||
func (r *rollbackerTerraform) rollback(ctx context.Context) error {
|
||||
if err := r.client.Destroy(ctx); err != nil {
|
||||
func (r *rollbackerTerraform) rollback(ctx context.Context, logLevel terraform.LogLevel) error {
|
||||
if err := r.client.Destroy(ctx, logLevel); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.client.CleanUpWorkspace()
|
||||
@ -50,9 +52,9 @@ type rollbackerQEMU struct {
|
||||
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 {
|
||||
retErr = r.client.Destroy(ctx)
|
||||
retErr = r.client.Destroy(ctx, logLevel)
|
||||
}
|
||||
if retErr := errors.Join(retErr, r.libvirt.Stop(ctx)); retErr != nil {
|
||||
return retErr
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -42,7 +43,7 @@ func TestRollbackTerraform(t *testing.T) {
|
||||
client: tc.tfClient,
|
||||
}
|
||||
|
||||
err := rollbacker.rollback(context.Background())
|
||||
err := rollbacker.rollback(context.Background(), terraform.LogLevelNone)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
if tc.tfClient.cleanUpWorkspaceErr == nil {
|
||||
@ -98,7 +99,7 @@ func TestRollbackQEMU(t *testing.T) {
|
||||
createdWorkspace: tc.createdWorkspace,
|
||||
}
|
||||
|
||||
err := rollbacker.rollback(context.Background())
|
||||
err := rollbacker.rollback(context.Background(), terraform.LogLevelNone)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
if tc.tfClient.cleanUpWorkspaceErr == nil {
|
||||
|
@ -33,7 +33,7 @@ func NewTerminator() *Terminator {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
if retErr == nil {
|
||||
retErr = t.newLibvirtRunner().Stop(ctx)
|
||||
@ -46,11 +46,11 @@ func (t *Terminator) Terminate(ctx context.Context) (retErr error) {
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
|
||||
return t.terminateTerraform(ctx, cl)
|
||||
return t.terminateTerraform(ctx, cl, logLevel)
|
||||
}
|
||||
|
||||
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient) error {
|
||||
if err := cl.Destroy(ctx); err != nil {
|
||||
func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient, logLevel terraform.LogLevel) error {
|
||||
if err := cl.Destroy(ctx, logLevel); err != nil {
|
||||
return err
|
||||
}
|
||||
return cl.CleanUpWorkspace()
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"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 {
|
||||
assert.Error(err)
|
||||
|
@ -120,6 +120,7 @@ go_test(
|
||||
"//cli/internal/helm",
|
||||
"//cli/internal/iamid",
|
||||
"//cli/internal/kubernetes",
|
||||
"//cli/internal/terraform",
|
||||
"//disk-mapper/recoverproto",
|
||||
"//internal/atls",
|
||||
"//internal/attestation/measurements",
|
||||
|
@ -12,18 +12,15 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||
"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/gcpshared"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
)
|
||||
|
||||
type cloudCreator interface {
|
||||
Create(
|
||||
ctx context.Context,
|
||||
provider cloudprovider.Provider,
|
||||
config *config.Config,
|
||||
insType string,
|
||||
coordCount, nodeCount int,
|
||||
opts cloudcmd.CreateOptions,
|
||||
) (clusterid.File, error)
|
||||
}
|
||||
|
||||
@ -31,15 +28,15 @@ type cloudIAMCreator interface {
|
||||
Create(
|
||||
ctx context.Context,
|
||||
provider cloudprovider.Provider,
|
||||
iamConfig *cloudcmd.IAMConfig,
|
||||
opts *cloudcmd.IAMConfigOptions,
|
||||
) (iamid.File, error)
|
||||
}
|
||||
|
||||
type iamDestroyer interface {
|
||||
DestroyIAMConfiguration(ctx context.Context) error
|
||||
DestroyIAMConfiguration(ctx context.Context, logLevel terraform.LogLevel) error
|
||||
GetTfstateServiceAccountKey(ctx context.Context) (gcpshared.ServiceAccountKey, error)
|
||||
}
|
||||
|
||||
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/clusterid"
|
||||
"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/gcpshared"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
@ -34,13 +34,10 @@ type stubCloudCreator struct {
|
||||
|
||||
func (c *stubCloudCreator) Create(
|
||||
_ context.Context,
|
||||
provider cloudprovider.Provider,
|
||||
_ *config.Config,
|
||||
_ string,
|
||||
_, _ int,
|
||||
opts cloudcmd.CreateOptions,
|
||||
) (clusterid.File, error) {
|
||||
c.createCalled = true
|
||||
c.id.CloudProvider = provider
|
||||
c.id.CloudProvider = opts.Provider
|
||||
return c.id, c.createErr
|
||||
}
|
||||
|
||||
@ -49,7 +46,7 @@ type stubCloudTerminator struct {
|
||||
terminateErr error
|
||||
}
|
||||
|
||||
func (c *stubCloudTerminator) Terminate(context.Context) error {
|
||||
func (c *stubCloudTerminator) Terminate(_ context.Context, _ terraform.LogLevel) error {
|
||||
c.called = true
|
||||
return c.terminateErr
|
||||
}
|
||||
@ -67,7 +64,7 @@ type stubIAMCreator struct {
|
||||
func (c *stubIAMCreator) Create(
|
||||
_ context.Context,
|
||||
provider cloudprovider.Provider,
|
||||
_ *cloudcmd.IAMConfig,
|
||||
_ *cloudcmd.IAMConfigOptions,
|
||||
) (iamid.File, error) {
|
||||
c.createCalled = true
|
||||
c.id.CloudProvider = provider
|
||||
@ -82,7 +79,7 @@ type stubIAMDestroyer struct {
|
||||
getTfstateKeyErr error
|
||||
}
|
||||
|
||||
func (d *stubIAMDestroyer) DestroyIAMConfiguration(_ context.Context) error {
|
||||
func (d *stubIAMDestroyer) DestroyIAMConfiguration(_ context.Context, _ terraform.LogLevel) error {
|
||||
d.destroyCalled = true
|
||||
return d.destroyErr
|
||||
}
|
||||
|
@ -153,7 +153,15 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
|
||||
}
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
return translateCreateErrors(cmd, err)
|
||||
@ -190,7 +198,7 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
|
||||
|
||||
yes, err := cmd.Flags().GetBool("yes")
|
||||
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)
|
||||
|
||||
@ -206,10 +214,21 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
|
||||
}
|
||||
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{
|
||||
controllerCount: controllerCount,
|
||||
workerCount: workerCount,
|
||||
configPath: configPath,
|
||||
tfLogLevel: logLevel,
|
||||
force: force,
|
||||
yes: yes,
|
||||
}, nil
|
||||
@ -220,6 +239,7 @@ type createFlags struct {
|
||||
controllerCount int
|
||||
workerCount int
|
||||
configPath string
|
||||
tfLogLevel terraform.LogLevel
|
||||
force bool
|
||||
yes bool
|
||||
}
|
||||
|
@ -185,6 +185,7 @@ func TestCreate(t *testing.T) {
|
||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // 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 {
|
||||
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/iamid"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"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 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 {
|
||||
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.
|
||||
func newIAMCreator(cmd *cobra.Command) (*iamCreator, error) {
|
||||
func newIAMCreator(cmd *cobra.Command, logLevel terraform.LogLevel) (*iamCreator, error) {
|
||||
spinner, err := newSpinnerOrStderr(cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating spinner: %w", err)
|
||||
@ -170,13 +181,17 @@ func newIAMCreator(cmd *cobra.Command) (*iamCreator, error) {
|
||||
if err != nil {
|
||||
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{
|
||||
cmd: cmd,
|
||||
spinner: spinner,
|
||||
log: log,
|
||||
creator: cloudcmd.NewIAMCreator(spinner),
|
||||
fileHandler: file.NewHandler(afero.NewOsFs()),
|
||||
iamConfig: &cloudcmd.IAMConfig{},
|
||||
iamConfig: &cloudcmd.IAMConfigOptions{
|
||||
TFLogLevel: logLevel,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -188,7 +203,7 @@ type iamCreator struct {
|
||||
fileHandler file.Handler
|
||||
provider cloudprovider.Provider
|
||||
providerCreator providerIAMCreator
|
||||
iamConfig *cloudcmd.IAMConfig
|
||||
iamConfig *cloudcmd.IAMConfigOptions
|
||||
log debugLog
|
||||
}
|
||||
|
||||
@ -361,7 +376,7 @@ type providerIAMCreator interface {
|
||||
// writeOutputValuesToConfig writes the output values of the IAM creation to the constellation config 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(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(iamFile iamid.File, fileHandler file.Handler) error
|
||||
}
|
||||
@ -369,7 +384,7 @@ type providerIAMCreator interface {
|
||||
// awsIAMCreator implements the providerIAMCreator interface for AWS.
|
||||
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")
|
||||
if err != nil {
|
||||
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.
|
||||
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")
|
||||
if err != nil {
|
||||
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.
|
||||
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")
|
||||
if err != nil {
|
||||
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().Bool("yes", false, "")
|
||||
cmd.Flags().String("name", "constell", "")
|
||||
cmd.Flags().String("tf-log", "NONE", "")
|
||||
|
||||
if tc.zoneFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("zone", tc.zoneFlag))
|
||||
@ -306,7 +307,7 @@ func TestIAMCreateAWS(t *testing.T) {
|
||||
spinner: &nopSpinner{},
|
||||
creator: tc.creator,
|
||||
fileHandler: fileHandler,
|
||||
iamConfig: &cloudcmd.IAMConfig{},
|
||||
iamConfig: &cloudcmd.IAMConfigOptions{},
|
||||
provider: tc.provider,
|
||||
providerCreator: &awsIAMCreator{},
|
||||
}
|
||||
@ -550,12 +551,13 @@ func TestIAMCreateAzure(t *testing.T) {
|
||||
cmd.SetErr(&bytes.Buffer{})
|
||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||
|
||||
// register persistent flag manually
|
||||
// register persistent flags manually
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "")
|
||||
cmd.Flags().Bool("generate-config", false, "")
|
||||
cmd.Flags().String("kubernetes", semver.MajorMinor(config.Default().KubernetesVersion), "")
|
||||
cmd.Flags().Bool("yes", false, "")
|
||||
cmd.Flags().String("name", "constell", "")
|
||||
cmd.Flags().String("tf-log", "NONE", "")
|
||||
|
||||
if tc.regionFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("region", tc.regionFlag))
|
||||
@ -587,7 +589,7 @@ func TestIAMCreateAzure(t *testing.T) {
|
||||
spinner: &nopSpinner{},
|
||||
creator: tc.creator,
|
||||
fileHandler: fileHandler,
|
||||
iamConfig: &cloudcmd.IAMConfig{},
|
||||
iamConfig: &cloudcmd.IAMConfigOptions{},
|
||||
provider: tc.provider,
|
||||
providerCreator: &azureIAMCreator{},
|
||||
}
|
||||
@ -862,6 +864,7 @@ func TestIAMCreateGCP(t *testing.T) {
|
||||
cmd.Flags().String("kubernetes", semver.MajorMinor(config.Default().KubernetesVersion), "")
|
||||
cmd.Flags().Bool("yes", false, "")
|
||||
cmd.Flags().String("name", "constell", "")
|
||||
cmd.Flags().String("tf-log", "NONE", "")
|
||||
|
||||
if tc.zoneFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("zone", tc.zoneFlag))
|
||||
@ -893,7 +896,7 @@ func TestIAMCreateGCP(t *testing.T) {
|
||||
spinner: &nopSpinner{},
|
||||
creator: tc.creator,
|
||||
fileHandler: fileHandler,
|
||||
iamConfig: &cloudcmd.IAMConfig{},
|
||||
iamConfig: &cloudcmd.IAMConfigOptions{},
|
||||
provider: tc.provider,
|
||||
providerCreator: &gcpIAMCreator{},
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
|
||||
"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/constants"
|
||||
"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 {
|
||||
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
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
|
||||
yes, err := cmd.Flags().GetBool("yes")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.log.Debugf("\"yes\" flag is set to %t", yes)
|
||||
|
||||
gcpFileExists := false
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if !yes {
|
||||
if !flags.yes {
|
||||
// Confirmation
|
||||
confirmString := "Do you really want to destroy your IAM configuration?"
|
||||
if gcpFileExists {
|
||||
@ -119,7 +119,7 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr
|
||||
|
||||
spinner.Start("Destroying IAM configuration", false)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -155,3 +155,32 @@ func (c *destroyCmd) deleteGCPServiceAccountKeyFile(cmd *cobra.Command, destroye
|
||||
c.log.Debugf("Successfully deleted %q", constants.GCPServiceAccountKeyFile)
|
||||
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.SetErr(&bytes.Buffer{})
|
||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||
|
||||
// register persistent flags manually
|
||||
cmd.Flags().String("tf-log", "NONE", "")
|
||||
|
||||
assert.NoError(cmd.Flags().Set("yes", tc.yesFlag))
|
||||
|
||||
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/libvirt"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"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)
|
||||
}
|
||||
|
||||
flags, err := m.parseUpFlags(cmd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing flags: %w", err)
|
||||
}
|
||||
|
||||
fileHandler := file.NewHandler(afero.NewOsFs())
|
||||
|
||||
// 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 {
|
||||
return fmt.Errorf("preparing config: %w", err)
|
||||
}
|
||||
|
||||
// create cluster
|
||||
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()
|
||||
if err != nil {
|
||||
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.
|
||||
func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*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)
|
||||
}
|
||||
|
||||
func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler, flags upFlags) (*config.Config, error) {
|
||||
// check for existing config
|
||||
if configPath != "" {
|
||||
conf, err := config.New(fileHandler, configPath, force)
|
||||
if flags.configPath != "" {
|
||||
conf, err := config.New(fileHandler, flags.configPath, flags.force)
|
||||
var configValidationErr *config.ValidationError
|
||||
if errors.As(err, &configValidationErr) {
|
||||
cmd.PrintErrln(configValidationErr.LongMessage())
|
||||
@ -199,11 +195,11 @@ func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler)
|
||||
}
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
_, err = fileHandler.Stat(constants.ConfigFilename)
|
||||
_, err := fileHandler.Stat(constants.ConfigFilename)
|
||||
if err == nil {
|
||||
// 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.")
|
||||
@ -227,9 +223,17 @@ func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler)
|
||||
}
|
||||
|
||||
// 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")
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@ -271,3 +275,39 @@ func (m *miniUpCmd) initializeMiniCluster(cmd *cobra.Command, fileHandler file.H
|
||||
m.log.Debugf("Initialized mini cluster")
|
||||
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/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/file"
|
||||
)
|
||||
@ -48,12 +49,12 @@ func runTerminate(cmd *cobra.Command, _ []string) error {
|
||||
|
||||
func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.Handler, spinner spinnerInterf,
|
||||
) error {
|
||||
yesFlag, err := cmd.Flags().GetBool("yes")
|
||||
flags, err := parseTerminateFlags(cmd)
|
||||
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("All of its associated resources will be DESTROYED.")
|
||||
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)
|
||||
err = terminator.Terminate(cmd.Context())
|
||||
err = terminator.Terminate(cmd.Context(), flags.logLevel)
|
||||
spinner.Stop()
|
||||
if err != nil {
|
||||
return fmt.Errorf("terminating Constellation cluster: %w", err)
|
||||
@ -87,3 +88,28 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.
|
||||
|
||||
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.SetIn(bytes.NewBufferString(tc.stdin))
|
||||
|
||||
// register persistent flags manually
|
||||
cmd.Flags().String("tf-log", "NONE", "")
|
||||
|
||||
require.NotNil(tc.setupFs)
|
||||
fileHandler := file.NewHandler(tc.setupFs(require, tc.idFile))
|
||||
|
||||
|
@ -5,6 +5,7 @@ go_library(
|
||||
name = "terraform",
|
||||
srcs = [
|
||||
"loader.go",
|
||||
"logging.go",
|
||||
"terraform.go",
|
||||
"variables.go",
|
||||
],
|
||||
@ -73,6 +74,7 @@ go_library(
|
||||
visibility = ["//cli:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/constants",
|
||||
"//internal/file",
|
||||
"@com_github_hashicorp_go_version//:go-version",
|
||||
"@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 (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/hashicorp/go-version"
|
||||
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.
|
||||
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 {
|
||||
return CreateOutput{}, err
|
||||
return CreateOutput{}, fmt.Errorf("terraform init: %w", err)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return CreateOutput{}, err
|
||||
return CreateOutput{}, fmt.Errorf("terraform show: %w", err)
|
||||
}
|
||||
|
||||
ipOutput, ok := tfState.Values.Outputs["ip"]
|
||||
@ -177,7 +183,11 @@ type AWSIAMOutput struct {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return IAMOutput{}, err
|
||||
}
|
||||
@ -285,9 +295,13 @@ func (c *Client) CreateIAMConfig(ctx context.Context, provider cloudprovider.Pro
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
return fmt.Errorf("terraform init: %w", err)
|
||||
}
|
||||
return c.tf.Destroy(ctx)
|
||||
}
|
||||
@ -354,9 +368,24 @@ func (c *Client) writeVars(vars Variables) error {
|
||||
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 {
|
||||
Apply(context.Context, ...tfexec.ApplyOption) error
|
||||
Destroy(context.Context, ...tfexec.DestroyOption) error
|
||||
Init(context.Context, ...tfexec.InitOption) 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(),
|
||||
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": {
|
||||
pathBase: "terraform",
|
||||
provider: cloudprovider.QEMU,
|
||||
@ -406,7 +422,7 @@ func TestCreateCluster(t *testing.T) {
|
||||
|
||||
path := path.Join(tc.pathBase, strings.ToLower(tc.provider.String()))
|
||||
require.NoError(c.PrepareWorkspace(path, tc.vars))
|
||||
tfOutput, err := c.CreateCluster(context.Background())
|
||||
tfOutput, err := c.CreateCluster(context.Background(), LogLevelDebug)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
@ -481,6 +497,22 @@ func TestCreateIAM(t *testing.T) {
|
||||
wantErr bool
|
||||
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": {
|
||||
pathBase: path.Join("terraform", "iam"),
|
||||
provider: cloudprovider.GCP,
|
||||
@ -685,7 +717,7 @@ func TestCreateIAM(t *testing.T) {
|
||||
|
||||
path := path.Join(tc.pathBase, strings.ToLower(tc.provider.String()))
|
||||
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 {
|
||||
assert.Error(err)
|
||||
@ -698,6 +730,7 @@ func TestCreateIAM(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDestroyInstances(t *testing.T) {
|
||||
someErr := errors.New("some error")
|
||||
testCases := map[string]struct {
|
||||
tf *stubTerraform
|
||||
wantErr bool
|
||||
@ -706,9 +739,15 @@ func TestDestroyInstances(t *testing.T) {
|
||||
tf: &stubTerraform{},
|
||||
},
|
||||
"destroy fails": {
|
||||
tf: &stubTerraform{
|
||||
destroyErr: errors.New("error"),
|
||||
},
|
||||
tf: &stubTerraform{destroyErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"setLog fails": {
|
||||
tf: &stubTerraform{setLogErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"setLogPath fails": {
|
||||
tf: &stubTerraform{setLogPathErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
@ -721,7 +760,7 @@ func TestDestroyInstances(t *testing.T) {
|
||||
tf: tc.tf,
|
||||
}
|
||||
|
||||
err := c.Destroy(context.Background())
|
||||
err := c.Destroy(context.Background(), LogLevelDebug)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
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 {
|
||||
applyErr error
|
||||
destroyErr error
|
||||
initErr error
|
||||
showErr error
|
||||
showState *tfjson.State
|
||||
applyErr error
|
||||
destroyErr error
|
||||
initErr error
|
||||
showErr error
|
||||
setLogErr error
|
||||
setLogPathErr error
|
||||
showState *tfjson.State
|
||||
}
|
||||
|
||||
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) {
|
||||
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")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -84,6 +85,7 @@ constellation config generate {aws|azure|gcp|openstack|qemu} [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -114,6 +116,7 @@ constellation config fetch-measurements [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -140,6 +143,7 @@ constellation config instance-types [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -166,6 +170,7 @@ constellation config kubernetes-versions [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -195,6 +200,7 @@ constellation create [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -226,6 +232,7 @@ constellation init [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -248,6 +255,7 @@ Manage MiniConstellation clusters.
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -275,8 +283,9 @@ constellation mini up [flags]
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--debug enable debug logging
|
||||
--force disable version compatibility checks - might result in corrupted clusters
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -304,6 +313,7 @@ constellation mini down [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -333,6 +343,7 @@ constellation verify [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -355,6 +366,7 @@ Find and apply upgrades to your Constellation cluster.
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -384,6 +396,7 @@ constellation upgrade check [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -413,6 +426,7 @@ constellation upgrade apply [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -443,6 +457,7 @@ constellation recover [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -472,6 +487,7 @@ constellation terminate [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -498,6 +514,7 @@ constellation version [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -520,6 +537,7 @@ Work with the IAM configuration on your cloud provider.
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -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")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -576,6 +595,7 @@ constellation iam create aws [flags]
|
||||
--force disable version compatibility checks - might result in corrupted clusters
|
||||
--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")
|
||||
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||
-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
|
||||
--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")
|
||||
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||
-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
|
||||
--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")
|
||||
--tf-log string sets the Terraform log level (default "NONE" - no logs) (default "NONE")
|
||||
-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")
|
||||
--debug enable debug logging
|
||||
--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
|
||||
@ -699,5 +722,6 @@ constellation status [flags]
|
||||
--config string path to the configuration file (default "constellation-conf.yaml")
|
||||
--debug enable debug logging
|
||||
--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"
|
||||
// MiniConstellationUID is a sentinel value for the UID of a mini constellation.
|
||||
MiniConstellationUID = "mini"
|
||||
// TerraformLogFile is the file name of the Terraform log file.
|
||||
TerraformLogFile = "terraform.log"
|
||||
|
||||
//
|
||||
// Kubernetes.
|
||||
|
Loading…
Reference in New Issue
Block a user