diff --git a/.gitignore b/.gitignore index bcc054848..e8ae7727e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ venv/ **/__pycache__/ __azurite* +digger diff --git a/pkg/digger/digger.go b/pkg/digger/digger.go index c14bb7cf3..a9dca5387 100644 --- a/pkg/digger/digger.go +++ b/pkg/digger/digger.go @@ -73,16 +73,19 @@ func RunCommandsPerProject(commandsPerProject []ProjectCommand, repoOwner string } var terraformExecutor terraform.TerraformExecutor - + projectPath := path.Join(workingDir, projectCommands.ProjectDir) if projectCommands.Terragrunt { - terraformExecutor = terraform.Terragrunt{WorkingDir: path.Join(workingDir, projectCommands.ProjectDir)} + terraformExecutor = terraform.Terragrunt{WorkingDir: projectPath} } else { - terraformExecutor = terraform.Terraform{WorkingDir: path.Join(workingDir, projectCommands.ProjectDir), Workspace: projectCommands.ProjectWorkspace} + terraformExecutor = terraform.Terraform{WorkingDir: projectPath, Workspace: projectCommands.ProjectWorkspace} } commandRunner := CommandRunner{} diggerExecutor := DiggerExecutor{ + repoOwner, + repoName, projectCommands.ProjectName, + projectPath, projectCommands.StateEnvVars, projectCommands.CommandEnvVars, projectCommands.ApplyStage, @@ -338,7 +341,10 @@ func parseProjectName(comment string) string { } type DiggerExecutor struct { + repoOwner string + repoName string projectName string + projectPath string stateEnvVars map[string]string commandEnvVars map[string]string applyStage *configuration.Stage @@ -378,11 +384,18 @@ func (c CommandRunner) Run(command string) (string, string, error) { } func (d DiggerExecutor) planFileName() string { - return d.projectName + ".tfplan" + return d.repoName + "#" + d.projectName + ".tfplan" } -func (d DiggerExecutor) Plan(prNumber int) error { +func (d DiggerExecutor) localPlanFilePath() string { + return path.Join(d.projectPath, d.planFileName()) +} + +func (d DiggerExecutor) storedPlanFilePath() string { + return path.Join(d.repoOwner, d.planFileName()) +} +func (d DiggerExecutor) Plan(prNumber int) error { res, err := d.lock.Lock(prNumber) if err != nil { return fmt.Errorf("error locking project: %v", err) @@ -418,7 +431,7 @@ func (d DiggerExecutor) Plan(prNumber int) error { return fmt.Errorf("error executing plan: %v", err) } if d.planStorage != nil { - err = d.planStorage.StorePlan(d.planFileName()) + err = d.planStorage.StorePlan(d.localPlanFilePath(), d.storedPlanFilePath()) if err != nil { return fmt.Errorf("error storing plan: %v", err) } @@ -443,7 +456,7 @@ func (d DiggerExecutor) Apply(prNumber int) error { var plansFilename *string if d.planStorage != nil { var err error - plansFilename, err = d.planStorage.RetrievePlan(d.planFileName()) + plansFilename, err = d.planStorage.RetrievePlan(d.localPlanFilePath(), d.storedPlanFilePath()) if err != nil { return fmt.Errorf("error retrieving plan: %v", err) } @@ -509,6 +522,10 @@ func (d DiggerExecutor) Unlock(prNumber int) error { if err != nil { return fmt.Errorf("failed to aquire lock: %s, %v", d.lock.LockId(), err) } + err = d.planStorage.DeleteStoredPlan(d.storedPlanFilePath()) + if err != nil { + return fmt.Errorf("failed to delete stored plan file '%v': %v", d.storedPlanFilePath(), err) + } return nil } diff --git a/pkg/digger/digger_test.go b/pkg/digger/digger_test.go index 9f33753cc..564516034 100644 --- a/pkg/digger/digger_test.go +++ b/pkg/digger/digger_test.go @@ -128,16 +128,21 @@ type MockPlanStorage struct { Commands []RunInfo } -func (m *MockPlanStorage) StorePlan(planFileName string) error { - m.Commands = append(m.Commands, RunInfo{"StorePlan", planFileName, time.Now()}) +func (m *MockPlanStorage) StorePlan(localPlanFilePath string, storedPlanFilePath string) error { + m.Commands = append(m.Commands, RunInfo{"StorePlan", localPlanFilePath, time.Now()}) return nil } -func (m *MockPlanStorage) RetrievePlan(planFileName string) (*string, error) { - m.Commands = append(m.Commands, RunInfo{"RetrievePlan", planFileName, time.Now()}) +func (m *MockPlanStorage) RetrievePlan(localPlanFilePath string, storedPlanFilePath string) (*string, error) { + m.Commands = append(m.Commands, RunInfo{"RetrievePlan", localPlanFilePath, time.Now()}) return nil, nil } +func (m *MockPlanStorage) DeleteStoredPlan(storedPlanFilePath string) error { + m.Commands = append(m.Commands, RunInfo{"DeleteStoredPlan", storedPlanFilePath, time.Now()}) + return nil +} + func TestCorrectCommandExecutionWhenApplying(t *testing.T) { commandRunner := &MockCommandRunner{} @@ -177,7 +182,7 @@ func TestCorrectCommandExecutionWhenApplying(t *testing.T) { commandStrings := allCommandsInOrderWithParams(terraformExecutor, commandRunner, prManager, lock, planStorage) - assert.Equal(t, []string{"RetrievePlan .tfplan", "IsMergeable 1", "Lock 1", "Init ", "Apply ", "LockId ", "PublishComment 1
\n Apply for ****\n\n ```terraform\n\n ```\n
", "Run echo", "LockId "}, commandStrings) + assert.Equal(t, []string{"RetrievePlan #.tfplan", "IsMergeable 1", "Lock 1", "Init ", "Apply ", "LockId ", "PublishComment 1
\n Apply for ****\n\n ```terraform\n\n ```\n
", "Run echo", "LockId "}, commandStrings) } func TestCorrectCommandExecutionWhenPlanning(t *testing.T) { @@ -219,7 +224,7 @@ func TestCorrectCommandExecutionWhenPlanning(t *testing.T) { commandStrings := allCommandsInOrderWithParams(terraformExecutor, commandRunner, prManager, lock, planStorage) - assert.Equal(t, []string{"Lock 1", "Init ", "Plan -out .tfplan", "StorePlan .tfplan", "LockId ", "PublishComment 1
\n Plan for ****\n\n ```terraform\n\n ```\n
", "Run echo", "LockId "}, commandStrings) + assert.Equal(t, []string{"Lock 1", "Init ", "Plan -out #.tfplan", "StorePlan #.tfplan", "LockId ", "PublishComment 1
\n Plan for ****\n\n ```terraform\n\n ```\n
", "Run echo", "LockId "}, commandStrings) } func allCommandsInOrderWithParams(terraformExecutor *MockTerraformExecutor, commandRunner *MockCommandRunner, prManager *MockPRManager, lock *MockProjectLock, planStorage *MockPlanStorage) []string { diff --git a/pkg/utils/mocks.go b/pkg/utils/mocks.go index 020ea06d9..4f986c48a 100644 --- a/pkg/utils/mocks.go +++ b/pkg/utils/mocks.go @@ -77,10 +77,14 @@ func (t MockPullRequestManager) IsClosed(prNumber int) (bool, error) { type MockPlanStorage struct { } -func (t MockPlanStorage) StorePlan(planfile string) error { +func (t MockPlanStorage) StorePlan(localPlanFilePath string, storedPlanFilePath string) error { return nil } -func (t MockPlanStorage) RetrievePlan(planfile string) (*string, error) { +func (t MockPlanStorage) RetrievePlan(localPlanFilePath string, storedPlanFilePath string) (*string, error) { return nil, nil } + +func (t MockPlanStorage) DeleteStoredPlan(storedPlanFilePath string) error { + return nil +} diff --git a/pkg/utils/plan_storage.go b/pkg/utils/plan_storage.go index 1ed83218d..a71e500db 100644 --- a/pkg/utils/plan_storage.go +++ b/pkg/utils/plan_storage.go @@ -14,8 +14,9 @@ import ( ) type PlanStorage interface { - StorePlan(planFileName string) error - RetrievePlan(planFileName string) (*string, error) + StorePlan(localPlanFilePath string, storedPlanFilePath string) error + RetrievePlan(localPlanFilePath string, storedPlanFilePath string) (*string, error) + DeleteStoredPlan(storedPlanFilePath string) error } type PlanStorageGcp struct { @@ -32,14 +33,14 @@ type GithubPlanStorage struct { ZipManager Zipper } -func (psg *PlanStorageGcp) StorePlan(planFileName string) error { - file, err := os.Open(planFileName) +func (psg *PlanStorageGcp) StorePlan(localPlanFilePath string, storedPlanFilePath string) error { + file, err := os.Open(localPlanFilePath) if err != nil { return fmt.Errorf("unable to open file: %v", err) } defer file.Close() - obj := psg.Bucket.Object(planFileName) + obj := psg.Bucket.Object(storedPlanFilePath) wc := obj.NewWriter(psg.Context) if _, err = io.Copy(wc, file); err != nil { @@ -54,15 +55,15 @@ func (psg *PlanStorageGcp) StorePlan(planFileName string) error { return nil } -func (psg *PlanStorageGcp) RetrievePlan(planFileName string) (*string, error) { - obj := psg.Bucket.Object(planFileName) +func (psg *PlanStorageGcp) RetrievePlan(localPlanFilePath string, storedPlanFilePath string) (*string, error) { + obj := psg.Bucket.Object(storedPlanFilePath) rc, err := obj.NewReader(psg.Context) if err != nil { return nil, fmt.Errorf("unable to read data from bucket: %v", err) } defer rc.Close() - file, err := os.Create(planFileName) + file, err := os.Create(localPlanFilePath) if err != nil { return nil, fmt.Errorf("unable to create file: %v", err) } @@ -72,15 +73,25 @@ func (psg *PlanStorageGcp) RetrievePlan(planFileName string) (*string, error) { return nil, fmt.Errorf("unable to write data to file: %v", err) } - return &planFileName, nil + return &localPlanFilePath, nil } -func (gps *GithubPlanStorage) StorePlan(planFileName string) error { - _ = fmt.Sprintf("Skipping storing plan %s. It should be achieved using actions/upload-artifact@v3", planFileName) +func (psg *PlanStorageGcp) DeleteStoredPlan(storedPlanFilePath string) error { + obj := psg.Bucket.Object(storedPlanFilePath) + err := obj.Delete(psg.Context) + + if err != nil { + return fmt.Errorf("unable to delete file '%v' from bucket: %v", storedPlanFilePath, err) + } return nil } -func (gps *GithubPlanStorage) RetrievePlan(planFileName string) (*string, error) { +func (gps *GithubPlanStorage) StorePlan(localPlanFilePath string, storedPlanFilePath string) error { + _ = fmt.Sprintf("Skipping storing plan %s. It should be achieved using actions/upload-artifact@v3", localPlanFilePath) + return nil +} + +func (gps *GithubPlanStorage) RetrievePlan(localPlanFilePath string, storedPlanFilePath string) (*string, error) { plansFilename, err := gps.DownloadLatestPlans() if err != nil { @@ -91,7 +102,7 @@ func (gps *GithubPlanStorage) RetrievePlan(planFileName string) (*string, error) return nil, fmt.Errorf("no plans found for this PR") } - plansFilename, err = gps.ZipManager.GetFileFromZip(plansFilename, planFileName) + plansFilename, err = gps.ZipManager.GetFileFromZip(plansFilename, storedPlanFilePath) if err != nil { return nil, fmt.Errorf("error extracting plan: %v", err) @@ -99,6 +110,10 @@ func (gps *GithubPlanStorage) RetrievePlan(planFileName string) (*string, error) return &plansFilename, nil } +func (gps *GithubPlanStorage) DeleteStoredPlan(storedPlanFilePath string) error { + return nil +} + func (gps *GithubPlanStorage) DownloadLatestPlans() (string, error) { artifacts, _, err := gps.Client.Actions.ListArtifacts(context.Background(), gps.Owner, gps.RepoName, &github.ListOptions{ PerPage: 100,