Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(policies): Add granular policy_sets #3086

Merged
merged 32 commits into from
Apr 21, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8ae774b
Initial work.
pseudomorph Jan 31, 2023
85a649e
Periodic push.
pseudomorph Feb 6, 2023
34163f6
Fmt and start adding args to approve_policies cmd.
pseudomorph Feb 7, 2023
5f1c697
keep funcs for now.
pseudomorph Feb 7, 2023
80f971c
Periodic push.
pseudomorph Feb 8, 2023
088cc5a
Periodic push.
pseudomorph Feb 9, 2023
1c7801c
fmt.
pseudomorph Feb 9, 2023
756e820
Move approve policies logic to project_command_runner.
pseudomorph Feb 9, 2023
2eec2a8
update some tests
pseudomorph Feb 10, 2023
67ab895
More test fixes.
pseudomorph Feb 10, 2023
f4a783b
update more tests. fix som logic.
pseudomorph Feb 14, 2023
3b6577e
more tests. add additional info to common data for custom templates.
pseudomorph Feb 15, 2023
807cc2e
fix apply with policies bug. update more tests/fmt
pseudomorph Feb 15, 2023
329d3e4
file perms
pseudomorph Feb 15, 2023
3dedc36
fix error parsing for conftest results.
pseudomorph Feb 15, 2023
12d7fe7
Update more tests and linting.
pseudomorph Feb 15, 2023
921efe3
update documentation.
pseudomorph Feb 16, 2023
a3ff09d
Address no-fail case. Address comments.
pseudomorph Feb 27, 2023
c53f108
Forgot changes.
pseudomorph Feb 27, 2023
a6001a4
Merge branch 'main' into granular_policy_checks
pseudomorph Feb 27, 2023
3fae6aa
fix markdown renderer
pseudomorph Feb 27, 2023
14a25ab
Fix policy fail logic. remove uneeded tmpl var
pseudomorph Mar 3, 2023
dcdfc68
targeted policy approvals fix
pseudomorph Mar 3, 2023
b677463
Merge branch 'main' into granular_policy_checks
jamengual Mar 14, 2023
976f80c
Merge branch 'main' into granular_policy_checks
jamengual Mar 20, 2023
f5ef4e2
Address PR comments.
rkstrickland Mar 22, 2023
c455664
Merge branch 'main' into granular_policy_checks
jamengual Mar 22, 2023
26810c2
Merge branch 'main' into granular_policy_checks
pseudomorph Apr 11, 2023
d6a9615
Merge branch 'main' into granular_policy_checks
pseudomorph Apr 11, 2023
50d3600
Merge branch 'main' into granular_policy_checks
pseudomorph Apr 18, 2023
4a0261f
empty commit to trigger build
pseudomorph Apr 19, 2023
245c6e6
Merge branch 'main' into granular_policy_checks
GenPage Apr 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions runatlantis.io/docs/custom-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@ Or a custom command
* `SHOWFILE` - Absolute path to the location where Atlantis expects the plan in json format to
either be generated (by show) or already exist (if running policy checks). Can be used to
override the built-in `plan`/`apply` commands, ex. `run: terraform show -json $PLANFILE > $SHOWFILE`.
* `POLICYCHECKFILE` - Absolute path to the location of policy check output if Atlantis runs policy checks.
See <LINK> for information of data structure.
* `BASE_REPO_NAME` - Name of the repository that the pull request will be merged into, ex. `atlantis`.
* `BASE_REPO_OWNER` - Owner of the repository that the pull request will be merged into, ex. `runatlantis`.
* `HEAD_REPO_NAME` - Name of the repository that is getting merged into the base repository, ex. `atlantis`.
Expand Down
18 changes: 18 additions & 0 deletions runatlantis.io/docs/policy-checking.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,21 @@ workflows:
### Quiet policy checks

By default, Atlantis will add a comment to all pull requests with the policy check result - both successes and failures. Version 0.21.0 added the [`--quiet-policy-checks`](server-configuration.html#quiet-policy-checks) option, which will instead only add comments when policy checks fail, significantly reducing the number of comments when most policy check results succeed.


### Data for custom run steps

When the policy check workflow runs, a file is created in the working directory which contains information about the status of each policy set tested. This data may be useful in custom run steps to generate metrics or notifications. The file contains JSON data in the following format:

```json
[
{
"PolicySetName": "policy1",
"ConftestOutput": "",
"Passed": false,
"ReqApprovals": 1,
"CurApprovals": 0
}
]

```
1 change: 1 addition & 0 deletions server/controllers/events/events_controller_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
Ok(t, err)

projectCommandRunner := &events.DefaultProjectCommandRunner{
VcsClient: e2eVCSClient,
Locker: projectLocker,
LockURLGenerator: &mockLockURLGenerator{},
InitStepRunner: &runtime.InitStepRunner{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
Ran Policy Check for dir: `.` workspace: `default`

**Policy Check Error**
```
exit status 1
Checking plan against the following policies:
test_policy
**Policy Check Failed**: Some policy sets did not pass.
#### Policy Set: `test_policy`
```diff
FAIL - <redacted plan file> - main - WARNING: Null Resource creation is prohibited.

1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions

```
* :heavy_check_mark: To **approve** failing policies an authorized approver can comment:


#### Policy Approval Status:
```
policy set: test_policy: requires: 1 approval(s), have: 0.
```
* :heavy_check_mark: To **approve** this project, comment:
* `atlantis approve_policies -d .`
* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan -d .`

---
* :heavy_check_mark: To **approve** all unapplied plans from this pull request, comment:
* `atlantis approve_policies`
* :repeat: Or, address the policy failure by modifying the codebase and re-planning.
* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:
* `atlantis unlock`
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan`
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
Ran Approve Policies for 1 projects:

1. dir: `.` workspace: `default`

### 1. dir: `.` workspace: `default`
**Approve Policies Error**
```
contact policy owners to approve failing policies
1 error occurred:
* policy set: test_policy user runatlantis is not a policy owner - please contact policy owners to approve failing policies


```
#### Policy Approval Status:
```
policy set: test_policy: requires: 1 approval(s), have: 0.
```
* :heavy_check_mark: To **approve** this project, comment:
* `atlantis approve_policies -d .`
* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan -d .`

---
* :heavy_check_mark: To **approve** all unapplied plans from this pull request, comment:
* `atlantis approve_policies`
* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:
* `atlantis unlock`
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan`
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
Ran Policy Check for dir: `.` workspace: `default`

**Policy Check Error**
```
exit status 1
Checking plan against the following policies:
test_policy
**Policy Check Failed**: Some policy sets did not pass.
#### Policy Set: `test_policy`
```diff
FAIL - <redacted plan file> - main - WARNING: Null Resource creation is prohibited.

1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions

```
* :heavy_check_mark: To **approve** failing policies an authorized approver can comment:


#### Policy Approval Status:
```
policy set: test_policy: requires: 1 approval(s), have: 0.
```
* :heavy_check_mark: To **approve** this project, comment:
* `atlantis approve_policies -d .`
* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan -d .`

---
* :heavy_check_mark: To **approve** all unapplied plans from this pull request, comment:
* `atlantis approve_policies`
* :repeat: Or, address the policy failure by modifying the codebase and re-planning.
* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:
* `atlantis unlock`
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan`
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
Ran Policy Check for dir: `.` workspace: `default`

**Policy Check Error**
```
exit status 1
Checking plan against the following policies:
test_policy
**Policy Check Failed**: Some policy sets did not pass.
#### Policy Set: `test_policy`
```diff
FAIL - <redacted plan file> - null_resource_policy - WARNING: Null Resource creation is prohibited.

1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions

```
* :heavy_check_mark: To **approve** failing policies an authorized approver can comment:


#### Policy Approval Status:
```
policy set: test_policy: requires: 1 approval(s), have: 0.
```
* :heavy_check_mark: To **approve** this project, comment:
* `atlantis approve_policies -d .`
* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan -d .`

---
* :heavy_check_mark: To **approve** all unapplied plans from this pull request, comment:
* `atlantis approve_policies`
* :repeat: Or, address the policy failure by modifying the codebase and re-planning.
* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:
* `atlantis unlock`
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan`
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Approved Policies for 1 projects:
1. dir: `.` workspace: `default`


i
pseudomorph marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ Ran Policy Check for 2 projects:
1. dir: `dir2` workspace: `default`

### 1. dir: `dir1` workspace: `default`
#### Policy Set: `test_policy`
```diff
Checking plan against the following policies:
test_policy

1 test, 1 passed, 0 warnings, 0 failures, 0 exceptions

```


* :arrow_forward: To **apply** this plan, comment:
* `atlantis apply -d dir1`
* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
Expand All @@ -19,22 +20,30 @@ Checking plan against the following policies:

---
### 2. dir: `dir2` workspace: `default`
**Policy Check Error**
```
exit status 1
Checking plan against the following policies:
test_policy
**Policy Check Failed**: Some policy sets did not pass.
#### Policy Set: `test_policy`
```diff
FAIL - <redacted plan file> - main - WARNING: Forbidden Resource creation is prohibited.

1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions

```
* :heavy_check_mark: To **approve** failing policies an authorized approver can comment:
* `atlantis approve_policies`
* :repeat: Or, address the policy failure by modifying the codebase and re-planning.


#### Policy Approval Status:
```
policy set: test_policy: requires: 1 approval(s), have: 0.
```
* :heavy_check_mark: To **approve** this project, comment:
* `atlantis approve_policies -d dir2`
* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan -d dir2`

---
* :fast_forward: To **apply** all unapplied plans from this pull request, comment:
* `atlantis apply`
* :heavy_check_mark: To **approve** all unapplied plans from this pull request, comment:
* `atlantis approve_policies`
* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:
* `atlantis unlock`
* `atlantis unlock`
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan`
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
Ran Policy Check for dir: `.` workspace: `default`

**Policy Check Error**
```
exit status 1
Checking plan against the following policies:
test_policy
**Policy Check Failed**: Some policy sets did not pass.
#### Policy Set: `test_policy`
```diff
FAIL - <redacted plan file> - main - WARNING: Null Resource creation is prohibited.

1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions

```
* :heavy_check_mark: To **approve** failing policies an authorized approver can comment:


#### Policy Approval Status:
```
policy set: test_policy: requires: 1 approval(s), have: 0.
```
* :heavy_check_mark: To **approve** this project, comment:
* `atlantis approve_policies -d .`
* :put_litter_in_its_place: To **delete** this plan click [here](lock-url)
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan -d .`

---
* :heavy_check_mark: To **approve** all unapplied plans from this pull request, comment:
* `atlantis approve_policies`
* :repeat: Or, address the policy failure by modifying the codebase and re-planning.
* :put_litter_in_its_place: To delete all plans and locks for the PR, comment:
* `atlantis unlock`
* :repeat: To re-run policies **plan** this project again by commenting:
* `atlantis plan`
4 changes: 4 additions & 0 deletions server/core/config/parser_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1467,11 +1467,13 @@ policies:
},
PolicySets: valid.PolicySets{
Version: conftestVersion,
ReviewCount: 1,
PolicySets: []valid.PolicySet{
{
Name: "good-policy",
Path: "rel/path/to/policy",
Source: valid.LocalPolicySet,
ReviewCount: 1,
},
},
},
Expand Down Expand Up @@ -1802,11 +1804,13 @@ func TestParserValidator_ParseGlobalCfgJSON(t *testing.T) {
},
PolicySets: valid.PolicySets{
Version: conftestVersion,
ReviewCount: 1,
PolicySets: []valid.PolicySet{
{
Name: "good-policy",
Path: "rel/path/to/policy",
Source: valid.LocalPolicySet,
ReviewCount: 1,
},
},
},
Expand Down
30 changes: 22 additions & 8 deletions server/core/config/raw/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ package raw

import (
pseudomorph marked this conversation as resolved.
Show resolved Hide resolved
validation "github.com/go-ozzo/ozzo-validation"
"github.com/hashicorp/go-version"
version "github.com/hashicorp/go-version"
"github.com/runatlantis/atlantis/server/core/config/valid"
)

// PolicySets is the raw schema for repo-level atlantis.yaml config.
type PolicySets struct {
Version *string `yaml:"conftest_version,omitempty" json:"conftest_version,omitempty"`
Owners PolicyOwners `yaml:"owners,omitempty" json:"owners,omitempty"`
PolicySets []PolicySet `yaml:"policy_sets" json:"policy_sets"`
Version *string `yaml:"conftest_version,omitempty" json:"conftest_version,omitempty"`
Owners PolicyOwners `yaml:"owners,omitempty" json:"owners,omitempty"`
PolicySets []PolicySet `yaml:"policy_sets" json:"policy_sets"`
ReviewCount int `yaml:"review_count,omitempty" json:"review_count,omitempty"`
pseudomorph marked this conversation as resolved.
Show resolved Hide resolved
}

func (p PolicySets) Validate() error {
Expand All @@ -27,10 +28,20 @@ func (p PolicySets) ToValid() valid.PolicySets {
policySets.Version, _ = version.NewVersion(*p.Version)
}

// Default number of required reviews for all policy sets should be 1.
policySets.ReviewCount = p.ReviewCount
if policySets.ReviewCount == 0 {
policySets.ReviewCount = 1
}

policySets.Owners = p.Owners.ToValid()

validPolicySets := make([]valid.PolicySet, 0)
for _, rawPolicySet := range p.PolicySets {
// Default to top-level review count if not specified.
if rawPolicySet.ReviewCount == 0 {
rawPolicySet.ReviewCount = policySets.ReviewCount
}
validPolicySets = append(validPolicySets, rawPolicySet.ToValid())
}
policySets.PolicySets = validPolicySets
Expand All @@ -57,16 +68,18 @@ func (o PolicyOwners) ToValid() valid.PolicyOwners {
}

type PolicySet struct {
Path string `yaml:"path" json:"path"`
Source string `yaml:"source" json:"source"`
Name string `yaml:"name" json:"name"`
Owners PolicyOwners `yaml:"owners,omitempty" json:"owners,omitempty"`
Path string `yaml:"path" json:"path"`
Source string `yaml:"source" json:"source"`
Name string `yaml:"name" json:"name"`
Owners PolicyOwners `yaml:"owners,omitempty" json:"owners,omitempty"`
ReviewCount int `yaml:"review_count,omitempty" json:"review_count,omitempty"`
}

func (p PolicySet) Validate() error {
return validation.ValidateStruct(&p,
validation.Field(&p.Name, validation.Required.Error("is required")),
validation.Field(&p.Owners),
validation.Field(&p.ReviewCount),
validation.Field(&p.Path, validation.Required.Error("is required")),
validation.Field(&p.Source, validation.In(valid.LocalPolicySet, valid.GithubPolicySet).Error("only 'local' and 'github' source types are supported")),
)
Expand All @@ -78,6 +91,7 @@ func (p PolicySet) ToValid() valid.PolicySet {
policySet.Name = p.Name
policySet.Path = p.Path
policySet.Source = p.Source
policySet.ReviewCount = p.ReviewCount
policySet.Owners = p.Owners.ToValid()

return policySet
Expand Down
Loading