Skip to content

Commit 6f583aa

Browse files
mootptanubhavmishra
authored andcommitted
fixed preapply commands and added postapply/postplan (runatlantis#102)
* fixed preapply commands and added postapply * removed duplicate code and pass in stage name. Added post_plan. Updated docs
1 parent afb34df commit 6f583aa

8 files changed

+83
-40
lines changed

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ Now when Atlantis executes it will use the `terraform{version}` executable.
146146
## Project-Specific Customization
147147
An `atlantis.yaml` config file in your project root (which is not necessarily the repo root) can be used to customize
148148
- what commands Atlantis runs **before** `plan` and `apply` with `pre_plan` and `pre_apply`
149+
- what commands Atlantis runs **after** `plan` and `apply` with `post_plan` and `post_apply`
149150
- additional arguments to be supplied to specific terraform commands with `extra_arguments`
150151
- what version of Terraform to use (see [Terraform Versions](#terraform-versions))
151152

@@ -158,16 +159,22 @@ terraform_version: 0.8.8 # optional version
158159
pre_plan:
159160
commands:
160161
- "curl http://example.com"
162+
post_plan:
163+
commands:
164+
- "curl http://example.com"
161165
pre_apply:
162166
commands:
163167
- "curl http://example.com"
168+
post_apply:
169+
commands:
170+
- "curl http://example.com"
164171
extra_arguments:
165172
- command: plan
166173
arguments:
167174
- "-tfvars=myvars.tfvars"
168175
```
169176
170-
When running the `pre_plan` and `pre_apply` commands the following environment variables are available
177+
When running the `pre_plan`, `post_plan`, `pre_apply`, and `post_apply` commands the following environment variables are available
171178
- `ENVIRONMENT`: if an environment argument is supplied to `atlantis plan` or `atlantis apply` this will
172179
be the value of that argument. Else it will be `default`
173180
- `ATLANTIS_TERRAFORM_VERSION`: local version of `terraform` or the version from `terraform_version` if specified, ex. `0.10.0`

prerun/pre_run.go run/run.go

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Package prerun handles running commands prior to the
1+
// Package run handles running commands prior and following the
22
// regular Atlantis commands.
3-
package prerun
3+
package run
44

55
import (
66
"bufio"
@@ -17,28 +17,29 @@ import (
1717

1818
const inlineShebang = "#!/bin/sh -e"
1919

20-
type PreRun struct{}
20+
type Run struct{}
2121

2222
// Execute runs the commands by writing them as a script to disk
2323
// and then executing the script.
24-
func (p *PreRun) Execute(
24+
func (p *Run) Execute(
2525
log *logging.SimpleLogger,
2626
commands []string,
2727
path string,
2828
environment string,
29-
terraformVersion *version.Version) (string, error) {
29+
terraformVersion *version.Version,
30+
stage string) (string, error) {
3031
// we create a script from the commands provided
3132
if len(commands) == 0 {
32-
return "", errors.New("prerun commands cannot be empty")
33+
return "", errors.Errorf("%s commands cannot be empty", stage)
3334
}
3435

35-
s, err := createScript(commands)
36+
s, err := createScript(commands, stage)
3637
if err != nil {
3738
return "", err
3839
}
3940
defer os.Remove(s)
4041

41-
log.Info("running prerun commands: %v", commands)
42+
log.Info("running %s commands: %v", stage, commands)
4243

4344
// set environment variable for the run.
4445
// this is to support scripts to use the ENVIRONMENT, ATLANTIS_TERRAFORM_VERSION
@@ -49,10 +50,10 @@ func (p *PreRun) Execute(
4950
return execute(s)
5051
}
5152

52-
func createScript(cmds []string) (string, error) {
53+
func createScript(cmds []string, stage string) (string, error) {
5354
tmp, err := ioutil.TempFile("/tmp", "atlantis-temp-script")
5455
if err != nil {
55-
return "", errors.Wrap(err, "preparing pre run shell script")
56+
return "", errors.Wrapf(err, "preparing %s shell script", stage)
5657
}
5758

5859
scriptName := tmp.Name()
@@ -62,7 +63,7 @@ func createScript(cmds []string) (string, error) {
6263
writer.WriteString(fmt.Sprintf("%s\n", inlineShebang))
6364
cmdsJoined := strings.Join(cmds, "\n")
6465
if _, err := writer.WriteString(cmdsJoined); err != nil {
65-
return "", errors.Wrap(err, "preparing pre run")
66+
return "", errors.Wrapf(err, "preparing %s", stage)
6667
}
6768

6869
if err := writer.Flush(); err != nil {
@@ -71,7 +72,7 @@ func createScript(cmds []string) (string, error) {
7172
tmp.Close()
7273

7374
if err := os.Chmod(scriptName, 0755); err != nil {
74-
return "", errors.Wrap(err, "making pre run script executable")
75+
return "", errors.Wrapf(err, "making %s script executable", stage)
7576
}
7677

7778
return scriptName, nil
+10-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package prerun
1+
package run
22

33
import (
44
"log"
@@ -11,33 +11,33 @@ import (
1111
)
1212

1313
var logger = logging.NewSimpleLogger("", log.New(os.Stderr, "", log.LstdFlags), false, logging.Debug)
14-
var preRun = &PreRun{}
14+
var run = &Run{}
1515

16-
func TestPreRunCreateScript_valid(t *testing.T) {
16+
func TestRunCreateScript_valid(t *testing.T) {
1717
cmds := []string{"echo", "date"}
18-
scriptName, err := createScript(cmds)
18+
scriptName, err := createScript(cmds, "post_apply")
1919
Assert(t, scriptName != "", "there should be a script name")
2020
Assert(t, err == nil, "there should not be an error")
2121
}
2222

23-
func TestPreRunExecuteScript_invalid(t *testing.T) {
23+
func TestRunExecuteScript_invalid(t *testing.T) {
2424
cmds := []string{"invalid", "command"}
25-
scriptName, _ := createScript(cmds)
25+
scriptName, _ := createScript(cmds, "post_apply")
2626
_, err := execute(scriptName)
2727
Assert(t, err != nil, "there should be an error")
2828
}
2929

30-
func TestPreRunExecuteScript_valid(t *testing.T) {
30+
func TestRunExecuteScript_valid(t *testing.T) {
3131
cmds := []string{"echo", "date"}
32-
scriptName, _ := createScript(cmds)
32+
scriptName, _ := createScript(cmds, "post_apply")
3333
output, err := execute(scriptName)
3434
Assert(t, err == nil, "there should not be an error")
3535
Assert(t, output != "", "there should be output")
3636
}
3737

38-
func TestPreRun_valid(t *testing.T) {
38+
func TestRun_valid(t *testing.T) {
3939
cmds := []string{"echo", "date"}
4040
version, _ := version.NewVersion("0.8.8")
41-
_, err := preRun.Execute(logger, cmds, "/tmp/atlantis", "staging", version)
41+
_, err := run.Execute(logger, cmds, "/tmp/atlantis", "staging", version, "post_apply")
4242
Ok(t, err)
4343
}

server/apply_executor.go

+15-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/hootsuite/atlantis/github"
1414
"github.com/hootsuite/atlantis/locking"
1515
"github.com/hootsuite/atlantis/models"
16-
"github.com/hootsuite/atlantis/prerun"
16+
"github.com/hootsuite/atlantis/run"
1717
"github.com/hootsuite/atlantis/terraform"
1818
)
1919

@@ -25,7 +25,7 @@ type ApplyExecutor struct {
2525
githubCommentRenderer *GithubCommentRenderer
2626
lockingClient *locking.Client
2727
requireApproval bool
28-
preRun *prerun.PreRun
28+
run *run.Run
2929
configReader *ConfigReader
3030
concurrentRunLocker *ConcurrentRunLocker
3131
workspace *Workspace
@@ -117,7 +117,7 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P
117117
var applyExtraArgs []string
118118
var config ProjectConfig
119119
if a.configReader.Exists(absolutePath) {
120-
config, err := a.configReader.Read(absolutePath)
120+
config, err = a.configReader.Read(absolutePath)
121121
if err != nil {
122122
return ProjectResult{Error: err}
123123
}
@@ -132,6 +132,7 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P
132132
if a.awsConfig != nil {
133133
awsSession, err := a.awsConfig.CreateSession(ctx.User.Username)
134134
if err != nil {
135+
ctx.Log.Err(err.Error())
135136
return ProjectResult{Error: err}
136137
}
137138
creds, err := awsSession.Config.Credentials.Get()
@@ -156,7 +157,7 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P
156157
}
157158
constraints, _ := version.NewConstraint(">= 0.9.0")
158159
if constraints.Check(terraformVersion) {
159-
ctx.Log.Info("determined that we are running terraform with version >= 0.9.0")
160+
ctx.Log.Info("determined that we are running terraform with version >= 0.9.0. Running version %s", terraformVersion)
160161
_, err := a.terraform.RunInitAndEnv(ctx.Log, absolutePath, tfEnv, config.GetExtraArguments("init"), credsEnvVars, terraformVersion)
161162
if err != nil {
162163
return ProjectResult{Error: err}
@@ -165,9 +166,9 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P
165166

166167
// if there are pre apply commands then run them
167168
if len(config.PreApply.Commands) > 0 {
168-
_, err := a.preRun.Execute(ctx.Log, config.PreApply.Commands, absolutePath, ctx.Command.Environment, config.TerraformVersion)
169+
_, err := a.run.Execute(ctx.Log, config.PreApply.Commands, absolutePath, tfEnv, terraformVersion, "pre_apply")
169170
if err != nil {
170-
return ProjectResult{Error: err}
171+
return ProjectResult{Error: errors.Wrap(err, "running pre apply commands")}
171172
}
172173
}
173174

@@ -178,6 +179,14 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P
178179
}
179180
ctx.Log.Info("apply succeeded")
180181

182+
// if there are post apply commands then run them
183+
if len(config.PostApply.Commands) > 0 {
184+
_, err := a.run.Execute(ctx.Log, config.PostApply.Commands, absolutePath, tfEnv, terraformVersion, "post_apply")
185+
if err != nil {
186+
return ProjectResult{Error: errors.Wrap(err, "running post apply commands")}
187+
}
188+
}
189+
181190
return ProjectResult{ApplySuccess: output}
182191
}
183192

server/plan_executor.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/hootsuite/atlantis/github"
1313
"github.com/hootsuite/atlantis/locking"
1414
"github.com/hootsuite/atlantis/models"
15-
"github.com/hootsuite/atlantis/prerun"
15+
"github.com/hootsuite/atlantis/run"
1616
"github.com/hootsuite/atlantis/terraform"
1717
"github.com/pkg/errors"
1818
)
@@ -29,7 +29,7 @@ type PlanExecutor struct {
2929
lockingClient *locking.Client
3030
// LockURL is a function that given a lock id will return a url for lock view
3131
LockURL func(id string) (url string)
32-
preRun *prerun.PreRun
32+
run *run.Run
3333
configReader *ConfigReader
3434
concurrentRunLocker *ConcurrentRunLocker
3535
workspace *Workspace
@@ -151,13 +151,13 @@ func (p *PlanExecutor) plan(ctx *CommandContext, repoDir string, project models.
151151
}
152152
constraints, _ := version.NewConstraint(">= 0.9.0")
153153
if constraints.Check(terraformVersion) {
154-
ctx.Log.Info("determined that we are running terraform with version >= 0.9.0")
154+
ctx.Log.Info("determined that we are running terraform with version >= 0.9.0. Running version %s", terraformVersion)
155155
_, err := p.terraform.RunInitAndEnv(ctx.Log, absolutePath, tfEnv, config.GetExtraArguments("init"), credsEnvVars, terraformVersion)
156156
if err != nil {
157157
return ProjectResult{Error: err}
158158
}
159159
} else {
160-
ctx.Log.Info("determined that we are running terraform with version < 0.9.0")
160+
ctx.Log.Info("determined that we are running terraform with version < 0.9.0. Running version %s", terraformVersion)
161161
terraformGetCmd := append([]string{"get", "-no-color"}, config.GetExtraArguments("get")...)
162162
_, err := p.terraform.RunCommandWithVersion(ctx.Log, absolutePath, terraformGetCmd, nil, terraformVersion)
163163
if err != nil {
@@ -167,7 +167,7 @@ func (p *PlanExecutor) plan(ctx *CommandContext, repoDir string, project models.
167167

168168
// if there are pre plan commands then run them
169169
if len(config.PrePlan.Commands) > 0 {
170-
_, err := p.preRun.Execute(ctx.Log, config.PrePlan.Commands, absolutePath, tfEnv, terraformVersion)
170+
_, err := p.run.Execute(ctx.Log, config.PrePlan.Commands, absolutePath, tfEnv, terraformVersion, "pre_plan")
171171
if err != nil {
172172
return ProjectResult{Error: errors.Wrap(err, "running pre plan commands")}
173173
}
@@ -192,6 +192,14 @@ func (p *PlanExecutor) plan(ctx *CommandContext, repoDir string, project models.
192192
}
193193
ctx.Log.Info("plan succeeded")
194194

195+
// if there are post plan commands then run them
196+
if len(config.PostPlan.Commands) > 0 {
197+
_, err := p.run.Execute(ctx.Log, config.PostPlan.Commands, absolutePath, tfEnv, terraformVersion, "post_plan")
198+
if err != nil {
199+
return ProjectResult{Error: errors.Wrap(err, "running post plan commands")}
200+
}
201+
}
202+
195203
return ProjectResult{
196204
PlanSuccess: &PlanSuccess{
197205
TerraformOutput: output,

server/project_config.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,34 @@ type PrePlan struct {
1616
Commands []string `yaml:"commands"`
1717
}
1818

19+
type PostPlan struct {
20+
Commands []string `yaml:"commands"`
21+
}
22+
1923
type PreApply struct {
2024
Commands []string `yaml:"commands"`
2125
}
2226

27+
type PostApply struct {
28+
Commands []string `yaml:"commands"`
29+
}
30+
2331
type ConfigReader struct{}
2432

2533
type ProjectConfigYaml struct {
2634
PrePlan PrePlan `yaml:"pre_plan"`
35+
PostPlan PostPlan `yaml:"post_plan"`
2736
PreApply PreApply `yaml:"pre_apply"`
37+
PostApply PostApply `yaml:"post_apply"`
2838
TerraformVersion string `yaml:"terraform_version"`
2939
ExtraArguments []CommandExtraArguments `yaml:"extra_arguments"`
3040
}
3141

3242
type ProjectConfig struct {
33-
PrePlan PrePlan
34-
PreApply PreApply
43+
PrePlan PrePlan
44+
PostPlan PostPlan
45+
PreApply PreApply
46+
PostApply PostApply
3547
// TerraformVersion is the version specified in the config file or nil if version wasn't specified
3648
TerraformVersion *version.Version
3749
ExtraArguments []CommandExtraArguments
@@ -70,8 +82,10 @@ func (c *ConfigReader) Read(execPath string) (ProjectConfig, error) {
7082
return ProjectConfig{
7183
TerraformVersion: v,
7284
ExtraArguments: pcYaml.ExtraArguments,
85+
PostApply: pcYaml.PostApply,
7386
PreApply: pcYaml.PreApply,
7487
PrePlan: pcYaml.PrePlan,
88+
PostPlan: pcYaml.PostPlan,
7589
}, nil
7690
}
7791

server/project_config_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ var tempConfigFile = "/tmp/" + ProjectConfigFile
1212
var projectConfigFileStr = `
1313
---
1414
terraform_version: "0.0.1"
15+
post_apply:
16+
commands:
17+
- "echo"
18+
- "date"
1519
pre_apply:
1620
commands:
1721
- "echo"

server/server.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
"github.com/hootsuite/atlantis/locking/boltdb"
2020
"github.com/hootsuite/atlantis/logging"
2121
"github.com/hootsuite/atlantis/models"
22-
"github.com/hootsuite/atlantis/prerun"
22+
"github.com/hootsuite/atlantis/run"
2323
"github.com/hootsuite/atlantis/terraform"
2424
homedir "github.com/mitchellh/go-homedir"
2525
"github.com/pkg/errors"
@@ -103,7 +103,7 @@ func NewServer(config ServerConfig) (*Server, error) {
103103
return nil, err
104104
}
105105
lockingClient := locking.NewClient(boltdb)
106-
preRun := &prerun.PreRun{}
106+
run := &run.Run{}
107107
configReader := &ConfigReader{}
108108
concurrentRunLocker := NewConcurrentRunLocker()
109109
workspace := &Workspace{
@@ -117,7 +117,7 @@ func NewServer(config ServerConfig) (*Server, error) {
117117
githubCommentRenderer: githubComments,
118118
lockingClient: lockingClient,
119119
requireApproval: config.RequireApproval,
120-
preRun: preRun,
120+
run: run,
121121
configReader: configReader,
122122
concurrentRunLocker: concurrentRunLocker,
123123
workspace: workspace,
@@ -129,7 +129,7 @@ func NewServer(config ServerConfig) (*Server, error) {
129129
terraform: terraformClient,
130130
githubCommentRenderer: githubComments,
131131
lockingClient: lockingClient,
132-
preRun: preRun,
132+
run: run,
133133
configReader: configReader,
134134
concurrentRunLocker: concurrentRunLocker,
135135
workspace: workspace,

0 commit comments

Comments
 (0)