diff --git a/server/aws.go b/aws/aws.go similarity index 60% rename from server/aws.go rename to aws/aws.go index 47a9c496e..8243c504b 100644 --- a/server/aws.go +++ b/aws/aws.go @@ -1,4 +1,4 @@ -package server +package aws import ( "fmt" @@ -11,18 +11,18 @@ import ( "github.com/aws/aws-sdk-go/service/sts" ) -const awsAssumeRoleSessionName = "atlantis" +const assumeRoleSessionName = "atlantis" -type AWSConfig struct { - AWSRegion string - AWSRoleArn string - AWSSessionName string +type Config struct { + Region string + RoleArn string + SessionName string } -// CreateAWSSession creates a new valid AWS session to be used by AWS clients -func (c *AWSConfig) CreateAWSSession() (*session.Session, error) { +// CreateSession creates a new valid AWS session to be used by AWS clients +func (c *Config) CreateSession() (*session.Session, error) { session, err := session.NewSessionWithOptions(session.Options{ - Config: aws.Config{Region: aws.String(c.AWSRegion)}, + Config: aws.Config{Region: aws.String(c.Region)}, SharedConfigState: session.SharedConfigEnable, }) if err != nil { @@ -35,7 +35,7 @@ func (c *AWSConfig) CreateAWSSession() (*session.Session, error) { } // generate a new session if aws role is provided - if c.AWSRoleArn != "" { + if c.RoleArn != "" { return c.assumeRole(session), nil } @@ -43,17 +43,17 @@ func (c *AWSConfig) CreateAWSSession() (*session.Session, error) { } // assumeRole calls Amazon's Security Token Service and attempts to assume roleArn and provide credentials for that role -func (c *AWSConfig) assumeRole(s *session.Session) *session.Session { - if c.AWSSessionName == "" { - c.AWSSessionName = awsAssumeRoleSessionName +func (c *Config) assumeRole(s *session.Session) *session.Session { + if c.SessionName == "" { + c.SessionName = assumeRoleSessionName } stsClient := sts.New(s, s.Config) - creds := stscreds.NewCredentialsWithClient(stsClient, c.AWSRoleArn, func(p *stscreds.AssumeRoleProvider) { - p.RoleSessionName = c.AWSSessionName + creds := stscreds.NewCredentialsWithClient(stsClient, c.RoleArn, func(p *stscreds.AssumeRoleProvider) { + p.RoleSessionName = c.SessionName // override default 15 minute time p.Duration = time.Duration(30) * time.Minute }) // now assume role - return session.New(&aws.Config{Credentials: creds, Region: aws.String(c.AWSRegion)}) + return session.New(&aws.Config{Credentials: creds, Region: aws.String(c.Region)}) } diff --git a/github/github_client.go b/github/github_client.go new file mode 100644 index 000000000..492a9e0b3 --- /dev/null +++ b/github/github_client.go @@ -0,0 +1,100 @@ +package github + +import ( + "context" + + "fmt" + "net/url" + "strings" + + "github.com/google/go-github/github" + "github.com/hootsuite/atlantis/models" + "github.com/pkg/errors" +) + +type Client struct { + client *github.Client + ctx context.Context +} + +func NewClient(hostname string, user string, pass string) (*Client, error) { + tp := github.BasicAuthTransport{ + Username: strings.TrimSpace(user), + Password: strings.TrimSpace(pass), + } + client := github.NewClient(tp.Client()) + // If we're using github.com then we don't need to do any additional configuration + // for the client. It we're using Github Enterprise, then we need to manually + // set the base url for the API + if hostname != "github.com" { + baseURL := fmt.Sprintf("https://%s/api/v3/", hostname) + base, err := url.Parse(baseURL) + if err != nil { + return nil, errors.Wrapf(err, "Invalid github hostname trying to parse %s", baseURL) + } + client.BaseURL = base + } + + return &Client{ + client: client, + ctx: context.Background(), + }, nil +} + +// GetModifiedFiles returns the names of files that were modified in the pull request. +// The names include the path to the file from the repo root, ex. parent/child/file.txt +func (c *Client) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) { + var files []string + nextPage := 0 + for { + opts := github.ListOptions{ + PerPage: 300, + } + if nextPage != 0 { + opts.Page = nextPage + } + pageFiles, resp, err := c.client.PullRequests.ListFiles(c.ctx, repo.Owner, repo.Name, pull.Num, &opts) + if err != nil { + return files, err + } + for _, f := range pageFiles { + files = append(files, f.GetFilename()) + } + if resp.NextPage == 0 { + break + } + nextPage = resp.NextPage + } + return files, nil +} + +func (c *Client) CreateComment(repo models.Repo, pull models.PullRequest, comment string) error { + _, _, err := c.client.Issues.CreateComment(c.ctx, repo.Owner, repo.Name, pull.Num, &github.IssueComment{Body: &comment}) + return err +} + +func (c *Client) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) { + reviews, _, err := c.client.PullRequests.ListReviews(c.ctx, repo.Owner, repo.Name, pull.Num, nil) + if err != nil { + return false, errors.Wrap(err, "getting reviews") + } + for _, review := range reviews { + if review != nil && review.GetState() == "APPROVED" { + return true, nil + } + } + return false, nil +} + +func (c *Client) GetPullRequest(repo models.Repo, num int) (*github.PullRequest, *github.Response, error) { + return c.client.PullRequests.Get(c.ctx, repo.Owner, repo.Name, num) +} + +func (c *Client) UpdateStatus(repo models.Repo, pull models.PullRequest, state string, description string, context string) error { + status := &github.RepoStatus{ + State: github.String(state), + Description: github.String(description), + Context: github.String(context)} + _, _, err := c.client.Repositories.CreateStatus(c.ctx, repo.Owner, repo.Name, pull.HeadCommit, status) + return err +} diff --git a/server/apply_executor.go b/server/apply_executor.go index a2068225e..b72f7dae5 100644 --- a/server/apply_executor.go +++ b/server/apply_executor.go @@ -10,16 +10,19 @@ 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" "github.com/hootsuite/atlantis/prerun" + "github.com/hootsuite/atlantis/terraform" ) type ApplyExecutor struct { - github *GithubClient + github *github.Client githubStatus *GithubStatus - awsConfig *AWSConfig - terraform *TerraformClient + awsConfig *aws.Config + terraform *terraform.Client githubCommentRenderer *GithubCommentRenderer lockingClient *locking.Client requireApproval bool @@ -60,7 +63,7 @@ func (n NoPlansFailure) Template() *CompiledTemplate { return NoPlansFailureTmpl } -func (a *ApplyExecutor) execute(ctx *CommandContext, github *GithubClient) { +func (a *ApplyExecutor) execute(ctx *CommandContext, github *github.Client) { if a.concurrentRunLocker.TryLock(ctx.BaseRepo.FullName, ctx.Command.environment, ctx.Pull.Num) != true { ctx.Log.Info("run was locked by a concurrent run") github.CreateComment(ctx.BaseRepo, ctx.Pull, "This environment is currently locked by another command that is running for this pull request. Wait until command is complete and try again") @@ -168,7 +171,7 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P constraints, _ := version.NewConstraint(">= 0.9.0") if constraints.Check(terraformVersion) { // run terraform init and environment - outputs, err := a.terraform.RunTerraformInitAndEnv(projectAbsolutePath, tfEnv, config) + outputs, err := a.terraform.RunInitAndEnv(projectAbsolutePath, tfEnv, config.GetExtraArguments("init")) if err != nil { msg := fmt.Sprintf("terraform init and environment commands failed. %s %v", outputs, err) ctx.Log.Err(msg) @@ -196,8 +199,8 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P // need to get auth data from assumed role // todo: de-duplicate calls to assumeRole - a.awsConfig.AWSSessionName = ctx.User.Username - awsSession, err := a.awsConfig.CreateAWSSession() + a.awsConfig.SessionName = ctx.User.Username + awsSession, err := a.awsConfig.CreateSession() if err != nil { ctx.Log.Err(err.Error()) return PathResult{ @@ -220,7 +223,7 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P tfApplyCmd := []string{"apply", "-no-color", plan.LocalPath} // append terraform arguments from config file tfApplyCmd = append(tfApplyCmd, terraformApplyExtraArgs...) - terraformApplyCmdArgs, output, err := a.terraform.RunTerraformCommand(projectAbsolutePath, tfApplyCmd, []string{ + terraformApplyCmdArgs, output, err := a.terraform.RunCommand(projectAbsolutePath, tfApplyCmd, []string{ fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", credVals.AccessKeyID), fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", credVals.SecretAccessKey), fmt.Sprintf("AWS_SESSION_TOKEN=%s", credVals.SessionToken), diff --git a/server/command_handler.go b/server/command_handler.go index 59e1f45fa..75772e091 100644 --- a/server/command_handler.go +++ b/server/command_handler.go @@ -3,6 +3,7 @@ package server import ( "fmt" + "github.com/hootsuite/atlantis/github" "github.com/hootsuite/atlantis/logging" "github.com/hootsuite/atlantis/recovery" ) @@ -11,7 +12,7 @@ type CommandHandler struct { planExecutor *PlanExecutor applyExecutor *ApplyExecutor helpExecutor *HelpExecutor - githubClient *GithubClient + githubClient *github.Client eventParser *EventParser logger *logging.SimpleLogger } diff --git a/server/github_client.go b/server/github_client.go deleted file mode 100644 index 682ee59ad..000000000 --- a/server/github_client.go +++ /dev/null @@ -1,68 +0,0 @@ -package server - -import ( - "context" - - "github.com/google/go-github/github" - "github.com/hootsuite/atlantis/models" - "github.com/pkg/errors" -) - -type GithubClient struct { - client *github.Client - ctx context.Context -} - -// GetModifiedFiles returns the names of files that were modified in the pull request. -// The names include the path to the file from the repo root, ex. parent/child/file.txt -func (g *GithubClient) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) { - var files []string - nextPage := 0 - for { - opts := github.ListOptions{ - PerPage: 300, - } - if nextPage != 0 { - opts.Page = nextPage - } - pageFiles, resp, err := g.client.PullRequests.ListFiles(g.ctx, repo.Owner, repo.Name, pull.Num, &opts) - if err != nil { - return files, err - } - for _, f := range pageFiles { - files = append(files, f.GetFilename()) - } - if resp.NextPage == 0 { - break - } - nextPage = resp.NextPage - } - return files, nil -} - -func (g *GithubClient) CreateComment(repo models.Repo, pull models.PullRequest, comment string) error { - _, _, err := g.client.Issues.CreateComment(g.ctx, repo.Owner, repo.Name, pull.Num, &github.IssueComment{Body: &comment}) - return err -} - -func (g *GithubClient) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) { - reviews, _, err := g.client.PullRequests.ListReviews(g.ctx, repo.Owner, repo.Name, pull.Num, nil) - if err != nil { - return false, errors.Wrap(err, "getting reviews") - } - for _, review := range reviews { - if review != nil && review.GetState() == "APPROVED" { - return true, nil - } - } - return false, nil -} - -func (g *GithubClient) GetPullRequest(repo models.Repo, num int) (*github.PullRequest, *github.Response, error) { - return g.client.PullRequests.Get(g.ctx, repo.Owner, repo.Name, num) -} - -func (g *GithubClient) UpdateStatus(repo models.Repo, pull models.PullRequest, status *github.RepoStatus) error { - _, _, err := g.client.Repositories.CreateStatus(g.ctx, repo.Owner, repo.Name, pull.HeadCommit, status) - return err -} diff --git a/server/github_status.go b/server/github_status.go index c695b4769..e4d87cb20 100644 --- a/server/github_status.go +++ b/server/github_status.go @@ -5,7 +5,7 @@ import ( "strings" - "github.com/google/go-github/github" + "github.com/hootsuite/atlantis/github" "github.com/hootsuite/atlantis/models" ) @@ -22,7 +22,7 @@ const ( ) type GithubStatus struct { - client *GithubClient + client *github.Client } func (s Status) String() string { @@ -40,11 +40,8 @@ func (s Status) String() string { } func (g *GithubStatus) Update(repo models.Repo, pull models.PullRequest, status Status, step string) error { - repoStatus := github.RepoStatus{ - State: github.String(status.String()), - Description: github.String(fmt.Sprintf("%s %s", strings.Title(step), strings.Title(status.String()))), - Context: github.String(statusContext)} - return g.client.UpdateStatus(repo, pull, &repoStatus) + description := fmt.Sprintf("%s %s", strings.Title(step), strings.Title(status.String())) + return g.client.UpdateStatus(repo, pull, status.String(), description, statusContext) } func (g *GithubStatus) UpdatePathResult(ctx *CommandContext, pathResults []PathResult) error { diff --git a/server/help_executor.go b/server/help_executor.go index 2a1e9597a..aef738de3 100644 --- a/server/help_executor.go +++ b/server/help_executor.go @@ -1,6 +1,9 @@ package server -import "github.com/spf13/viper" +import ( + "github.com/hootsuite/atlantis/github" + "github.com/spf13/viper" +) type HelpExecutor struct{} @@ -30,7 +33,7 @@ atlantis apply staging atlantis apply ` -func (h *HelpExecutor) execute(ctx *CommandContext, github *GithubClient) { +func (h *HelpExecutor) execute(ctx *CommandContext, github *github.Client) { ctx.Log.Info("generating help comment....") github.CreateComment(ctx.BaseRepo, ctx.Pull, helpComment) return diff --git a/middleware/middleware.go b/server/middleware.go similarity index 97% rename from middleware/middleware.go rename to server/middleware.go index b25117773..e35d9d296 100644 --- a/middleware/middleware.go +++ b/server/middleware.go @@ -1,4 +1,4 @@ -package middleware +package server import ( "net/http" diff --git a/server/plan_executor.go b/server/plan_executor.go index 7bca2c470..dbccf0ed9 100644 --- a/server/plan_executor.go +++ b/server/plan_executor.go @@ -8,19 +8,22 @@ 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" "github.com/hootsuite/atlantis/prerun" + "github.com/hootsuite/atlantis/terraform" "github.com/pkg/errors" ) // PlanExecutor handles everything related to running the Terraform plan including integration with S3, Terraform, and GitHub type PlanExecutor struct { - github *GithubClient + github *github.Client githubStatus *GithubStatus - awsConfig *AWSConfig + awsConfig *aws.Config s3Bucket string - terraform *TerraformClient + terraform *terraform.Client githubCommentRenderer *GithubCommentRenderer lockingClient *locking.Client // LockURL is a function that given a lock id will return a url for lock view @@ -72,7 +75,7 @@ func (e EnvironmentFailure) Template() *CompiledTemplate { return EnvironmentErrorTmpl } -func (p *PlanExecutor) execute(ctx *CommandContext, github *GithubClient) { +func (p *PlanExecutor) execute(ctx *CommandContext, github *github.Client) { if p.concurrentRunLocker.TryLock(ctx.BaseRepo.FullName, ctx.Command.environment, ctx.Pull.Num) != true { ctx.Log.Info("run was locked by a concurrent run") github.CreateComment(ctx.BaseRepo, ctx.Pull, "This environment is currently locked by another command that is running for this pull request. Wait until command is complete and try again") @@ -142,7 +145,7 @@ func (p *PlanExecutor) setupAndPlan(ctx *CommandContext) ExecutionResult { constraints, _ := version.NewConstraint(">= 0.9.0") if constraints.Check(terraformVersion) { // run terraform init and environment - outputs, err := p.terraform.RunTerraformInitAndEnv(absolutePath, tfEnv, config) + outputs, err := p.terraform.RunInitAndEnv(absolutePath, tfEnv, config.GetExtraArguments("init")) if err != nil { errMsg := fmt.Sprintf("terraform init and environment commands failed. %s %v", outputs, err) ctx.Log.Err(errMsg) @@ -152,7 +155,7 @@ func (p *PlanExecutor) setupAndPlan(ctx *CommandContext) ExecutionResult { } else { // run terraform get for 0.8.8 and below terraformGetCmd := append([]string{"get", "-no-color"}, config.GetExtraArguments("get")...) - _, output, err := p.terraform.RunTerraformCommand(absolutePath, terraformGetCmd, nil) + _, output, err := p.terraform.RunCommand(absolutePath, terraformGetCmd, nil) if err != nil { errMsg := fmt.Sprintf("terraform get failed. %s %v", output, err) ctx.Log.Err(errMsg) @@ -219,8 +222,8 @@ func (p *PlanExecutor) plan( } // set pull request creator as the session name - p.awsConfig.AWSSessionName = ctx.Pull.Author - awsSession, err := p.awsConfig.CreateAWSSession() + p.awsConfig.SessionName = ctx.Pull.Author + awsSession, err := p.awsConfig.CreateSession() if err != nil { ctx.Log.Err(err.Error()) return PathResult{ @@ -239,7 +242,7 @@ func (p *PlanExecutor) plan( } } - terraformPlanCmdArgs, output, err := p.terraform.RunTerraformCommand(filepath.Join(repoDir, project.Path), tfPlanCmd, []string{ + terraformPlanCmdArgs, output, err := p.terraform.RunCommand(filepath.Join(repoDir, project.Path), tfPlanCmd, []string{ fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", credVals.AccessKeyID), fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", credVals.SecretAccessKey), fmt.Sprintf("AWS_SESSION_TOKEN=%s", credVals.SessionToken), diff --git a/server/pull_closed_executor.go b/server/pull_closed_executor.go index f9a3ed3ca..f49f9c5d9 100644 --- a/server/pull_closed_executor.go +++ b/server/pull_closed_executor.go @@ -6,6 +6,7 @@ import ( "strings" "text/template" + "github.com/hootsuite/atlantis/github" "github.com/hootsuite/atlantis/locking" "github.com/hootsuite/atlantis/models" "github.com/pkg/errors" @@ -13,7 +14,7 @@ import ( type PullClosedExecutor struct { locking *locking.Client - github *GithubClient + github *github.Client workspace *Workspace } diff --git a/server/server.go b/server/server.go index 2d087e6b4..e0e5e03d5 100644 --- a/server/server.go +++ b/server/server.go @@ -1,7 +1,6 @@ package server import ( - "context" "fmt" "io/ioutil" "log" @@ -13,15 +12,17 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/elazarl/go-bindata-assetfs" - "github.com/google/go-github/github" + 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" "github.com/hootsuite/atlantis/locking/dynamodb" "github.com/hootsuite/atlantis/logging" - "github.com/hootsuite/atlantis/middleware" "github.com/hootsuite/atlantis/models" "github.com/hootsuite/atlantis/prerun" + "github.com/hootsuite/atlantis/terraform" homedir "github.com/mitchellh/go-homedir" "github.com/pkg/errors" "github.com/urfave/cli" @@ -110,33 +111,25 @@ func NewServer(config ServerConfig) (*Server, error) { config.DataDir = expanded } - tp := github.BasicAuthTransport{ - Username: strings.TrimSpace(config.GithubUser), - Password: strings.TrimSpace(config.GithubPassword), + githubClient, err := github.NewClient(config.GithubHostname, config.GithubUser, config.GithubPassword) + if err != nil { + return nil, err } - githubBaseClient := github.NewClient(tp.Client()) - githubClientCtx := context.Background() - ghHostname := fmt.Sprintf("https://%s/api/v3/", config.GithubHostname) - if config.GithubHostname == "api.github.com" { - ghHostname = fmt.Sprintf("https://%s/", config.GithubHostname) - } - githubBaseClient.BaseURL, _ = url.Parse(ghHostname) - githubClient := &GithubClient{client: githubBaseClient, ctx: githubClientCtx} githubStatus := &GithubStatus{client: githubClient} - terraformClient, err := NewTerraformClient() + terraformClient, err := terraform.NewClient() if err != nil { return nil, errors.Wrap(err, "initializing terraform") } githubComments := &GithubCommentRenderer{} - awsConfig := &AWSConfig{ - AWSRegion: config.AWSRegion, - AWSRoleArn: config.AssumeRole, + awsConfig := &aws.Config{ + Region: config.AWSRegion, + RoleArn: config.AssumeRole, } var awsSession *session.Session var lockingClient *locking.Client if config.LockingBackend == LockingDynamoDBBackend { - awsSession, err = awsConfig.CreateAWSSession() + awsSession, err = awsConfig.CreateSession() if err != nil { return nil, errors.Wrap(err, "creating aws session for DynamoDB") } @@ -228,7 +221,7 @@ func (s *Server) Start() error { PrintStack: false, StackAll: false, StackSize: 1024 * 8, - }, middleware.NewNon200Logger(s.logger)) + }, NewNon200Logger(s.logger)) n.UseHandler(s.router) s.logger.Info("Atlantis started - listening on port %v", s.port) return cli.NewExitError(http.ListenAndServe(fmt.Sprintf(":%d", s.port), n), 1) @@ -363,11 +356,11 @@ func (s *Server) postEvents(w http.ResponseWriter, r *http.Request) { } } - event, _ := github.ParseWebHook(github.WebHookType(r), payload) + event, _ := gh.ParseWebHook(gh.WebHookType(r), payload) switch event := event.(type) { - case *github.IssueCommentEvent: + case *gh.IssueCommentEvent: s.handleCommentEvent(w, event, githubReqID) - case *github.PullRequestEvent: + case *gh.PullRequestEvent: s.handlePullRequestEvent(w, event, githubReqID) default: s.logger.Debug("Ignoring unsupported event %s", githubReqID) @@ -376,7 +369,7 @@ func (s *Server) postEvents(w http.ResponseWriter, r *http.Request) { } // handlePullRequestEvent will delete any locks associated with the pull request -func (s *Server) handlePullRequestEvent(w http.ResponseWriter, pullEvent *github.PullRequestEvent, githubReqID string) { +func (s *Server) handlePullRequestEvent(w http.ResponseWriter, pullEvent *gh.PullRequestEvent, githubReqID string) { if pullEvent.GetAction() != "closed" { s.logger.Debug("Ignoring pull request event since action was not closed %s", githubReqID) fmt.Fprintln(w, "Ignoring") @@ -407,7 +400,7 @@ func (s *Server) handlePullRequestEvent(w http.ResponseWriter, pullEvent *github fmt.Fprint(w, "Pull request cleaned successfully") } -func (s *Server) handleCommentEvent(w http.ResponseWriter, event *github.IssueCommentEvent, githubReqID string) { +func (s *Server) handleCommentEvent(w http.ResponseWriter, event *gh.IssueCommentEvent, githubReqID string) { if event.GetAction() != "created" { s.logger.Debug("Ignoring comment event since action was not created %s", githubReqID) fmt.Fprintln(w, "Ignoring") diff --git a/server/terraform_client.go b/terraform/terraform_client.go similarity index 61% rename from server/terraform_client.go rename to terraform/terraform_client.go index 863a78362..5a40eba36 100644 --- a/server/terraform_client.go +++ b/terraform/terraform_client.go @@ -1,4 +1,4 @@ -package server +package terraform import ( "fmt" @@ -9,13 +9,13 @@ import ( "github.com/pkg/errors" ) -type TerraformClient struct { +type Client struct { defaultVersion *version.Version } -var terraformVersionRegex = regexp.MustCompile("Terraform v(.*)\n") +var versionRegex = regexp.MustCompile("Terraform v(.*)\n") -func NewTerraformClient() (*TerraformClient, error) { +func NewClient() (*Client, error) { versionCmdOutput, err := exec.Command("terraform", "version").CombinedOutput() output := string(versionCmdOutput) if err != nil { @@ -26,7 +26,7 @@ func NewTerraformClient() (*TerraformClient, error) { } return nil, errors.Wrapf(err, "running terraform version: %s", output) } - match := terraformVersionRegex.FindStringSubmatch(output) + match := versionRegex.FindStringSubmatch(output) if len(match) <= 1 { return nil, fmt.Errorf("could not parse terraform version from %s", output) } @@ -35,23 +35,23 @@ func NewTerraformClient() (*TerraformClient, error) { return nil, errors.Wrap(err, "parsing terraform version") } - return &TerraformClient{ + return &Client{ defaultVersion: version, }, nil } -func (t *TerraformClient) RunTerraformCommand(path string, tfCmd []string, tfEnvVars []string) ([]string, string, error) { - return t.RunTerraformCommandWithVersion(path, tfCmd, tfEnvVars, t.defaultVersion) +func (c *Client) RunCommand(path string, tfCmd []string, tfEnvVars []string) ([]string, string, error) { + return c.RunCommandWithVersion(path, tfCmd, tfEnvVars, c.defaultVersion) } -func (t *TerraformClient) Version() *version.Version { - return t.defaultVersion +func (c *Client) Version() *version.Version { + return c.defaultVersion } -func (t *TerraformClient) RunTerraformCommandWithVersion(path string, tfCmd []string, tfEnvVars []string, v *version.Version) ([]string, string, error) { +func (c *Client) RunCommandWithVersion(path string, tfCmd []string, tfEnvVars []string, v *version.Version) ([]string, 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(t.defaultVersion) { + if !v.Equal(c.defaultVersion) { tfExecutable = fmt.Sprintf("%s%s", tfExecutable, v.String()) } terraformCmd := exec.Command(tfExecutable, tfCmd...) @@ -68,21 +68,21 @@ func (t *TerraformClient) RunTerraformCommandWithVersion(path string, tfCmd []st return terraformCmd.Args, output, nil } -func (t *TerraformClient) RunTerraformInitAndEnv(path string, env string, config ProjectConfig) ([]string, error) { +func (c *Client) RunInitAndEnv(path string, env string, extraArgs []string) ([]string, error) { var outputs []string // run terraform init - _, output, err := t.RunTerraformCommand(path, append([]string{"init", "-no-color"}, config.GetExtraArguments("init")...), []string{}) + _, output, err := c.RunCommand(path, append([]string{"init", "-no-color"}, extraArgs...), []string{}) if err != nil { return nil, errors.Wrapf(err, "running terraform init: %s", output) } outputs = append(outputs, output) // run terraform env new and select - _, output, err = t.RunTerraformCommand(path, []string{"env", "select", "-no-color", env}, []string{}) + _, output, err = c.RunCommand(path, []string{"env", "select", "-no-color", env}, []string{}) if err != nil { // if terraform env select fails we will run terraform env new // to create a new environment - _, output, err = t.RunTerraformCommand(path, []string{"env", "new", "-no-color", env}, []string{}) + _, output, err = c.RunCommand(path, []string{"env", "new", "-no-color", env}, []string{}) if err != nil { return nil, errors.Wrapf(err, "running terraform environment command: %s", output) }