Move workspace path functions to sub-package of cmd

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-08-08 15:42:06 +02:00 committed by Daniel Weiße
parent 99c579b45a
commit 89b342900f
21 changed files with 213 additions and 189 deletions

View file

@ -36,7 +36,6 @@ go_library(
"validargs.go", "validargs.go",
"verify.go", "verify.go",
"version.go", "version.go",
"workspace.go",
], ],
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/cmd", importpath = "github.com/edgelesssys/constellation/v2/cli/internal/cmd",
visibility = ["//cli:__subpackages__"], visibility = ["//cli:__subpackages__"],
@ -44,6 +43,7 @@ go_library(
"//bootstrapper/initproto", "//bootstrapper/initproto",
"//cli/internal/cloudcmd", "//cli/internal/cloudcmd",
"//cli/internal/clusterid", "//cli/internal/clusterid",
"//cli/internal/cmd/pathprefix",
"//cli/internal/featureset", "//cli/internal/featureset",
"//cli/internal/helm", "//cli/internal/helm",
"//cli/internal/kubernetes", "//cli/internal/kubernetes",
@ -140,6 +140,7 @@ go_test(
"//bootstrapper/initproto", "//bootstrapper/initproto",
"//cli/internal/cloudcmd", "//cli/internal/cloudcmd",
"//cli/internal/clusterid", "//cli/internal/clusterid",
"//cli/internal/cmd/pathprefix",
"//cli/internal/helm", "//cli/internal/helm",
"//cli/internal/kubernetes", "//cli/internal/kubernetes",
"//cli/internal/terraform", "//cli/internal/terraform",

View file

@ -12,9 +12,9 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"path/filepath"
"time" "time"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/featureset" "github.com/edgelesssys/constellation/v2/cli/internal/featureset"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi" "github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
@ -50,7 +50,7 @@ type fetchMeasurementsFlags struct {
signatureURL *url.URL signatureURL *url.URL
insecure bool insecure bool
force bool force bool
workspace string pf pathprefix.PathPrefixer
} }
type configFetchMeasurementsCmd struct { type configFetchMeasurementsCmd struct {
@ -90,7 +90,7 @@ func (cfm *configFetchMeasurementsCmd) configFetchMeasurements(
return errors.New("fetching measurements is not supported") return errors.New("fetching measurements is not supported")
} }
cfm.log.Debugf("Loading configuration file from %q", filepath.Join(flags.workspace, constants.ConfigFilename)) cfm.log.Debugf("Loading configuration file from %q", flags.pf.PrefixPath(constants.ConfigFilename))
conf, err := config.New(fileHandler, constants.ConfigFilename, fetcher, flags.force) conf, err := config.New(fileHandler, constants.ConfigFilename, fetcher, flags.force)
var configValidationErr *config.ValidationError var configValidationErr *config.ValidationError
@ -173,7 +173,7 @@ func (cfm *configFetchMeasurementsCmd) configFetchMeasurements(
if err := fileHandler.WriteYAML(constants.ConfigFilename, conf, file.OptOverwrite); err != nil { if err := fileHandler.WriteYAML(constants.ConfigFilename, conf, file.OptOverwrite); err != nil {
return err return err
} }
cfm.log.Debugf("Configuration written to %s", configPath(flags.workspace)) cfm.log.Debugf("Configuration written to %s", flags.pf.PrefixPath(constants.ConfigFilename))
cmd.Print("Successfully fetched measurements and updated Configuration\n") cmd.Print("Successfully fetched measurements and updated Configuration\n")
return nil return nil
} }
@ -194,7 +194,7 @@ func (cfm *configFetchMeasurementsCmd) parseURLFlag(cmd *cobra.Command, flag str
} }
func (cfm *configFetchMeasurementsCmd) parseFetchMeasurementsFlags(cmd *cobra.Command) (*fetchMeasurementsFlags, error) { func (cfm *configFetchMeasurementsCmd) parseFetchMeasurementsFlags(cmd *cobra.Command) (*fetchMeasurementsFlags, error) {
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return nil, fmt.Errorf("parsing workspace argument: %w", err) return nil, fmt.Errorf("parsing workspace argument: %w", err)
} }
@ -226,7 +226,7 @@ func (cfm *configFetchMeasurementsCmd) parseFetchMeasurementsFlags(cmd *cobra.Co
signatureURL: measurementsSignatureURL, signatureURL: measurementsSignatureURL,
insecure: insecure, insecure: insecure,
force: force, force: force,
workspace: workspace, pf: pathprefix.New(workDir),
}, nil }, nil
} }

View file

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility" "github.com/edgelesssys/constellation/v2/internal/compatibility"
@ -41,7 +42,7 @@ func newConfigGenerateCmd() *cobra.Command {
} }
type generateFlags struct { type generateFlags struct {
workspace string pf pathprefix.PathPrefixer
k8sVersion string k8sVersion string
attestationVariant variant.Variant attestationVariant variant.Variant
} }
@ -80,7 +81,7 @@ func (cg *configGenerateCmd) configGenerate(cmd *cobra.Command, fileHandler file
return err return err
} }
cmd.Println("Config file written to", configPath(flags.workspace)) cmd.Println("Config file written to", flags.pf.PrefixPath(constants.ConfigFilename))
cmd.Println("Please fill in your CSP-specific configuration before proceeding.") cmd.Println("Please fill in your CSP-specific configuration before proceeding.")
cmd.Println("For more information refer to the documentation:") cmd.Println("For more information refer to the documentation:")
cmd.Println("\thttps://docs.edgeless.systems/constellation/getting-started/first-steps") cmd.Println("\thttps://docs.edgeless.systems/constellation/getting-started/first-steps")
@ -137,7 +138,7 @@ func supportedVersions() string {
} }
func parseGenerateFlags(cmd *cobra.Command) (generateFlags, error) { func parseGenerateFlags(cmd *cobra.Command) (generateFlags, error) {
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return generateFlags{}, fmt.Errorf("parsing workspace flag: %w", err) return generateFlags{}, fmt.Errorf("parsing workspace flag: %w", err)
} }
@ -166,7 +167,7 @@ func parseGenerateFlags(cmd *cobra.Command) (generateFlags, error) {
} }
} }
return generateFlags{ return generateFlags{
workspace: workspace, pf: pathprefix.New(workDir),
k8sVersion: resolvedVersion, k8sVersion: resolvedVersion,
attestationVariant: attestationVariant, attestationVariant: attestationVariant,
}, nil }, nil

View file

@ -12,6 +12,7 @@ import (
"io/fs" "io/fs"
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi" "github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
@ -44,6 +45,7 @@ func NewCreateCmd() *cobra.Command {
type createCmd struct { type createCmd struct {
log debugLog log debugLog
pf pathprefix.PathPrefixer
} }
func runCreate(cmd *cobra.Command, _ []string) error { func runCreate(cmd *cobra.Command, _ []string) error {
@ -71,11 +73,11 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
return err return err
} }
c.log.Debugf("Using flags: %+v", flags) c.log.Debugf("Using flags: %+v", flags)
if err := c.checkDirClean(flags.workspace, fileHandler); err != nil { if err := c.checkDirClean(fileHandler); err != nil {
return err return err
} }
c.log.Debugf("Loading configuration file from %q", configPath(flags.workspace)) c.log.Debugf("Loading configuration file from %q", c.pf.PrefixPath(constants.ConfigFilename))
conf, err := config.New(fileHandler, constants.ConfigFilename, fetcher, flags.force) conf, err := config.New(fileHandler, constants.ConfigFilename, fetcher, flags.force)
c.log.Debugf("Configuration file loaded: %+v", conf) c.log.Debugf("Configuration file loaded: %+v", conf)
var configValidationErr *config.ValidationError var configValidationErr *config.ValidationError
@ -168,7 +170,7 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
idFile, err := creator.Create(cmd.Context(), opts) idFile, err := creator.Create(cmd.Context(), opts)
spinner.Stop() spinner.Stop()
if err != nil { if err != nil {
return translateCreateErrors(cmd, flags.workspace, err) return translateCreateErrors(cmd, c.pf, err)
} }
c.log.Debugf("Successfully created the cloud resources for the cluster") c.log.Debugf("Successfully created the cloud resources for the cluster")
@ -188,11 +190,12 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
} }
c.log.Debugf("Yes flag is %t", yes) c.log.Debugf("Yes flag is %t", yes)
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return createFlags{}, fmt.Errorf("parsing config path argument: %w", err) return createFlags{}, fmt.Errorf("parsing config path argument: %w", err)
} }
c.log.Debugf("Workspace set to %q", workspace) c.log.Debugf("Workspace set to %q", workDir)
c.pf = pathprefix.New(workDir)
force, err := cmd.Flags().GetBool("force") force, err := cmd.Flags().GetBool("force")
if err != nil { if err != nil {
@ -208,10 +211,9 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
if err != nil { if err != nil {
return createFlags{}, fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err) 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", terraformLogPath(workspace), logLevel.String()) c.log.Debugf("Terraform logs will be written into %s at level %s", c.pf.PrefixPath(constants.TerraformLogFile), logLevel.String())
return createFlags{ return createFlags{
workspace: workspace,
tfLogLevel: logLevel, tfLogLevel: logLevel,
force: force, force: force,
yes: yes, yes: yes,
@ -220,44 +222,43 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
// createFlags contains the parsed flags of the create command. // createFlags contains the parsed flags of the create command.
type createFlags struct { type createFlags struct {
workspace string
tfLogLevel terraform.LogLevel tfLogLevel terraform.LogLevel
force bool force bool
yes bool yes bool
} }
// checkDirClean checks if files of a previous Constellation are left in the current working dir. // checkDirClean checks if files of a previous Constellation are left in the current working dir.
func (c *createCmd) checkDirClean(workspace string, fileHandler file.Handler) error { func (c *createCmd) checkDirClean(fileHandler file.Handler) error {
c.log.Debugf("Checking admin configuration file") c.log.Debugf("Checking admin configuration file")
if _, err := fileHandler.Stat(constants.AdminConfFilename); !errors.Is(err, fs.ErrNotExist) { if _, err := fileHandler.Stat(constants.AdminConfFilename); !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("file '%s' already exists in working directory, run 'constellation terminate' before creating a new one", adminConfPath(workspace)) return fmt.Errorf("file '%s' already exists in working directory, run 'constellation terminate' before creating a new one", c.pf.PrefixPath(constants.AdminConfFilename))
} }
c.log.Debugf("Checking master secrets file") c.log.Debugf("Checking master secrets file")
if _, err := fileHandler.Stat(constants.MasterSecretFilename); !errors.Is(err, fs.ErrNotExist) { if _, err := fileHandler.Stat(constants.MasterSecretFilename); !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("file '%s' already exists in working directory. Constellation won't overwrite previous master secrets. Move it somewhere or delete it before creating a new cluster", masterSecretPath(workspace)) return fmt.Errorf("file '%s' already exists in working directory. Constellation won't overwrite previous master secrets. Move it somewhere or delete it before creating a new cluster", c.pf.PrefixPath(constants.MasterSecretFilename))
} }
c.log.Debugf("Checking cluster IDs file") c.log.Debugf("Checking cluster IDs file")
if _, err := fileHandler.Stat(constants.ClusterIDsFilename); !errors.Is(err, fs.ErrNotExist) { if _, err := fileHandler.Stat(constants.ClusterIDsFilename); !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("file '%s' already exists in working directory. Constellation won't overwrite previous cluster IDs. Move it somewhere or delete it before creating a new cluster", clusterIDsPath(workspace)) return fmt.Errorf("file '%s' already exists in working directory. Constellation won't overwrite previous cluster IDs. Move it somewhere or delete it before creating a new cluster", c.pf.PrefixPath(constants.ClusterIDsFilename))
} }
return nil return nil
} }
func translateCreateErrors(cmd *cobra.Command, workspace string, err error) error { func translateCreateErrors(cmd *cobra.Command, pf pathprefix.PathPrefixer, err error) error {
switch { switch {
case errors.Is(err, terraform.ErrTerraformWorkspaceDifferentFiles): case errors.Is(err, terraform.ErrTerraformWorkspaceDifferentFiles):
cmd.PrintErrln("\nYour current working directory contains an existing Terraform workspace which does not match the expected state.") cmd.PrintErrln("\nYour current working directory contains an existing Terraform workspace which does not match the expected state.")
cmd.PrintErrln("This can be due to a mix up between providers, versions or an otherwise corrupted workspace.") cmd.PrintErrln("This can be due to a mix up between providers, versions or an otherwise corrupted workspace.")
cmd.PrintErrln("Before creating a new cluster, try \"constellation terminate\".") cmd.PrintErrln("Before creating a new cluster, try \"constellation terminate\".")
cmd.PrintErrf("If this does not work, either move or delete the directory %q.\n", terraformClusterWorkspace(workspace)) cmd.PrintErrf("If this does not work, either move or delete the directory %q.\n", pf.PrefixPath(constants.TerraformWorkingDir))
cmd.PrintErrln("Please only delete the directory if you made sure that all created cloud resources have been terminated.") cmd.PrintErrln("Please only delete the directory if you made sure that all created cloud resources have been terminated.")
return err return err
case errors.Is(err, terraform.ErrTerraformWorkspaceExistsWithDifferentVariables): case errors.Is(err, terraform.ErrTerraformWorkspaceExistsWithDifferentVariables):
cmd.PrintErrln("\nYour current working directory contains an existing Terraform workspace which was initiated with different input variables.") cmd.PrintErrln("\nYour current working directory contains an existing Terraform workspace which was initiated with different input variables.")
cmd.PrintErrln("This can be the case if you have tried to create a cluster before with different options which did not complete, or the workspace is corrupted.") cmd.PrintErrln("This can be the case if you have tried to create a cluster before with different options which did not complete, or the workspace is corrupted.")
cmd.PrintErrln("Before creating a new cluster, try \"constellation terminate\".") cmd.PrintErrln("Before creating a new cluster, try \"constellation terminate\".")
cmd.PrintErrf("If this does not work, either move or delete the directory %q.\n", terraformClusterWorkspace(workspace)) cmd.PrintErrf("If this does not work, either move or delete the directory %q.\n", pf.PrefixPath(constants.TerraformWorkingDir))
cmd.PrintErrln("Please only delete the directory if you made sure that all created cloud resources have been terminated.") cmd.PrintErrln("Please only delete the directory if you made sure that all created cloud resources have been terminated.")
return err return err
default: default:

View file

@ -255,7 +255,7 @@ func TestCheckDirClean(t *testing.T) {
require.NoError(tc.fileHandler.Write(f, []byte{1, 2, 3}, file.OptNone)) require.NoError(tc.fileHandler.Write(f, []byte{1, 2, 3}, file.OptNone))
} }
c := &createCmd{log: logger.NewTest(t)} c := &createCmd{log: logger.NewTest(t)}
err := c.checkDirClean("", tc.fileHandler) err := c.checkDirClean(tc.fileHandler)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)

View file

@ -14,6 +14,7 @@ import (
"strings" "strings"
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
@ -129,15 +130,15 @@ func newIAMCreateGCPCmd() *cobra.Command {
// createRunIAMFunc is the entrypoint for the iam create command. It sets up the iamCreator // createRunIAMFunc is the entrypoint for the iam create command. It sets up the iamCreator
// and starts IAM creation for the specific cloud provider. // and starts IAM creation for the specific cloud provider.
func createRunIAMFunc(provider cloudprovider.Provider) func(cmd *cobra.Command, args []string) error { func createRunIAMFunc(provider cloudprovider.Provider) func(cmd *cobra.Command, args []string) error {
var providerCreator func(workspace string) providerIAMCreator var providerCreator func(pf pathprefix.PathPrefixer) providerIAMCreator
switch provider { switch provider {
case cloudprovider.AWS: case cloudprovider.AWS:
providerCreator = func(string) providerIAMCreator { return &awsIAMCreator{} } providerCreator = func(pathprefix.PathPrefixer) providerIAMCreator { return &awsIAMCreator{} }
case cloudprovider.Azure: case cloudprovider.Azure:
providerCreator = func(string) providerIAMCreator { return &azureIAMCreator{} } providerCreator = func(pathprefix.PathPrefixer) providerIAMCreator { return &azureIAMCreator{} }
case cloudprovider.GCP: case cloudprovider.GCP:
providerCreator = func(workspace string) providerIAMCreator { providerCreator = func(pf pathprefix.PathPrefixer) providerIAMCreator {
return &gcpIAMCreator{workspace} return &gcpIAMCreator{pf}
} }
default: default:
return func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error {
@ -154,25 +155,26 @@ func createRunIAMFunc(provider cloudprovider.Provider) func(cmd *cobra.Command,
if err != nil { if err != nil {
return fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err) return fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err)
} }
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return fmt.Errorf("parsing workspace string: %w", err) return fmt.Errorf("parsing workspace string: %w", err)
} }
pf := pathprefix.New(workDir)
iamCreator, err := newIAMCreator(cmd, workspace, logLevel) iamCreator, err := newIAMCreator(cmd, pf, logLevel)
if err != nil { if err != nil {
return fmt.Errorf("creating iamCreator: %w", err) return fmt.Errorf("creating iamCreator: %w", err)
} }
defer iamCreator.spinner.Stop() defer iamCreator.spinner.Stop()
defer iamCreator.log.Sync() defer iamCreator.log.Sync()
iamCreator.provider = provider iamCreator.provider = provider
iamCreator.providerCreator = providerCreator(workspace) iamCreator.providerCreator = providerCreator(pf)
return iamCreator.create(cmd.Context()) return iamCreator.create(cmd.Context())
} }
} }
// newIAMCreator creates a new iamiamCreator. // newIAMCreator creates a new iamiamCreator.
func newIAMCreator(cmd *cobra.Command, workspace string, logLevel terraform.LogLevel) (*iamCreator, error) { func newIAMCreator(cmd *cobra.Command, pf pathprefix.PathPrefixer, 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)
@ -181,7 +183,7 @@ func newIAMCreator(cmd *cobra.Command, workspace string, logLevel terraform.LogL
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", terraformLogPath(workspace), logLevel.String()) log.Debugf("Terraform logs will be written into %s at level %s", pf.PrefixPath(constants.TerraformLogFile), logLevel.String())
return &iamCreator{ return &iamCreator{
cmd: cmd, cmd: cmd,
@ -206,6 +208,7 @@ type iamCreator struct {
providerCreator providerIAMCreator providerCreator providerIAMCreator
iamConfig *cloudcmd.IAMConfigOptions iamConfig *cloudcmd.IAMConfigOptions
log debugLog log debugLog
pf pathprefix.PathPrefixer
} }
// create IAM configuration on the iamCreator's cloud provider. // create IAM configuration on the iamCreator's cloud provider.
@ -216,7 +219,7 @@ func (c *iamCreator) create(ctx context.Context) error {
} }
c.log.Debugf("Using flags: %+v", flags) c.log.Debugf("Using flags: %+v", flags)
if err := c.checkWorkingDir(flags.workspace); err != nil { if err := c.checkWorkingDir(); err != nil {
return err return err
} }
@ -235,14 +238,14 @@ func (c *iamCreator) create(ctx context.Context) error {
var conf config.Config var conf config.Config
if flags.updateConfig { if flags.updateConfig {
c.log.Debugf("Parsing config %s", configPath(flags.workspace)) c.log.Debugf("Parsing config %s", c.pf.PrefixPath(constants.ConfigFilename))
if err = c.fileHandler.ReadYAML(constants.ConfigFilename, &conf); err != nil { if err = c.fileHandler.ReadYAML(constants.ConfigFilename, &conf); err != nil {
return fmt.Errorf("error reading the configuration file: %w", err) return fmt.Errorf("error reading the configuration file: %w", err)
} }
if err := validateConfigWithFlagCompatibility(c.provider, conf, flags); err != nil { if err := validateConfigWithFlagCompatibility(c.provider, conf, flags); err != nil {
return err return err
} }
c.cmd.Printf("The configuration file %q will be automatically updated with the IAM values and zone/region information.\n", configPath(flags.workspace)) c.cmd.Printf("The configuration file %q will be automatically updated with the IAM values and zone/region information.\n", c.pf.PrefixPath(constants.ConfigFilename))
} }
c.spinner.Start("Creating", false) c.spinner.Start("Creating", false)
@ -260,12 +263,12 @@ func (c *iamCreator) create(ctx context.Context) error {
} }
if flags.updateConfig { if flags.updateConfig {
c.log.Debugf("Writing IAM configuration to %s", configPath(flags.workspace)) c.log.Debugf("Writing IAM configuration to %s", c.pf.PrefixPath(constants.ConfigFilename))
c.providerCreator.writeOutputValuesToConfig(&conf, flags, iamFile) c.providerCreator.writeOutputValuesToConfig(&conf, flags, iamFile)
if err := c.fileHandler.WriteYAML(constants.ConfigFilename, conf, file.OptOverwrite); err != nil { if err := c.fileHandler.WriteYAML(constants.ConfigFilename, conf, file.OptOverwrite); err != nil {
return err return err
} }
c.cmd.Printf("Your IAM configuration was created and filled into %s successfully.\n", configPath(flags.workspace)) c.cmd.Printf("Your IAM configuration was created and filled into %s successfully.\n", c.pf.PrefixPath(constants.ConfigFilename))
return nil return nil
} }
@ -277,10 +280,12 @@ func (c *iamCreator) create(ctx context.Context) error {
// parseFlagsAndSetupConfig parses the flags of the iam create command and fills the values into the IAM config (output values of the command). // parseFlagsAndSetupConfig parses the flags of the iam create command and fills the values into the IAM config (output values of the command).
func (c *iamCreator) parseFlagsAndSetupConfig() (iamFlags, error) { func (c *iamCreator) parseFlagsAndSetupConfig() (iamFlags, error) {
cwd, err := c.cmd.Flags().GetString("workspace") workDir, err := c.cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return iamFlags{}, fmt.Errorf("parsing config string: %w", err) return iamFlags{}, fmt.Errorf("parsing config string: %w", err)
} }
c.pf = pathprefix.New(workDir)
yesFlag, err := c.cmd.Flags().GetBool("yes") yesFlag, err := c.cmd.Flags().GetBool("yes")
if err != nil { if err != nil {
return iamFlags{}, fmt.Errorf("parsing yes bool: %w", err) return iamFlags{}, fmt.Errorf("parsing yes bool: %w", err)
@ -291,7 +296,6 @@ func (c *iamCreator) parseFlagsAndSetupConfig() (iamFlags, error) {
} }
flags := iamFlags{ flags := iamFlags{
workspace: cwd,
yesFlag: yesFlag, yesFlag: yesFlag,
updateConfig: updateConfig, updateConfig: updateConfig,
} }
@ -305,9 +309,9 @@ func (c *iamCreator) parseFlagsAndSetupConfig() (iamFlags, error) {
} }
// checkWorkingDir checks if the current working directory already contains a Terraform dir. // checkWorkingDir checks if the current working directory already contains a Terraform dir.
func (c *iamCreator) checkWorkingDir(workspace string) error { func (c *iamCreator) checkWorkingDir() error {
if _, err := c.fileHandler.Stat(constants.TerraformIAMWorkingDir); err == nil { if _, err := c.fileHandler.Stat(constants.TerraformIAMWorkingDir); err == nil {
return fmt.Errorf("the current working directory already contains the Terraform workspace directory %q. Please run the command in a different directory or destroy the existing workspace", terraformIAMWorkspace(workspace)) return fmt.Errorf("the current working directory already contains the Terraform workspace directory %q. Please run the command in a different directory or destroy the existing workspace", c.pf.PrefixPath(constants.TerraformIAMWorkingDir))
} }
return nil return nil
} }
@ -317,7 +321,6 @@ type iamFlags struct {
aws awsFlags aws awsFlags
azure azureFlags azure azureFlags
gcp gcpFlags gcp gcpFlags
workspace string
yesFlag bool yesFlag bool
updateConfig bool updateConfig bool
} }
@ -488,7 +491,7 @@ func (c *azureIAMCreator) parseAndWriteIDFile(_ cloudcmd.IAMOutput, _ file.Handl
// gcpIAMCreator implements the providerIAMCreator interface for GCP. // gcpIAMCreator implements the providerIAMCreator interface for GCP.
type gcpIAMCreator struct { type gcpIAMCreator struct {
workspace string pf pathprefix.PathPrefixer
} }
func (c *gcpIAMCreator) parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfigOptions) (iamFlags, error) { func (c *gcpIAMCreator) parseFlagsAndSetupConfig(cmd *cobra.Command, flags iamFlags, iamConfig *cloudcmd.IAMConfigOptions) (iamFlags, error) {
@ -552,12 +555,12 @@ func (c *gcpIAMCreator) printOutputValues(cmd *cobra.Command, flags iamFlags, _
cmd.Printf("projectID:\t\t%s\n", flags.gcp.projectID) cmd.Printf("projectID:\t\t%s\n", flags.gcp.projectID)
cmd.Printf("region:\t\t\t%s\n", flags.gcp.region) cmd.Printf("region:\t\t\t%s\n", flags.gcp.region)
cmd.Printf("zone:\t\t\t%s\n", flags.gcp.zone) cmd.Printf("zone:\t\t\t%s\n", flags.gcp.zone)
cmd.Printf("serviceAccountKeyPath:\t%s\n\n", gcpServiceAccountKeyPath(c.workspace)) cmd.Printf("serviceAccountKeyPath:\t%s\n\n", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename))
} }
func (c *gcpIAMCreator) writeOutputValuesToConfig(conf *config.Config, flags iamFlags, _ cloudcmd.IAMOutput) { func (c *gcpIAMCreator) writeOutputValuesToConfig(conf *config.Config, flags iamFlags, _ cloudcmd.IAMOutput) {
conf.Provider.GCP.Project = flags.gcp.projectID conf.Provider.GCP.Project = flags.gcp.projectID
conf.Provider.GCP.ServiceAccountKeyPath = gcpServiceAccountKeyFile // File was created in workspace, so only the filename is needed. conf.Provider.GCP.ServiceAccountKeyPath = constants.GCPServiceAccountKeyFilename // File was created in workspace, so only the filename is needed.
conf.Provider.GCP.Region = flags.gcp.region conf.Provider.GCP.Region = flags.gcp.region
conf.Provider.GCP.Zone = flags.gcp.zone conf.Provider.GCP.Zone = flags.gcp.zone
for groupName, group := range conf.NodeGroups { for groupName, group := range conf.NodeGroups {
@ -573,7 +576,7 @@ func (c *gcpIAMCreator) parseAndWriteIDFile(iamFile cloudcmd.IAMOutput, fileHand
return err return err
} }
return fileHandler.WriteJSON(gcpServiceAccountKeyFile, tmpOut, file.OptNone) return fileHandler.WriteJSON(constants.GCPServiceAccountKeyFilename, tmpOut, file.OptNone)
} }
// parseIDFile parses the given base64 encoded JSON string of the GCP service account key and returns a map. // parseIDFile parses the given base64 encoded JSON string of the GCP service account key and returns a map.

View file

@ -707,13 +707,13 @@ func TestIAMCreateGCP(t *testing.T) {
readConfig := &config.Config{} readConfig := &config.Config{}
readErr := fileHandler.ReadYAML(constants.ConfigFilename, readConfig) readErr := fileHandler.ReadYAML(constants.ConfigFilename, readConfig)
require.NoError(readErr) require.NoError(readErr)
assert.Equal(gcpServiceAccountKeyFile, readConfig.Provider.GCP.ServiceAccountKeyPath) assert.Equal(constants.GCPServiceAccountKeyFilename, readConfig.Provider.GCP.ServiceAccountKeyPath)
} }
require.NoError(err) require.NoError(err)
assert.True(tc.creator.createCalled) assert.True(tc.creator.createCalled)
assert.Equal(tc.creator.id.GCPOutput, validIAMIDFile.GCPOutput) assert.Equal(tc.creator.id.GCPOutput, validIAMIDFile.GCPOutput)
readServiceAccountKey := &map[string]string{} readServiceAccountKey := &map[string]string{}
readErr := fileHandler.ReadJSON(gcpServiceAccountKeyFile, readServiceAccountKey) readErr := fileHandler.ReadJSON(constants.GCPServiceAccountKeyFilename, readServiceAccountKey)
require.NoError(readErr) require.NoError(readErr)
assert.Equal("not_a_secret", (*readServiceAccountKey)["private_key_id"]) assert.Equal("not_a_secret", (*readServiceAccountKey)["private_key_id"])
}) })

View file

@ -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/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "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"
@ -51,6 +52,7 @@ func runIAMDestroy(cmd *cobra.Command, _ []string) error {
type destroyCmd struct { type destroyCmd struct {
log debugLog log debugLog
pf pathprefix.PathPrefixer
} }
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 {
@ -60,27 +62,27 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr
} }
// 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", adminConfPath(flags.workspace)) c.log.Debugf("Checking if %q exists", c.pf.PrefixPath(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", adminConfPath(flags.workspace)) return fmt.Errorf("file %q still exists, please make sure to terminate your cluster before destroying your IAM configuration", c.pf.PrefixPath(constants.AdminConfFilename))
} }
c.log.Debugf("Checking if %q exists", clusterIDsPath(flags.workspace)) c.log.Debugf("Checking if %q exists", c.pf.PrefixPath(constants.ClusterIDsFilename))
_, err = fsHandler.Stat(constants.ClusterIDsFilename) _, err = fsHandler.Stat(constants.ClusterIDsFilename)
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", clusterIDsPath(flags.workspace)) return fmt.Errorf("file %q still exists, please make sure to terminate your cluster before destroying your IAM configuration", c.pf.PrefixPath(constants.ClusterIDsFilename))
} }
gcpFileExists := false gcpFileExists := false
c.log.Debugf("Checking if %q exists", gcpServiceAccountKeyPath(flags.workspace)) c.log.Debugf("Checking if %q exists", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename))
_, err = fsHandler.Stat(gcpServiceAccountKeyFile) _, err = fsHandler.Stat(constants.GCPServiceAccountKeyFilename)
if err != nil { if err != nil {
if !errors.Is(err, os.ErrNotExist) { if !errors.Is(err, os.ErrNotExist) {
return err return err
} }
} else { } else {
c.log.Debugf("%q exists", gcpServiceAccountKeyPath(flags.workspace)) c.log.Debugf("%q exists", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename))
gcpFileExists = true gcpFileExists = true
} }
@ -88,7 +90,7 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr
// Confirmation // Confirmation
confirmString := "Do you really want to destroy your IAM configuration? Note that this will remove all resources in the resource group." confirmString := "Do you really want to destroy your IAM configuration? Note that this will remove all resources in the resource group."
if gcpFileExists { if gcpFileExists {
confirmString += fmt.Sprintf("\nThis will also delete %q", gcpServiceAccountKeyPath(flags.workspace)) confirmString += fmt.Sprintf("\nThis will also delete %q", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename))
} }
ok, err := askToConfirm(cmd, confirmString) ok, err := askToConfirm(cmd, confirmString)
if err != nil { if err != nil {
@ -101,8 +103,8 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr
} }
if gcpFileExists { if gcpFileExists {
c.log.Debugf("Starting to delete %q", gcpServiceAccountKeyPath(flags.workspace)) c.log.Debugf("Starting to delete %q", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename))
proceed, err := c.deleteGCPServiceAccountKeyFile(cmd, destroyer, flags.workspace, fsHandler) proceed, err := c.deleteGCPServiceAccountKeyFile(cmd, destroyer, fsHandler)
if err != nil { if err != nil {
return err return err
} }
@ -125,11 +127,11 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr
return nil return nil
} }
func (c *destroyCmd) deleteGCPServiceAccountKeyFile(cmd *cobra.Command, destroyer iamDestroyer, workspace string, fsHandler file.Handler) (bool, error) { func (c *destroyCmd) deleteGCPServiceAccountKeyFile(cmd *cobra.Command, destroyer iamDestroyer, fsHandler file.Handler) (bool, error) {
var fileSaKey gcpshared.ServiceAccountKey var fileSaKey gcpshared.ServiceAccountKey
c.log.Debugf("Parsing %q", gcpServiceAccountKeyPath(workspace)) c.log.Debugf("Parsing %q", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename))
if err := fsHandler.ReadJSON(gcpServiceAccountKeyFile, &fileSaKey); err != nil { if err := fsHandler.ReadJSON(constants.GCPServiceAccountKeyFilename, &fileSaKey); err != nil {
return false, err return false, err
} }
@ -141,21 +143,20 @@ func (c *destroyCmd) deleteGCPServiceAccountKeyFile(cmd *cobra.Command, destroye
c.log.Debugf("Checking if keys are the same") c.log.Debugf("Checking if keys are the same")
if tfSaKey != fileSaKey { if tfSaKey != fileSaKey {
cmd.Printf("The key in %q don't match up with your Terraform state. %q will not be deleted.\n", gcpServiceAccountKeyPath(workspace), gcpServiceAccountKeyPath(workspace)) cmd.Printf("The key in %q don't match up with your Terraform state. %q will not be deleted.\n", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename), c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename))
return true, nil return true, nil
} }
if err := fsHandler.Remove(gcpServiceAccountKeyFile); err != nil { if err := fsHandler.Remove(constants.GCPServiceAccountKeyFilename); err != nil {
return false, err return false, err
} }
c.log.Debugf("Successfully deleted %q", gcpServiceAccountKeyPath(workspace)) c.log.Debugf("Successfully deleted %q", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename))
return true, nil return true, nil
} }
type destroyFlags struct { type destroyFlags struct {
yes bool yes bool
workspace string
tfLogLevel terraform.LogLevel tfLogLevel terraform.LogLevel
} }
@ -167,11 +168,12 @@ func (c *destroyCmd) parseDestroyFlags(cmd *cobra.Command) (destroyFlags, error)
} }
c.log.Debugf("Yes flag is %t", yes) c.log.Debugf("Yes flag is %t", yes)
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return destroyFlags{}, fmt.Errorf("parsing workspace string: %w", err) return destroyFlags{}, fmt.Errorf("parsing workspace string: %w", err)
} }
c.log.Debugf("Workspace set to %q", workspace) c.log.Debugf("Workspace set to %q", workDir)
c.pf = pathprefix.New(workDir)
logLevelString, err := cmd.Flags().GetString("tf-log") logLevelString, err := cmd.Flags().GetString("tf-log")
if err != nil { if err != nil {
@ -181,11 +183,10 @@ func (c *destroyCmd) parseDestroyFlags(cmd *cobra.Command) (destroyFlags, error)
if err != nil { if err != nil {
return destroyFlags{}, fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err) 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", terraformLogPath(workspace), logLevel.String()) c.log.Debugf("Terraform logs will be written into %s at level %s", c.pf.PrefixPath(constants.TerraformWorkingDir), logLevel.String())
return destroyFlags{ return destroyFlags{
tfLogLevel: logLevel, tfLogLevel: logLevel,
workspace: workspace,
yes: yes, yes: yes,
}, nil }, nil
} }

View file

@ -24,7 +24,7 @@ func TestIAMDestroy(t *testing.T) {
newFsExists := func() file.Handler { newFsExists := func() file.Handler {
fh := file.NewHandler(afero.NewMemMapFs()) fh := file.NewHandler(afero.NewMemMapFs())
require.NoError(fh.Write(gcpServiceAccountKeyFile, []byte("{}"))) require.NoError(fh.Write(constants.GCPServiceAccountKeyFilename, []byte("{}")))
return fh return fh
} }
newFsMissing := func() file.Handler { newFsMissing := func() file.Handler {
@ -147,12 +147,12 @@ func TestDeleteGCPServiceAccountKeyFile(t *testing.T) {
newFs := func() file.Handler { newFs := func() file.Handler {
fs := file.NewHandler(afero.NewMemMapFs()) fs := file.NewHandler(afero.NewMemMapFs())
require.NoError(fs.Write(gcpServiceAccountKeyFile, []byte(gcpFile))) require.NoError(fs.Write(constants.GCPServiceAccountKeyFilename, []byte(gcpFile)))
return fs return fs
} }
newFsInvalidJSON := func() file.Handler { newFsInvalidJSON := func() file.Handler {
fh := file.NewHandler(afero.NewMemMapFs()) fh := file.NewHandler(afero.NewMemMapFs())
require.NoError(fh.Write(gcpServiceAccountKeyFile, []byte("asdf"))) require.NoError(fh.Write(constants.GCPServiceAccountKeyFilename, []byte("asdf")))
return fh return fh
} }
@ -202,7 +202,7 @@ func TestDeleteGCPServiceAccountKeyFile(t *testing.T) {
c := &destroyCmd{log: logger.NewTest(t)} c := &destroyCmd{log: logger.NewTest(t)}
proceed, err := c.deleteGCPServiceAccountKeyFile(cmd, tc.destroyer, "", tc.fsHandler) proceed, err := c.deleteGCPServiceAccountKeyFile(cmd, tc.destroyer, tc.fsHandler)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
} else { } else {

View file

@ -37,6 +37,7 @@ import (
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto" "github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
"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/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/helm" "github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/azureshared" "github.com/edgelesssys/constellation/v2/internal/cloud/azureshared"
@ -79,6 +80,7 @@ type initCmd struct {
fileHandler file.Handler fileHandler file.Handler
helmInstaller initializer helmInstaller initializer
clusterShower clusterShower clusterShower clusterShower
pf pathprefix.PathPrefixer
} }
type clusterShower interface { type clusterShower interface {
@ -143,7 +145,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator atls.V
return err return err
} }
i.log.Debugf("Using flags: %+v", flags) i.log.Debugf("Using flags: %+v", flags)
i.log.Debugf("Loading configuration file from %q", configPath(flags.workspace)) i.log.Debugf("Loading configuration file from %q", i.pf.PrefixPath(constants.ConfigFilename))
conf, err := config.New(i.fileHandler, constants.ConfigFilename, configFetcher, flags.force) conf, err := config.New(i.fileHandler, constants.ConfigFilename, configFetcher, flags.force)
var configValidationErr *config.ValidationError var configValidationErr *config.ValidationError
if errors.As(err, &configValidationErr) { if errors.As(err, &configValidationErr) {
@ -193,14 +195,14 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator atls.V
return fmt.Errorf("creating new validator: %w", err) return fmt.Errorf("creating new validator: %w", err)
} }
i.log.Debugf("Created a new validator") i.log.Debugf("Created a new validator")
serviceAccURI, err := i.getMarshaledServiceAccountURI(provider, conf, flags.workspace) serviceAccURI, err := i.getMarshaledServiceAccountURI(provider, conf)
if err != nil { if err != nil {
return err return err
} }
i.log.Debugf("Successfully marshaled service account URI") i.log.Debugf("Successfully marshaled service account URI")
i.log.Debugf("Generating master secret") i.log.Debugf("Generating master secret")
masterSecret, err := i.generateMasterSecret(cmd.OutOrStdout(), flags.workspace) masterSecret, err := i.generateMasterSecret(cmd.OutOrStdout())
if err != nil { if err != nil {
return fmt.Errorf("generating master secret: %w", err) return fmt.Errorf("generating master secret: %w", err)
} }
@ -246,7 +248,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator atls.V
idFile.CloudProvider = provider idFile.CloudProvider = provider
bufferedOutput := &bytes.Buffer{} bufferedOutput := &bytes.Buffer{}
if err := i.writeOutput(idFile, resp, flags.mergeConfigs, bufferedOutput, flags.workspace); err != nil { if err := i.writeOutput(idFile, resp, flags.mergeConfigs, bufferedOutput); err != nil {
return err return err
} }
@ -394,8 +396,7 @@ func (d *initDoer) handleGRPCStateChanges(ctx context.Context, wg *sync.WaitGrou
} }
func (i *initCmd) writeOutput( func (i *initCmd) writeOutput(
idFile clusterid.File, initResp *initproto.InitSuccessResponse, idFile clusterid.File, initResp *initproto.InitSuccessResponse, mergeConfig bool, wr io.Writer,
mergeConfig bool, wr io.Writer, workspace string,
) error { ) error {
fmt.Fprint(wr, "Your Constellation cluster was successfully initialized.\n\n") fmt.Fprint(wr, "Your Constellation cluster was successfully initialized.\n\n")
@ -406,14 +407,14 @@ func (i *initCmd) writeOutput(
tw := tabwriter.NewWriter(wr, 0, 0, 2, ' ', 0) tw := tabwriter.NewWriter(wr, 0, 0, 2, ' ', 0)
// writeRow(tw, "Constellation cluster's owner identifier", ownerID) // writeRow(tw, "Constellation cluster's owner identifier", ownerID)
writeRow(tw, "Constellation cluster identifier", clusterID) writeRow(tw, "Constellation cluster identifier", clusterID)
writeRow(tw, "Kubernetes configuration", adminConfPath(workspace)) writeRow(tw, "Kubernetes configuration", i.pf.PrefixPath(constants.AdminConfFilename))
tw.Flush() tw.Flush()
fmt.Fprintln(wr) fmt.Fprintln(wr)
if err := i.fileHandler.Write(constants.AdminConfFilename, initResp.GetKubeconfig(), file.OptNone); err != nil { if err := i.fileHandler.Write(constants.AdminConfFilename, initResp.GetKubeconfig(), file.OptNone); err != nil {
return fmt.Errorf("writing kubeconfig: %w", err) return fmt.Errorf("writing kubeconfig: %w", err)
} }
i.log.Debugf("Kubeconfig written to %s", adminConfPath(workspace)) i.log.Debugf("Kubeconfig written to %s", i.pf.PrefixPath(constants.AdminConfFilename))
if mergeConfig { if mergeConfig {
if err := i.merger.mergeConfigs(constants.AdminConfFilename, i.fileHandler); err != nil { if err := i.merger.mergeConfigs(constants.AdminConfFilename, i.fileHandler); err != nil {
@ -430,11 +431,17 @@ func (i *initCmd) writeOutput(
if err := i.fileHandler.WriteJSON(constants.ClusterIDsFilename, idFile, file.OptOverwrite); err != nil { if err := i.fileHandler.WriteJSON(constants.ClusterIDsFilename, idFile, file.OptOverwrite); err != nil {
return fmt.Errorf("writing Constellation ID file: %w", err) return fmt.Errorf("writing Constellation ID file: %w", err)
} }
i.log.Debugf("Constellation ID file written to %s", clusterIDsPath(workspace)) i.log.Debugf("Constellation ID file written to %s", i.pf.PrefixPath(constants.ClusterIDsFilename))
if !mergeConfig { if !mergeConfig {
fmt.Fprintln(wr, "You can now connect to your cluster by executing:") fmt.Fprintln(wr, "You can now connect to your cluster by executing:")
fmt.Fprintf(wr, "\texport KUBECONFIG=\"$PWD/%s\"\n", adminConfPath(workspace))
exportPath, err := filepath.Abs(i.pf.PrefixPath(constants.AdminConfFilename))
if err != nil {
return fmt.Errorf("getting absolute path to kubeconfig: %w", err)
}
fmt.Fprintf(wr, "\texport KUBECONFIG=%q\n", exportPath)
} else { } else {
fmt.Fprintln(wr, "Constellation kubeconfig merged with default config.") fmt.Fprintln(wr, "Constellation kubeconfig merged with default config.")
@ -469,11 +476,12 @@ func (i *initCmd) evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
helmWaitMode = helm.WaitModeNone helmWaitMode = helm.WaitModeNone
} }
i.log.Debugf("Helm wait flag is %t", skipHelmWait) i.log.Debugf("Helm wait flag is %t", skipHelmWait)
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return initFlags{}, fmt.Errorf("parsing config path flag: %w", err) return initFlags{}, fmt.Errorf("parsing config path flag: %w", err)
} }
i.log.Debugf("Configuration path flag is %q", configPath) i.pf = pathprefix.New(workDir)
mergeConfigs, err := cmd.Flags().GetBool("merge-kubeconfig") mergeConfigs, err := cmd.Flags().GetBool("merge-kubeconfig")
if err != nil { if err != nil {
return initFlags{}, fmt.Errorf("parsing merge-kubeconfig flag: %w", err) return initFlags{}, fmt.Errorf("parsing merge-kubeconfig flag: %w", err)
@ -487,7 +495,6 @@ func (i *initCmd) evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
i.log.Debugf("force flag is %t", force) i.log.Debugf("force flag is %t", force)
return initFlags{ return initFlags{
workspace: workspace,
conformance: conformance, conformance: conformance,
helmWaitMode: helmWaitMode, helmWaitMode: helmWaitMode,
force: force, force: force,
@ -497,7 +504,6 @@ func (i *initCmd) evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
// initFlags are the resulting values of flag preprocessing. // initFlags are the resulting values of flag preprocessing.
type initFlags struct { type initFlags struct {
workspace string
conformance bool conformance bool
helmWaitMode helm.WaitMode helmWaitMode helm.WaitMode
force bool force bool
@ -505,7 +511,7 @@ type initFlags struct {
} }
// readOrGenerateMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret. // readOrGenerateMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret.
func (i *initCmd) generateMasterSecret(outWriter io.Writer, workspace string) (uri.MasterSecret, error) { func (i *initCmd) generateMasterSecret(outWriter io.Writer) (uri.MasterSecret, error) {
// No file given, generate a new secret, and save it to disk // No file given, generate a new secret, and save it to disk
i.log.Debugf("Generating new master secret") i.log.Debugf("Generating new master secret")
key, err := crypto.GenerateRandomBytes(crypto.MasterSecretLengthDefault) key, err := crypto.GenerateRandomBytes(crypto.MasterSecretLengthDefault)
@ -524,21 +530,21 @@ func (i *initCmd) generateMasterSecret(outWriter io.Writer, workspace string) (u
if err := i.fileHandler.WriteJSON(constants.MasterSecretFilename, secret, file.OptNone); err != nil { if err := i.fileHandler.WriteJSON(constants.MasterSecretFilename, secret, file.OptNone); err != nil {
return uri.MasterSecret{}, err return uri.MasterSecret{}, err
} }
fmt.Fprintf(outWriter, "Your Constellation master secret was successfully written to %q\n", masterSecretPath(workspace)) fmt.Fprintf(outWriter, "Your Constellation master secret was successfully written to %q\n", i.pf.PrefixPath(constants.MasterSecretFilename))
return secret, nil return secret, nil
} }
func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, workspace string, func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config,
) (string, error) { ) (string, error) {
i.log.Debugf("Getting service account URI") i.log.Debugf("Getting service account URI")
switch provider { switch provider {
case cloudprovider.GCP: case cloudprovider.GCP:
i.log.Debugf("Handling case for GCP") i.log.Debugf("Handling case for GCP")
i.log.Debugf("GCP service account key path %s", filepath.Join(workspace, config.Provider.GCP.ServiceAccountKeyPath)) i.log.Debugf("GCP service account key path %s", i.pf.PrefixPath(config.Provider.GCP.ServiceAccountKeyPath))
var key gcpshared.ServiceAccountKey var key gcpshared.ServiceAccountKey
if err := i.fileHandler.ReadJSON(config.Provider.GCP.ServiceAccountKeyPath, &key); err != nil { if err := i.fileHandler.ReadJSON(config.Provider.GCP.ServiceAccountKeyPath, &key); err != nil {
return "", fmt.Errorf("reading service account key from path %q: %w", filepath.Join(workspace, config.Provider.GCP.ServiceAccountKeyPath), err) return "", fmt.Errorf("reading service account key from path %q: %w", i.pf.PrefixPath(config.Provider.GCP.ServiceAccountKeyPath), err)
} }
i.log.Debugf("Read GCP service account key from path") i.log.Debugf("Read GCP service account key from path")
return key.ToCloudServiceAccountURI(), nil return key.ToCloudServiceAccountURI(), nil

View file

@ -21,6 +21,7 @@ import (
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto" "github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid" "github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/helm" "github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/atls"
@ -302,7 +303,7 @@ func TestWriteOutput(t *testing.T) {
IP: "cluster-ip", IP: "cluster-ip",
} }
i := newInitCmd(nil, nil, fileHandler, nil, &stubMerger{}, logger.NewTest(t)) i := newInitCmd(nil, nil, fileHandler, nil, &stubMerger{}, logger.NewTest(t))
err := i.writeOutput(idFile, resp.GetInitSuccess(), false, &out, "") err := i.writeOutput(idFile, resp.GetInitSuccess(), false, &out)
require.NoError(err) require.NoError(err)
// assert.Contains(out.String(), ownerID) // assert.Contains(out.String(), ownerID)
assert.Contains(out.String(), clusterID) assert.Contains(out.String(), clusterID)
@ -323,17 +324,19 @@ func TestWriteOutput(t *testing.T) {
require.NoError(afs.Remove(constants.AdminConfFilename)) require.NoError(afs.Remove(constants.AdminConfFilename))
// test custom workspace // test custom workspace
err = i.writeOutput(idFile, resp.GetInitSuccess(), true, &out, "some/path") i.pf = pathprefix.New("/some/path")
err = i.writeOutput(idFile, resp.GetInitSuccess(), true, &out)
require.NoError(err) require.NoError(err)
// assert.Contains(out.String(), ownerID) // assert.Contains(out.String(), ownerID)
assert.Contains(out.String(), clusterID) assert.Contains(out.String(), clusterID)
assert.Contains(out.String(), adminConfPath("some/path")) assert.Contains(out.String(), i.pf.PrefixPath(constants.AdminConfFilename))
out.Reset() out.Reset()
// File is written to current working dir, we simply pass the workspace for generating readable user output // File is written to current working dir, we simply pass the workspace for generating readable user output
require.NoError(afs.Remove(constants.AdminConfFilename)) require.NoError(afs.Remove(constants.AdminConfFilename))
i.pf = pathprefix.PathPrefixer{}
// test config merging // test config merging
err = i.writeOutput(idFile, resp.GetInitSuccess(), true, &out, "") err = i.writeOutput(idFile, resp.GetInitSuccess(), true, &out)
require.NoError(err) require.NoError(err)
// assert.Contains(out.String(), ownerID) // assert.Contains(out.String(), ownerID)
assert.Contains(out.String(), clusterID) assert.Contains(out.String(), clusterID)
@ -345,7 +348,7 @@ func TestWriteOutput(t *testing.T) {
// test config merging with env vars set // test config merging with env vars set
i.merger = &stubMerger{envVar: "/some/path/to/kubeconfig"} i.merger = &stubMerger{envVar: "/some/path/to/kubeconfig"}
err = i.writeOutput(idFile, resp.GetInitSuccess(), true, &out, "") err = i.writeOutput(idFile, resp.GetInitSuccess(), true, &out)
require.NoError(err) require.NoError(err)
// assert.Contains(out.String(), ownerID) // assert.Contains(out.String(), ownerID)
assert.Contains(out.String(), clusterID) assert.Contains(out.String(), clusterID)
@ -393,7 +396,7 @@ func TestGenerateMasterSecret(t *testing.T) {
var out bytes.Buffer var out bytes.Buffer
i := newInitCmd(nil, nil, fileHandler, nil, nil, logger.NewTest(t)) i := newInitCmd(nil, nil, fileHandler, nil, nil, logger.NewTest(t))
secret, err := i.generateMasterSecret(&out, "") secret, err := i.generateMasterSecret(&out)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)

View file

@ -13,6 +13,7 @@ import (
"net" "net"
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/featureset" "github.com/edgelesssys/constellation/v2/cli/internal/featureset"
"github.com/edgelesssys/constellation/v2/cli/internal/helm" "github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt" "github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
@ -229,16 +230,16 @@ type upFlags struct {
func (m *miniUpCmd) parseUpFlags(cmd *cobra.Command) (upFlags, error) { func (m *miniUpCmd) parseUpFlags(cmd *cobra.Command) (upFlags, error) {
m.log.Debugf("Preparing configuration") m.log.Debugf("Preparing configuration")
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return upFlags{}, fmt.Errorf("parsing config string: %w", err) return upFlags{}, fmt.Errorf("parsing config string: %w", err)
} }
m.log.Debugf("Configuration path is %q", configPath) m.log.Debugf("Workspace set to %q", workDir)
force, err := cmd.Flags().GetBool("force") force, err := cmd.Flags().GetBool("force")
if err != nil { if err != nil {
return upFlags{}, fmt.Errorf("parsing force bool: %w", err) return upFlags{}, fmt.Errorf("parsing force bool: %w", err)
} }
m.log.Debugf("force flag is %q", configPath) m.log.Debugf("force flag is %q", force)
logLevelString, err := cmd.Flags().GetString("tf-log") logLevelString, err := cmd.Flags().GetString("tf-log")
if err != nil { if err != nil {
@ -248,7 +249,7 @@ func (m *miniUpCmd) parseUpFlags(cmd *cobra.Command) (upFlags, error) {
if err != nil { if err != nil {
return upFlags{}, fmt.Errorf("parsing Terraform log level %s: %w", logLevelString, err) 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", terraformLogPath(workspace), logLevel.String()) m.log.Debugf("Terraform logs will be written into %s at level %s", pathprefix.New(workDir).PrefixPath(constants.TerraformLogFile), logLevel.String())
return upFlags{ return upFlags{
force: force, force: force,

View file

@ -0,0 +1,16 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "workspace",
srcs = ["workspace.go"],
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/cmd/workspace",
visibility = ["//cli:__subpackages__"],
deps = ["//internal/constants"],
)
go_library(
name = "pathprefix",
srcs = ["pathprefix.go"],
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix",
visibility = ["//cli:__subpackages__"],
)

View file

@ -0,0 +1,38 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
/*
Package pathprefix is used to print correct filepaths for a configured workspace.
The default workspace is the current working directory.
Users may override the default workspace using the --workspace flag.
The functions defined here should be used when printing any filepath to the user,
as they might otherwise be incorrect if the user has changed the workspace.
The prefixer MUST not be used when accessing files, as the workspace is changed
using os.Chdir() before the command is executed.
*/
package pathprefix
import (
"path/filepath"
)
// PathPrefixer is used to prefix paths with the configured workspace.
type PathPrefixer struct {
workspace string
}
// New returns a new PathPrefixer.
func New(workspace string) PathPrefixer {
return PathPrefixer{workspace: workspace}
}
// PrefixPath prefixes the given path with the configured workspace.
func (p PathPrefixer) PrefixPath(path string) string {
return filepath.Clean(filepath.Join(p.workspace, path))
}

View file

@ -17,6 +17,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/clusterid" "github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto" "github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/atls"
@ -50,6 +51,7 @@ func NewRecoverCmd() *cobra.Command {
type recoverCmd struct { type recoverCmd struct {
log debugLog log debugLog
configFetcher attestationconfigapi.Fetcher configFetcher attestationconfigapi.Fetcher
pf pathprefix.PathPrefixer
} }
func runRecover(cmd *cobra.Command, _ []string) error { func runRecover(cmd *cobra.Command, _ []string) error {
@ -77,12 +79,12 @@ func (r *recoverCmd) recover(
r.log.Debugf("Using flags: %+v", flags) r.log.Debugf("Using flags: %+v", flags)
var masterSecret uri.MasterSecret var masterSecret uri.MasterSecret
r.log.Debugf("Loading master secret file from %s", masterSecretPath(flags.workspace)) r.log.Debugf("Loading master secret file from %s", r.pf.PrefixPath(constants.MasterSecretFilename))
if err := fileHandler.ReadJSON(constants.MasterSecretFilename, &masterSecret); err != nil { if err := fileHandler.ReadJSON(constants.MasterSecretFilename, &masterSecret); err != nil {
return err return err
} }
r.log.Debugf("Loading configuration file from %q", configPath(flags.workspace)) r.log.Debugf("Loading configuration file from %q", r.pf.PrefixPath(constants.ConfigFilename))
conf, err := config.New(fileHandler, constants.ConfigFilename, r.configFetcher, flags.force) conf, err := config.New(fileHandler, constants.ConfigFilename, r.configFetcher, flags.force)
var configValidationErr *config.ValidationError var configValidationErr *config.ValidationError
if errors.As(err, &configValidationErr) { if errors.As(err, &configValidationErr) {
@ -210,18 +212,18 @@ func (d *recoverDoer) setURIs(kmsURI, storageURI string) {
} }
type recoverFlags struct { type recoverFlags struct {
endpoint string endpoint string
workspace string maaURL string
maaURL string force bool
force bool
} }
func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFlags, error) { func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFlags, error) {
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return recoverFlags{}, fmt.Errorf("parsing config path argument: %w", err) return recoverFlags{}, fmt.Errorf("parsing config path argument: %w", err)
} }
r.log.Debugf("Workspace set to %q", workspace) r.log.Debugf("Workspace set to %q", workDir)
r.pf = pathprefix.New(workDir)
var idFile clusterid.File var idFile clusterid.File
if err := fileHandler.ReadJSON(constants.ClusterIDsFilename, &idFile); err != nil && !errors.Is(err, afero.ErrFileNotFound) { if err := fileHandler.ReadJSON(constants.ClusterIDsFilename, &idFile); err != nil && !errors.Is(err, afero.ErrFileNotFound) {
@ -248,10 +250,9 @@ func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Hand
} }
return recoverFlags{ return recoverFlags{
endpoint: endpoint, endpoint: endpoint,
workspace: workspace, maaURL: idFile.AttestationURL,
maaURL: idFile.AttestationURL, force: force,
force: force,
}, nil }, nil
} }

View file

@ -208,8 +208,7 @@ func TestParseRecoverFlags(t *testing.T) {
"all args set": { "all args set": {
args: []string{"-e", "192.0.2.42:2", "--workspace", "./constellation-workspace"}, args: []string{"-e", "192.0.2.42:2", "--workspace", "./constellation-workspace"},
wantFlags: recoverFlags{ wantFlags: recoverFlags{
endpoint: "192.0.2.42:2", endpoint: "192.0.2.42:2",
workspace: "./constellation-workspace",
}, },
}, },
} }

View file

@ -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/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "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"
@ -53,6 +54,7 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.
if err != nil { if err != nil {
return fmt.Errorf("parsing flags: %w", err) return fmt.Errorf("parsing flags: %w", err)
} }
pf := pathprefix.New(flags.workspace)
if !flags.yes { if !flags.yes {
cmd.Println("You are about to terminate a Constellation cluster.") cmd.Println("You are about to terminate a Constellation cluster.")
@ -79,11 +81,11 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.
var removeErr error var removeErr error
if err := fileHandler.Remove(constants.AdminConfFilename); err != nil && !errors.Is(err, fs.ErrNotExist) { if err := fileHandler.Remove(constants.AdminConfFilename); err != nil && !errors.Is(err, fs.ErrNotExist) {
removeErr = errors.Join(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", adminConfPath(flags.workspace))) removeErr = errors.Join(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", pf.PrefixPath(constants.AdminConfFilename)))
} }
if err := fileHandler.Remove(constants.ClusterIDsFilename); err != nil && !errors.Is(err, fs.ErrNotExist) { if err := fileHandler.Remove(constants.ClusterIDsFilename); err != nil && !errors.Is(err, fs.ErrNotExist) {
removeErr = errors.Join(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", clusterIDsPath(flags.workspace))) removeErr = errors.Join(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", pf.PrefixPath(constants.ClusterIDsFilename)))
} }
return removeErr return removeErr

View file

@ -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/clusterid" "github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/helm" "github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/kubernetes" "github.com/edgelesssys/constellation/v2/cli/internal/kubernetes"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/cli/internal/terraform"
@ -284,7 +285,7 @@ func (u *upgradeApplyCmd) migrateTerraform(
cmd.Printf("Terraform migrations applied successfully and output written to: %s\n"+ cmd.Printf("Terraform migrations applied successfully and output written to: %s\n"+
"A backup of the pre-upgrade state has been written to: %s\n", "A backup of the pre-upgrade state has been written to: %s\n",
clusterIDsPath(flags.workspace), filepath.Join(opts.UpgradeWorkspace, u.upgrader.GetUpgradeID(), constants.TerraformUpgradeBackupDir)) flags.pf.PrefixPath(constants.ClusterIDsFilename), flags.pf.PrefixPath(filepath.Join(opts.UpgradeWorkspace, u.upgrader.GetUpgradeID(), constants.TerraformUpgradeBackupDir)))
} else { } else {
u.log.Debugf("No Terraform diff detected") u.log.Debugf("No Terraform diff detected")
} }
@ -377,7 +378,7 @@ func (u *upgradeApplyCmd) handleServiceUpgrade(cmd *cobra.Command, conf *config.
} }
func parseUpgradeApplyFlags(cmd *cobra.Command) (upgradeApplyFlags, error) { func parseUpgradeApplyFlags(cmd *cobra.Command) (upgradeApplyFlags, error) {
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return upgradeApplyFlags{}, err return upgradeApplyFlags{}, err
} }
@ -407,7 +408,7 @@ func parseUpgradeApplyFlags(cmd *cobra.Command) (upgradeApplyFlags, error) {
} }
return upgradeApplyFlags{ return upgradeApplyFlags{
workspace: workspace, pf: pathprefix.New(workDir),
yes: yes, yes: yes,
upgradeTimeout: timeout, upgradeTimeout: timeout,
force: force, force: force,
@ -429,7 +430,7 @@ func mergeClusterIDFile(clusterIDPath string, newIDFile clusterid.File, fileHand
} }
type upgradeApplyFlags struct { type upgradeApplyFlags struct {
workspace string pf pathprefix.PathPrefixer
yes bool yes bool
upgradeTimeout time.Duration upgradeTimeout time.Duration
force bool force bool

View file

@ -24,6 +24,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/clusterid" "github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
@ -89,7 +90,7 @@ func (c *verifyCmd) verify(cmd *cobra.Command, fileHandler file.Handler, verifyC
} }
c.log.Debugf("Using flags: %+v", flags) c.log.Debugf("Using flags: %+v", flags)
c.log.Debugf("Loading configuration file from %q", configPath(flags.workspace)) c.log.Debugf("Loading configuration file from %q", flags.pf.PrefixPath(constants.ConfigFilename))
conf, err := config.New(fileHandler, constants.ConfigFilename, configFetcher, flags.force) conf, err := config.New(fileHandler, constants.ConfigFilename, configFetcher, flags.force)
var configValidationErr *config.ValidationError var configValidationErr *config.ValidationError
if errors.As(err, &configValidationErr) { if errors.As(err, &configValidationErr) {
@ -149,11 +150,12 @@ func (c *verifyCmd) verify(cmd *cobra.Command, fileHandler file.Handler, verifyC
} }
func (c *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handler) (verifyFlags, error) { func (c *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handler) (verifyFlags, error) {
workspace, err := cmd.Flags().GetString("workspace") workDir, err := cmd.Flags().GetString("workspace")
if err != nil { if err != nil {
return verifyFlags{}, fmt.Errorf("parsing config path argument: %w", err) return verifyFlags{}, fmt.Errorf("parsing config path argument: %w", err)
} }
c.log.Debugf("Flag 'workspace' set to %q", workspace) c.log.Debugf("Flag 'workspace' set to %q", workDir)
pf := pathprefix.New(workDir)
ownerID := "" ownerID := ""
clusterID, err := cmd.Flags().GetString("cluster-id") clusterID, err := cmd.Flags().GetString("cluster-id")
@ -189,13 +191,13 @@ func (c *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle
emptyEndpoint := endpoint == "" emptyEndpoint := endpoint == ""
emptyIDs := ownerID == "" && clusterID == "" emptyIDs := ownerID == "" && clusterID == ""
if emptyEndpoint || emptyIDs { if emptyEndpoint || emptyIDs {
c.log.Debugf("Trying to supplement empty flag values from %q", clusterIDsPath(workspace)) c.log.Debugf("Trying to supplement empty flag values from %q", pf.PrefixPath(constants.ClusterIDsFilename))
if emptyEndpoint { if emptyEndpoint {
cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", clusterIDsPath(workspace)) cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", pf.PrefixPath(constants.ClusterIDsFilename))
endpoint = idFile.IP endpoint = idFile.IP
} }
if emptyIDs { if emptyIDs {
cmd.Printf("Using ID from %q. Specify --cluster-id to override this.\n", clusterIDsPath(workspace)) cmd.Printf("Using ID from %q. Specify --cluster-id to override this.\n", pf.PrefixPath(constants.ClusterIDsFilename))
ownerID = idFile.OwnerID ownerID = idFile.OwnerID
clusterID = idFile.ClusterID clusterID = idFile.ClusterID
} }
@ -212,7 +214,7 @@ func (c *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle
return verifyFlags{ return verifyFlags{
endpoint: endpoint, endpoint: endpoint,
workspace: workspace, pf: pf,
ownerID: ownerID, ownerID: ownerID,
clusterID: clusterID, clusterID: clusterID,
maaURL: idFile.AttestationURL, maaURL: idFile.AttestationURL,
@ -225,10 +227,10 @@ type verifyFlags struct {
endpoint string endpoint string
ownerID string ownerID string
clusterID string clusterID string
workspace string
maaURL string maaURL string
rawOutput bool rawOutput bool
force bool force bool
pf pathprefix.PathPrefixer
} }
func addPortIfMissing(endpoint string, defaultPort int) (string, error) { func addPortIfMissing(endpoint string, defaultPort int) (string, error) {

View file

@ -1,54 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package cmd
import (
"path/filepath"
"github.com/edgelesssys/constellation/v2/internal/constants"
)
// Users may override the default workspace using the --workspace flag.
// The default workspace is the current working directory.
// The following functions return paths relative to the set workspace,
// and should be used when printing the path to the user.
// The MUST not be used when accessing files, as the workspace is changed
// using os.Chdir() before the command is executed.
func adminConfPath(workspace string) string {
return filepath.Join(workspace, constants.AdminConfFilename)
}
func configPath(workspace string) string {
return filepath.Join(workspace, constants.ConfigFilename)
}
func clusterIDsPath(workspace string) string {
return filepath.Join(workspace, constants.ClusterIDsFilename)
}
func masterSecretPath(workspace string) string {
return filepath.Join(workspace, constants.MasterSecretFilename)
}
func terraformClusterWorkspace(workspace string) string {
return filepath.Join(workspace, constants.TerraformWorkingDir)
}
func terraformIAMWorkspace(workspace string) string {
return filepath.Join(workspace, constants.TerraformIAMWorkingDir)
}
func terraformLogPath(workspace string) string {
return filepath.Join(workspace, constants.TerraformLogFile)
}
const gcpServiceAccountKeyFile = "gcpServiceAccountKey.json"
func gcpServiceAccountKeyPath(workspace string) string {
return filepath.Join(workspace, gcpServiceAccountKeyFile)
}

View file

@ -88,6 +88,8 @@ const (
TerraformWorkingDir = "constellation-terraform" TerraformWorkingDir = "constellation-terraform"
// TerraformIAMWorkingDir is the directory name for the Terraform IAM Client workspace. // TerraformIAMWorkingDir is the directory name for the Terraform IAM Client workspace.
TerraformIAMWorkingDir = "constellation-iam-terraform" TerraformIAMWorkingDir = "constellation-iam-terraform"
// GCPServiceAccountKeyFilename is the file name for the GCP service account key file.
GCPServiceAccountKeyFilename = "gcpServiceAccountKey.json"
// ErrorLog file which contains server errors during init. // ErrorLog file which contains server errors during init.
ErrorLog = "constellation-cluster.log" ErrorLog = "constellation-cluster.log"
// ControlPlaneAdminConfFilename filepath to control plane kubernetes admin config. // ControlPlaneAdminConfFilename filepath to control plane kubernetes admin config.