Skip to content

Commit

Permalink
fix: planstorage functionality (#222)
Browse files Browse the repository at this point in the history
  • Loading branch information
fleroux514 authored May 10, 2023
1 parent 26c1b90 commit f2d4bf9
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
venv/
**/__pycache__/
__azurite*
digger
31 changes: 24 additions & 7 deletions pkg/digger/digger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
}

Expand Down
17 changes: 11 additions & 6 deletions pkg/digger/digger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -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 <details>\n <summary>Apply for ****</summary>\n\n ```terraform\n\n ```\n</details>", "Run echo", "LockId "}, commandStrings)
assert.Equal(t, []string{"RetrievePlan #.tfplan", "IsMergeable 1", "Lock 1", "Init ", "Apply ", "LockId ", "PublishComment 1 <details>\n <summary>Apply for ****</summary>\n\n ```terraform\n\n ```\n</details>", "Run echo", "LockId "}, commandStrings)
}

func TestCorrectCommandExecutionWhenPlanning(t *testing.T) {
Expand Down Expand Up @@ -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 <details>\n <summary>Plan for ****</summary>\n\n ```terraform\n\n ```\n</details>", "Run echo", "LockId "}, commandStrings)
assert.Equal(t, []string{"Lock 1", "Init ", "Plan -out #.tfplan", "StorePlan #.tfplan", "LockId ", "PublishComment 1 <details>\n <summary>Plan for ****</summary>\n\n ```terraform\n\n ```\n</details>", "Run echo", "LockId "}, commandStrings)
}

func allCommandsInOrderWithParams(terraformExecutor *MockTerraformExecutor, commandRunner *MockCommandRunner, prManager *MockPRManager, lock *MockProjectLock, planStorage *MockPlanStorage) []string {
Expand Down
8 changes: 6 additions & 2 deletions pkg/utils/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
41 changes: 28 additions & 13 deletions pkg/utils/plan_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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)
}
Expand All @@ -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 {
Expand All @@ -91,14 +102,18 @@ 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)
}
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,
Expand Down

0 comments on commit f2d4bf9

Please sign in to comment.