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: add plan_requirements capability #2979

Merged
merged 10 commits into from Jan 21, 2023
6 changes: 4 additions & 2 deletions cmd/server.go
Expand Up @@ -1045,15 +1045,17 @@ func (s *ServerCmd) deprecationWarnings(userConfig *server.UserConfig) error {
yamlCfg := "---\nrepos:\n- id: /.*/"
jsonCfg := `{"repos":[{"id":"/.*/"`
if len(commandReqs) > 0 {
yamlCfg += fmt.Sprintf("\n plan_requirements: [%s]", strings.Join(commandReqs, ", "))
yamlCfg += fmt.Sprintf("\n apply_requirements: [%s]", strings.Join(commandReqs, ", "))
yamlCfg += fmt.Sprintf("\n import_requirements: [%s]", strings.Join(commandReqs, ", "))
jsonCfg += fmt.Sprintf(`, "plan_requirements":["%s"]`, strings.Join(commandReqs, "\", \""))
jsonCfg += fmt.Sprintf(`, "apply_requirements":["%s"]`, strings.Join(commandReqs, "\", \""))
jsonCfg += fmt.Sprintf(`, "import_requirements":["%s"]`, strings.Join(commandReqs, "\", \""))
}
if userConfig.AllowRepoConfig {
deprecatedFlags = append(deprecatedFlags, AllowRepoConfigFlag)
yamlCfg += "\n allowed_overrides: [apply_requirements, import_requirements, workflow]\n allow_custom_workflows: true"
jsonCfg += `, "allowed_overrides":["apply_requirements","import_requirements","workflow"], "allow_custom_workflows":true`
yamlCfg += "\n allowed_overrides: [plan_requirements, apply_requirements, import_requirements, workflow]\n allow_custom_workflows: true"
jsonCfg += `, "allowed_overrides":["plan_requirements","apply_requirements","import_requirements","workflow"], "allow_custom_workflows":true`
}
jsonCfg += "}]}"

Expand Down
24 changes: 16 additions & 8 deletions runatlantis.io/docs/command-requirements.md
Expand Up @@ -70,19 +70,20 @@ Set the `mergeable` requirement by:
apply_requirements: [mergeable]
```

1. Or by allowing an `atlantis.yaml` file to specify `apply_requirements` and `import_requirements` keys in the `repos.yaml` config:
1. Or by allowing an `atlantis.yaml` file to specify `plan_requirements`, `apply_requirements` and `import_requirements` keys in the `repos.yaml` config:
#### repos.yaml
```yaml
repos:
- id: /.*/
allowed_overrides: [apply_requirements, import_requirements]
allowed_overrides: [plan_requirements, apply_requirements, import_requirements]
```

#### atlantis.yaml
```yaml
version: 3
projects:
- dir: .
plan_requirements: [mergeable]
apply_requirements: [mergeable]
import_requirements: [mergeable]
```
Expand Down Expand Up @@ -153,26 +154,28 @@ Applies to `merge` checkout strategy only.

#### Usage
You can set the `undiverged` requirement by:
1. Creating a `repos.yaml` file with `apply_requirements` and `import_requirements` keys:
1. Creating a `repos.yaml` file with `plan_requirements`, `apply_requirements` and `import_requirements` keys:
```yaml
repos:
- id: /.*/
plan_requirements: [undiverged]
apply_requirements: [undiverged]
import_requirements: [undiverged]
```
1. Or by allowing an `atlantis.yaml` file to specify the `apply_requirements` and `import_requirements` keys in your `repos.yaml` config:
1. Or by allowing an `atlantis.yaml` file to specify the `plan_requirements`, `apply_requirements` and `import_requirements` keys in your `repos.yaml` config:
#### repos.yaml
```yaml
repos:
- id: /.*/
allowed_overrides: [apply_requirements, import_requirements]
allowed_overrides: [plan_requirements, apply_requirements, import_requirements]
```

#### atlantis.yaml
```yaml
version: 3
projects:
- dir: .
plan_requirements: [undiverged]
apply_requirements: [undiverged]
import_requirements: [undiverged]
```
Expand All @@ -199,31 +202,34 @@ If you only want some projects/repos to have apply requirements, then you must
```yaml
repos:
- id: /.*/
plan_requirements: [approved]
apply_requirements: [approved]
import_requirements: [approved]
# Regex that defaults all repos to requiring approval
- id: /github.com/runatlantis/.*/
# Regex to match any repo under the atlantis namespace, and not require approval
# except for repos that might match later in the chain
plan_requirements: []
apply_requirements: []
import_requirements: []
- id: github.com/runatlantis/atlantis
plan_requirements: [approved]
apply_requirements: [approved]
import_requirements: [approved]
# Exact string match of the github.com/runatlantis/atlantis repo
# that sets apply_requirements to approved
```

1. Specify which projects have which requirements via an `atlantis.yaml` file, and allowing
`apply_requirements` and `import_requirements` to be set in `atlantis.yaml` by the server side `repos.yaml`
`plan_requirements`, `apply_requirements` and `import_requirements` to be set in `atlantis.yaml` by the server side `repos.yaml`
config.

For example if I have two directories, `staging` and `production`, I might use:
#### repos.yaml
```yaml
repos:
- id: /.*/
allowed_overrides: [apply_requirements, import_requirements]
allowed_overrides: [plan_requirements, apply_requirements, import_requirements]
# Allow any repo to specify apply_requirements in atlantis.yaml
```

Expand All @@ -232,13 +238,15 @@ If you only want some projects/repos to have apply requirements, then you must
version: 3
projects:
- dir: staging
# By default, apply_requirements and import_requirements are empty so this
# By default, plan_requirements, apply_requirements and import_requirements are empty so this
# isn't strictly necessary.
plan_requirements: []
apply_requirements: []
import_requirements: []
- dir: production
# This requirement will only apply to the
# production directory.
plan_requirements: [mergeable]
apply_requirements: [mergeable]
import_requirements: [mergeable]
```
Expand Down
6 changes: 5 additions & 1 deletion runatlantis.io/docs/repo-level-atlantis-yaml.md
Expand Up @@ -61,6 +61,7 @@ projects:
autoplan:
when_modified: ["*.tf", "../modules/**/*.tf"]
enabled: true
plan_requirements: [mergeable, approved, undiverged]
apply_requirements: [mergeable, approved, undiverged]
import_requirements: [mergeable, approved, undiverged]
workflow: myworkflow
Expand Down Expand Up @@ -245,11 +246,12 @@ version: 3
projects:
- dir: staging
- dir: production
plan_requirements: [approved]
apply_requirements: [approved]
import_requirements: [approved]
```
:::warning
`apply_requirements` and `import_requirements` are restricted keys so this repo will need to be configured
`plan_requirements`, `apply_requirements` and `import_requirements` are restricted keys so this repo will need to be configured
to be allowed to set this key. See [Server-Side Repo Config Use Cases](server-side-repo-config.html#repos-can-set-their-own-apply-an-applicable-subcommand).
:::

Expand Down Expand Up @@ -300,6 +302,7 @@ delete_source_branch_on_merge: false
repo_locking: true
autoplan:
terraform_version: 0.11.0
plan_requirements: ["approved"]
apply_requirements: ["approved"]
import_requirements: ["approved"]
workflow: myworkflow
Expand All @@ -316,6 +319,7 @@ workflow: myworkflow
| repo_locking | bool | `true` | no | Get a repository lock in this project when plan. |
| autoplan | [Autoplan](#autoplan) | none | no | A custom autoplan configuration. If not specified, will use the autoplan config. See [Autoplanning](autoplanning.html). |
| terraform_version | string | none | no | A specific Terraform version to use when running commands for this project. Must be [Semver compatible](https://semver.org/), ex. `v0.11.0`, `0.12.0-beta1`. |
| plan_requirements<br />*(restricted)* | array[string] | none | no | Requirements that must be satisfied before `atlantis plan` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.html) for more details. |
| apply_requirements<br />*(restricted)* | array[string] | none | no | Requirements that must be satisfied before `atlantis apply` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.html) for more details. |
| import_requirements<br />*(restricted)* | array[string] | none | no | Requirements that must be satisfied before `atlantis import` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.html) for more details. |
| workflow <br />*(restricted)* | string | none | no | A custom workflow. If not specified, Atlantis will use its default workflow. |
Expand Down
24 changes: 18 additions & 6 deletions runatlantis.io/docs/server-side-repo-config.md
Expand Up @@ -39,6 +39,9 @@ repos:
# By default, atlantis.yaml is used.
repo_config_file: path/to/atlantis.yaml

# plan_requirements sets the Plan Requirements for all repos that match.
plan_requirements: [approved, mergeable, undiverged]

# apply_requirements sets the Apply Requirements for all repos that match.
apply_requirements: [approved, mergeable, undiverged]

Expand Down Expand Up @@ -106,13 +109,14 @@ Here are some of the reasons you might want to use a repo config.

### Requiring PR Is Approved Before an applicable subcommand
If you want to require that all (or specific) repos must have pull requests
approved before Atlantis will allow running `apply` or `import`, use the `apply_requirements` or `import_requirements` keys.
approved before Atlantis will allow running `apply` or `import`, use the `plan_requirements`, `apply_requirements` or `import_requirements` keys.

For all repos:
```yaml
# repos.yaml
repos:
- id: /.*/
plan_requirements: [approved]
apply_requirements: [approved]
import_requirements: [approved]
```
Expand All @@ -122,6 +126,7 @@ For a specific repo:
# repos.yaml
repos:
- id: github.com/myorg/myrepo
plan_requirements: [approved]
apply_requirements: [approved]
import_requirements: [approved]
```
Expand All @@ -130,13 +135,14 @@ See [Command Requirements](command-requirements.html) for more details.

### Requiring PR Is "Mergeable" Before Apply or Import
If you want to require that all (or specific) repos must have pull requests
in a mergeable state before Atlantis will allow running `apply` or `import`, use the `apply_requirements` or `import_requirements` keys.
in a mergeable state before Atlantis will allow running `apply` or `import`, use the `plan_requirements`, `apply_requirements` or `import_requirements` keys.

For all repos:
```yaml
# repos.yaml
repos:
- id: /.*/
plan_requirements: [mergeable]
apply_requirements: [mergeable]
import_requirements: [mergeable]
```
Expand All @@ -146,6 +152,7 @@ For a specific repo:
# repos.yaml
repos:
- id: github.com/myorg/myrepo
plan_requirements: [mergeable]
apply_requirements: [mergeable]
import_requirements: [mergeable]
```
Expand All @@ -162,33 +169,36 @@ To allow all repos to override the default:
repos:
- id: /.*/
# The default will be approved.
plan_requirements: [approved]
apply_requirements: [approved]
import_requirements: [approved]

# But all repos can set their own using atlantis.yaml
allowed_overrides: [apply_requirements, import_requirements]
allowed_overrides: [plan_requirements, apply_requirements, import_requirements]
```
To allow only a specific repo to override the default:
```yaml
# repos.yaml
repos:
# Set a default for all repos.
- id: /.*/
plan_requirements: [approved]
apply_requirements: [approved]
import_requirements: [approved]

# Allow a specific repo to override.
- id: github.com/myorg/myrepo
allowed_overrides: [apply_requirements, import_requirements]
allowed_overrides: [plan_requirements, apply_requirements, import_requirements]
```

Then each allowed repo can have an `atlantis.yaml` file that
sets `apply_requirements` or `import_requirements` to an empty array (disabling the requirement).
sets `plan_requirements`, `apply_requirements` or `import_requirements` to an empty array (disabling the requirement).
```yaml
# atlantis.yaml in the repo root or set repo_config_file in repos.yaml
version: 3
projects:
- dir: .
plan_requirements: []
apply_requirements: []
import_requirements: []
```
Expand Down Expand Up @@ -440,6 +450,7 @@ Each servers handle different repository config files.
repos:
- id: /.*/
branch: /.*/
plan_requirements: []
apply_requirements: []
import_requirements: []
workflow: default
Expand Down Expand Up @@ -468,7 +479,8 @@ If you set a workflow with the key `default`, it will override this.
| id | string | none | yes | Value can be a regular expression when specified as /&lt;regex&gt;/ or an exact string match. Repo IDs are of the form `{vcs hostname}/{org}/{name}`, ex. `github.com/owner/repo`. Hostname is specified without scheme or port. For Bitbucket Server, {org} is the **name** of the project, not the key. |
| branch | string | none | no | An regex matching pull requests by base branch (the branch the pull request is getting merged into). By default, all branches are matched |
| repo_config_file | string | none | no | Repo config file path in this repo. By default, use `atlantis.yaml` which is located on repository root. When multiple atlantis servers work with the same repo, please set different file names. |
| workflow | string | none | no | A custom workflow. |
| workflow | string | none | no | A custom workflow.
| plan_requirements | []string | none | no | Requirements that must be satisfied before `atlantis plan` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.html) for more details. | |
| apply_requirements | []string | none | no | Requirements that must be satisfied before `atlantis apply` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.html) for more details. |
| import_requirements | []string | none | no | Requirements that must be satisfied before `atlantis import` can be run. Currently the only supported requirements are `approved`, `mergeable`, and `undiverged`. See [Command Requirements](command-requirements.html) for more details. |
| allowed_overrides | []string | none | no | A list of restricted keys that `atlantis.yaml` files can override. The only supported keys are `apply_requirements`, `workflow`, `delete_source_branch_on_merge` and `repo_locking` |
Expand Down
5 changes: 3 additions & 2 deletions server/controllers/events/events_controller_e2e_test.go
Expand Up @@ -1257,6 +1257,8 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
userConfig.QuietPolicyChecks,
)

e2ePullReqStatusFetcher := vcs.NewPullReqStatusFetcher(e2eVCSClient, "atlantis-test")

planCommandRunner := events.NewPlanCommandRunner(
false,
false,
Expand All @@ -1275,10 +1277,9 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
boltdb,
lockingClient,
discardApprovalOnPlan,
e2ePullReqStatusFetcher,
)

e2ePullReqStatusFetcher := vcs.NewPullReqStatusFetcher(e2eVCSClient, "atlantis-test")

applyCommandRunner := events.NewApplyCommandRunner(
e2eVCSClient,
false,
Expand Down
13 changes: 10 additions & 3 deletions server/core/config/parser_validator_test.go
Expand Up @@ -1308,7 +1308,13 @@ func TestParseGlobalCfg(t *testing.T) {
input: `repos:
- id: /.*/
allowed_overrides: [invalid]`,
expErr: "repos: (0: (allowed_overrides: \"invalid\" is not a valid override, only \"apply_requirements\", \"import_requirements\", \"workflow\", \"delete_source_branch_on_merge\" and \"repo_locking\" are supported.).).",
expErr: "repos: (0: (allowed_overrides: \"invalid\" is not a valid override, only \"plan_requirements\", \"apply_requirements\", \"import_requirements\", \"workflow\", \"delete_source_branch_on_merge\" and \"repo_locking\" are supported.).).",
},
"invalid plan_requirement": {
input: `repos:
- id: /.*/
plan_requirements: [invalid]`,
expErr: "repos: (0: (plan_requirements: \"invalid\" is not a valid plan_requirement, only \"approved\", \"mergeable\" and \"undiverged\" are supported.).).",
},
"invalid apply_requirement": {
input: `repos:
Expand Down Expand Up @@ -1394,7 +1400,7 @@ repos:
workflow: custom1
post_workflow_hooks:
- run: custom workflow command
allowed_overrides: [apply_requirements, import_requirements, workflow, delete_source_branch_on_merge]
allowed_overrides: [plan_requirements, apply_requirements, import_requirements, workflow, delete_source_branch_on_merge]
allow_custom_workflows: true
- id: /.*/
branch: /(master|main)/
Expand Down Expand Up @@ -1445,7 +1451,7 @@ policies:
PreWorkflowHooks: preWorkflowHooks,
Workflow: &customWorkflow1,
PostWorkflowHooks: postWorkflowHooks,
AllowedOverrides: []string{"apply_requirements", "import_requirements", "workflow", "delete_source_branch_on_merge"},
AllowedOverrides: []string{"plan_requirements", "apply_requirements", "import_requirements", "workflow", "delete_source_branch_on_merge"},
AllowCustomWorkflows: Bool(true),
},
{
Expand Down Expand Up @@ -1528,6 +1534,7 @@ workflows:
{
IDRegex: regexp.MustCompile(".*"),
BranchRegex: regexp.MustCompile(".*"),
PlanRequirements: []string{},
ApplyRequirements: []string{},
ImportRequirements: []string{},
Workflow: &valid.Workflow{
Expand Down