From dd7bb3177d8a631f37386130410317430c90b2fc Mon Sep 17 00:00:00 2001 From: Mohamed Habib Date: Tue, 7 Nov 2023 16:27:29 +0000 Subject: [PATCH] add support for opentofu (#738) * add support for opentofu Former-commit-id: aba7045c33b290c745fe8a7c8d1e89394901cf96 --- .github/workflows/go.yml | 5 + action.yml | 15 +++ cmd/digger/main.go | 7 ++ libs/digger_config/config.go | 1 + libs/digger_config/converters.go | 1 + libs/digger_config/yaml.go | 1 + libs/orchestrator/github/github.go | 57 +++++----- libs/orchestrator/models.go | 1 + pkg/azure/azure.go | 46 +++++---- pkg/core/terraform/opentofu.go | 155 ++++++++++++++++++++++++++++ pkg/core/terraform/opentofu_test.go | 63 +++++++++++ pkg/core/terraform/terragrunt.go | 75 ++++++++++++++ pkg/core/terraform/tf.go | 81 --------------- pkg/digger/digger.go | 24 +++-- pkg/digger/digger_test.go | 26 ++--- pkg/gitlab/gitlab.go | 33 +++--- 16 files changed, 424 insertions(+), 167 deletions(-) create mode 100644 pkg/core/terraform/opentofu.go create mode 100644 pkg/core/terraform/opentofu_test.go create mode 100644 pkg/core/terraform/terragrunt.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 192160724..0c00bd017 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -13,6 +13,11 @@ jobs: go-version: 1.21.1 id: go + - name: Setup Opentofu + uses: opentofu/setup-opentofu@v1 + with: + tofu_version: 1.6.0-alpha3 + - name: Check out code into the Go module directory uses: actions/checkout@v4 diff --git a/action.yml b/action.yml index a1b03e37b..e6899f6c2 100644 --- a/action.yml +++ b/action.yml @@ -53,10 +53,18 @@ inputs: description: Setup terragrunt required: false default: 'false' + setup-opentofu: + description: Setup OpenToFu + required: false + default: 'false' terragrunt-version: description: Terragrunt version required: false default: v0.48.6 + opentofu-version: + description: OpenTofu version + required: false + default: v1.6.0-alpha3 setup-terraform: description: Setup terraform required: false @@ -214,6 +222,13 @@ runs: terragrunt_version: ${{ inputs.terragrunt-version }} if: inputs.setup-terragrunt == 'true' + - name: Setup OpenTofu + uses: opentofu/setup-opentofu@v1.0.1 + with: + tofu_version: ${{ inputs.opentofu-version }} + if: inputs.setup-opentofu == 'true' + + - name: Setup Checkov run: | python3 -m venv .venv diff --git a/cmd/digger/main.go b/cmd/digger/main.go index 5c623714e..29d4cf467 100644 --- a/cmd/digger/main.go +++ b/cmd/digger/main.go @@ -209,6 +209,7 @@ func gitHubCI(lock core_locking.Lock, policyChecker core_policy.Checker, backend ProjectDir: projectConfig.Dir, ProjectWorkspace: projectConfig.Workspace, Terragrunt: projectConfig.Terragrunt, + OpenTofu: projectConfig.OpenTofu, Commands: []string{command}, ApplyStage: orchestrator.ToConfigStage(workflow.Apply), PlanStage: orchestrator.ToConfigStage(workflow.Plan), @@ -238,6 +239,7 @@ func gitHubCI(lock core_locking.Lock, policyChecker core_policy.Checker, backend ProjectDir: projectConfig.Dir, ProjectWorkspace: projectConfig.Workspace, Terragrunt: projectConfig.Terragrunt, + OpenTofu: projectConfig.OpenTofu, Commands: []string{"digger drift-detect"}, ApplyStage: orchestrator.ToConfigStage(workflow.Apply), PlanStage: orchestrator.ToConfigStage(workflow.Plan), @@ -606,6 +608,7 @@ func bitbucketCI(lock core_locking.Lock, policyChecker core_policy.Checker, back ProjectDir: projectConfig.Dir, ProjectWorkspace: projectConfig.Workspace, Terragrunt: projectConfig.Terragrunt, + OpenTofu: projectConfig.OpenTofu, Commands: []string{command}, ApplyStage: orchestrator.ToConfigStage(workflow.Apply), PlanStage: orchestrator.ToConfigStage(workflow.Plan), @@ -635,6 +638,7 @@ func bitbucketCI(lock core_locking.Lock, policyChecker core_policy.Checker, back ProjectDir: projectConfig.Dir, ProjectWorkspace: projectConfig.Workspace, Terragrunt: projectConfig.Terragrunt, + OpenTofu: projectConfig.OpenTofu, Commands: []string{"digger drift-detect"}, ApplyStage: orchestrator.ToConfigStage(workflow.Apply), PlanStage: orchestrator.ToConfigStage(workflow.Plan), @@ -664,6 +668,7 @@ func bitbucketCI(lock core_locking.Lock, policyChecker core_policy.Checker, back ProjectDir: projectConfig.Dir, ProjectWorkspace: projectConfig.Workspace, Terragrunt: projectConfig.Terragrunt, + OpenTofu: projectConfig.OpenTofu, Commands: workflow.Configuration.OnCommitToDefault, ApplyStage: orchestrator.ToConfigStage(workflow.Apply), PlanStage: orchestrator.ToConfigStage(workflow.Plan), @@ -690,6 +695,7 @@ func bitbucketCI(lock core_locking.Lock, policyChecker core_policy.Checker, back ProjectDir: projectConfig.Dir, ProjectWorkspace: projectConfig.Workspace, Terragrunt: projectConfig.Terragrunt, + OpenTofu: projectConfig.OpenTofu, Commands: []string{"digger plan"}, ApplyStage: orchestrator.ToConfigStage(workflow.Apply), PlanStage: orchestrator.ToConfigStage(workflow.Plan), @@ -734,6 +740,7 @@ func bitbucketCI(lock core_locking.Lock, policyChecker core_policy.Checker, back ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: workflow.Configuration.OnPullRequestPushed, ApplyStage: orchestrator.ToConfigStage(workflow.Apply), PlanStage: orchestrator.ToConfigStage(workflow.Plan), diff --git a/libs/digger_config/config.go b/libs/digger_config/config.go index 1a35c4752..84a57f817 100644 --- a/libs/digger_config/config.go +++ b/libs/digger_config/config.go @@ -17,6 +17,7 @@ type Project struct { Dir string Workspace string Terragrunt bool + OpenTofu bool Workflow string IncludePatterns []string ExcludePatterns []string diff --git a/libs/digger_config/converters.go b/libs/digger_config/converters.go index 075ca8c6d..3b9425a0e 100644 --- a/libs/digger_config/converters.go +++ b/libs/digger_config/converters.go @@ -26,6 +26,7 @@ func copyProjects(projects []*ProjectYaml) []Project { p.Dir, p.Workspace, p.Terragrunt, + p.OpenTofu, p.Workflow, p.IncludePatterns, p.ExcludePatterns, diff --git a/libs/digger_config/yaml.go b/libs/digger_config/yaml.go index 6c8d1f2dc..670ce4f9d 100644 --- a/libs/digger_config/yaml.go +++ b/libs/digger_config/yaml.go @@ -24,6 +24,7 @@ type ProjectYaml struct { Dir string `yaml:"dir"` Workspace string `yaml:"workspace"` Terragrunt bool `yaml:"terragrunt"` + OpenTofu bool `yaml:"opentofu"` Workflow string `yaml:"workflow"` IncludePatterns []string `yaml:"include_patterns,omitempty"` ExcludePatterns []string `yaml:"exclude_patterns,omitempty"` diff --git a/libs/orchestrator/github/github.go b/libs/orchestrator/github/github.go index 71da2460b..7b2f19c92 100644 --- a/libs/orchestrator/github/github.go +++ b/libs/orchestrator/github/github.go @@ -4,7 +4,7 @@ import ( "context" "fmt" "github.com/diggerhq/digger/libs/digger_config" - orchestrator2 "github.com/diggerhq/digger/libs/orchestrator" + orchestrator "github.com/diggerhq/digger/libs/orchestrator" "github.com/dominikbraun/graph" "log" "strings" @@ -65,11 +65,11 @@ func (svc *GithubService) PublishComment(prNumber int, comment string) error { return err } -func (svc *GithubService) GetComments(prNumber int) ([]orchestrator2.Comment, error) { +func (svc *GithubService) GetComments(prNumber int) ([]orchestrator.Comment, error) { comments, _, err := svc.Client.Issues.ListComments(context.Background(), svc.Owner, svc.RepoName, prNumber, &github.IssueListCommentsOptions{ListOptions: github.ListOptions{PerPage: 100}}) - commentBodies := make([]orchestrator2.Comment, len(comments)) + commentBodies := make([]orchestrator.Comment, len(comments)) for i, comment := range comments { - commentBodies[i] = orchestrator2.Comment{ + commentBodies[i] = orchestrator.Comment{ Id: *comment.ID, Body: comment.Body, } @@ -177,8 +177,8 @@ func (svc *GithubService) GetBranchName(prNumber int) (string, error) { return pr.Head.GetRef(), nil } -func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impactedProjects []digger_config.Project, requestedProject *digger_config.Project, workflows map[string]digger_config.Workflow) ([]orchestrator2.Job, bool, error) { - jobs := make([]orchestrator2.Job, 0) +func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impactedProjects []digger_config.Project, requestedProject *digger_config.Project, workflows map[string]digger_config.Workflow) ([]orchestrator.Job, bool, error) { + jobs := make([]orchestrator.Job, 0) for _, project := range impactedProjects { workflow, ok := workflows[project.Workflow] @@ -190,15 +190,15 @@ func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impac pullRequestNumber := payload.PullRequest.Number if *payload.Action == "closed" && *payload.PullRequest.Merged && *(payload.PullRequest.Base).Ref == *(payload.Repo).DefaultBranch { - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, ProjectWorkflow: project.Workflow, Terragrunt: project.Terragrunt, Commands: workflow.Configuration.OnCommitToDefault, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), CommandEnvVars: commandEnvVars, StateEnvVars: stateEnvVars, PullRequestNumber: pullRequestNumber, @@ -207,15 +207,16 @@ func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impac RequestedBy: *payload.Sender.Login, }) } else if *payload.Action == "opened" || *payload.Action == "reopened" || *payload.Action == "synchronize" { - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, ProjectWorkflow: project.Workflow, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: workflow.Configuration.OnPullRequestPushed, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), CommandEnvVars: commandEnvVars, StateEnvVars: stateEnvVars, PullRequestNumber: pullRequestNumber, @@ -224,15 +225,16 @@ func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impac RequestedBy: *payload.Sender.Login, }) } else if *payload.Action == "closed" { - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, ProjectWorkflow: project.Workflow, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: workflow.Configuration.OnPullRequestClosed, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), CommandEnvVars: commandEnvVars, StateEnvVars: stateEnvVars, PullRequestNumber: pullRequestNumber, @@ -245,8 +247,8 @@ func ConvertGithubPullRequestEventToJobs(payload *github.PullRequestEvent, impac return jobs, true, nil } -func ConvertGithubIssueCommentEventToJobs(payload *github.IssueCommentEvent, impactedProjects []digger_config.Project, requestedProject *digger_config.Project, workflows map[string]digger_config.Workflow) ([]orchestrator2.Job, bool, error) { - jobs := make([]orchestrator2.Job, 0) +func ConvertGithubIssueCommentEventToJobs(payload *github.IssueCommentEvent, impactedProjects []digger_config.Project, requestedProject *digger_config.Project, workflows map[string]digger_config.Workflow) ([]orchestrator.Job, bool, error) { + jobs := make([]orchestrator.Job, 0) supportedCommands := []string{"digger plan", "digger apply", "digger unlock", "digger lock"} @@ -277,22 +279,23 @@ func ConvertGithubIssueCommentEventToJobs(payload *github.IssueCommentEvent, imp stateEnvVars, commandEnvVars := digger_config.CollectTerraformEnvConfig(workflow.EnvVars) workspace := project.Workspace - workspaceOverride, err := orchestrator2.ParseWorkspace(*payload.Comment.Body) + workspaceOverride, err := orchestrator.ParseWorkspace(*payload.Comment.Body) if err != nil { - return []orchestrator2.Job{}, false, err + return []orchestrator.Job{}, false, err } if workspaceOverride != "" { workspace = workspaceOverride } - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: workspace, ProjectWorkflow: project.Workflow, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: []string{command}, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), CommandEnvVars: commandEnvVars, StateEnvVars: stateEnvVars, PullRequestNumber: issueNumber, @@ -306,7 +309,7 @@ func ConvertGithubIssueCommentEventToJobs(payload *github.IssueCommentEvent, imp return jobs, coversAllImpactedProjects, nil } -func ProcessGitHubEvent(ghEvent interface{}, diggerConfig *digger_config.DiggerConfig, ciService orchestrator2.PullRequestService) ([]digger_config.Project, *digger_config.Project, int, error) { +func ProcessGitHubEvent(ghEvent interface{}, diggerConfig *digger_config.DiggerConfig, ciService orchestrator.PullRequestService) ([]digger_config.Project, *digger_config.Project, int, error) { var impactedProjects []digger_config.Project var prNumber int @@ -329,7 +332,7 @@ func ProcessGitHubEvent(ghEvent interface{}, diggerConfig *digger_config.DiggerC } impactedProjects = diggerConfig.GetModifiedProjects(changedFiles) - requestedProject := orchestrator2.ParseProjectName(*event.Comment.Body) + requestedProject := orchestrator.ParseProjectName(*event.Comment.Body) if requestedProject == "" { return impactedProjects, nil, prNumber, nil @@ -348,7 +351,7 @@ func ProcessGitHubEvent(ghEvent interface{}, diggerConfig *digger_config.DiggerC return impactedProjects, nil, prNumber, nil } -func ProcessGitHubPullRequestEvent(payload *github.PullRequestEvent, diggerConfig *digger_config.DiggerConfig, dependencyGraph graph.Graph[string, digger_config.Project], ciService orchestrator2.PullRequestService) ([]digger_config.Project, int, error) { +func ProcessGitHubPullRequestEvent(payload *github.PullRequestEvent, diggerConfig *digger_config.DiggerConfig, dependencyGraph graph.Graph[string, digger_config.Project], ciService orchestrator.PullRequestService) ([]digger_config.Project, int, error) { var impactedProjects []digger_config.Project var prNumber int prNumber = *payload.PullRequest.Number @@ -417,7 +420,7 @@ func FindAllProjectsDependantOnImpactedProjects(impactedProjects []digger_config return impactedProjectsWithDependantProjects, nil } -func ProcessGitHubIssueCommentEvent(payload *github.IssueCommentEvent, diggerConfig *digger_config.DiggerConfig, dependencyGraph graph.Graph[string, digger_config.Project], ciService orchestrator2.PullRequestService) ([]digger_config.Project, *digger_config.Project, int, error) { +func ProcessGitHubIssueCommentEvent(payload *github.IssueCommentEvent, diggerConfig *digger_config.DiggerConfig, dependencyGraph graph.Graph[string, digger_config.Project], ciService orchestrator.PullRequestService) ([]digger_config.Project, *digger_config.Project, int, error) { var impactedProjects []digger_config.Project var prNumber int @@ -437,7 +440,7 @@ func ProcessGitHubIssueCommentEvent(payload *github.IssueCommentEvent, diggerCon } } - requestedProject := orchestrator2.ParseProjectName(*payload.Comment.Body) + requestedProject := orchestrator.ParseProjectName(*payload.Comment.Body) if requestedProject == "" { return impactedProjects, nil, prNumber, nil diff --git a/libs/orchestrator/models.go b/libs/orchestrator/models.go index 8e8a2efc8..ac8e9ba14 100644 --- a/libs/orchestrator/models.go +++ b/libs/orchestrator/models.go @@ -10,6 +10,7 @@ type Job struct { ProjectWorkspace string ProjectWorkflow string Terragrunt bool + OpenTofu bool Commands []string ApplyStage *Stage PlanStage *Stage diff --git a/pkg/azure/azure.go b/pkg/azure/azure.go index 515b7b911..a069eb793 100644 --- a/pkg/azure/azure.go +++ b/pkg/azure/azure.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" digger_config2 "github.com/diggerhq/digger/libs/digger_config" - orchestrator2 "github.com/diggerhq/digger/libs/orchestrator" + orchestrator "github.com/diggerhq/digger/libs/orchestrator" "github.com/diggerhq/digger/pkg/utils" "github.com/microsoft/azure-devops-go-api/azuredevops" "github.com/microsoft/azure-devops-go-api/azuredevops/git" @@ -335,7 +335,7 @@ func (a *AzureReposService) GetBranchName(prNumber int) (string, error) { return "", nil } -func (a *AzureReposService) GetComments(prNumber int) ([]orchestrator2.Comment, error) { +func (a *AzureReposService) GetComments(prNumber int) ([]orchestrator.Comment, error) { comments, err := a.Client.GetComments(context.Background(), git.GetCommentsArgs{ Project: &a.ProjectName, RepositoryId: &a.RepositoryId, @@ -344,9 +344,9 @@ func (a *AzureReposService) GetComments(prNumber int) ([]orchestrator2.Comment, if err != nil { return nil, err } - var result []orchestrator2.Comment + var result []orchestrator.Comment for _, comment := range *comments { - result = append(result, orchestrator2.Comment{ + result = append(result, orchestrator.Comment{ Id: *comment.Id, Body: comment.Content, }) @@ -355,7 +355,7 @@ func (a *AzureReposService) GetComments(prNumber int) ([]orchestrator2.Comment, } -func ProcessAzureReposEvent(azureEvent interface{}, diggerConfig *digger_config2.DiggerConfig, ciService orchestrator2.PullRequestService) ([]digger_config2.Project, *digger_config2.Project, int, error) { +func ProcessAzureReposEvent(azureEvent interface{}, diggerConfig *digger_config2.DiggerConfig, ciService orchestrator.PullRequestService) ([]digger_config2.Project, *digger_config2.Project, int, error) { var impactedProjects []digger_config2.Project var prNumber int @@ -397,8 +397,8 @@ func ProcessAzureReposEvent(azureEvent interface{}, diggerConfig *digger_config2 return impactedProjects, nil, prNumber, nil } -func ConvertAzureEventToCommands(parseAzureContext Azure, impactedProjects []digger_config2.Project, requestedProject *digger_config2.Project, workflows map[string]digger_config2.Workflow) ([]orchestrator2.Job, bool, error) { - jobs := make([]orchestrator2.Job, 0) +func ConvertAzureEventToCommands(parseAzureContext Azure, impactedProjects []digger_config2.Project, requestedProject *digger_config2.Project, workflows map[string]digger_config2.Workflow) ([]orchestrator.Job, bool, error) { + jobs := make([]orchestrator.Job, 0) //&dependencyGraph, diggerProjectNamespace, parsedAzureContext.BaseUrl, parsedAzureContext.EventType, prNumber, switch parseAzureContext.EventType { case AzurePrCreated, AzurePrUpdated, AzurePrReopened: @@ -410,14 +410,15 @@ func ConvertAzureEventToCommands(parseAzureContext Azure, impactedProjects []dig prNumber := parseAzureContext.Event.(AzurePrEvent).Resource.PullRequestId stateEnvVars, commandEnvVars := digger_config2.CollectTerraformEnvConfig(workflow.EnvVars) - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: workflow.Configuration.OnPullRequestPushed, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), PullRequestNumber: &prNumber, EventName: parseAzureContext.EventType, RequestedBy: parseAzureContext.BaseUrl, @@ -436,14 +437,15 @@ func ConvertAzureEventToCommands(parseAzureContext Azure, impactedProjects []dig prNumber := parseAzureContext.Event.(AzurePrEvent).Resource.PullRequestId stateEnvVars, commandEnvVars := digger_config2.CollectTerraformEnvConfig(workflow.EnvVars) - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: workflow.Configuration.OnPullRequestClosed, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), PullRequestNumber: &prNumber, EventName: parseAzureContext.EventType, RequestedBy: parseAzureContext.BaseUrl, @@ -462,14 +464,15 @@ func ConvertAzureEventToCommands(parseAzureContext Azure, impactedProjects []dig return nil, false, fmt.Errorf("failed to find workflow digger_config '%s' for project '%s'", project.Workflow, project.Name) } stateEnvVars, commandEnvVars := digger_config2.CollectTerraformEnvConfig(workflow.EnvVars) - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: workflow.Configuration.OnCommitToDefault, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), PullRequestNumber: &prNumber, EventName: parseAzureContext.EventType, RequestedBy: parseAzureContext.BaseUrl, @@ -503,7 +506,7 @@ func ConvertAzureEventToCommands(parseAzureContext Azure, impactedProjects []dig workspace := project.Workspace workspaceOverride, err := utils.ParseWorkspace(diggerCommand) if err != nil { - return []orchestrator2.Job{}, coversAllImpactedProjects, err + return []orchestrator.Job{}, coversAllImpactedProjects, err } if workspaceOverride != "" { workspace = workspaceOverride @@ -514,14 +517,15 @@ func ConvertAzureEventToCommands(parseAzureContext Azure, impactedProjects []dig } stateEnvVars, commandEnvVars := digger_config2.CollectTerraformEnvConfig(workflow.EnvVars) - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: workspace, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: []string{command}, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), PullRequestNumber: &prNumber, EventName: parseAzureContext.EventType, RequestedBy: parseAzureContext.BaseUrl, @@ -535,6 +539,6 @@ func ConvertAzureEventToCommands(parseAzureContext Azure, impactedProjects []dig return jobs, coversAllImpactedProjects, nil default: - return []orchestrator2.Job{}, true, fmt.Errorf("unsupported Azure event type: %v", parseAzureContext.EventType) + return []orchestrator.Job{}, true, fmt.Errorf("unsupported Azure event type: %v", parseAzureContext.EventType) } } diff --git a/pkg/core/terraform/opentofu.go b/pkg/core/terraform/opentofu.go new file mode 100644 index 000000000..87cd1de58 --- /dev/null +++ b/pkg/core/terraform/opentofu.go @@ -0,0 +1,155 @@ +package terraform + +import ( + "bytes" + "fmt" + "io" + "log" + "os" + "os/exec" + "strings" +) + +type OpenTofu struct { + WorkingDir string + Workspace string +} + +func (tf OpenTofu) Init(params []string, envs map[string]string) (string, string, error) { + params = append(params, "-upgrade=true") + params = append(params, "-input=false") + params = append(params, "-no-color") + stdout, stderr, _, err := tf.runOpentofuCommand(true, "init", envs, params...) + return stdout, stderr, err +} + +func (tf OpenTofu) Apply(params []string, plan *string, envs map[string]string) (string, string, error) { + err := tf.switchToWorkspace(envs) + if err != nil { + log.Printf("Fatal: Error terraform to workspace %v", err) + return "", "", err + } + params = append(append(append(params, "-input=false"), "-no-color"), "-auto-approve") + if plan != nil { + params = append(params, *plan) + } + stdout, stderr, _, err := tf.runOpentofuCommand(true, "apply", envs, params...) + return stdout, stderr, err +} + +func (tf OpenTofu) Plan(params []string, envs map[string]string) (bool, string, string, error) { + + workspaces, _, _, err := tf.runOpentofuCommand(false, "workspace", envs, "list") + if err != nil { + return false, "", "", err + } + workspaces = tf.formatOpentofuWorkspaces(workspaces) + if strings.Contains(workspaces, tf.Workspace) { + _, _, _, err := tf.runOpentofuCommand(true, "workspace", envs, "select", tf.Workspace) + if err != nil { + return false, "", "", err + } + } else { + _, _, _, err := tf.runOpentofuCommand(true, "workspace", envs, "new", tf.Workspace) + if err != nil { + return false, "", "", err + } + } + params = append(append(append(params, "-input=false"), "-no-color"), "-detailed-exitcode") + stdout, stderr, statusCode, err := tf.runOpentofuCommand(true, "plan", envs, params...) + if err != nil && statusCode != 2 { + return false, "", "", err + } + return statusCode == 2, stdout, stderr, nil +} + +func (tf OpenTofu) Show(params []string, envs map[string]string) (string, string, error) { + stdout, stderr, _, err := tf.runOpentofuCommand(false, "show", envs, params...) + if err != nil { + return "", "", err + } + return stdout, stderr, nil +} + +func (tf OpenTofu) Destroy(params []string, envs map[string]string) (string, string, error) { + err := tf.switchToWorkspace(envs) + if err != nil { + log.Printf("Fatal: Error terraform to workspace %v", err) + return "", "", err + } + params = append(append(append(params, "-input=false"), "-no-color"), "-auto-approve") + stdout, stderr, _, err := tf.runOpentofuCommand(true, "destroy", envs, params...) + return stdout, stderr, err +} + +func (tf OpenTofu) switchToWorkspace(envs map[string]string) error { + workspaces, _, _, err := tf.runOpentofuCommand(false, "workspace", envs, "list") + if err != nil { + return err + } + workspaces = tf.formatOpentofuWorkspaces(workspaces) + if strings.Contains(workspaces, tf.Workspace) { + _, _, _, err := tf.runOpentofuCommand(true, "workspace", envs, "select", tf.Workspace) + if err != nil { + return err + } + } else { + _, _, _, err := tf.runOpentofuCommand(true, "workspace", envs, "new", tf.Workspace) + if err != nil { + return err + } + } + return nil +} + +func (tf OpenTofu) runOpentofuCommand(printOutputToStdout bool, command string, envs map[string]string, arg ...string) (string, string, int, error) { + args := []string{command} + args = append(args, arg...) + + expandedArgs := make([]string, 0) + for _, p := range args { + s := os.ExpandEnv(p) + s = strings.TrimSpace(s) + if s != "" { + expandedArgs = append(expandedArgs, s) + } + } + + var mwout, mwerr io.Writer + var stdout, stderr bytes.Buffer + if printOutputToStdout { + mwout = io.MultiWriter(os.Stdout, &stdout) + mwerr = io.MultiWriter(os.Stderr, &stderr) + } else { + mwout = io.Writer(&stdout) + mwerr = io.Writer(&stderr) + } + + cmd := exec.Command("tofu", expandedArgs...) + log.Printf("Running command: opentofu %v", expandedArgs) + cmd.Dir = tf.WorkingDir + + env := os.Environ() + for k, v := range envs { + env = append(env, fmt.Sprintf("%s=%s", k, v)) + } + cmd.Env = env + cmd.Stdout = mwout + cmd.Stderr = mwerr + + err := cmd.Run() + + // terraform plan can return 2 if there are changes to be applied, so we don't want to fail in that case + if err != nil && cmd.ProcessState.ExitCode() != 2 { + log.Println("Error:", err) + } + + return stdout.String(), stderr.String(), cmd.ProcessState.ExitCode(), err +} + +func (tf OpenTofu) formatOpentofuWorkspaces(list string) string { + list = strings.TrimSpace(list) + char_replace := strings.NewReplacer("*", "", "\n", ",", " ", "") + list = char_replace.Replace(list) + return list +} diff --git a/pkg/core/terraform/opentofu_test.go b/pkg/core/terraform/opentofu_test.go new file mode 100644 index 000000000..1a8692da7 --- /dev/null +++ b/pkg/core/terraform/opentofu_test.go @@ -0,0 +1,63 @@ +package terraform + +import ( + "github.com/stretchr/testify/assert" + "log" + "os" + "testing" +) + +func TestExecuteTofuPlan(t *testing.T) { + dir := CreateTestTerraformProject() + defer func(name string) { + err := os.RemoveAll(name) + if err != nil { + log.Fatal(err) + } + }(dir) + + CreateValidTerraformTestFile(dir) + + tf := OpenTofu{WorkingDir: dir, Workspace: "dev"} + tf.Init([]string{}, map[string]string{}) + _, _, _, err := tf.Plan([]string{}, map[string]string{}) + assert.NoError(t, err) +} + +func TestExecuteTofuApply(t *testing.T) { + dir := CreateTestTerraformProject() + defer func(name string) { + err := os.RemoveAll(name) + if err != nil { + log.Fatal(err) + } + }(dir) + + CreateValidTerraformTestFile(dir) + + tf := OpenTofu{WorkingDir: dir, Workspace: "dev"} + tf.Init([]string{}, map[string]string{}) + _, _, _, err := tf.Plan([]string{}, map[string]string{}) + assert.NoError(t, err) +} + +func TestExecuteTofuApplyDefaultWorkspace(t *testing.T) { + dir := CreateTestTerraformProject() + defer func(name string) { + err := os.RemoveAll(name) + if err != nil { + log.Fatal(err) + } + }(dir) + + CreateValidTerraformTestFile(dir) + + tf := OpenTofu{WorkingDir: dir, Workspace: "default"} + tf.Init([]string{}, map[string]string{}) + var planArgs []string + planArgs = append(planArgs, "-out", "plan.tfplan") + tf.Plan(planArgs, map[string]string{}) + plan := "plan.tfplan" + _, _, err := tf.Apply([]string{}, &plan, map[string]string{}) + assert.NoError(t, err) +} diff --git a/pkg/core/terraform/terragrunt.go b/pkg/core/terraform/terragrunt.go new file mode 100644 index 000000000..0b5dc82c3 --- /dev/null +++ b/pkg/core/terraform/terragrunt.go @@ -0,0 +1,75 @@ +package terraform + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" +) + +type Terragrunt struct { + WorkingDir string +} + +func (terragrunt Terragrunt) Init(params []string, envs map[string]string) (string, string, error) { + return terragrunt.runTerragruntCommand("init", envs, params...) + +} + +func (terragrunt Terragrunt) Apply(params []string, plan *string, envs map[string]string) (string, string, error) { + params = append(params, "--auto-approve") + params = append(params, "--terragrunt-non-interactive") + if plan != nil { + params = append(params, *plan) + } + stdout, stderr, err := terragrunt.runTerragruntCommand("apply", envs, params...) + return stdout, stderr, err +} + +func (terragrunt Terragrunt) Destroy(params []string, envs map[string]string) (string, string, error) { + params = append(params, "--auto-approve") + params = append(params, "--terragrunt-non-interactive") + stdout, stderr, err := terragrunt.runTerragruntCommand("destroy", envs, params...) + return stdout, stderr, err +} + +func (terragrunt Terragrunt) Plan(params []string, envs map[string]string) (bool, string, string, error) { + stdout, stderr, err := terragrunt.runTerragruntCommand("plan", envs, params...) + return true, stdout, stderr, err +} + +func (terragrunt Terragrunt) Show(params []string, envs map[string]string) (string, string, error) { + stdout, stderr, err := terragrunt.runTerragruntCommand("show", envs, params...) + return stdout, stderr, err +} + +func (terragrunt Terragrunt) runTerragruntCommand(command string, envs map[string]string, arg ...string) (string, string, error) { + args := []string{command} + args = append(args, arg...) + cmd := exec.Command("terragrunt", args...) + cmd.Dir = terragrunt.WorkingDir + + env := os.Environ() + env = append(env, "TF_CLI_ARGS=-no-color") + env = append(env, "TF_IN_AUTOMATION=true") + + for k, v := range envs { + env = append(env, fmt.Sprintf("%s=%s", k, v)) + } + + cmd.Env = env + + var stdout, stderr bytes.Buffer + mwout := io.MultiWriter(os.Stdout, &stdout) + mwerr := io.MultiWriter(os.Stderr, &stderr) + cmd.Stdout = mwout + cmd.Stderr = mwerr + err := cmd.Run() + + if err != nil { + return stdout.String(), stderr.String(), fmt.Errorf("error: %v", err) + } + + return stdout.String(), stderr.String(), err +} diff --git a/pkg/core/terraform/tf.go b/pkg/core/terraform/tf.go index f33d98747..0bafc0e03 100644 --- a/pkg/core/terraform/tf.go +++ b/pkg/core/terraform/tf.go @@ -18,77 +18,11 @@ type TerraformExecutor interface { Show([]string, map[string]string) (string, string, error) } -type Terragrunt struct { - WorkingDir string -} - type Terraform struct { WorkingDir string Workspace string } -func (terragrunt Terragrunt) Init(params []string, envs map[string]string) (string, string, error) { - return terragrunt.runTerragruntCommand("init", envs, params...) - -} - -func (terragrunt Terragrunt) Apply(params []string, plan *string, envs map[string]string) (string, string, error) { - params = append(params, "--auto-approve") - params = append(params, "--terragrunt-non-interactive") - if plan != nil { - params = append(params, *plan) - } - stdout, stderr, err := terragrunt.runTerragruntCommand("apply", envs, params...) - return stdout, stderr, err -} - -func (terragrunt Terragrunt) Destroy(params []string, envs map[string]string) (string, string, error) { - params = append(params, "--auto-approve") - params = append(params, "--terragrunt-non-interactive") - stdout, stderr, err := terragrunt.runTerragruntCommand("destroy", envs, params...) - return stdout, stderr, err -} - -func (terragrunt Terragrunt) Plan(params []string, envs map[string]string) (bool, string, string, error) { - stdout, stderr, err := terragrunt.runTerragruntCommand("plan", envs, params...) - return true, stdout, stderr, err -} - -func (terragrunt Terragrunt) Show(params []string, envs map[string]string) (string, string, error) { - stdout, stderr, err := terragrunt.runTerragruntCommand("show", envs, params...) - return stdout, stderr, err -} - -func (terragrunt Terragrunt) runTerragruntCommand(command string, envs map[string]string, arg ...string) (string, string, error) { - args := []string{command} - args = append(args, arg...) - cmd := exec.Command("terragrunt", args...) - cmd.Dir = terragrunt.WorkingDir - - env := os.Environ() - env = append(env, "TF_CLI_ARGS=-no-color") - env = append(env, "TF_IN_AUTOMATION=true") - - for k, v := range envs { - env = append(env, fmt.Sprintf("%s=%s", k, v)) - } - - cmd.Env = env - - var stdout, stderr bytes.Buffer - mwout := io.MultiWriter(os.Stdout, &stdout) - mwerr := io.MultiWriter(os.Stderr, &stderr) - cmd.Stdout = mwout - cmd.Stderr = mwerr - err := cmd.Run() - - if err != nil { - return stdout.String(), stderr.String(), fmt.Errorf("error: %v", err) - } - - return stdout.String(), stderr.String(), err -} - func (tf Terraform) Init(params []string, envs map[string]string) (string, string, error) { params = append(params, "-upgrade=true") params = append(params, "-input=false") @@ -192,21 +126,6 @@ type StdWriter struct { print bool } -func (sw *StdWriter) Write(data []byte) (n int, err error) { - s := string(data) - if sw.print { - print(s) - } - - sw.data = append(sw.data, data...) - return 0, nil -} - -func (sw *StdWriter) GetString() string { - s := string(sw.data) - return s -} - func (tf Terraform) formatTerraformWorkspaces(list string) string { list = strings.TrimSpace(list) diff --git a/pkg/digger/digger.go b/pkg/digger/digger.go index 54c08f9b2..f99c70ebb 100644 --- a/pkg/digger/digger.go +++ b/pkg/digger/digger.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" config "github.com/diggerhq/digger/libs/digger_config" - orchestrator2 "github.com/diggerhq/digger/libs/orchestrator" + orchestrator "github.com/diggerhq/digger/libs/orchestrator" "github.com/diggerhq/digger/pkg/core/backend" "github.com/diggerhq/digger/pkg/core/execution" core_locking "github.com/diggerhq/digger/pkg/core/locking" @@ -67,9 +67,9 @@ func DetectCI() CIName { } func RunJobs( - jobs []orchestrator2.Job, - prService orchestrator2.PullRequestService, - orgService orchestrator2.OrgService, + jobs []orchestrator.Job, + prService orchestrator.PullRequestService, + orgService orchestrator.OrgService, lock core_locking.Lock, reporter core_reporting.Reporter, planStorage storage.PlanStorage, @@ -145,7 +145,7 @@ func reportPolicyError(projectName string, command string, requestedBy string, r return msg } -func run(command string, job orchestrator2.Job, policyChecker policy.Checker, orgService orchestrator2.OrgService, SCMOrganisation string, SCMrepository string, requestedBy string, reporter core_reporting.Reporter, lock core_locking.Lock, prService orchestrator2.PullRequestService, projectNamespace string, workingDir string, planStorage storage.PlanStorage, appliesPerProject map[string]bool) (string, error) { +func run(command string, job orchestrator.Job, policyChecker policy.Checker, orgService orchestrator.OrgService, SCMOrganisation string, SCMrepository string, requestedBy string, reporter core_reporting.Reporter, lock core_locking.Lock, prService orchestrator.PullRequestService, projectNamespace string, workingDir string, planStorage storage.PlanStorage, appliesPerProject map[string]bool) (string, error) { log.Printf("Running '%s' for project '%s' (workflow: %s)\n", command, job.ProjectName, job.ProjectWorkflow) allowedToPerformCommand, err := policyChecker.CheckAccessPolicy(orgService, SCMOrganisation, SCMrepository, job.ProjectName, command, requestedBy) @@ -173,6 +173,8 @@ func run(command string, job orchestrator2.Job, policyChecker policy.Checker, or projectPath := path.Join(workingDir, job.ProjectDir) if job.Terragrunt { terraformExecutor = terraform.Terragrunt{WorkingDir: projectPath} + } else if job.OpenTofu { + terraformExecutor = terraform.OpenTofu{WorkingDir: projectPath, Workspace: job.ProjectWorkspace} } else { terraformExecutor = terraform.Terraform{WorkingDir: projectPath, Workspace: job.ProjectWorkspace} } @@ -394,10 +396,10 @@ func reportTerraformPlanOutput(reporter core_reporting.Reporter, projectId strin } func RunJob( - job orchestrator2.Job, + job orchestrator.Job, repo string, requestedBy string, - orgService orchestrator2.OrgService, + orgService orchestrator.OrgService, policyChecker policy.Checker, planStorage storage.PlanStorage, backendApi backend.Api, @@ -433,6 +435,8 @@ func RunJob( projectPath := path.Join(workingDir, job.ProjectDir) if job.Terragrunt { terraformExecutor = terraform.Terragrunt{WorkingDir: projectPath} + } else if job.OpenTofu { + terraformExecutor = terraform.OpenTofu{WorkingDir: projectPath, Workspace: job.ProjectWorkspace} } else { terraformExecutor = terraform.Terraform{WorkingDir: projectPath, Workspace: job.ProjectWorkspace} } @@ -628,8 +632,8 @@ func runDriftDetection(policyChecker policy.Checker, SCMOrganisation string, SCM return plan, nil } -func SortedCommandsByDependency(project []orchestrator2.Job, dependencyGraph *graph.Graph[string, config.Project]) []orchestrator2.Job { - var sortedCommands []orchestrator2.Job +func SortedCommandsByDependency(project []orchestrator.Job, dependencyGraph *graph.Graph[string, config.Project]) []orchestrator.Job { + var sortedCommands []orchestrator.Job sortedGraph, err := graph.StableTopologicalSort(*dependencyGraph, func(s string, s2 string) bool { return s < s2 }) @@ -647,7 +651,7 @@ func SortedCommandsByDependency(project []orchestrator2.Job, dependencyGraph *gr return sortedCommands } -func MergePullRequest(ciService orchestrator2.PullRequestService, prNumber int) { +func MergePullRequest(ciService orchestrator.PullRequestService, prNumber int) { time.Sleep(5 * time.Second) // CheckAccessPolicy if it was manually merged diff --git a/pkg/digger/digger_test.go b/pkg/digger/digger_test.go index f36f24509..8d96608a8 100644 --- a/pkg/digger/digger_test.go +++ b/pkg/digger/digger_test.go @@ -2,7 +2,7 @@ package digger import ( configuration "github.com/diggerhq/digger/libs/digger_config" - orchestrator2 "github.com/diggerhq/digger/libs/orchestrator" + orchestrator "github.com/diggerhq/digger/libs/orchestrator" "github.com/diggerhq/digger/pkg/core/execution" "github.com/diggerhq/digger/pkg/reporting" "github.com/diggerhq/digger/pkg/utils" @@ -116,9 +116,9 @@ func (m *MockPRManager) IsClosed(prNumber int) (bool, error) { return false, nil } -func (m *MockPRManager) GetComments(prNumber int) ([]orchestrator2.Comment, error) { +func (m *MockPRManager) GetComments(prNumber int) ([]orchestrator.Comment, error) { m.Commands = append(m.Commands, RunInfo{"GetComments", strconv.Itoa(prNumber), time.Now()}) - return []orchestrator2.Comment{}, nil + return []orchestrator.Comment{}, nil } func (m *MockPRManager) EditComment(prNumber int, id interface{}, comment string) error { @@ -222,8 +222,8 @@ func TestCorrectCommandExecutionWhenApplying(t *testing.T) { } planPathProvider := &MockPlanPathProvider{} executor := execution.DiggerExecutor{ - ApplyStage: &orchestrator2.Stage{ - Steps: []orchestrator2.Step{ + ApplyStage: &orchestrator.Stage{ + Steps: []orchestrator.Step{ { Action: "init", ExtraArgs: nil, @@ -241,7 +241,7 @@ func TestCorrectCommandExecutionWhenApplying(t *testing.T) { }, }, }, - PlanStage: &orchestrator2.Stage{}, + PlanStage: &orchestrator.Stage{}, CommandRunner: commandRunner, TerraformExecutor: terraformExecutor, Reporter: reporter, @@ -270,8 +270,8 @@ func TestCorrectCommandExecutionWhenDestroying(t *testing.T) { } planPathProvider := &MockPlanPathProvider{} executor := execution.DiggerExecutor{ - ApplyStage: &orchestrator2.Stage{ - Steps: []orchestrator2.Step{ + ApplyStage: &orchestrator.Stage{ + Steps: []orchestrator.Step{ { Action: "init", ExtraArgs: nil, @@ -284,7 +284,7 @@ func TestCorrectCommandExecutionWhenDestroying(t *testing.T) { }, }, }, - PlanStage: &orchestrator2.Stage{}, + PlanStage: &orchestrator.Stage{}, CommandRunner: commandRunner, TerraformExecutor: terraformExecutor, Reporter: reporter, @@ -311,9 +311,9 @@ func TestCorrectCommandExecutionWhenPlanning(t *testing.T) { planPathProvider := &MockPlanPathProvider{} executor := execution.DiggerExecutor{ - ApplyStage: &orchestrator2.Stage{}, - PlanStage: &orchestrator2.Stage{ - Steps: []orchestrator2.Step{ + ApplyStage: &orchestrator.Stage{}, + PlanStage: &orchestrator.Stage{ + Steps: []orchestrator.Step{ { Action: "init", ExtraArgs: nil, @@ -382,7 +382,7 @@ func TestSortedCommandByDependency(t *testing.T) { // jobs []models.Job, // dependencyGraph *graph.Graph[string, string], - jobs := []orchestrator2.Job{ + jobs := []orchestrator.Job{ { ProjectName: "project1", Commands: []string{ diff --git a/pkg/gitlab/gitlab.go b/pkg/gitlab/gitlab.go index fc4a93c48..97d4be79c 100644 --- a/pkg/gitlab/gitlab.go +++ b/pkg/gitlab/gitlab.go @@ -3,7 +3,7 @@ package gitlab import ( "fmt" "github.com/diggerhq/digger/libs/digger_config" - orchestrator2 "github.com/diggerhq/digger/libs/orchestrator" + orchestrator "github.com/diggerhq/digger/libs/orchestrator" "github.com/diggerhq/digger/pkg/utils" "log" "strings" @@ -233,7 +233,7 @@ func (gitlabService GitLabService) EditComment(prNumber int, id interface{}, com return nil } -func (gitlabService GitLabService) GetComments(prNumber int) ([]orchestrator2.Comment, error) { +func (gitlabService GitLabService) GetComments(prNumber int) ([]orchestrator.Comment, error) { //TODO implement me return nil, nil } @@ -280,8 +280,8 @@ const ( MergeRequestComment = GitLabEventType("merge_request_commented") ) -func ConvertGitLabEventToCommands(event GitLabEvent, gitLabContext *GitLabContext, impactedProjects []digger_config.Project, requestedProject *digger_config.Project, workflows map[string]digger_config.Workflow) ([]orchestrator2.Job, bool, error) { - jobs := make([]orchestrator2.Job, 0) +func ConvertGitLabEventToCommands(event GitLabEvent, gitLabContext *GitLabContext, impactedProjects []digger_config.Project, requestedProject *digger_config.Project, workflows map[string]digger_config.Workflow) ([]orchestrator.Job, bool, error) { + jobs := make([]orchestrator.Job, 0) log.Printf("ConvertGitLabEventToCommands, event.EventType: %s\n", event.EventType) switch event.EventType { @@ -293,14 +293,15 @@ func ConvertGitLabEventToCommands(event GitLabEvent, gitLabContext *GitLabContex } stateEnvVars, commandEnvVars := digger_config.CollectTerraformEnvConfig(workflow.EnvVars) - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: workflow.Configuration.OnPullRequestPushed, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), PullRequestNumber: gitLabContext.MergeRequestIId, EventName: gitLabContext.EventType.String(), RequestedBy: gitLabContext.GitlabUserName, @@ -317,14 +318,15 @@ func ConvertGitLabEventToCommands(event GitLabEvent, gitLabContext *GitLabContex return nil, true, fmt.Errorf("failed to find workflow digger_config '%s' for project '%s'", project.Workflow, project.Name) } stateEnvVars, commandEnvVars := digger_config.CollectTerraformEnvConfig(workflow.EnvVars) - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: project.Workspace, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: workflow.Configuration.OnPullRequestClosed, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), PullRequestNumber: gitLabContext.MergeRequestIId, EventName: gitLabContext.EventType.String(), RequestedBy: gitLabContext.GitlabUserName, @@ -362,20 +364,21 @@ func ConvertGitLabEventToCommands(event GitLabEvent, gitLabContext *GitLabContex workspace := project.Workspace workspaceOverride, err := utils.ParseWorkspace(diggerCommand) if err != nil { - return []orchestrator2.Job{}, false, err + return []orchestrator.Job{}, false, err } if workspaceOverride != "" { workspace = workspaceOverride } stateEnvVars, commandEnvVars := digger_config.CollectTerraformEnvConfig(workflow.EnvVars) - jobs = append(jobs, orchestrator2.Job{ + jobs = append(jobs, orchestrator.Job{ ProjectName: project.Name, ProjectDir: project.Dir, ProjectWorkspace: workspace, Terragrunt: project.Terragrunt, + OpenTofu: project.OpenTofu, Commands: []string{command}, - ApplyStage: orchestrator2.ToConfigStage(workflow.Apply), - PlanStage: orchestrator2.ToConfigStage(workflow.Plan), + ApplyStage: orchestrator.ToConfigStage(workflow.Apply), + PlanStage: orchestrator.ToConfigStage(workflow.Plan), PullRequestNumber: gitLabContext.MergeRequestIId, EventName: gitLabContext.EventType.String(), RequestedBy: gitLabContext.GitlabUserName, @@ -389,6 +392,6 @@ func ConvertGitLabEventToCommands(event GitLabEvent, gitLabContext *GitLabContex return jobs, coversAllImpactedProjects, nil default: - return []orchestrator2.Job{}, false, fmt.Errorf("unsupported GitLab event type: %v", event) + return []orchestrator.Job{}, false, fmt.Errorf("unsupported GitLab event type: %v", event) } }