mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-02-23 08:20:15 -05:00
Create separate Terraform workspace directory
This commit is contained in:
parent
7f5a1dd901
commit
4a2cba988c
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Creator creates cloud resources.
|
// Creator creates cloud resources.
|
||||||
@ -35,7 +36,7 @@ func NewCreator(out io.Writer) *Creator {
|
|||||||
return &Creator{
|
return &Creator{
|
||||||
out: out,
|
out: out,
|
||||||
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
||||||
return terraform.New(ctx)
|
return terraform.New(ctx, constants.TerraformWorkingDir)
|
||||||
},
|
},
|
||||||
newLibvirtRunner: func() libvirtRunner {
|
newLibvirtRunner: func() libvirtRunner {
|
||||||
return libvirt.New()
|
return libvirt.New()
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Terminator deletes cloud provider resources.
|
// Terminator deletes cloud provider resources.
|
||||||
@ -23,7 +24,7 @@ type Terminator struct {
|
|||||||
func NewTerminator() *Terminator {
|
func NewTerminator() *Terminator {
|
||||||
return &Terminator{
|
return &Terminator{
|
||||||
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
newTerraformClient: func(ctx context.Context) (terraformClient, error) {
|
||||||
return terraform.New(ctx)
|
return terraform.New(ctx, constants.TerraformWorkingDir)
|
||||||
},
|
},
|
||||||
newLibvirtRunner: func() libvirtRunner {
|
newLibvirtRunner: func() libvirtRunner {
|
||||||
return libvirt.New()
|
return libvirt.New()
|
||||||
|
@ -53,7 +53,6 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.
|
|||||||
if !yesFlag {
|
if !yesFlag {
|
||||||
cmd.Println("You are about to terminate a Constellation cluster.")
|
cmd.Println("You are about to terminate a Constellation cluster.")
|
||||||
cmd.Println("All of its associated resources will be DESTROYED.")
|
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.")
|
cmd.Println("This action is irreversible and ALL DATA WILL BE LOST.")
|
||||||
ok, err := askToConfirm(cmd, "Do you want to continue?")
|
ok, err := askToConfirm(cmd, "Do you want to continue?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
@ -24,7 +25,7 @@ var terraformFS embed.FS
|
|||||||
|
|
||||||
// prepareWorkspace loads the embedded Terraform files,
|
// prepareWorkspace loads the embedded Terraform files,
|
||||||
// and writes them into the workspace.
|
// 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
|
// use path.Join to ensure no forward slashes are used to read the embedded FS
|
||||||
rootDir := path.Join("terraform", strings.ToLower(provider.String()))
|
rootDir := path.Join("terraform", strings.ToLower(provider.String()))
|
||||||
return fs.WalkDir(terraformFS, rootDir, func(path string, d fs.DirEntry, err error) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fileName := strings.TrimPrefix(path, rootDir+"/")
|
fileName := strings.Replace(filepath.Join(workingDir, path), rootDir+"/", "", 1)
|
||||||
return fileHandler.Write(fileName, content, file.OptMkdirAll)
|
return fileHandler.Write(fileName, content, file.OptMkdirAll)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanUpWorkspace removes files that were loaded into the workspace.
|
func cleanUpWorkspace(fileHandler file.Handler, workingDir string) error {
|
||||||
func cleanUpWorkspace(fileHandler file.Handler) error {
|
return ignoreFileNotFoundErr(fileHandler.RemoveAll(workingDir))
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignoreFileNotFoundErr ignores the error if it is a file not found error.
|
// ignoreFileNotFoundErr ignores the error if it is a file not found error.
|
||||||
|
@ -8,9 +8,11 @@ package terraform
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -58,12 +60,12 @@ func TestLoader(t *testing.T) {
|
|||||||
|
|
||||||
file := file.NewHandler(afero.NewMemMapFs())
|
file := file.NewHandler(afero.NewMemMapFs())
|
||||||
|
|
||||||
err := prepareWorkspace(file, tc.provider)
|
err := prepareWorkspace(file, tc.provider, constants.TerraformWorkingDir)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
checkFiles(t, file, func(err error) { assert.NoError(err) }, tc.fileList)
|
checkFiles(t, file, func(err error) { assert.NoError(err) }, tc.fileList)
|
||||||
|
|
||||||
err = cleanUpWorkspace(file)
|
err = cleanUpWorkspace(file, constants.TerraformWorkingDir)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
checkFiles(t, file, func(err error) { assert.ErrorIs(err, fs.ErrNotExist) }, tc.fileList)
|
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) {
|
func checkFiles(t *testing.T, file file.Handler, assertion func(error), files []string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
_, err := file.Stat(f)
|
path := filepath.Join(constants.TerraformWorkingDir, f)
|
||||||
|
_, err := file.Stat(path)
|
||||||
assertion(err)
|
assertion(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ package terraform
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
@ -33,22 +34,26 @@ type Client struct {
|
|||||||
tf tfInterface
|
tf tfInterface
|
||||||
|
|
||||||
file file.Handler
|
file file.Handler
|
||||||
|
workingDir string
|
||||||
remove func()
|
remove func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New sets up a new Client for Terraform.
|
// New sets up a new Client for Terraform.
|
||||||
func New(ctx context.Context) (*Client, error) {
|
func New(ctx context.Context, workingDir string) (*Client, error) {
|
||||||
tf, remove, err := GetExecutable(ctx, ".")
|
file := file.NewHandler(afero.NewOsFs())
|
||||||
|
if err := file.MkdirAll(workingDir); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tf, remove, err := GetExecutable(ctx, workingDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
file := file.NewHandler(afero.NewOsFs())
|
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
tf: tf,
|
tf: tf,
|
||||||
remove: remove,
|
remove: remove,
|
||||||
file: file,
|
file: file,
|
||||||
|
workingDir: workingDir,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +61,7 @@ func New(ctx context.Context) (*Client, error) {
|
|||||||
func (c *Client) CreateCluster(
|
func (c *Client) CreateCluster(
|
||||||
ctx context.Context, provider cloudprovider.Provider, name string, vars Variables,
|
ctx context.Context, provider cloudprovider.Provider, name string, vars Variables,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
if err := prepareWorkspace(c.file, provider); err != nil {
|
if err := prepareWorkspace(c.file, provider, c.workingDir); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +69,7 @@ func (c *Client) CreateCluster(
|
|||||||
return "", err
|
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
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,23 +106,7 @@ func (c *Client) RemoveInstaller() {
|
|||||||
|
|
||||||
// CleanUpWorkspace removes terraform files from the current directory.
|
// CleanUpWorkspace removes terraform files from the current directory.
|
||||||
func (c *Client) CleanUpWorkspace() error {
|
func (c *Client) CleanUpWorkspace() error {
|
||||||
if err := cleanUpWorkspace(c.file); err != nil {
|
if err := cleanUpWorkspace(c.file, c.workingDir); 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 {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,9 +10,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/hashicorp/terraform-exec/tfexec"
|
"github.com/hashicorp/terraform-exec/tfexec"
|
||||||
tfjson "github.com/hashicorp/terraform-json"
|
tfjson "github.com/hashicorp/terraform-json"
|
||||||
@ -124,6 +126,7 @@ func TestCreateCluster(t *testing.T) {
|
|||||||
c := &Client{
|
c := &Client{
|
||||||
tf: tc.tf,
|
tf: tc.tf,
|
||||||
file: file.NewHandler(tc.fs),
|
file: file.NewHandler(tc.fs),
|
||||||
|
workingDir: constants.TerraformWorkingDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, err := c.CreateCluster(context.Background(), tc.provider, "test", tc.vars)
|
ip, err := c.CreateCluster(context.Background(), tc.provider, "test", tc.vars)
|
||||||
@ -207,6 +210,7 @@ func TestCleanupWorkspace(t *testing.T) {
|
|||||||
c := &Client{
|
c := &Client{
|
||||||
file: file,
|
file: file,
|
||||||
tf: &stubTerraform{},
|
tf: &stubTerraform{},
|
||||||
|
workingDir: constants.TerraformWorkingDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.CleanUpWorkspace()
|
err := c.CleanUpWorkspace()
|
||||||
@ -215,11 +219,11 @@ func TestCleanupWorkspace(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
_, err = file.Stat("terraform.tfvars")
|
_, err = file.Stat(filepath.Join(c.workingDir, "terraform.tfvars"))
|
||||||
assert.ErrorIs(err, fs.ErrNotExist)
|
assert.ErrorIs(err, fs.ErrNotExist)
|
||||||
_, err = file.Stat("terraform.tfstate")
|
_, err = file.Stat(filepath.Join(c.workingDir, "terraform.tfstate"))
|
||||||
assert.ErrorIs(err, fs.ErrNotExist)
|
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)
|
assert.ErrorIs(err, fs.ErrNotExist)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,6 @@ This should give the following output:
|
|||||||
$ constellation terminate
|
$ constellation terminate
|
||||||
You are about to terminate a Constellation cluster.
|
You are about to terminate a Constellation cluster.
|
||||||
All of its associated resources will be DESTROYED.
|
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.
|
This action is irreversible and ALL DATA WILL BE LOST.
|
||||||
Do you want to continue? [y/n]:
|
Do you want to continue? [y/n]:
|
||||||
```
|
```
|
||||||
|
@ -77,6 +77,8 @@ const (
|
|||||||
AdminConfFilename = "constellation-admin.conf"
|
AdminConfFilename = "constellation-admin.conf"
|
||||||
// MasterSecretFilename filename of Constellation mastersecret.
|
// MasterSecretFilename filename of Constellation mastersecret.
|
||||||
MasterSecretFilename = "constellation-mastersecret.json"
|
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 filepath to control plane kubernetes admin config.
|
||||||
ControlPlaneAdminConfFilename = "/etc/kubernetes/admin.conf"
|
ControlPlaneAdminConfFilename = "/etc/kubernetes/admin.conf"
|
||||||
// KubectlPath path to kubectl binary.
|
// KubectlPath path to kubectl binary.
|
||||||
|
@ -170,3 +170,8 @@ func (h *Handler) RemoveAll(name string) error {
|
|||||||
func (h *Handler) Stat(name string) (fs.FileInfo, error) {
|
func (h *Handler) Stat(name string) (fs.FileInfo, error) {
|
||||||
return h.fs.Stat(name)
|
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)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user