diff --git a/README.md b/README.md index 0af7afa..1ce750f 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,8 @@ folders up to `$HOME` `migration = "./migration"` refers to the directory where the migration files are located, this path is relative to the config file.
-`history` is where you can configure where to store the history, currently its only `storage "s3"` that's supported.
+`history` is where you can configure where to store the history, currently its only `storage "s3"` that's +supported.
`state` is where you configure where your state is located, also here its only `state "s3"` that's supported. A nice feature in the config file is the support for variables using the common `${variable_name}` syntax, How to @@ -106,8 +107,8 @@ optional. The `migrate` blocks have 2 supported types: `move` and `remove`.
On `move` you need to specify a `from` and `to` block, while on `remove` you can provide the `state` and `resource` variable.
-In both cases `state` refers to the location within the s3_bucket. `resource` refers to the resource type + name in -the state file. +In both cases `state` refers to the location within the s3_bucket. `resource` refers to the resource type + name in the +state file. ### Integrate with terragrunt: @@ -143,6 +144,7 @@ before_hook "run_migrations" { } ``` -Notice the `--cv`(config-variables) flag, here we specify the `ACCOUNT_ID` and the `ASSUME_ROLE` variable that we use in the config file.
-Also notice the `prod` sub-command, this is telling tgmigrate to only apply migrations for the prod environment.
+Notice the `--cv`(config-variables) flag, here we specify the `ACCOUNT_ID` and the `ASSUME_ROLE` variable that we use in +the config file.
+Also notice the `prod` sub-command, this is telling tgmigrate to only apply migrations for the prod environment.
This means that the migration file above would be applied in this case, because it has `prod` in the environments list. \ No newline at end of file diff --git a/command/apply.go b/command/apply.go index 9c2f656..35553fa 100644 --- a/command/apply.go +++ b/command/apply.go @@ -1,7 +1,11 @@ package command import ( + "github.com/aleksanderaleksic/tgmigrate/common" + "github.com/aleksanderaleksic/tgmigrate/config" + "github.com/aleksanderaleksic/tgmigrate/history" "github.com/aleksanderaleksic/tgmigrate/migration" + "github.com/aleksanderaleksic/tgmigrate/state" "github.com/urfave/cli/v2" ) @@ -9,7 +13,7 @@ type ApplyCommand struct { Runner *migration.Runner } -func (command ApplyCommand) GetCLICommand() *cli.Command { +func (command *ApplyCommand) GetCLICommand() *cli.Command { cmd := cli.Command{ Name: "apply", Aliases: nil, @@ -19,15 +23,18 @@ func (command ApplyCommand) GetCLICommand() *cli.Command { ArgsUsage: "", Category: "", BashComplete: nil, - Before: nil, - After: func(context *cli.Context) error { - command.Runner.Cleanup() - return nil + Before: command.initialize, + After: nil, + Action: command.runAll, + OnUsageError: nil, + Subcommands: nil, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "dryrun", + Aliases: []string{"d"}, + Usage: "Dont do any permanent changes, like actually uploading the state changes", + }, }, - Action: command.runAll, - OnUsageError: nil, - Subcommands: nil, - Flags: nil, SkipFlagParsing: false, HideHelp: false, HideHelpCommand: false, @@ -39,10 +46,52 @@ func (command ApplyCommand) GetCLICommand() *cli.Command { return &cmd } -func (command ApplyCommand) runAll(c *cli.Context) error { +func (command *ApplyCommand) runAll(c *cli.Context) error { environment := c.Args().First() if environment == "" { return command.Runner.Apply(nil) } return command.Runner.Apply(&environment) } + +func (command *ApplyCommand) initialize(c *cli.Context) error { + cfg, err := config.GetConfigFile(c) + if err != nil { + return err + } + + chc := common.Cache{ + ConfigFilePath: cfg.Path, + } + + ctx := common.Context{ + SkipUserInteraction: c.Bool("y"), + DryRun: c.Bool("d"), + } + + migrationFiles, err := migration.GetMigrationFiles(*cfg) + if err != nil { + return err + } + + stateInterface, err := state.GetStateInterface(*cfg, ctx, chc) + if err != nil { + return err + } + + historyInterface, err := history.GetHistoryInterface(*cfg, ctx, chc) + if err != nil { + return err + } + + runner := migration.Runner{ + Context: &ctx, + HistoryInterface: historyInterface, + StateInterface: stateInterface, + MigrationFiles: *migrationFiles, + } + + command.Runner = &runner + + return nil +} diff --git a/common/cache.go b/common/cache.go new file mode 100644 index 0000000..bd58c4d --- /dev/null +++ b/common/cache.go @@ -0,0 +1,17 @@ +package common + +import ( + "path/filepath" +) + +type Cache struct { + ConfigFilePath string +} + +func (c Cache) GetCacheDirectoryPath() string { + return filepath.Join(filepath.Dir(c.ConfigFilePath), ".tgmigrate_cache") +} + +func (c Cache) filepath(name string) string { + return filepath.Join(c.GetCacheDirectoryPath(), name) +} diff --git a/config/config.go b/config/config.go index 9056f6d..2e2dfc9 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" ) var defaultConfigFile = ".tgmigrate.hcl" @@ -30,14 +31,16 @@ type File struct { } type Config struct { + Path string MigrationDir string AbsoluteMigrationDir string History History State State } -func GetConfigFile(ctx *cli.Context, configVariables map[string]cty.Value) (*Config, error) { - confFilePath := getConfigFilePathFromFlags(ctx) +func GetConfigFile(ctx *cli.Context) (*Config, error) { + configVariables := getConfigVariables(ctx) + confFilePath := GetConfigFilePathFromFlags(ctx) source, err := ioutil.ReadFile(confFilePath) if err != nil { return nil, fmt.Errorf("unable to find %s in current or parrent directories, a config file is required", defaultConfigFile) @@ -56,11 +59,11 @@ func GetConfigFile(ctx *cli.Context, configVariables map[string]cty.Value) (*Con return conf, nil } -func parseConfigFile(filename string, source []byte, ctx *hcl.EvalContext) (*Config, error) { +func parseConfigFile(filePath string, source []byte, ctx *hcl.EvalContext) (*Config, error) { var f File - err := hclsimple.Decode(filename, source, nil, &f) + err := hclsimple.Decode(filePath, source, nil, &f) if err != nil { - return nil, fmt.Errorf("failed to decode config file: %s, err: %s", filename, err) + return nil, fmt.Errorf("failed to decode config file: %s, err: %s", filePath, err) } historyStorageConfig, err := getHistoryStorageConfig(f, ctx) @@ -73,12 +76,13 @@ func parseConfigFile(filename string, source []byte, ctx *hcl.EvalContext) (*Con return nil, err } - path, err := getAbsoluteMigrationDirPath(filename, f.Migration.MigrationDir) + path, err := getAbsoluteMigrationDirPath(filePath, f.Migration.MigrationDir) if err != nil { return nil, err } cfg := Config{ + Path: filePath, MigrationDir: f.Migration.MigrationDir, AbsoluteMigrationDir: *path, History: History{ @@ -129,7 +133,7 @@ func getAbsoluteMigrationDirPath(configFilePath string, migrationDir string) (*s return &migrationDirectory, nil } -func getConfigFilePathFromFlags(c *cli.Context) string { +func GetConfigFilePathFromFlags(c *cli.Context) string { configFlagValue := c.String("config") if configFlagValue != "" { @@ -173,3 +177,25 @@ func findFirstConfigFileInParentFolders() (*string, error) { return &defaultConfigFile, nil } + +func getConfigVariables(c *cli.Context) map[string]cty.Value { + configVariablesFlag := c.String("cv") + + if configVariablesFlag == "" { + return map[string]cty.Value{} + } + + rawKeyValue := strings.Split(configVariablesFlag, ";") + + var keyValue = map[string]cty.Value{} + + for _, raw := range rawKeyValue { + if raw == "" { + break + } + split := strings.Split(raw, "=") + keyValue[split[0]] = cty.StringVal(split[1]) + } + + return keyValue +} diff --git a/config/config_history_storage_s3.go b/config/config_history_storage_s3.go index b01ac49..775c282 100644 --- a/config/config_history_storage_s3.go +++ b/config/config_history_storage_s3.go @@ -3,15 +3,13 @@ package config import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/gohcl" - "os" ) type S3HistoryStorageConfig struct { - Bucket string `hcl:"bucket"` - Region string `hcl:"region"` - Key string `hcl:"key"` - AssumeRole *string `hcl:"assume_role,optional"` - historyPath *string + Bucket string `hcl:"bucket"` + Region string `hcl:"region"` + Key string `hcl:"key"` + AssumeRole *string `hcl:"assume_role,optional"` } func ParseHistoryS3StorageConfig(configFile File, ctx *hcl.EvalContext) (*S3HistoryStorageConfig, error) { @@ -24,13 +22,3 @@ func ParseHistoryS3StorageConfig(configFile File, ctx *hcl.EvalContext) (*S3Hist return &config, nil } - -func (s *S3HistoryStorageConfig) GetLocalHistoryPath() string { - if s.historyPath != nil { - return *s.historyPath - } - dirName := s.Bucket + "_history" - s.historyPath = &dirName - _ = os.Mkdir(dirName, 0777) - return *s.historyPath -} diff --git a/config/config_state.go b/config/config_state.go index 4139d2b..f349c94 100644 --- a/config/config_state.go +++ b/config/config_state.go @@ -8,7 +8,5 @@ type State struct { } type StateConfig interface { - GetStateDirectory() string - GetBackupStateDirectory() string GetStateFileName() string } diff --git a/config/config_state_s3.go b/config/config_state_s3.go index 27098e3..5a102f8 100644 --- a/config/config_state_s3.go +++ b/config/config_state_s3.go @@ -3,16 +3,13 @@ package config import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/gohcl" - "os" ) type S3StateConfig struct { - Bucket string `hcl:"bucket"` - Region string `hcl:"region"` - StateFileName *string `hcl:"state_file_name,optional"` - AssumeRole *string `hcl:"assume_role,optional"` - stateDirectory *string - backupStateDirectory *string + Bucket string `hcl:"bucket"` + Region string `hcl:"region"` + StateFileName *string `hcl:"state_file_name,optional"` + AssumeRole *string `hcl:"assume_role,optional"` } func ParseS3StateConfig(configFile File, ctx *hcl.EvalContext) (*S3StateConfig, error) { @@ -26,20 +23,6 @@ func ParseS3StateConfig(configFile File, ctx *hcl.EvalContext) (*S3StateConfig, return &config, nil } -func (s *S3StateConfig) GetStateDirectory() string { - if s.stateDirectory != nil { - return *s.stateDirectory - } - dirName := s.Bucket + "_state" - s.stateDirectory = &dirName - _ = os.Mkdir(dirName, 0777) - return *s.stateDirectory -} - -func (s *S3StateConfig) GetBackupStateDirectory() string { - return s.GetStateDirectory() -} - func (s S3StateConfig) GetStateFileName() string { if s.StateFileName != nil { return *s.StateFileName diff --git a/example/.tgmigrate.hcl b/example/.tgmigrate.hcl index 3708639..96d27ab 100644 --- a/example/.tgmigrate.hcl +++ b/example/.tgmigrate.hcl @@ -3,16 +3,16 @@ migration { history { storage "s3" { - bucket = "airthings-terraform-states-${ACCOUNT}" + bucket = "airthings-terraform-states-${ACCOUNT_ID}" region = "us-east-1" - assume_role = "arn:aws:iam::${ACCOUNT}:role/admin" + assume_role = "arn:aws:iam::${ACCOUNT_ID}:role/admin" key = "history.json" } } state "s3" { - bucket = "airthings-terraform-states-${ACCOUNT}" + bucket = "airthings-terraform-states-${ACCOUNT_ID}" region = "us-east-1" - assume_role = "arn:aws:iam::${ACCOUNT}:role/admin" + assume_role = "arn:aws:iam::${ACCOUNT_ID}:role/admin" } } \ No newline at end of file diff --git a/history/history.go b/history/history.go index 0c69b29..1d7655e 100644 --- a/history/history.go +++ b/history/history.go @@ -21,7 +21,7 @@ type History interface { Cleanup() } -func GetHistoryInterface(c config.Config, ctx common.Context) (History, error) { +func GetHistoryInterface(c config.Config, ctx common.Context, cache common.Cache) (History, error) { switch c.History.Storage.Type { case "s3": conf := c.History.Storage.Config.(*config.S3HistoryStorageConfig) @@ -48,6 +48,7 @@ func GetHistoryInterface(c config.Config, ctx common.Context) (History, error) { context: ctx, S3StorageConfig: *conf, session: *sess, + Cache: cache, }, nil default: return nil, fmt.Errorf("unknown history storage type: %s", c.History.Storage.Type) @@ -55,6 +56,11 @@ func GetHistoryInterface(c config.Config, ctx common.Context) (History, error) { } func writeStorageHistory(path string, storageHistory StorageHistory) error { + err := os.MkdirAll(filepath.Dir(path), os.ModePerm) + if err != nil { + return err + } + encodedStorageHistory, err := EncodeStorageHistory(storageHistory) if err != nil { return err diff --git a/history/s3_history.go b/history/s3_history.go index 8ab539b..d791ebf 100644 --- a/history/s3_history.go +++ b/history/s3_history.go @@ -6,6 +6,7 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/seqsense/s3sync" "os" + "path/filepath" ) type S3History struct { @@ -13,6 +14,7 @@ type S3History struct { S3StorageConfig config.S3HistoryStorageConfig session session.Session StorageHistory *StorageHistory + Cache common.Cache } func (h S3History) IsMigrationApplied(hash string) (*Result, error) { @@ -25,7 +27,7 @@ func (h S3History) IsMigrationApplied(hash string) (*Result, error) { } func (h *S3History) InitializeHistory(ctx common.Context) (*StorageHistory, error) { - historyPath := h.S3StorageConfig.GetLocalHistoryPath() + "/" + h.S3StorageConfig.Key + historyPath := h.getHistoryStoragePath() err := s3sync.New(&h.session).Sync("s3://"+h.S3StorageConfig.Bucket+"/"+h.S3StorageConfig.Key, historyPath) if err != nil { @@ -47,7 +49,7 @@ func (h *S3History) StoreMigrationObject(migrationName string, result Result, fi } func (h *S3History) WriteToStorage() error { - historyPath := h.S3StorageConfig.GetLocalHistoryPath() + "/" + h.S3StorageConfig.Key + historyPath := h.getHistoryStoragePath() err := writeToStorage(historyPath, *h.StorageHistory) if err != nil { @@ -70,6 +72,10 @@ func (h *S3History) WriteToStorage() error { return nil } +func (h S3History) getHistoryStoragePath() string { + return filepath.Join(h.Cache.GetCacheDirectoryPath(), "history", "history.json") +} + func (h S3History) Cleanup() { - os.RemoveAll(h.S3StorageConfig.GetLocalHistoryPath()) + os.RemoveAll(h.getHistoryStoragePath()) } diff --git a/main.go b/main.go index fc3dc11..c30967a 100644 --- a/main.go +++ b/main.go @@ -2,24 +2,16 @@ package main import ( "github.com/aleksanderaleksic/tgmigrate/command" - "github.com/aleksanderaleksic/tgmigrate/common" - "github.com/aleksanderaleksic/tgmigrate/config" - "github.com/aleksanderaleksic/tgmigrate/history" - "github.com/aleksanderaleksic/tgmigrate/migration" - "github.com/aleksanderaleksic/tgmigrate/state" "github.com/urfave/cli/v2" - "github.com/zclconf/go-cty/cty" "log" "os" - "strings" ) // Version is a version number. var version = "0.1.1" func main() { - var runner migration.Runner - var applyCommand = &command.ApplyCommand{Runner: &runner} + var applyCommand = command.ApplyCommand{} app := &cli.App{ Version: version, @@ -35,11 +27,6 @@ func main() { Aliases: []string{"y"}, Usage: "Skip all user interaction", }, - &cli.BoolFlag{ - Name: "dryrun", - Aliases: []string{"d"}, - Usage: "Dont do any permanent changes, like actually uploading the state changes", - }, &cli.StringFlag{ Name: "conf-variables", Aliases: []string{"cv"}, @@ -47,14 +34,6 @@ func main() { EnvVars: []string{"TG-MIGRATE_CONFIG_VARIABLES"}, }, }, - Before: func(context *cli.Context) error { - r, err := Initialize(context) - if err != nil { - return err - } - runner = *r - return nil - }, Commands: []*cli.Command{ applyCommand.GetCLICommand(), }, @@ -65,66 +44,3 @@ func main() { log.Fatal(err) } } - -func Initialize(c *cli.Context) (*migration.Runner, error) { - configVariables := getConfigVariables(c) - cfg, err := config.GetConfigFile(c, configVariables) - if err != nil { - return nil, err - } - - ctx := common.Context{ - SkipUserInteraction: c.Bool("y"), - DryRun: c.Bool("d"), - } - - migrationFiles, err := migration.GetMigrationFiles(*cfg) - if err != nil { - return nil, err - } - - stateInterface, err := state.GetStateInterface(*cfg, ctx) - if err != nil { - return nil, err - } - - historyInterface, err := history.GetHistoryInterface(*cfg, ctx) - if err != nil { - return nil, err - } - _, err = historyInterface.InitializeHistory(ctx) - if err != nil { - return nil, err - } - - runner := migration.Runner{ - Context: &ctx, - HistoryInterface: historyInterface, - StateInterface: stateInterface, - MigrationFiles: *migrationFiles, - } - - return &runner, nil -} - -func getConfigVariables(c *cli.Context) map[string]cty.Value { - configVariablesFlag := c.String("cv") - - if configVariablesFlag == "" { - return map[string]cty.Value{} - } - - rawKeyValue := strings.Split(configVariablesFlag, ";") - - var keyValue = map[string]cty.Value{} - - for _, raw := range rawKeyValue { - if raw == "" { - break - } - split := strings.Split(raw, "=") - keyValue[split[0]] = cty.StringVal(split[1]) - } - - return keyValue -} diff --git a/migration/migration_runner.go b/migration/migration_runner.go index 922463c..f052e74 100644 --- a/migration/migration_runner.go +++ b/migration/migration_runner.go @@ -15,11 +15,19 @@ type Runner struct { } func (r Runner) Apply(environment *string) error { + _, err := r.HistoryInterface.InitializeHistory(*r.Context) + if err != nil { + return err + } + migrationsToBeApplied, err := r.getMigrationsToBeApplied(environment) if err != nil { return err } + defer r.HistoryInterface.Cleanup() + defer r.StateInterface.Cleanup() + if len(*migrationsToBeApplied) == 0 { fmt.Println("No migrations will be applied") return nil @@ -99,16 +107,13 @@ func (r Runner) Apply(environment *string) error { if err != nil { return err } - return nil -} -func (r Runner) Cleanup() { - if r.StateInterface != nil { - r.StateInterface.Cleanup() - } - if r.HistoryInterface != nil { - r.HistoryInterface.Cleanup() + err = r.StateInterface.Complete() + if err != nil { + return err } + + return nil } func (r Runner) getMigrationsToBeApplied(environment *string) (*[]File, error) { diff --git a/state/s3_state.go b/state/s3_state.go index 010e64d..d410eb5 100644 --- a/state/s3_state.go +++ b/state/s3_state.go @@ -5,7 +5,6 @@ import ( "github.com/aleksanderaleksic/tgmigrate/config" "github.com/hashicorp/terraform-exec/tfexec" "os" - "path/filepath" ) type S3State struct { @@ -13,10 +12,16 @@ type S3State struct { State config.State Sync S3Sync Terraform *tfexec.Terraform + Cache common.Cache } func (s *S3State) InitializeState() error { - tf, err := initializeTerraformExec(s.State) + err := os.MkdirAll(getStateDirPath(s.Cache), os.ModePerm) + if err != nil { + return err + } + + tf, err := initializeTerraformExec(getStateDirPath(s.Cache)) s.Terraform = tf if err != nil { return err @@ -40,18 +45,13 @@ func (s S3State) Complete() error { } func (s S3State) Move(from ResourceContext, to ResourceContext) (bool, error) { - return move(s.Terraform, s.getAbsoluteStateDirPath(), s.State.Config.GetStateFileName(), from, to) + return move(s.Terraform, getStateDirPath(s.Cache), s.State.Config.GetStateFileName(), from, to) } func (s S3State) Remove(resource ResourceContext) (bool, error) { - return remove(s.Terraform, s.getAbsoluteStateDirPath(), s.State.Config.GetStateFileName(), resource) + return remove(s.Terraform, getStateDirPath(s.Cache), s.State.Config.GetStateFileName(), resource) } func (s S3State) Cleanup() { - os.RemoveAll(s.State.Config.GetStateDirectory()) -} - -func (s S3State) getAbsoluteStateDirPath() string { - path, _ := filepath.Abs(s.State.Config.GetStateDirectory()) - return path + os.RemoveAll(getStateDirPath(s.Cache)) } diff --git a/state/s3_state_sync.go b/state/s3_state_sync.go index 6d3c904..3b3e3ca 100644 --- a/state/s3_state_sync.go +++ b/state/s3_state_sync.go @@ -1,6 +1,7 @@ package state import ( + "github.com/aleksanderaleksic/tgmigrate/common" "github.com/aleksanderaleksic/tgmigrate/config" "github.com/aws/aws-sdk-go/aws/session" "github.com/seqsense/s3sync" @@ -12,11 +13,12 @@ import ( type S3Sync struct { config config.S3StateConfig session session.Session + cache common.Cache } func (s S3Sync) DownSync3State() error { syncManager := s3sync.New(&s.session) - err := syncManager.Sync("s3://"+s.config.Bucket, s.config.GetStateDirectory()) + err := syncManager.Sync("s3://"+s.config.Bucket, getStateDirPath(s.cache)) if err != nil { return err } @@ -25,23 +27,23 @@ func (s S3Sync) DownSync3State() error { } func (s S3Sync) UpSync3State(dryRun bool) error { + stateDirPath := getStateDirPath(s.cache) //Remove all backup files, dont want to upload them to s3 - _ = filepath.Walk(s.config.GetStateDirectory(), - func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if info.IsDir() { - return nil - } - if !strings.HasSuffix(path, ".backup") { - return nil - } - - os.RemoveAll(path) - + _ = filepath.Walk(stateDirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + if !strings.HasSuffix(path, ".backup") { return nil - }) + } + + os.RemoveAll(path) + + return nil + }) var syncManager *s3sync.Manager if dryRun { @@ -50,7 +52,7 @@ func (s S3Sync) UpSync3State(dryRun bool) error { syncManager = s3sync.New(&s.session) } - err := syncManager.Sync(s.config.GetStateDirectory(), "s3://"+s.config.Bucket) + err := syncManager.Sync(stateDirPath, "s3://"+s.config.Bucket) if err != nil { return err } diff --git a/state/state.go b/state/state.go index f6b92cc..a688031 100644 --- a/state/state.go +++ b/state/state.go @@ -21,12 +21,12 @@ type ResourceContext struct { type State interface { InitializeState() error Complete() error - Cleanup() Move(from ResourceContext, to ResourceContext) (bool, error) Remove(resource ResourceContext) (bool, error) + Cleanup() } -func GetStateInterface(c config.Config, ctx common.Context) (State, error) { +func GetStateInterface(c config.Config, ctx common.Context, cache common.Cache) (State, error) { switch c.State.Type { case "s3": conf := *c.State.Config.(*config.S3StateConfig) @@ -56,21 +56,23 @@ func GetStateInterface(c config.Config, ctx common.Context) (State, error) { Sync: S3Sync{ config: conf, session: *sess, + cache: cache, }, Terraform: nil, + Cache: cache, }, nil default: return nil, fmt.Errorf("unknown history storage type: %s", c.History.Storage.Type) } } -func initializeTerraformExec(stateConfig config.State) (*tfexec.Terraform, error) { +func initializeTerraformExec(workingDirectory string) (*tfexec.Terraform, error) { execPath, err := exec.LookPath("terraform") if err != nil { return nil, err } - workingDir, _ := filepath.Abs(stateConfig.Config.GetStateDirectory()) + workingDir, _ := filepath.Abs(workingDirectory) tf, err := tfexec.NewTerraform(workingDir, execPath) if err != nil { return nil, err @@ -129,3 +131,8 @@ func remove(terraform *tfexec.Terraform, stateDir string, stateFileName string, } return true, err } + +func getStateDirPath(c common.Cache) string { + path, _ := filepath.Abs(filepath.Join(c.GetCacheDirectoryPath(), "state")) + return path +}