Create separate Terraform workspace directory

This commit is contained in:
Nils Hanke 2022-11-14 18:18:58 +01:00 committed by Nils Hanke
parent 7f5a1dd901
commit 4a2cba988c
10 changed files with 50 additions and 60 deletions

View File

@ -21,6 +21,7 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
)
// Creator creates cloud resources.
@ -35,7 +36,7 @@ func NewCreator(out io.Writer) *Creator {
return &Creator{
out: out,
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
return terraform.New(ctx)
return terraform.New(ctx, constants.TerraformWorkingDir)
},
newLibvirtRunner: func() libvirtRunner {
return libvirt.New()

View File

@ -11,6 +11,7 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/constants"
)
// Terminator deletes cloud provider resources.
@ -23,7 +24,7 @@ type Terminator struct {
func NewTerminator() *Terminator {
return &Terminator{
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
return terraform.New(ctx)
return terraform.New(ctx, constants.TerraformWorkingDir)
},
newLibvirtRunner: func() libvirtRunner {
return libvirt.New()

View File

@ -53,7 +53,6 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.
if !yesFlag {
cmd.Println("You are about to terminate a Constellation cluster.")
cmd.Println("All of its associated resources will be DESTROYED.")
cmd.Println("This includes any other Terraform workspace in the current directory.")
cmd.Println("This action is irreversible and ALL DATA WILL BE LOST.")
ok, err := askToConfirm(cmd, "Do you want to continue?")
if err != nil {

View File

@ -11,6 +11,7 @@ import (
"errors"
"io/fs"
"path"
"path/filepath"
"strings"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
@ -24,7 +25,7 @@ var terraformFS embed.FS
// prepareWorkspace loads the embedded Terraform files,
// and writes them into the workspace.
func prepareWorkspace(fileHandler file.Handler, provider cloudprovider.Provider) error {
func prepareWorkspace(fileHandler file.Handler, provider cloudprovider.Provider, workingDir string) error {
// use path.Join to ensure no forward slashes are used to read the embedded FS
rootDir := path.Join("terraform", strings.ToLower(provider.String()))
return fs.WalkDir(terraformFS, rootDir, func(path string, d fs.DirEntry, err error) error {
@ -39,27 +40,13 @@ func prepareWorkspace(fileHandler file.Handler, provider cloudprovider.Provider)
if err != nil {
return err
}
fileName := strings.TrimPrefix(path, rootDir+"/")
fileName := strings.Replace(filepath.Join(workingDir, path), rootDir+"/", "", 1)
return fileHandler.Write(fileName, content, file.OptMkdirAll)
})
}
// cleanUpWorkspace removes files that were loaded into the workspace.
func cleanUpWorkspace(fileHandler file.Handler) error {
// try to remove any terraform files in the workspace
for _, csp := range []string{"aws", "azure", "gcp", "qemu"} {
rootDir := path.Join("terraform", csp)
if err := fs.WalkDir(terraformFS, rootDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
fileName := strings.TrimPrefix(path, rootDir+"/")
return ignoreFileNotFoundErr(fileHandler.RemoveAll(fileName))
}); err != nil {
return err
}
}
return nil
func cleanUpWorkspace(fileHandler file.Handler, workingDir string) error {
return ignoreFileNotFoundErr(fileHandler.RemoveAll(workingDir))
}
// ignoreFileNotFoundErr ignores the error if it is a file not found error.

View File

@ -8,9 +8,11 @@ package terraform
import (
"io/fs"
"path/filepath"
"testing"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
@ -58,12 +60,12 @@ func TestLoader(t *testing.T) {
file := file.NewHandler(afero.NewMemMapFs())
err := prepareWorkspace(file, tc.provider)
err := prepareWorkspace(file, tc.provider, constants.TerraformWorkingDir)
require.NoError(err)
checkFiles(t, file, func(err error) { assert.NoError(err) }, tc.fileList)
err = cleanUpWorkspace(file)
err = cleanUpWorkspace(file, constants.TerraformWorkingDir)
require.NoError(err)
checkFiles(t, file, func(err error) { assert.ErrorIs(err, fs.ErrNotExist) }, tc.fileList)
@ -74,7 +76,8 @@ func TestLoader(t *testing.T) {
func checkFiles(t *testing.T, file file.Handler, assertion func(error), files []string) {
t.Helper()
for _, f := range files {
_, err := file.Stat(f)
path := filepath.Join(constants.TerraformWorkingDir, f)
_, err := file.Stat(path)
assertion(err)
}
}

View File

@ -9,6 +9,7 @@ package terraform
import (
"context"
"errors"
"path/filepath"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/file"
@ -33,22 +34,26 @@ type Client struct {
tf tfInterface
file file.Handler
workingDir string
remove func()
}
// New sets up a new Client for Terraform.
func New(ctx context.Context) (*Client, error) {
tf, remove, err := GetExecutable(ctx, ".")
func New(ctx context.Context, workingDir string) (*Client, error) {
file := file.NewHandler(afero.NewOsFs())
if err := file.MkdirAll(workingDir); err != nil {
return nil, err
}
tf, remove, err := GetExecutable(ctx, workingDir)
if err != nil {
return nil, err
}
file := file.NewHandler(afero.NewOsFs())
return &Client{
tf: tf,
remove: remove,
file: file,
workingDir: workingDir,
}, nil
}
@ -56,7 +61,7 @@ func New(ctx context.Context) (*Client, error) {
func (c *Client) CreateCluster(
ctx context.Context, provider cloudprovider.Provider, name string, vars Variables,
) (string, error) {
if err := prepareWorkspace(c.file, provider); err != nil {
if err := prepareWorkspace(c.file, provider, c.workingDir); err != nil {
return "", err
}
@ -64,7 +69,7 @@ func (c *Client) CreateCluster(
return "", err
}
if err := c.file.Write(terraformVarsFile, []byte(vars.String())); err != nil {
if err := c.file.Write(filepath.Join(c.workingDir, terraformVarsFile), []byte(vars.String())); err != nil {
return "", err
}
@ -101,23 +106,7 @@ func (c *Client) RemoveInstaller() {
// CleanUpWorkspace removes terraform files from the current directory.
func (c *Client) CleanUpWorkspace() error {
if err := cleanUpWorkspace(c.file); err != nil {
return err
}
if err := ignoreFileNotFoundErr(c.file.Remove("terraform.tfvars")); err != nil {
return err
}
if err := ignoreFileNotFoundErr(c.file.Remove("terraform.tfstate")); err != nil {
return err
}
if err := ignoreFileNotFoundErr(c.file.Remove("terraform.tfstate.backup")); err != nil {
return err
}
if err := ignoreFileNotFoundErr(c.file.Remove(".terraform.lock.hcl")); err != nil {
return err
}
if err := ignoreFileNotFoundErr(c.file.RemoveAll(".terraform")); err != nil {
if err := cleanUpWorkspace(c.file, c.workingDir); err != nil {
return err
}

View File

@ -10,9 +10,11 @@ import (
"context"
"errors"
"io/fs"
"path/filepath"
"testing"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/hashicorp/terraform-exec/tfexec"
tfjson "github.com/hashicorp/terraform-json"
@ -124,6 +126,7 @@ func TestCreateCluster(t *testing.T) {
c := &Client{
tf: tc.tf,
file: file.NewHandler(tc.fs),
workingDir: constants.TerraformWorkingDir,
}
ip, err := c.CreateCluster(context.Background(), tc.provider, "test", tc.vars)
@ -207,6 +210,7 @@ func TestCleanupWorkspace(t *testing.T) {
c := &Client{
file: file,
tf: &stubTerraform{},
workingDir: constants.TerraformWorkingDir,
}
err := c.CleanUpWorkspace()
@ -215,11 +219,11 @@ func TestCleanupWorkspace(t *testing.T) {
return
}
assert.NoError(err)
_, err = file.Stat("terraform.tfvars")
_, err = file.Stat(filepath.Join(c.workingDir, "terraform.tfvars"))
assert.ErrorIs(err, fs.ErrNotExist)
_, err = file.Stat("terraform.tfstate")
_, err = file.Stat(filepath.Join(c.workingDir, "terraform.tfstate"))
assert.ErrorIs(err, fs.ErrNotExist)
_, err = file.Stat("terraform.tfstate.backup")
_, err = file.Stat(filepath.Join(c.workingDir, "terraform.tfstate.backup"))
assert.ErrorIs(err, fs.ErrNotExist)
})
}

View File

@ -325,7 +325,6 @@ This should give the following output:
$ constellation terminate
You are about to terminate a Constellation cluster.
All of its associated resources will be DESTROYED.
This includes any other Terraform workspace in the current directory.
This action is irreversible and ALL DATA WILL BE LOST.
Do you want to continue? [y/n]:
```

View File

@ -77,6 +77,8 @@ const (
AdminConfFilename = "constellation-admin.conf"
// MasterSecretFilename filename of Constellation mastersecret.
MasterSecretFilename = "constellation-mastersecret.json"
// TerraformWorkingDir is the directory name for the TerraformClient workspace.
TerraformWorkingDir = "constellation-terraform"
// ControlPlaneAdminConfFilename filepath to control plane kubernetes admin config.
ControlPlaneAdminConfFilename = "/etc/kubernetes/admin.conf"
// KubectlPath path to kubectl binary.

View File

@ -170,3 +170,8 @@ func (h *Handler) RemoveAll(name string) error {
func (h *Handler) Stat(name string) (fs.FileInfo, error) {
return h.fs.Stat(name)
}
// MkdirAll creates a directory path and all parents that does not exist yet.
func (h *Handler) MkdirAll(name string) error {
return h.fs.MkdirAll(name, 0o700)
}