From 89b342900fded88b56478a19faba86d03cb3e6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wei=C3=9Fe?= Date: Tue, 8 Aug 2023 15:42:06 +0200 Subject: [PATCH] Move workspace path functions to sub-package of cmd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Weiße --- cli/internal/cmd/BUILD.bazel | 3 +- cli/internal/cmd/configfetchmeasurements.go | 12 ++--- cli/internal/cmd/configgenerate.go | 9 ++-- cli/internal/cmd/create.go | 31 ++++++------ cli/internal/cmd/create_test.go | 2 +- cli/internal/cmd/iamcreate.go | 51 ++++++++++--------- cli/internal/cmd/iamcreate_test.go | 4 +- cli/internal/cmd/iamdestroy.go | 43 ++++++++-------- cli/internal/cmd/iamdestroy_test.go | 8 +-- cli/internal/cmd/init.go | 44 +++++++++-------- cli/internal/cmd/init_test.go | 15 +++--- cli/internal/cmd/miniup.go | 9 ++-- cli/internal/cmd/pathprefix/BUILD.bazel | 16 ++++++ cli/internal/cmd/pathprefix/pathprefix.go | 38 +++++++++++++++ cli/internal/cmd/recover.go | 25 +++++----- cli/internal/cmd/recover_test.go | 3 +- cli/internal/cmd/terminate.go | 6 ++- cli/internal/cmd/upgradeapply.go | 9 ++-- cli/internal/cmd/verify.go | 18 ++++--- cli/internal/cmd/workspace.go | 54 --------------------- internal/constants/constants.go | 2 + 21 files changed, 213 insertions(+), 189 deletions(-) create mode 100644 cli/internal/cmd/pathprefix/BUILD.bazel create mode 100644 cli/internal/cmd/pathprefix/pathprefix.go delete mode 100644 cli/internal/cmd/workspace.go diff --git a/cli/internal/cmd/BUILD.bazel b/cli/internal/cmd/BUILD.bazel index 9c4c6a9fb..6acd46655 100644 --- a/cli/internal/cmd/BUILD.bazel +++ b/cli/internal/cmd/BUILD.bazel @@ -36,7 +36,6 @@ go_library( "validargs.go", "verify.go", "version.go", - "workspace.go", ], importpath = "github.com/edgelesssys/constellation/v2/cli/internal/cmd", visibility = ["//cli:__subpackages__"], @@ -44,6 +43,7 @@ go_library( "//bootstrapper/initproto", "//cli/internal/cloudcmd", "//cli/internal/clusterid", + "//cli/internal/cmd/pathprefix", "//cli/internal/featureset", "//cli/internal/helm", "//cli/internal/kubernetes", @@ -140,6 +140,7 @@ go_test( "//bootstrapper/initproto", "//cli/internal/cloudcmd", "//cli/internal/clusterid", + "//cli/internal/cmd/pathprefix", "//cli/internal/helm", "//cli/internal/kubernetes", "//cli/internal/terraform", diff --git a/cli/internal/cmd/configfetchmeasurements.go b/cli/internal/cmd/configfetchmeasurements.go index bd023ac0d..6d293dcf4 100644 --- a/cli/internal/cmd/configfetchmeasurements.go +++ b/cli/internal/cmd/configfetchmeasurements.go @@ -12,9 +12,9 @@ import ( "fmt" "net/http" "net/url" - "path/filepath" "time" + "github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix" "github.com/edgelesssys/constellation/v2/cli/internal/featureset" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/versionsapi" @@ -50,7 +50,7 @@ type fetchMeasurementsFlags struct { signatureURL *url.URL insecure bool force bool - workspace string + pf pathprefix.PathPrefixer } type configFetchMeasurementsCmd struct { @@ -90,7 +90,7 @@ func (cfm *configFetchMeasurementsCmd) configFetchMeasurements( 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) var configValidationErr *config.ValidationError @@ -173,7 +173,7 @@ func (cfm *configFetchMeasurementsCmd) configFetchMeasurements( if err := fileHandler.WriteYAML(constants.ConfigFilename, conf, file.OptOverwrite); err != nil { 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") return nil } @@ -194,7 +194,7 @@ func (cfm *configFetchMeasurementsCmd) parseURLFlag(cmd *cobra.Command, flag str } func (cfm *configFetchMeasurementsCmd) parseFetchMeasurementsFlags(cmd *cobra.Command) (*fetchMeasurementsFlags, error) { - workspace, err := cmd.Flags().GetString("workspace") + workDir, err := cmd.Flags().GetString("workspace") if err != nil { return nil, fmt.Errorf("parsing workspace argument: %w", err) } @@ -226,7 +226,7 @@ func (cfm *configFetchMeasurementsCmd) parseFetchMeasurementsFlags(cmd *cobra.Co signatureURL: measurementsSignatureURL, insecure: insecure, force: force, - workspace: workspace, + pf: pathprefix.New(workDir), }, nil } diff --git a/cli/internal/cmd/configgenerate.go b/cli/internal/cmd/configgenerate.go index ef88bcd6d..9f38b9dc4 100644 --- a/cli/internal/cmd/configgenerate.go +++ b/cli/internal/cmd/configgenerate.go @@ -10,6 +10,7 @@ import ( "fmt" "strings" + "github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix" "github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/compatibility" @@ -41,7 +42,7 @@ func newConfigGenerateCmd() *cobra.Command { } type generateFlags struct { - workspace string + pf pathprefix.PathPrefixer k8sVersion string attestationVariant variant.Variant } @@ -80,7 +81,7 @@ func (cg *configGenerateCmd) configGenerate(cmd *cobra.Command, fileHandler file 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("For more information refer to the documentation:") 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) { - workspace, err := cmd.Flags().GetString("workspace") + workDir, err := cmd.Flags().GetString("workspace") if err != nil { return generateFlags{}, fmt.Errorf("parsing workspace flag: %w", err) } @@ -166,7 +167,7 @@ func parseGenerateFlags(cmd *cobra.Command) (generateFlags, error) { } } return generateFlags{ - workspace: workspace, + pf: pathprefix.New(workDir), k8sVersion: resolvedVersion, attestationVariant: attestationVariant, }, nil diff --git a/cli/internal/cmd/create.go b/cli/internal/cmd/create.go index e7a7f970f..3c3c957ce 100644 --- a/cli/internal/cmd/create.go +++ b/cli/internal/cmd/create.go @@ -12,6 +12,7 @@ import ( "io/fs" "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/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/api/versionsapi" @@ -44,6 +45,7 @@ func NewCreateCmd() *cobra.Command { type createCmd struct { log debugLog + pf pathprefix.PathPrefixer } func runCreate(cmd *cobra.Command, _ []string) error { @@ -71,11 +73,11 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler return err } 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 } - 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) c.log.Debugf("Configuration file loaded: %+v", conf) 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) spinner.Stop() 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") @@ -188,11 +190,12 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) { } c.log.Debugf("Yes flag is %t", yes) - workspace, err := cmd.Flags().GetString("workspace") + workDir, err := cmd.Flags().GetString("workspace") if err != nil { 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") if err != nil { @@ -208,10 +211,9 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) { 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", 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{ - workspace: workspace, tfLogLevel: logLevel, force: force, yes: yes, @@ -220,44 +222,43 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) { // createFlags contains the parsed flags of the create command. type createFlags struct { - workspace string tfLogLevel terraform.LogLevel force bool yes bool } // 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") 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") 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") 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 } -func translateCreateErrors(cmd *cobra.Command, workspace string, err error) error { +func translateCreateErrors(cmd *cobra.Command, pf pathprefix.PathPrefixer, err error) error { switch { 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("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.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.") return err 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("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.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.") return err default: diff --git a/cli/internal/cmd/create_test.go b/cli/internal/cmd/create_test.go index e4dacafe6..128bd61f0 100644 --- a/cli/internal/cmd/create_test.go +++ b/cli/internal/cmd/create_test.go @@ -255,7 +255,7 @@ func TestCheckDirClean(t *testing.T) { require.NoError(tc.fileHandler.Write(f, []byte{1, 2, 3}, file.OptNone)) } c := &createCmd{log: logger.NewTest(t)} - err := c.checkDirClean("", tc.fileHandler) + err := c.checkDirClean(tc.fileHandler) if tc.wantErr { assert.Error(err) diff --git a/cli/internal/cmd/iamcreate.go b/cli/internal/cmd/iamcreate.go index 286262a79..6f5d68546 100644 --- a/cli/internal/cmd/iamcreate.go +++ b/cli/internal/cmd/iamcreate.go @@ -14,6 +14,7 @@ import ( "strings" "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/internal/cloud/cloudprovider" "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 // and starts IAM creation for the specific cloud provider. 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 { case cloudprovider.AWS: - providerCreator = func(string) providerIAMCreator { return &awsIAMCreator{} } + providerCreator = func(pathprefix.PathPrefixer) providerIAMCreator { return &awsIAMCreator{} } case cloudprovider.Azure: - providerCreator = func(string) providerIAMCreator { return &azureIAMCreator{} } + providerCreator = func(pathprefix.PathPrefixer) providerIAMCreator { return &azureIAMCreator{} } case cloudprovider.GCP: - providerCreator = func(workspace string) providerIAMCreator { - return &gcpIAMCreator{workspace} + providerCreator = func(pf pathprefix.PathPrefixer) providerIAMCreator { + return &gcpIAMCreator{pf} } default: return func(cmd *cobra.Command, args []string) error { @@ -154,25 +155,26 @@ func createRunIAMFunc(provider cloudprovider.Provider) func(cmd *cobra.Command, if err != nil { 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 { 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 { return fmt.Errorf("creating iamCreator: %w", err) } defer iamCreator.spinner.Stop() defer iamCreator.log.Sync() iamCreator.provider = provider - iamCreator.providerCreator = providerCreator(workspace) + iamCreator.providerCreator = providerCreator(pf) return iamCreator.create(cmd.Context()) } } // 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) if err != nil { 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 { 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{ cmd: cmd, @@ -206,6 +208,7 @@ type iamCreator struct { providerCreator providerIAMCreator iamConfig *cloudcmd.IAMConfigOptions log debugLog + pf pathprefix.PathPrefixer } // 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) - if err := c.checkWorkingDir(flags.workspace); err != nil { + if err := c.checkWorkingDir(); err != nil { return err } @@ -235,14 +238,14 @@ func (c *iamCreator) create(ctx context.Context) error { var conf config.Config 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 { return fmt.Errorf("error reading the configuration file: %w", err) } if err := validateConfigWithFlagCompatibility(c.provider, conf, flags); err != nil { 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) @@ -260,12 +263,12 @@ func (c *iamCreator) create(ctx context.Context) error { } 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) if err := c.fileHandler.WriteYAML(constants.ConfigFilename, conf, file.OptOverwrite); err != nil { 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 } @@ -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). func (c *iamCreator) parseFlagsAndSetupConfig() (iamFlags, error) { - cwd, err := c.cmd.Flags().GetString("workspace") + workDir, err := c.cmd.Flags().GetString("workspace") if err != nil { return iamFlags{}, fmt.Errorf("parsing config string: %w", err) } + c.pf = pathprefix.New(workDir) + yesFlag, err := c.cmd.Flags().GetBool("yes") if err != nil { return iamFlags{}, fmt.Errorf("parsing yes bool: %w", err) @@ -291,7 +296,6 @@ func (c *iamCreator) parseFlagsAndSetupConfig() (iamFlags, error) { } flags := iamFlags{ - workspace: cwd, yesFlag: yesFlag, updateConfig: updateConfig, } @@ -305,9 +309,9 @@ func (c *iamCreator) parseFlagsAndSetupConfig() (iamFlags, error) { } // 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 { - 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 } @@ -317,7 +321,6 @@ type iamFlags struct { aws awsFlags azure azureFlags gcp gcpFlags - workspace string yesFlag bool updateConfig bool } @@ -488,7 +491,7 @@ func (c *azureIAMCreator) parseAndWriteIDFile(_ cloudcmd.IAMOutput, _ file.Handl // gcpIAMCreator implements the providerIAMCreator interface for GCP. type gcpIAMCreator struct { - workspace string + pf pathprefix.PathPrefixer } 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("region:\t\t\t%s\n", flags.gcp.region) 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) { 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.Zone = flags.gcp.zone for groupName, group := range conf.NodeGroups { @@ -573,7 +576,7 @@ func (c *gcpIAMCreator) parseAndWriteIDFile(iamFile cloudcmd.IAMOutput, fileHand 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. diff --git a/cli/internal/cmd/iamcreate_test.go b/cli/internal/cmd/iamcreate_test.go index 2bd61036f..fee84186d 100644 --- a/cli/internal/cmd/iamcreate_test.go +++ b/cli/internal/cmd/iamcreate_test.go @@ -707,13 +707,13 @@ func TestIAMCreateGCP(t *testing.T) { readConfig := &config.Config{} readErr := fileHandler.ReadYAML(constants.ConfigFilename, readConfig) require.NoError(readErr) - assert.Equal(gcpServiceAccountKeyFile, readConfig.Provider.GCP.ServiceAccountKeyPath) + assert.Equal(constants.GCPServiceAccountKeyFilename, readConfig.Provider.GCP.ServiceAccountKeyPath) } require.NoError(err) assert.True(tc.creator.createCalled) assert.Equal(tc.creator.id.GCPOutput, validIAMIDFile.GCPOutput) readServiceAccountKey := &map[string]string{} - readErr := fileHandler.ReadJSON(gcpServiceAccountKeyFile, readServiceAccountKey) + readErr := fileHandler.ReadJSON(constants.GCPServiceAccountKeyFilename, readServiceAccountKey) require.NoError(readErr) assert.Equal("not_a_secret", (*readServiceAccountKey)["private_key_id"]) }) diff --git a/cli/internal/cmd/iamdestroy.go b/cli/internal/cmd/iamdestroy.go index 09d2e3dd6..7f930e43f 100644 --- a/cli/internal/cmd/iamdestroy.go +++ b/cli/internal/cmd/iamdestroy.go @@ -11,6 +11,7 @@ import ( "os" "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/internal/cloud/gcpshared" "github.com/edgelesssys/constellation/v2/internal/constants" @@ -51,6 +52,7 @@ func runIAMDestroy(cmd *cobra.Command, _ []string) error { type destroyCmd struct { log debugLog + pf pathprefix.PathPrefixer } 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 - 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) 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) 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 - c.log.Debugf("Checking if %q exists", gcpServiceAccountKeyPath(flags.workspace)) - _, err = fsHandler.Stat(gcpServiceAccountKeyFile) + c.log.Debugf("Checking if %q exists", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename)) + _, err = fsHandler.Stat(constants.GCPServiceAccountKeyFilename) if err != nil { if !errors.Is(err, os.ErrNotExist) { return err } } else { - c.log.Debugf("%q exists", gcpServiceAccountKeyPath(flags.workspace)) + c.log.Debugf("%q exists", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename)) gcpFileExists = true } @@ -88,7 +90,7 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr // Confirmation confirmString := "Do you really want to destroy your IAM configuration? Note that this will remove all resources in the resource group." 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) if err != nil { @@ -101,8 +103,8 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr } if gcpFileExists { - c.log.Debugf("Starting to delete %q", gcpServiceAccountKeyPath(flags.workspace)) - proceed, err := c.deleteGCPServiceAccountKeyFile(cmd, destroyer, flags.workspace, fsHandler) + c.log.Debugf("Starting to delete %q", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename)) + proceed, err := c.deleteGCPServiceAccountKeyFile(cmd, destroyer, fsHandler) if err != nil { return err } @@ -125,11 +127,11 @@ func (c *destroyCmd) iamDestroy(cmd *cobra.Command, spinner spinnerInterf, destr 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 - c.log.Debugf("Parsing %q", gcpServiceAccountKeyPath(workspace)) - if err := fsHandler.ReadJSON(gcpServiceAccountKeyFile, &fileSaKey); err != nil { + c.log.Debugf("Parsing %q", c.pf.PrefixPath(constants.GCPServiceAccountKeyFilename)) + if err := fsHandler.ReadJSON(constants.GCPServiceAccountKeyFilename, &fileSaKey); err != nil { return false, err } @@ -141,21 +143,20 @@ func (c *destroyCmd) deleteGCPServiceAccountKeyFile(cmd *cobra.Command, destroye c.log.Debugf("Checking if keys are the same") 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 } - if err := fsHandler.Remove(gcpServiceAccountKeyFile); err != nil { + if err := fsHandler.Remove(constants.GCPServiceAccountKeyFilename); err != nil { 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 } type destroyFlags struct { yes bool - workspace string tfLogLevel terraform.LogLevel } @@ -167,11 +168,12 @@ func (c *destroyCmd) parseDestroyFlags(cmd *cobra.Command) (destroyFlags, error) } c.log.Debugf("Yes flag is %t", yes) - workspace, err := cmd.Flags().GetString("workspace") + workDir, err := cmd.Flags().GetString("workspace") if err != nil { 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") if err != nil { @@ -181,11 +183,10 @@ func (c *destroyCmd) parseDestroyFlags(cmd *cobra.Command) (destroyFlags, error) 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", 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{ tfLogLevel: logLevel, - workspace: workspace, yes: yes, }, nil } diff --git a/cli/internal/cmd/iamdestroy_test.go b/cli/internal/cmd/iamdestroy_test.go index 5ad4d6249..d208ea9fc 100644 --- a/cli/internal/cmd/iamdestroy_test.go +++ b/cli/internal/cmd/iamdestroy_test.go @@ -24,7 +24,7 @@ func TestIAMDestroy(t *testing.T) { newFsExists := func() file.Handler { fh := file.NewHandler(afero.NewMemMapFs()) - require.NoError(fh.Write(gcpServiceAccountKeyFile, []byte("{}"))) + require.NoError(fh.Write(constants.GCPServiceAccountKeyFilename, []byte("{}"))) return fh } newFsMissing := func() file.Handler { @@ -147,12 +147,12 @@ func TestDeleteGCPServiceAccountKeyFile(t *testing.T) { newFs := func() file.Handler { fs := file.NewHandler(afero.NewMemMapFs()) - require.NoError(fs.Write(gcpServiceAccountKeyFile, []byte(gcpFile))) + require.NoError(fs.Write(constants.GCPServiceAccountKeyFilename, []byte(gcpFile))) return fs } newFsInvalidJSON := func() file.Handler { fh := file.NewHandler(afero.NewMemMapFs()) - require.NoError(fh.Write(gcpServiceAccountKeyFile, []byte("asdf"))) + require.NoError(fh.Write(constants.GCPServiceAccountKeyFilename, []byte("asdf"))) return fh } @@ -202,7 +202,7 @@ func TestDeleteGCPServiceAccountKeyFile(t *testing.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 { assert.Error(err) } else { diff --git a/cli/internal/cmd/init.go b/cli/internal/cmd/init.go index 7661ff81d..2fed1b6e1 100644 --- a/cli/internal/cmd/init.go +++ b/cli/internal/cmd/init.go @@ -37,6 +37,7 @@ import ( "github.com/edgelesssys/constellation/v2/bootstrapper/initproto" "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "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/terraform" "github.com/edgelesssys/constellation/v2/internal/cloud/azureshared" @@ -79,6 +80,7 @@ type initCmd struct { fileHandler file.Handler helmInstaller initializer clusterShower clusterShower + pf pathprefix.PathPrefixer } type clusterShower interface { @@ -143,7 +145,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator atls.V return err } 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) var configValidationErr *config.ValidationError 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) } i.log.Debugf("Created a new validator") - serviceAccURI, err := i.getMarshaledServiceAccountURI(provider, conf, flags.workspace) + serviceAccURI, err := i.getMarshaledServiceAccountURI(provider, conf) if err != nil { return err } i.log.Debugf("Successfully marshaled service account URI") i.log.Debugf("Generating master secret") - masterSecret, err := i.generateMasterSecret(cmd.OutOrStdout(), flags.workspace) + masterSecret, err := i.generateMasterSecret(cmd.OutOrStdout()) if err != nil { 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 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 } @@ -394,8 +396,7 @@ func (d *initDoer) handleGRPCStateChanges(ctx context.Context, wg *sync.WaitGrou } func (i *initCmd) writeOutput( - idFile clusterid.File, initResp *initproto.InitSuccessResponse, - mergeConfig bool, wr io.Writer, workspace string, + idFile clusterid.File, initResp *initproto.InitSuccessResponse, mergeConfig bool, wr io.Writer, ) error { 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) // writeRow(tw, "Constellation cluster's owner identifier", ownerID) writeRow(tw, "Constellation cluster identifier", clusterID) - writeRow(tw, "Kubernetes configuration", adminConfPath(workspace)) + writeRow(tw, "Kubernetes configuration", i.pf.PrefixPath(constants.AdminConfFilename)) tw.Flush() fmt.Fprintln(wr) if err := i.fileHandler.Write(constants.AdminConfFilename, initResp.GetKubeconfig(), file.OptNone); err != nil { 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 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 { 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 { 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 { 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 } i.log.Debugf("Helm wait flag is %t", skipHelmWait) - workspace, err := cmd.Flags().GetString("workspace") + workDir, err := cmd.Flags().GetString("workspace") if err != nil { 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") if err != nil { 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) return initFlags{ - workspace: workspace, conformance: conformance, helmWaitMode: helmWaitMode, force: force, @@ -497,7 +504,6 @@ func (i *initCmd) evalFlagArgs(cmd *cobra.Command) (initFlags, error) { // initFlags are the resulting values of flag preprocessing. type initFlags struct { - workspace string conformance bool helmWaitMode helm.WaitMode 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. -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 i.log.Debugf("Generating new master secret") 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 { 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 } -func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, workspace string, +func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, ) (string, error) { i.log.Debugf("Getting service account URI") switch provider { case cloudprovider.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 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") return key.ToCloudServiceAccountURI(), nil diff --git a/cli/internal/cmd/init_test.go b/cli/internal/cmd/init_test.go index 3cc92a6ce..fb36d1920 100644 --- a/cli/internal/cmd/init_test.go +++ b/cli/internal/cmd/init_test.go @@ -21,6 +21,7 @@ import ( "github.com/edgelesssys/constellation/v2/bootstrapper/initproto" "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/terraform" "github.com/edgelesssys/constellation/v2/internal/atls" @@ -302,7 +303,7 @@ func TestWriteOutput(t *testing.T) { IP: "cluster-ip", } 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) // assert.Contains(out.String(), ownerID) assert.Contains(out.String(), clusterID) @@ -323,17 +324,19 @@ func TestWriteOutput(t *testing.T) { require.NoError(afs.Remove(constants.AdminConfFilename)) // 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) // assert.Contains(out.String(), ownerID) assert.Contains(out.String(), clusterID) - assert.Contains(out.String(), adminConfPath("some/path")) + assert.Contains(out.String(), i.pf.PrefixPath(constants.AdminConfFilename)) out.Reset() // File is written to current working dir, we simply pass the workspace for generating readable user output require.NoError(afs.Remove(constants.AdminConfFilename)) + i.pf = pathprefix.PathPrefixer{} // test config merging - err = i.writeOutput(idFile, resp.GetInitSuccess(), true, &out, "") + err = i.writeOutput(idFile, resp.GetInitSuccess(), true, &out) require.NoError(err) // assert.Contains(out.String(), ownerID) assert.Contains(out.String(), clusterID) @@ -345,7 +348,7 @@ func TestWriteOutput(t *testing.T) { // test config merging with env vars set 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) // assert.Contains(out.String(), ownerID) assert.Contains(out.String(), clusterID) @@ -393,7 +396,7 @@ func TestGenerateMasterSecret(t *testing.T) { var out bytes.Buffer i := newInitCmd(nil, nil, fileHandler, nil, nil, logger.NewTest(t)) - secret, err := i.generateMasterSecret(&out, "") + secret, err := i.generateMasterSecret(&out) if tc.wantErr { assert.Error(err) diff --git a/cli/internal/cmd/miniup.go b/cli/internal/cmd/miniup.go index 0000a8dcb..0274ea154 100644 --- a/cli/internal/cmd/miniup.go +++ b/cli/internal/cmd/miniup.go @@ -13,6 +13,7 @@ import ( "net" "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/helm" "github.com/edgelesssys/constellation/v2/cli/internal/libvirt" @@ -229,16 +230,16 @@ type upFlags struct { func (m *miniUpCmd) parseUpFlags(cmd *cobra.Command) (upFlags, error) { m.log.Debugf("Preparing configuration") - workspace, err := cmd.Flags().GetString("workspace") + workDir, err := cmd.Flags().GetString("workspace") if err != nil { 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") if err != nil { 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") if err != nil { @@ -248,7 +249,7 @@ func (m *miniUpCmd) parseUpFlags(cmd *cobra.Command) (upFlags, error) { 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", 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{ force: force, diff --git a/cli/internal/cmd/pathprefix/BUILD.bazel b/cli/internal/cmd/pathprefix/BUILD.bazel new file mode 100644 index 000000000..0e37ac6ca --- /dev/null +++ b/cli/internal/cmd/pathprefix/BUILD.bazel @@ -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__"], +) diff --git a/cli/internal/cmd/pathprefix/pathprefix.go b/cli/internal/cmd/pathprefix/pathprefix.go new file mode 100644 index 000000000..ae7cfea4f --- /dev/null +++ b/cli/internal/cmd/pathprefix/pathprefix.go @@ -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)) +} diff --git a/cli/internal/cmd/recover.go b/cli/internal/cmd/recover.go index 5c97f679c..a837e9b69 100644 --- a/cli/internal/cmd/recover.go +++ b/cli/internal/cmd/recover.go @@ -17,6 +17,7 @@ import ( "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "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/internal/api/attestationconfigapi" "github.com/edgelesssys/constellation/v2/internal/atls" @@ -50,6 +51,7 @@ func NewRecoverCmd() *cobra.Command { type recoverCmd struct { log debugLog configFetcher attestationconfigapi.Fetcher + pf pathprefix.PathPrefixer } func runRecover(cmd *cobra.Command, _ []string) error { @@ -77,12 +79,12 @@ func (r *recoverCmd) recover( r.log.Debugf("Using flags: %+v", flags) 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 { 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) var configValidationErr *config.ValidationError if errors.As(err, &configValidationErr) { @@ -210,18 +212,18 @@ func (d *recoverDoer) setURIs(kmsURI, storageURI string) { } type recoverFlags struct { - endpoint string - workspace string - maaURL string - force bool + endpoint string + maaURL string + force bool } 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 { 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 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{ - endpoint: endpoint, - workspace: workspace, - maaURL: idFile.AttestationURL, - force: force, + endpoint: endpoint, + maaURL: idFile.AttestationURL, + force: force, }, nil } diff --git a/cli/internal/cmd/recover_test.go b/cli/internal/cmd/recover_test.go index f443ee739..780c3a886 100644 --- a/cli/internal/cmd/recover_test.go +++ b/cli/internal/cmd/recover_test.go @@ -208,8 +208,7 @@ func TestParseRecoverFlags(t *testing.T) { "all args set": { args: []string{"-e", "192.0.2.42:2", "--workspace", "./constellation-workspace"}, wantFlags: recoverFlags{ - endpoint: "192.0.2.42:2", - workspace: "./constellation-workspace", + endpoint: "192.0.2.42:2", }, }, } diff --git a/cli/internal/cmd/terminate.go b/cli/internal/cmd/terminate.go index 2326e6e1e..eeaa5d1ca 100644 --- a/cli/internal/cmd/terminate.go +++ b/cli/internal/cmd/terminate.go @@ -15,6 +15,7 @@ import ( "github.com/spf13/cobra" "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/internal/constants" "github.com/edgelesssys/constellation/v2/internal/file" @@ -53,6 +54,7 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file. if err != nil { return fmt.Errorf("parsing flags: %w", err) } + pf := pathprefix.New(flags.workspace) if !flags.yes { 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 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) { - 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 diff --git a/cli/internal/cmd/upgradeapply.go b/cli/internal/cmd/upgradeapply.go index 0a8f12ca6..300d5fe4e 100644 --- a/cli/internal/cmd/upgradeapply.go +++ b/cli/internal/cmd/upgradeapply.go @@ -15,6 +15,7 @@ import ( "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "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/kubernetes" "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"+ "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 { 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) { - workspace, err := cmd.Flags().GetString("workspace") + workDir, err := cmd.Flags().GetString("workspace") if err != nil { return upgradeApplyFlags{}, err } @@ -407,7 +408,7 @@ func parseUpgradeApplyFlags(cmd *cobra.Command) (upgradeApplyFlags, error) { } return upgradeApplyFlags{ - workspace: workspace, + pf: pathprefix.New(workDir), yes: yes, upgradeTimeout: timeout, force: force, @@ -429,7 +430,7 @@ func mergeClusterIDFile(clusterIDPath string, newIDFile clusterid.File, fileHand } type upgradeApplyFlags struct { - workspace string + pf pathprefix.PathPrefixer yes bool upgradeTimeout time.Duration force bool diff --git a/cli/internal/cmd/verify.go b/cli/internal/cmd/verify.go index bb0891d48..cf3134364 100644 --- a/cli/internal/cmd/verify.go +++ b/cli/internal/cmd/verify.go @@ -24,6 +24,7 @@ import ( "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "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/atls" "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("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) var configValidationErr *config.ValidationError 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) { - workspace, err := cmd.Flags().GetString("workspace") + workDir, err := cmd.Flags().GetString("workspace") if err != nil { 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 := "" clusterID, err := cmd.Flags().GetString("cluster-id") @@ -189,13 +191,13 @@ func (c *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle emptyEndpoint := endpoint == "" emptyIDs := ownerID == "" && clusterID == "" 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 { - 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 } 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 clusterID = idFile.ClusterID } @@ -212,7 +214,7 @@ func (c *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle return verifyFlags{ endpoint: endpoint, - workspace: workspace, + pf: pf, ownerID: ownerID, clusterID: clusterID, maaURL: idFile.AttestationURL, @@ -225,10 +227,10 @@ type verifyFlags struct { endpoint string ownerID string clusterID string - workspace string maaURL string rawOutput bool force bool + pf pathprefix.PathPrefixer } func addPortIfMissing(endpoint string, defaultPort int) (string, error) { diff --git a/cli/internal/cmd/workspace.go b/cli/internal/cmd/workspace.go deleted file mode 100644 index 5bd187270..000000000 --- a/cli/internal/cmd/workspace.go +++ /dev/null @@ -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) -} diff --git a/internal/constants/constants.go b/internal/constants/constants.go index fcd97ff0f..57a35cdf4 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -88,6 +88,8 @@ const ( TerraformWorkingDir = "constellation-terraform" // TerraformIAMWorkingDir is the directory name for the Terraform IAM Client workspace. 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 = "constellation-cluster.log" // ControlPlaneAdminConfFilename filepath to control plane kubernetes admin config.