-
Notifications
You must be signed in to change notification settings - Fork 53
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
fixed preapply commands and added postapply/postplan #102
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Package postrun handles running commands after the | ||
// regular Atlantis commands. | ||
package postrun | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
"strings" | ||
|
||
version "github.com/hashicorp/go-version" | ||
"github.com/hootsuite/atlantis/logging" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
const inlineShebang = "#!/bin/sh -e" | ||
|
||
type PostRun struct{} | ||
|
||
// Execute runs the commands by writing them as a script to disk | ||
// and then executing the script. | ||
func (p *PostRun) Execute( | ||
log *logging.SimpleLogger, | ||
commands []string, | ||
path string, | ||
environment string, | ||
terraformVersion *version.Version) (string, error) { | ||
// we create a script from the commands provided | ||
if len(commands) == 0 { | ||
return "", errors.New("postrun commands cannot be empty") | ||
} | ||
|
||
s, err := createScript(commands) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer os.Remove(s) | ||
|
||
log.Info("running postrun commands: %v", commands) | ||
|
||
// set environment variable for the run. | ||
// this is to support scripts to use the ENVIRONMENT, ATLANTIS_TERRAFORM_VERSION | ||
// and WORKSPACE variables in their scripts | ||
os.Setenv("ENVIRONMENT", environment) | ||
os.Setenv("ATLANTIS_TERRAFORM_VERSION", terraformVersion.String()) | ||
os.Setenv("WORKSPACE", path) | ||
return execute(s) | ||
} | ||
|
||
func createScript(cmds []string) (string, error) { | ||
tmp, err := ioutil.TempFile("/tmp", "atlantis-temp-script") | ||
if err != nil { | ||
return "", errors.Wrap(err, "preparing post run shell script") | ||
} | ||
|
||
scriptName := tmp.Name() | ||
|
||
// Write our contents to it | ||
writer := bufio.NewWriter(tmp) | ||
writer.WriteString(fmt.Sprintf("%s\n", inlineShebang)) | ||
cmdsJoined := strings.Join(cmds, "\n") | ||
if _, err := writer.WriteString(cmdsJoined); err != nil { | ||
return "", errors.Wrap(err, "preparing post run") | ||
} | ||
|
||
if err := writer.Flush(); err != nil { | ||
return "", errors.Wrap(err, "flushing contents to file") | ||
} | ||
tmp.Close() | ||
|
||
if err := os.Chmod(scriptName, 0755); err != nil { | ||
return "", errors.Wrap(err, "making post run script executable") | ||
} | ||
|
||
return scriptName, nil | ||
} | ||
|
||
func execute(script string) (string, error) { | ||
localCmd := exec.Command("sh", "-c", script) | ||
out, err := localCmd.CombinedOutput() | ||
output := string(out) | ||
if err != nil { | ||
return output, errors.Wrapf(err, "running script %s: %s", script, output) | ||
} | ||
|
||
return output, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package postrun | ||
|
||
import ( | ||
"log" | ||
"os" | ||
"testing" | ||
|
||
version "github.com/hashicorp/go-version" | ||
"github.com/hootsuite/atlantis/logging" | ||
. "github.com/hootsuite/atlantis/testing_util" | ||
) | ||
|
||
var logger = logging.NewSimpleLogger("", log.New(os.Stderr, "", log.LstdFlags), false, logging.Debug) | ||
var postRun = &PostRun{} | ||
|
||
func TestPostRunCreateScript_valid(t *testing.T) { | ||
cmds := []string{"echo", "date"} | ||
scriptName, err := createScript(cmds) | ||
Assert(t, scriptName != "", "there should be a script name") | ||
Assert(t, err == nil, "there should not be an error") | ||
} | ||
|
||
func TestPostRunExecuteScript_invalid(t *testing.T) { | ||
cmds := []string{"invalid", "command"} | ||
scriptName, _ := createScript(cmds) | ||
_, err := execute(scriptName) | ||
Assert(t, err != nil, "there should be an error") | ||
} | ||
|
||
func TestPostRunExecuteScript_valid(t *testing.T) { | ||
cmds := []string{"echo", "date"} | ||
scriptName, _ := createScript(cmds) | ||
output, err := execute(scriptName) | ||
Assert(t, err == nil, "there should not be an error") | ||
Assert(t, output != "", "there should be output") | ||
} | ||
|
||
func TestPostRun_valid(t *testing.T) { | ||
cmds := []string{"echo", "date"} | ||
version, _ := version.NewVersion("0.8.8") | ||
_, err := postRun.Execute(logger, cmds, "/tmp/atlantis", "staging", version) | ||
Ok(t, err) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ import ( | |
"github.com/hootsuite/atlantis/github" | ||
"github.com/hootsuite/atlantis/locking" | ||
"github.com/hootsuite/atlantis/models" | ||
"github.com/hootsuite/atlantis/postrun" | ||
"github.com/hootsuite/atlantis/prerun" | ||
"github.com/hootsuite/atlantis/terraform" | ||
) | ||
|
@@ -26,6 +27,7 @@ type ApplyExecutor struct { | |
lockingClient *locking.Client | ||
requireApproval bool | ||
preRun *prerun.PreRun | ||
postRun *postrun.PostRun | ||
configReader *ConfigReader | ||
concurrentRunLocker *ConcurrentRunLocker | ||
workspace *Workspace | ||
|
@@ -117,7 +119,7 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P | |
var applyExtraArgs []string | ||
var config ProjectConfig | ||
if a.configReader.Exists(absolutePath) { | ||
config, err := a.configReader.Read(absolutePath) | ||
config, err = a.configReader.Read(absolutePath) | ||
if err != nil { | ||
return ProjectResult{Error: err} | ||
} | ||
|
@@ -132,6 +134,7 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P | |
if a.awsConfig != nil { | ||
awsSession, err := a.awsConfig.CreateSession(ctx.User.Username) | ||
if err != nil { | ||
ctx.Log.Err(err.Error()) | ||
return ProjectResult{Error: err} | ||
} | ||
creds, err := awsSession.Config.Credentials.Get() | ||
|
@@ -156,7 +159,7 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P | |
} | ||
constraints, _ := version.NewConstraint(">= 0.9.0") | ||
if constraints.Check(terraformVersion) { | ||
ctx.Log.Info("determined that we are running terraform with version >= 0.9.0") | ||
ctx.Log.Info("determined that we are running terraform with version >= 0.9.0. Running version %s", terraformVersion) | ||
_, err := a.terraform.RunInitAndEnv(ctx.Log, absolutePath, tfEnv, config.GetExtraArguments("init"), credsEnvVars, terraformVersion) | ||
if err != nil { | ||
return ProjectResult{Error: err} | ||
|
@@ -165,9 +168,9 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P | |
|
||
// if there are pre apply commands then run them | ||
if len(config.PreApply.Commands) > 0 { | ||
_, err := a.preRun.Execute(ctx.Log, config.PreApply.Commands, absolutePath, ctx.Command.Environment, config.TerraformVersion) | ||
_, err := a.preRun.Execute(ctx.Log, config.PreApply.Commands, absolutePath, tfEnv, terraformVersion) | ||
if err != nil { | ||
return ProjectResult{Error: err} | ||
return ProjectResult{Error: errors.Wrap(err, "running pre apply commands")} | ||
} | ||
} | ||
|
||
|
@@ -178,6 +181,14 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P | |
} | ||
ctx.Log.Info("apply succeeded") | ||
|
||
// if there are post apply commands then run them | ||
if len(config.PostApply.Commands) > 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we are adding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure thing. for whatever reason i just figure |
||
_, err := a.postRun.Execute(ctx.Log, config.PostApply.Commands, absolutePath, tfEnv, terraformVersion) | ||
if err != nil { | ||
return ProjectResult{Error: errors.Wrap(err, "running post apply commands")} | ||
} | ||
} | ||
|
||
return ProjectResult{ApplySuccess: output} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a lot of redundant code here with
prerun
package doing the same thing. We should restructure this so we can use the code for bothprerun
andpostrun
pacakges.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do. I'll get rid of the copy paste replace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@anubhavmishra The code is basically the same, I could move duplicate functions with different names in a more general package "run" or we could just rename the package and functions to "run". What were you thinking?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep! run might make more sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay @anubhavmishra , settled on a more general package and opted to pass in the stage name(i.e "pre_plan", "post_plan", etc)