mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-29 01:38:30 +00:00
Remove assume role functionality. Rely on Terraform. Fixes #112
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,7 +1,13 @@
|
||||
# v0.1.1 (Unreleased)
|
||||
### Backwards Incompatibilities / Notes:
|
||||
* `--aws-assume-role-arn` and `--aws-region` flags removed. Instead, to name the
|
||||
assume role session with the GitHub username of the user running the Atlantis command
|
||||
use the `atlantis_user` terraform variable alongside Terraform's
|
||||
[built-in support](https://www.terraform.io/docs/providers/aws/#assume-role) for assume role
|
||||
(see https://github.com/hootsuite/atlantis/blob/master/README.md#assume-role-session-names)
|
||||
|
||||
### Improvements
|
||||
* Support for HTTPS cloning using Github username and token provided to atlantis server ([#117](https://github.com/hootsuite/atlantis/pull/117))
|
||||
* Set custom user agent for AWS interactions by atlantis server ([#115](https://github.com/hootsuite/atlantis/pull/115))
|
||||
* Support for HTTPS cloning using GitHub username and token provided to atlantis server ([#117](https://github.com/hootsuite/atlantis/pull/117))
|
||||
* Adding `post_plan` and `post_apply` commands ([#102](https://github.com/hootsuite/atlantis/pull/102))
|
||||
* Adding the ability to verify webhook secret ([#120](https://github.com/hootsuite/atlantis/pull/120))
|
||||
|
||||
|
||||
38
README.md
38
README.md
@@ -294,14 +294,42 @@ Atlantis simply shells out to `terraform` so you don't need to do anything speci
|
||||
As long as `terraform` works where you're hosting Atlantis, then Atlantis will work.
|
||||
See https://www.terraform.io/docs/providers/aws/#authentication for more detail.
|
||||
|
||||
### Multiple AWS Accounts
|
||||
Atlantis supports multiple AWS accounts through the use of Terraform's
|
||||
[AWS Authentication](https://www.terraform.io/docs/providers/aws/#authentication).
|
||||
|
||||
If you're using the [Shared Credentials file](https://www.terraform.io/docs/providers/aws/#shared-credentials-file)
|
||||
you'll need to ensure the server that Atlantis is executing on has the corresponding credentials file.
|
||||
|
||||
If you're using [Assume role](https://www.terraform.io/docs/providers/aws/#assume-role)
|
||||
you'll need to ensure that the credentials file has a `default` profile that is able
|
||||
to assume all required roles.
|
||||
|
||||
[Environment variables](https://www.terraform.io/docs/providers/aws/#environment-variables) authentication
|
||||
won't work for multiple accounts since Atlantis wouldn't know which environment variables to execute
|
||||
Terraform with.
|
||||
|
||||
### Assume Role Session Names
|
||||
Atlantis provides the ability to use AWS's Assume Role and **dynamically name the session** with the GitHub username of whoever commented `atlantis apply`.
|
||||
Atlantis injects the Terraform variable `atlantis_user` and sets it to the GitHub username of
|
||||
the user that is running the Atlantis command. This can be used to dynamically name the assume role
|
||||
session. This is used at Hootsuite so AWS API actions can be correlated with a specific user.
|
||||
|
||||
This is used at Hootsuite so AWS API actions can be correlated with a specific user.
|
||||
To take advantage of this feature, simply set the `--aws-assume-role-arn` flag to the
|
||||
role to be assumed: `arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME`.
|
||||
To take advantage of this feature, use Terraform's [built-in support](https://www.terraform.io/docs/providers/aws/#assume-role) for assume role
|
||||
and use the `atlantis_user` terraform variable
|
||||
|
||||
If you're using Terraform's [built-in support](https://www.terraform.io/docs/providers/aws/#assume-role) for assume role then there is no need to set this flag (unless you also want your sessions to take the name of the GitHub user).
|
||||
```hcl
|
||||
provider "aws" {
|
||||
assume_role {
|
||||
role_arn = "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME"
|
||||
session_name = "${var.atlantis_user}"
|
||||
}
|
||||
}
|
||||
|
||||
# need to define the atlantis_user variable to avoid terraform errors
|
||||
variable "atlantis_user" {
|
||||
default = "atlantis_user"
|
||||
}
|
||||
```
|
||||
|
||||
## Glossary
|
||||
#### Project
|
||||
|
||||
58
aws/aws.go
58
aws/aws.go
@@ -1,58 +0,0 @@
|
||||
// Package aws handles Amazon Web Services actions.
|
||||
package aws
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
)
|
||||
|
||||
const sessionDuration = 30 * time.Minute
|
||||
|
||||
// Config configures our aws clients.
|
||||
type Config struct {
|
||||
// Region to connect to for api calls.
|
||||
Region string
|
||||
// RoleARN is the arn of the role to be assumed.
|
||||
// If empty, we won't assume a role and will use the normal
|
||||
// AWS authentication methods
|
||||
RoleARN string
|
||||
}
|
||||
|
||||
// CreateSession creates a new valid AWS session to be used by AWS clients.
|
||||
// If RoleARN is not empty, we will assume a role and name that role whatever
|
||||
// is passed in as sessionName. Otherwise we'll create a
|
||||
// session using the AWS SDK's normal mechanism.
|
||||
func (c *Config) CreateSession(sessionName string) (*session.Session, error) {
|
||||
awsSession, err := session.NewSessionWithOptions(session.Options{
|
||||
Config: aws.Config{Region: aws.String(c.Region)},
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = awsSession.Config.Credentials.Get(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// generate a new session if aws role is provided
|
||||
if c.RoleARN != "" {
|
||||
return c.assumeRole(awsSession, sessionName), nil
|
||||
}
|
||||
|
||||
return awsSession, nil
|
||||
}
|
||||
|
||||
// assumeRole calls Amazon's Security Token Service and attempts to assume roleArn and provide credentials for that role.
|
||||
func (c *Config) assumeRole(s *session.Session, sessionName string) *session.Session {
|
||||
stsClient := sts.New(s, s.Config)
|
||||
creds := stscreds.NewCredentialsWithClient(stsClient, c.RoleARN, func(p *stscreds.AssumeRoleProvider) {
|
||||
p.RoleSessionName = sessionName
|
||||
// override default 15 minute time
|
||||
p.Duration = sessionDuration
|
||||
})
|
||||
return session.New(&aws.Config{Credentials: creds, Region: aws.String(c.Region)})
|
||||
}
|
||||
@@ -18,8 +18,6 @@ import (
|
||||
// 3. Add your flag's description etc. to the stringFlags, intFlags, or boolFlags slices
|
||||
const (
|
||||
atlantisURLFlag = "atlantis-url"
|
||||
awsAssumeRoleFlag = "aws-assume-role-arn"
|
||||
awsRegionFlag = "aws-region"
|
||||
configFlag = "config"
|
||||
dataDirFlag = "data-dir"
|
||||
ghHostnameFlag = "gh-hostname"
|
||||
@@ -36,15 +34,6 @@ var stringFlags = []stringFlag{
|
||||
name: atlantisURLFlag,
|
||||
description: "URL that Atlantis can be reached at. Defaults to http://$(hostname):$port where $port is from --" + portFlag + ".",
|
||||
},
|
||||
{
|
||||
name: awsAssumeRoleFlag,
|
||||
description: "ARN of the role to assume when running Terraform against AWS. If not using assume role, no need to set.",
|
||||
},
|
||||
{
|
||||
name: awsRegionFlag,
|
||||
description: "Amazon region to use for assume role. If not setting --" + awsAssumeRoleFlag + " then ignore.",
|
||||
value: "us-east-1",
|
||||
},
|
||||
{
|
||||
name: configFlag,
|
||||
description: "Path to config file.",
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
version "github.com/hashicorp/go-version"
|
||||
"github.com/hootsuite/atlantis/aws"
|
||||
"github.com/hootsuite/atlantis/github"
|
||||
"github.com/hootsuite/atlantis/locking"
|
||||
"github.com/hootsuite/atlantis/models"
|
||||
@@ -20,7 +19,6 @@ import (
|
||||
type ApplyExecutor struct {
|
||||
github *github.Client
|
||||
githubStatus *GithubStatus
|
||||
awsConfig *aws.Config
|
||||
terraform *terraform.Client
|
||||
githubCommentRenderer *GithubCommentRenderer
|
||||
lockingClient *locking.Client
|
||||
@@ -125,31 +123,6 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P
|
||||
applyExtraArgs = config.GetExtraArguments(ctx.Command.Name.String())
|
||||
}
|
||||
|
||||
// todo: de-duplicate this section between plan and apply
|
||||
var credsEnvVars []string
|
||||
// If awsConfig is nil we know that we're not using assume role and so
|
||||
// don't need to do an AWS calls ourselves
|
||||
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()
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "getting aws credentials")
|
||||
ctx.Log.Err(err.Error())
|
||||
return ProjectResult{Error: err}
|
||||
}
|
||||
ctx.Log.Info("created aws session")
|
||||
|
||||
credsEnvVars = []string{
|
||||
fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", creds.AccessKeyID),
|
||||
fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", creds.SecretAccessKey),
|
||||
fmt.Sprintf("AWS_SESSION_TOKEN=%s", creds.SessionToken),
|
||||
}
|
||||
}
|
||||
|
||||
// check if terraform version is >= 0.9.0
|
||||
terraformVersion := a.terraform.Version()
|
||||
if config.TerraformVersion != nil {
|
||||
@@ -158,7 +131,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. Running version %s", terraformVersion)
|
||||
_, err := a.terraform.RunInitAndEnv(ctx.Log, absolutePath, tfEnv, config.GetExtraArguments("init"), credsEnvVars, terraformVersion)
|
||||
_, err := a.terraform.RunInitAndEnv(ctx.Log, absolutePath, tfEnv, config.GetExtraArguments("init"), terraformVersion)
|
||||
if err != nil {
|
||||
return ProjectResult{Error: err}
|
||||
}
|
||||
@@ -172,8 +145,8 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P
|
||||
}
|
||||
}
|
||||
|
||||
tfApplyCmd := append([]string{"apply", "-no-color", plan.LocalPath}, applyExtraArgs...)
|
||||
output, err := a.terraform.RunCommandWithVersion(ctx.Log, absolutePath, tfApplyCmd, credsEnvVars, terraformVersion)
|
||||
tfApplyCmd := append([]string{"apply", "-no-color", "-var", fmt.Sprintf("%s=%s", atlantisUserTFVar, ctx.User.Username), plan.LocalPath}, applyExtraArgs...)
|
||||
output, err := a.terraform.RunCommandWithVersion(ctx.Log, absolutePath, tfApplyCmd, terraformVersion)
|
||||
if err != nil {
|
||||
return ProjectResult{Error: fmt.Errorf("%s\n%s", err.Error(), output)}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
|
||||
version "github.com/hashicorp/go-version"
|
||||
"github.com/hootsuite/atlantis/aws"
|
||||
"github.com/hootsuite/atlantis/github"
|
||||
"github.com/hootsuite/atlantis/locking"
|
||||
"github.com/hootsuite/atlantis/models"
|
||||
@@ -22,7 +21,6 @@ import (
|
||||
type PlanExecutor struct {
|
||||
github *github.Client
|
||||
githubStatus *GithubStatus
|
||||
awsConfig *aws.Config
|
||||
s3Bucket string
|
||||
terraform *terraform.Client
|
||||
githubCommentRenderer *GithubCommentRenderer
|
||||
@@ -119,31 +117,6 @@ func (p *PlanExecutor) plan(ctx *CommandContext, repoDir string, project models.
|
||||
planExtraArgs = config.GetExtraArguments(ctx.Command.Name.String())
|
||||
}
|
||||
|
||||
// todo: de-duplicate this section between plan and apply
|
||||
var credsEnvVars []string
|
||||
// If awsConfig is nil we know that we're not using assume role and so
|
||||
// don't need to do an AWS calls ourselves
|
||||
if p.awsConfig != nil {
|
||||
awsSession, err := p.awsConfig.CreateSession(ctx.User.Username)
|
||||
if err != nil {
|
||||
ctx.Log.Err(err.Error())
|
||||
return ProjectResult{Error: err}
|
||||
}
|
||||
creds, err := awsSession.Config.Credentials.Get()
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "getting aws credentials")
|
||||
ctx.Log.Err(err.Error())
|
||||
return ProjectResult{Error: err}
|
||||
}
|
||||
ctx.Log.Info("created aws session")
|
||||
|
||||
credsEnvVars = []string{
|
||||
fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", creds.AccessKeyID),
|
||||
fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", creds.SecretAccessKey),
|
||||
fmt.Sprintf("AWS_SESSION_TOKEN=%s", creds.SessionToken),
|
||||
}
|
||||
}
|
||||
|
||||
// check if terraform version is >= 0.9.0
|
||||
terraformVersion := p.terraform.Version()
|
||||
if config.TerraformVersion != nil {
|
||||
@@ -152,14 +125,14 @@ func (p *PlanExecutor) plan(ctx *CommandContext, repoDir string, project models.
|
||||
constraints, _ := version.NewConstraint(">= 0.9.0")
|
||||
if constraints.Check(terraformVersion) {
|
||||
ctx.Log.Info("determined that we are running terraform with version >= 0.9.0. Running version %s", terraformVersion)
|
||||
_, err := p.terraform.RunInitAndEnv(ctx.Log, absolutePath, tfEnv, config.GetExtraArguments("init"), credsEnvVars, terraformVersion)
|
||||
_, err := p.terraform.RunInitAndEnv(ctx.Log, absolutePath, tfEnv, config.GetExtraArguments("init"), terraformVersion)
|
||||
if err != nil {
|
||||
return ProjectResult{Error: err}
|
||||
}
|
||||
} else {
|
||||
ctx.Log.Info("determined that we are running terraform with version < 0.9.0. Running version %s", terraformVersion)
|
||||
terraformGetCmd := append([]string{"get", "-no-color"}, config.GetExtraArguments("get")...)
|
||||
_, err := p.terraform.RunCommandWithVersion(ctx.Log, absolutePath, terraformGetCmd, nil, terraformVersion)
|
||||
_, err := p.terraform.RunCommandWithVersion(ctx.Log, absolutePath, terraformGetCmd, terraformVersion)
|
||||
if err != nil {
|
||||
return ProjectResult{Error: err}
|
||||
}
|
||||
@@ -175,14 +148,14 @@ func (p *PlanExecutor) plan(ctx *CommandContext, repoDir string, project models.
|
||||
|
||||
// Run terraform plan
|
||||
planFile := filepath.Join(repoDir, project.Path, fmt.Sprintf("%s.tfplan", tfEnv))
|
||||
tfPlanCmd := append([]string{"plan", "-refresh", "-no-color", "-out", planFile}, planExtraArgs...)
|
||||
tfPlanCmd := append([]string{"plan", "-refresh", "-no-color", "-out", planFile, "-var", fmt.Sprintf("%s=%s", atlantisUserTFVar, ctx.User.Username)}, planExtraArgs...)
|
||||
|
||||
// check if env/{environment}.tfvars exist
|
||||
tfEnvFileName := filepath.Join("env", tfEnv+".tfvars")
|
||||
if _, err := os.Stat(filepath.Join(repoDir, project.Path, tfEnvFileName)); err == nil {
|
||||
tfPlanCmd = append(tfPlanCmd, "-var-file", tfEnvFileName)
|
||||
}
|
||||
output, err := p.terraform.RunCommandWithVersion(ctx.Log, filepath.Join(repoDir, project.Path), tfPlanCmd, credsEnvVars, terraformVersion)
|
||||
output, err := p.terraform.RunCommandWithVersion(ctx.Log, filepath.Join(repoDir, project.Path), tfPlanCmd, terraformVersion)
|
||||
if err != nil {
|
||||
// plan failed so unlock the state
|
||||
if _, err := p.lockingClient.Unlock(lockAttempt.LockKey); err != nil {
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
gh "github.com/google/go-github/github"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/hootsuite/atlantis/aws"
|
||||
"github.com/hootsuite/atlantis/github"
|
||||
"github.com/hootsuite/atlantis/locking"
|
||||
"github.com/hootsuite/atlantis/locking/boltdb"
|
||||
@@ -31,6 +30,9 @@ import (
|
||||
|
||||
const (
|
||||
lockRoute = "lock-detail"
|
||||
// atlantisUserTFVar is the name of the variable we execute terraform
|
||||
// with containing the github username of who is running the command
|
||||
atlantisUserTFVar = "atlantis_user"
|
||||
)
|
||||
|
||||
// Server listens for GitHub events and runs the necessary Atlantis command
|
||||
@@ -48,8 +50,6 @@ type Server struct {
|
||||
|
||||
// the mapstructure tags correspond to flags in cmd/server.go
|
||||
type ServerConfig struct {
|
||||
AWSRegion string `mapstructure:"aws-region"`
|
||||
AssumeRole string `mapstructure:"aws-assume-role-arn"`
|
||||
AtlantisURL string `mapstructure:"atlantis-url"`
|
||||
DataDir string `mapstructure:"data-dir"`
|
||||
GithubHostname string `mapstructure:"gh-hostname"`
|
||||
@@ -92,16 +92,6 @@ func NewServer(config ServerConfig) (*Server, error) {
|
||||
}
|
||||
githubComments := &GithubCommentRenderer{}
|
||||
|
||||
// a nil awsConfig indicates that we won't be doing any AWS
|
||||
// config in Atlantis
|
||||
var awsConfig *aws.Config
|
||||
if config.AssumeRole != "" {
|
||||
awsConfig = &aws.Config{
|
||||
Region: config.AWSRegion,
|
||||
RoleARN: config.AssumeRole,
|
||||
}
|
||||
}
|
||||
|
||||
boltdb, err := boltdb.New(config.DataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -116,7 +106,6 @@ func NewServer(config ServerConfig) (*Server, error) {
|
||||
applyExecutor := &ApplyExecutor{
|
||||
github: githubClient,
|
||||
githubStatus: githubStatus,
|
||||
awsConfig: awsConfig,
|
||||
terraform: terraformClient,
|
||||
githubCommentRenderer: githubComments,
|
||||
lockingClient: lockingClient,
|
||||
@@ -129,7 +118,6 @@ func NewServer(config ServerConfig) (*Server, error) {
|
||||
planExecutor := &PlanExecutor{
|
||||
github: githubClient,
|
||||
githubStatus: githubStatus,
|
||||
awsConfig: awsConfig,
|
||||
terraform: terraformClient,
|
||||
githubCommentRenderer: githubComments,
|
||||
lockingClient: lockingClient,
|
||||
@@ -308,9 +296,10 @@ func (s *Server) postEvents(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// webhook requests can either be application/json or application/x-www-form-urlencoded.
|
||||
// We accept both to make it easier on users that may choose x-www-form-urlencoded by mistake
|
||||
// todo: use go-github's ValidatePayload method if https://github.com/google/go-github/pull/693 is merged
|
||||
if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
|
||||
// GitHub stores the json payload as a form value
|
||||
payloadForm := r.PostFormValue("payload")
|
||||
payloadForm := r.FormValue("payload")
|
||||
if payloadForm == "" {
|
||||
s.respond(w, logging.Warn, http.StatusBadRequest, "request did not contain expected 'payload' form value")
|
||||
return
|
||||
|
||||
@@ -3,7 +3,6 @@ package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
|
||||
@@ -52,8 +51,8 @@ func (c *Client) Version() *version.Version {
|
||||
}
|
||||
|
||||
// RunCommandWithVersion executes the provided version of terraform with
|
||||
// the provided args and envVars in path.
|
||||
func (c *Client) RunCommandWithVersion(log *logging.SimpleLogger, path string, args []string, envVars []string, v *version.Version) (string, error) {
|
||||
// the provided args in path.
|
||||
func (c *Client) RunCommandWithVersion(log *logging.SimpleLogger, path string, args []string, v *version.Version) (string, error) {
|
||||
tfExecutable := "terraform"
|
||||
// if version is the same as the default, don't need to prepend the version name to the executable
|
||||
if !v.Equal(c.defaultVersion) {
|
||||
@@ -61,11 +60,6 @@ func (c *Client) RunCommandWithVersion(log *logging.SimpleLogger, path string, a
|
||||
}
|
||||
terraformCmd := exec.Command(tfExecutable, args...)
|
||||
terraformCmd.Dir = path
|
||||
if len(envVars) > 0 {
|
||||
// append current process's environment variables
|
||||
// this is to prevent the $PATH variable being removed from the environment
|
||||
terraformCmd.Env = append(os.Environ(), envVars...)
|
||||
}
|
||||
out, err := terraformCmd.CombinedOutput()
|
||||
commandStr := strings.Join(terraformCmd.Args, " ")
|
||||
if err != nil {
|
||||
@@ -80,21 +74,21 @@ func (c *Client) RunCommandWithVersion(log *logging.SimpleLogger, path string, a
|
||||
// RunInitAndEnv executes "terraform init" and "terraform env select" in path.
|
||||
// env is the environment to select and extraInitArgs are additional arguments
|
||||
// applied to the init command.
|
||||
func (c *Client) RunInitAndEnv(log *logging.SimpleLogger, path string, env string, extraInitArgs []string, envVars []string, version *version.Version) ([]string, error) {
|
||||
func (c *Client) RunInitAndEnv(log *logging.SimpleLogger, path string, env string, extraInitArgs []string, version *version.Version) ([]string, error) {
|
||||
var outputs []string
|
||||
// run terraform init
|
||||
output, err := c.RunCommandWithVersion(log, path, append([]string{"init", "-no-color"}, extraInitArgs...), envVars, version)
|
||||
output, err := c.RunCommandWithVersion(log, path, append([]string{"init", "-no-color"}, extraInitArgs...), version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputs = append(outputs, output)
|
||||
|
||||
// run terraform env new and select
|
||||
output, err = c.RunCommandWithVersion(log, path, []string{"env", "select", "-no-color", env}, envVars, version)
|
||||
output, err = c.RunCommandWithVersion(log, path, []string{"env", "select", "-no-color", env}, version)
|
||||
if err != nil {
|
||||
// if terraform env select fails we will run terraform env new
|
||||
// to create a new environment
|
||||
output, err = c.RunCommandWithVersion(log, path, []string{"env", "new", "-no-color", env}, envVars, version)
|
||||
output, err = c.RunCommandWithVersion(log, path, []string{"env", "new", "-no-color", env}, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user