2022-09-26 09:52:31 -04:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
package terraform
import (
2022-11-14 13:15:10 -05:00
"bytes"
2022-09-26 09:52:31 -04:00
"embed"
"errors"
2023-05-22 07:31:20 -04:00
"fmt"
2022-09-26 09:52:31 -04:00
"io/fs"
2023-05-30 06:02:43 -04:00
slashpath "path"
2022-11-14 12:18:58 -05:00
"path/filepath"
2022-09-26 09:52:31 -04:00
"strings"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/spf13/afero"
)
2022-11-16 10:33:51 -05:00
// ErrTerraformWorkspaceDifferentFiles is returned when a re-used existing Terraform workspace has different files than the ones to be extracted (e.g. due to a version mix-up or incomplete writes).
var ErrTerraformWorkspaceDifferentFiles = errors . New ( "creating cluster: trying to overwrite an existing Terraform file with a different version" )
2022-09-26 09:52:31 -04:00
//go:embed terraform/*
2022-10-25 04:10:46 -04:00
//go:embed terraform/*/.terraform.lock.hcl
2022-09-26 09:52:31 -04:00
var terraformFS embed . FS
// prepareWorkspace loads the embedded Terraform files,
// and writes them into the workspace.
2023-05-22 07:31:20 -04:00
func prepareWorkspace ( rootDir string , fileHandler file . Handler , workingDir string ) error {
return terraformCopier ( fileHandler , rootDir , workingDir )
}
// prepareUpgradeWorkspace takes the Terraform state file from the old workspace and the
// embedded Terraform files and writes them into the new workspace.
2023-06-21 03:22:32 -04:00
func prepareUpgradeWorkspace ( rootDir string , fileHandler file . Handler , oldWorkingDir , newWorkingDir , backupDir string ) error {
2023-05-22 07:31:20 -04:00
// backup old workspace
if err := fileHandler . CopyDir (
oldWorkingDir ,
2023-06-21 03:22:32 -04:00
backupDir ,
2023-05-22 07:31:20 -04:00
) ; err != nil {
return fmt . Errorf ( "backing up old workspace: %w" , err )
}
// copy state file
if err := fileHandler . CopyFile (
filepath . Join ( oldWorkingDir , "terraform.tfstate" ) ,
filepath . Join ( newWorkingDir , "terraform.tfstate" ) ,
file . OptMkdirAll ,
) ; err != nil {
return fmt . Errorf ( "copying state file: %w" , err )
}
return terraformCopier ( fileHandler , rootDir , newWorkingDir )
}
// terraformCopier copies the embedded Terraform files into the workspace.
func terraformCopier ( fileHandler file . Handler , rootDir , workingDir string ) error {
2023-05-30 06:02:43 -04:00
goEmbedRootDir := filepath . ToSlash ( rootDir )
return fs . WalkDir ( terraformFS , goEmbedRootDir , func ( path string , d fs . DirEntry , err error ) error {
2022-09-26 09:52:31 -04:00
if err != nil {
return err
}
if d . IsDir ( ) {
return nil
}
2023-05-30 06:02:43 -04:00
goEmbedPath := filepath . ToSlash ( path )
content , err := terraformFS . ReadFile ( goEmbedPath )
2022-09-26 09:52:31 -04:00
if err != nil {
return err
}
2023-05-30 06:02:43 -04:00
// normalize
fileName := strings . Replace ( slashpath . Join ( workingDir , path ) , goEmbedRootDir + "/" , "" , 1 )
2022-11-14 13:15:10 -05:00
if err := fileHandler . Write ( fileName , content , file . OptMkdirAll ) ; errors . Is ( err , afero . ErrFileExists ) {
// If a file already exists, check if it is identical. If yes, continue and don't write anything to disk.
// If no, don't overwrite it and instead throw an error. The affected file could be from a different version,
// provider, corrupted or manually modified in general.
existingFileContent , err := fileHandler . Read ( fileName )
if err != nil {
return err
}
if ! bytes . Equal ( content , existingFileContent ) {
2022-11-16 10:33:51 -05:00
return ErrTerraformWorkspaceDifferentFiles
2022-11-14 13:15:10 -05:00
}
return nil
} else if err != nil {
return err
}
return nil
2022-09-26 09:52:31 -04:00
} )
}
2022-11-14 12:18:58 -05:00
func cleanUpWorkspace ( fileHandler file . Handler , workingDir string ) error {
return ignoreFileNotFoundErr ( fileHandler . RemoveAll ( workingDir ) )
2022-09-26 09:52:31 -04:00
}
// ignoreFileNotFoundErr ignores the error if it is a file not found error.
func ignoreFileNotFoundErr ( err error ) error {
if errors . Is ( err , afero . ErrFileNotFound ) {
return nil
}
return err
}