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",
"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",

View File

@ -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
}

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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.

View File

@ -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"])
})

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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,

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/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
}

View File

@ -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",
},
},
}

View File

@ -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

View File

@ -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

View File

@ -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) {

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"
// 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.