Skip to content

Commit

Permalink
Merge branch 'main' into extend-api
Browse files Browse the repository at this point in the history
Signed-off-by: PePe Amengual <[email protected]>
  • Loading branch information
jamengual authored Feb 5, 2025
2 parents 16fe0d8 + 766dac9 commit 71e56f0
Show file tree
Hide file tree
Showing 22 changed files with 416 additions and 86 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
go-version-file: go.mod

- name: golangci-lint
uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6
uses: golangci/golangci-lint-action@e60da84bfae8c7920a47be973d75e15710aa8bd7 # v6
with:
# renovate: datasource=github-releases depName=golangci/golangci-lint
version: v1.62.2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ jobs:

# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
uses: github/codeql-action/upload-sarif@17a820bf2e43b47be2c72b39cc905417bc1ab6d0 # v3.28.6
with:
sarif_file: results.sarif
2 changes: 1 addition & 1 deletion Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM ghcr.io/runatlantis/atlantis:latest@sha256:f95cdf8f42370abf68d9e095930de8812e3b2a0dd66c9ffc39a69c8f49727989
FROM ghcr.io/runatlantis/atlantis:latest@sha256:9145babb08e8a3e80e6367af8bf8e443d75be622864ce80a0410a40de5e0f4ac
COPY atlantis /usr/local/bin/atlantis
WORKDIR /atlantis/src
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ require (
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.3
github.com/hashicorp/go-getter/v2 v2.2.3
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/hashicorp/hc-install v0.9.0
Expand Down Expand Up @@ -96,6 +95,7 @@ require (
github.com/gorilla/css v1.0.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand Down
39 changes: 37 additions & 2 deletions server/controllers/api_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type APIController struct {
RepoAllowlistChecker *events.RepoAllowlistChecker
Scope tally.Scope
VCSClient vcs.Client
WorkingDir events.WorkingDir
WorkingDirLocker events.WorkingDirLocker
CommitStatusUpdater events.CommitStatusUpdater
}

Expand Down Expand Up @@ -93,12 +95,18 @@ func (a *APIController) Plan(w http.ResponseWriter, r *http.Request) {
return
}

err = a.apiSetup(ctx)
if err != nil {
a.apiReportError(w, http.StatusInternalServerError, err)
return
}

result, err := a.apiPlan(request, ctx)
if err != nil {
a.apiReportError(w, http.StatusInternalServerError, err)
return
}
defer a.Locker.UnlockByPull(ctx.HeadRepo.FullName, 0) // nolint: errcheck
defer a.Locker.UnlockByPull(ctx.HeadRepo.FullName, ctx.Pull.Num) // nolint: errcheck
if result.HasErrors() {
code = http.StatusInternalServerError
}
Expand All @@ -121,13 +129,19 @@ func (a *APIController) Apply(w http.ResponseWriter, r *http.Request) {
return
}

err = a.apiSetup(ctx)
if err != nil {
a.apiReportError(w, http.StatusInternalServerError, err)
return
}

// We must first make the plan for all projects
_, err = a.apiPlan(request, ctx)
if err != nil {
a.apiReportError(w, http.StatusInternalServerError, err)
return
}
defer a.Locker.UnlockByPull(ctx.HeadRepo.FullName, 0) // nolint: errcheck
defer a.Locker.UnlockByPull(ctx.HeadRepo.FullName, ctx.Pull.Num) // nolint: errcheck

// We can now prepare and run the apply step
result, err := a.apiApply(request, ctx)
Expand All @@ -147,6 +161,27 @@ func (a *APIController) Apply(w http.ResponseWriter, r *http.Request) {
a.respond(w, logging.Warn, code, "%s", string(response))
}

func (a *APIController) apiSetup(ctx *command.Context) error {
pull := ctx.Pull
baseRepo := ctx.Pull.BaseRepo
headRepo := ctx.HeadRepo

unlockFn, err := a.WorkingDirLocker.TryLock(baseRepo.FullName, pull.Num, events.DefaultWorkspace, events.DefaultRepoRelDir)
if err != nil {
return err
}
ctx.Log.Debug("got workspace lock")
defer unlockFn()

// ensure workingDir is present
_, _, err = a.WorkingDir.Clone(ctx.Log, headRepo, pull, events.DefaultWorkspace)
if err != nil {
return err
}

return nil
}

func (a *APIController) apiPlan(request *APIRequest, ctx *command.Context) (*command.Result, error) {
cmds, cc, err := request.getCommands(ctx, a.ProjectCommandBuilder.BuildPlanCommands)
if err != nil {
Expand Down
192 changes: 176 additions & 16 deletions server/controllers/api_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,36 +70,194 @@ func testPlan(t *testing.T, ref, commit, baseBranch string) {

projectCommandBuilder.VerifyWasCalledOnce().BuildPlanCommands(cmdContext, commentCmd)
projectCommandRunner.VerifyWasCalledOnce().Plan(Any[command.ProjectContext]())

cases := []struct {
repository string
ref string
vcsType string
pr int
projects []string
paths []struct {
Directory string
Workspace string
}
}{
{
repository: "Repo",
ref: "main",
vcsType: "Gitlab",
projects: []string{"default"},
},
{
repository: "Repo",
ref: "main",
vcsType: "Gitlab",
pr: 1,
},
{
repository: "Repo",
ref: "main",
vcsType: "Gitlab",
paths: []struct {
Directory string
Workspace string
}{
{
Directory: ".",
Workspace: "myworkspace",
},
{
Directory: "./myworkspace2",
Workspace: "myworkspace2",
},
},
},
{
repository: "Repo",
ref: "main",
vcsType: "Gitlab",
pr: 1,
projects: []string{"test"},
paths: []struct {
Directory string
Workspace string
}{
{
Directory: ".",
Workspace: "myworkspace",
},
},
},
}

expectedCalls := 0
for _, c := range cases {
body, _ := json.Marshal(controllers.APIRequest{
Repository: c.repository,
Ref: c.ref,
Type: c.vcsType,
PR: c.pr,
Projects: c.projects,
Paths: c.paths,
})

req, _ := http.NewRequest("POST", "", bytes.NewBuffer(body))
req.Header.Set(atlantisTokenHeader, atlantisToken)
w := httptest.NewRecorder()
ac.Plan(w, req)
ResponseContains(t, w, http.StatusOK, "")

expectedCalls += len(c.projects)
expectedCalls += len(c.paths)
}

projectCommandBuilder.VerifyWasCalled(Times(expectedCalls)).BuildPlanCommands(Any[*command.Context](), Any[*events.CommentCommand]())
projectCommandRunner.VerifyWasCalled(Times(expectedCalls)).Plan(Any[command.ProjectContext]())
}

func TestAPIController_Apply(t *testing.T) {
ac, projectCommandBuilder, projectCommandRunner := setup(t)
body, _ := json.Marshal(controllers.APIRequest{
Repository: "Repo",
Ref: "main",
Type: "Gitlab",
Projects: []string{"default"},
})
req, _ := http.NewRequest("POST", "", bytes.NewBuffer(body))
req.Header.Set(atlantisTokenHeader, atlantisToken)
w := httptest.NewRecorder()
ac.Apply(w, req)
ResponseContains(t, w, http.StatusOK, "")
projectCommandBuilder.VerifyWasCalledOnce().BuildApplyCommands(Any[*command.Context](), Any[*events.CommentCommand]())
projectCommandRunner.VerifyWasCalledOnce().Plan(Any[command.ProjectContext]())
projectCommandRunner.VerifyWasCalledOnce().Apply(Any[command.ProjectContext]())

cases := []struct {
repository string
ref string
vcsType string
pr int
projects []string
paths []struct {
Directory string
Workspace string
}
}{
{
repository: "Repo",
ref: "main",
vcsType: "Gitlab",
projects: []string{"default"},
},
{
repository: "Repo",
ref: "main",
vcsType: "Gitlab",
pr: 1,
},
{
repository: "Repo",
ref: "main",
vcsType: "Gitlab",
paths: []struct {
Directory string
Workspace string
}{
{
Directory: ".",
Workspace: "myworkspace",
},
{
Directory: "./myworkspace2",
Workspace: "myworkspace2",
},
},
},
{
repository: "Repo",
ref: "main",
vcsType: "Gitlab",
pr: 1,
projects: []string{"test"},
paths: []struct {
Directory string
Workspace string
}{
{
Directory: ".",
Workspace: "myworkspace",
},
},
},
}

expectedCalls := 0
for _, c := range cases {
body, _ := json.Marshal(controllers.APIRequest{
Repository: c.repository,
Ref: c.ref,
Type: c.vcsType,
PR: c.pr,
Projects: c.projects,
Paths: c.paths,
})

req, _ := http.NewRequest("POST", "", bytes.NewBuffer(body))
req.Header.Set(atlantisTokenHeader, atlantisToken)
w := httptest.NewRecorder()
ac.Apply(w, req)
ResponseContains(t, w, http.StatusOK, "")

expectedCalls += len(c.projects)
expectedCalls += len(c.paths)
}

projectCommandBuilder.VerifyWasCalled(Times(expectedCalls)).BuildApplyCommands(Any[*command.Context](), Any[*events.CommentCommand]())
projectCommandRunner.VerifyWasCalled(Times(expectedCalls)).Plan(Any[command.ProjectContext]())
projectCommandRunner.VerifyWasCalled(Times(expectedCalls)).Apply(Any[command.ProjectContext]())
}

func setup(t *testing.T) (controllers.APIController, *MockProjectCommandBuilder, *MockProjectCommandRunner) {
RegisterMockTestingT(t)
locker := NewMockLocker()
logger := logging.NewNoopLogger(t)
scope, _, _ := metrics.NewLoggingScope(logger, "null")
parser := NewMockEventParsing()
vcsClient := NewMockClient()
repoAllowlistChecker, err := events.NewRepoAllowlistChecker("*")
scope, _, _ := metrics.NewLoggingScope(logger, "null")
vcsClient := NewMockClient()
workingDir := NewMockWorkingDir()
Ok(t, err)

workingDirLocker := NewMockWorkingDirLocker()
When(workingDirLocker.TryLock(Any[string](), Any[int](), Eq(events.DefaultWorkspace), Eq(events.DefaultRepoRelDir))).
ThenReturn(func() {}, nil)

projectCommandBuilder := NewMockProjectCommandBuilder()
When(projectCommandBuilder.BuildPlanCommands(Any[*command.Context](), Any[*events.CommentCommand]())).
ThenReturn([]command.ProjectContext{{
Expand Down Expand Up @@ -143,6 +301,8 @@ func setup(t *testing.T) (controllers.APIController, *MockProjectCommandBuilder,
PostWorkflowHooksCommandRunner: postWorkflowHooksCommandRunner,
VCSClient: vcsClient,
RepoAllowlistChecker: repoAllowlistChecker,
WorkingDir: workingDir,
WorkingDirLocker: workingDirLocker,
CommitStatusUpdater: commitStatusUpdater,
}
return ac, projectCommandBuilder, projectCommandRunner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ Ran Approve Policies for 1 projects:
### 1. dir: `.` workspace: `default`
**Approve Policies Error**
```
1 error occurred:
* policy set: test_policy user runatlantis is not a policy owner - please contact policy owners to approve failing policies


policy set: test_policy user runatlantis is not a policy owner - please contact policy owners to approve failing policies
```
#### Policy Approval Status:
```
Expand Down
4 changes: 4 additions & 0 deletions server/core/db/boltdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,7 @@ func (b *BoltDB) projectResultToProject(p command.ProjectResult) models.ProjectS
Status: p.PlanStatus(),
}
}

func (b *BoltDB) Close() error {
return b.db.Close()
}
Loading

0 comments on commit 71e56f0

Please sign in to comment.