diff --git a/cmd/server.go b/cmd/server.go index 3833c7bb1b..1415050dc4 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -73,6 +73,7 @@ const ( SSLCertFileFlag = "ssl-cert-file" SSLKeyFileFlag = "ssl-key-file" TFDownloadURLFlag = "tf-download-url" + VCSStatusName = "vcs-status-name" TFEHostnameFlag = "tfe-hostname" TFETokenFlag = "tfe-token" WriteGitCredsFlag = "write-git-creds" @@ -89,6 +90,7 @@ const ( DefaultPort = 4141 DefaultTFDownloadURL = "https://releases.hashicorp.com" DefaultTFEHostname = "app.terraform.io" + DefaultVCSStatusName = "atlantis" ) var stringFlags = map[string]stringFlag{ @@ -221,6 +223,10 @@ var stringFlags = map[string]stringFlag{ description: "Terraform version to default to (ex. v0.12.0). Will download if not yet on disk." + " If not set, Atlantis uses the terraform binary in its PATH.", }, + VCSStatusName: { + description: "Name used to identify Atlantis for pull request statuses.", + defaultValue: DefaultVCSStatusName, + }, } var boolFlags = map[string]boolFlag{ @@ -463,6 +469,9 @@ func (s *ServerCmd) setDefaults(c *server.UserConfig) { if c.TFDownloadURL == "" { c.TFDownloadURL = DefaultTFDownloadURL } + if c.VCSStatusName == "" { + c.VCSStatusName = DefaultVCSStatusName + } if c.TFEHostname == "" { c.TFEHostname = DefaultTFEHostname } diff --git a/cmd/server_test.go b/cmd/server_test.go index c7fe501855..b1067a4943 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -395,6 +395,7 @@ func TestExecute_Defaults(t *testing.T) { Equals(t, "https://releases.hashicorp.com", passedConfig.TFDownloadURL) Equals(t, "app.terraform.io", passedConfig.TFEHostname) Equals(t, "", passedConfig.TFEToken) + Equals(t, "atlantis", passedConfig.VCSStatusName) Equals(t, false, passedConfig.WriteGitCreds) } @@ -519,6 +520,7 @@ func TestExecute_Flags(t *testing.T) { cmd.TFDownloadURLFlag: "https://my-hostname.com", cmd.TFEHostnameFlag: "my-hostname", cmd.TFETokenFlag: "my-token", + cmd.VCSStatusName: "my-status", cmd.WriteGitCredsFlag: true, }) err := c.Execute() @@ -559,6 +561,7 @@ func TestExecute_Flags(t *testing.T) { Equals(t, "https://my-hostname.com", passedConfig.TFDownloadURL) Equals(t, "my-hostname", passedConfig.TFEHostname) Equals(t, "my-token", passedConfig.TFEToken) + Equals(t, "my-status", passedConfig.VCSStatusName) Equals(t, true, passedConfig.WriteGitCreds) } @@ -600,6 +603,7 @@ ssl-key-file: key-file tf-download-url: https://my-hostname.com tfe-hostname: my-hostname tfe-token: my-token +vcs-status-name: my-status write-git-creds: true `) defer os.Remove(tmpFile) // nolint: errcheck @@ -644,6 +648,7 @@ write-git-creds: true Equals(t, "https://my-hostname.com", passedConfig.TFDownloadURL) Equals(t, "my-hostname", passedConfig.TFEHostname) Equals(t, "my-token", passedConfig.TFEToken) + Equals(t, "my-status", passedConfig.VCSStatusName) Equals(t, true, passedConfig.WriteGitCreds) } @@ -684,6 +689,7 @@ ssl-key-file: key-file tf-download-url: https://my-hostname.com tfe-hostname: my-hostname tfe-token: my-token +vcs-status-name: my-status write-git-creds: true `) defer os.Remove(tmpFile) // nolint: errcheck @@ -725,6 +731,7 @@ write-git-creds: true "TF_DOWNLOAD_URL": "https://override-my-hostname.com", "TFE_HOSTNAME": "override-my-hostname", "TFE_TOKEN": "override-my-token", + "VCS_STATUS_NAME": "override-status-name", "WRITE_GIT_CREDS": "false", } { os.Setenv("ATLANTIS_"+name, value) // nolint: errcheck @@ -769,6 +776,7 @@ write-git-creds: true Equals(t, "https://override-my-hostname.com", passedConfig.TFDownloadURL) Equals(t, "override-my-hostname", passedConfig.TFEHostname) Equals(t, "override-my-token", passedConfig.TFEToken) + Equals(t, "override-status-name", passedConfig.VCSStatusName) Equals(t, false, passedConfig.WriteGitCreds) } @@ -808,8 +816,10 @@ slack-token: slack-token ssl-cert-file: cert-file ssl-key-file: key-file tf-download-url: https://my-hostname.com +status-name: status-name tfe-hostname: my-hostname tfe-token: my-token +vcs-status-name: my-status write-git-creds: true `) @@ -850,6 +860,7 @@ write-git-creds: true cmd.TFDownloadURLFlag: "https://override-my-hostname.com", cmd.TFEHostnameFlag: "override-my-hostname", cmd.TFETokenFlag: "override-my-token", + cmd.VCSStatusName: "override-status-name", cmd.WriteGitCredsFlag: false, }) err := c.Execute() @@ -888,6 +899,7 @@ write-git-creds: true Equals(t, "https://override-my-hostname.com", passedConfig.TFDownloadURL) Equals(t, "override-my-hostname", passedConfig.TFEHostname) Equals(t, "override-my-token", passedConfig.TFEToken) + Equals(t, "override-status-name", passedConfig.VCSStatusName) Equals(t, false, passedConfig.WriteGitCreds) } @@ -929,8 +941,10 @@ func TestExecute_FlagEnvVarOverride(t *testing.T) { "SSL_CERT_FILE": "cert-file", "SSL_KEY_FILE": "key-file", "TF_DOWNLOAD_URL": "https://my-hostname.com", + "STATUS_NAME": "status-name", "TFE_HOSTNAME": "my-hostname", "TFE_TOKEN": "my-token", + "VCS_STATUS_NAME": "my-status", "WRITE_GIT_CREDS": "true", } for name, value := range envVars { @@ -979,6 +993,7 @@ func TestExecute_FlagEnvVarOverride(t *testing.T) { cmd.TFDownloadURLFlag: "https://override-my-hostname.com", cmd.TFEHostnameFlag: "override-my-hostname", cmd.TFETokenFlag: "override-my-token", + cmd.VCSStatusName: "override-status-name", cmd.WriteGitCredsFlag: false, }) err := c.Execute() @@ -1019,6 +1034,7 @@ func TestExecute_FlagEnvVarOverride(t *testing.T) { Equals(t, "https://override-my-hostname.com", passedConfig.TFDownloadURL) Equals(t, "override-my-hostname", passedConfig.TFEHostname) Equals(t, "override-my-token", passedConfig.TFEToken) + Equals(t, "override-status-name", passedConfig.VCSStatusName) Equals(t, false, passedConfig.WriteGitCreds) } diff --git a/go.sum b/go.sum index 724e8b870c..e403d02977 100644 --- a/go.sum +++ b/go.sum @@ -430,7 +430,11 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 h1:Dh6fw+p6FyRl5x/FvNswO1ji0lIGzm3KP8Y9VkS9PTE= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191113232020-e2727e816f5a h1:3IG7HNvPBDvrxpnTWA6zpeNCS5ydX6cdt6oOiGlC8qg= +golang.org/x/tools v0.0.0-20191113232020-e2727e816f5a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= diff --git a/runatlantis.io/docs/server-configuration.md b/runatlantis.io/docs/server-configuration.md index 2fb25cc867..115e57e3f2 100644 --- a/runatlantis.io/docs/server-configuration.md +++ b/runatlantis.io/docs/server-configuration.md @@ -448,6 +448,15 @@ Values are chosen in this order: ``` A token for Terraform Cloud/Terraform Enteprise integration. See [Terraform Cloud](terraform-cloud.html) for more details. +* ### `--vcs-status-name` + ```bash + atlantis server --vcs-status-name="atlantis-dev" + ``` + Name used to identify Atlantis when updating a pull request status. Defaults to `atlantis`. + + This is useful when running multiple Atlantis servers against a single repository so you can + give each Atlantis server its own unique name to prevent the statuses clashing. + * ### `--write-git-creds` ```bash atlantis server --write-git-creds diff --git a/server/events/command_runner_test.go b/server/events/command_runner_test.go index 261184731f..25db650a3c 100644 --- a/server/events/command_runner_test.go +++ b/server/events/command_runner_test.go @@ -62,7 +62,7 @@ func setup(t *testing.T) *vcsmocks.MockClient { ThenReturn(pullLogger) ch = events.DefaultCommandRunner{ VCSClient: vcsClient, - CommitStatusUpdater: &events.DefaultCommitStatusUpdater{vcsClient}, + CommitStatusUpdater: &events.DefaultCommitStatusUpdater{vcsClient, "atlantis"}, EventParser: eventParsing, MarkdownRenderer: &events.MarkdownRenderer{}, GithubPullGetter: githubGetter, diff --git a/server/events/commit_status_updater.go b/server/events/commit_status_updater.go index 47e4846a52..2d8238febd 100644 --- a/server/events/commit_status_updater.go +++ b/server/events/commit_status_updater.go @@ -40,10 +40,12 @@ type CommitStatusUpdater interface { // DefaultCommitStatusUpdater implements CommitStatusUpdater. type DefaultCommitStatusUpdater struct { Client vcs.Client + // StatusName is the name used to identify Atlantis when creating PR statuses. + StatusName string } func (d *DefaultCommitStatusUpdater) UpdateCombined(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName) error { - src := fmt.Sprintf("atlantis/%s", command.String()) + src := fmt.Sprintf("%s/%s", d.StatusName, command.String()) var descripWords string switch status { case models.PendingCommitStatus: @@ -58,7 +60,7 @@ func (d *DefaultCommitStatusUpdater) UpdateCombined(repo models.Repo, pull model } func (d *DefaultCommitStatusUpdater) UpdateCombinedCount(repo models.Repo, pull models.PullRequest, status models.CommitStatus, command models.CommandName, numSuccess int, numTotal int) error { - src := fmt.Sprintf("atlantis/%s", command.String()) + src := fmt.Sprintf("%s/%s", d.StatusName, command.String()) cmdVerb := "planned" if command == models.ApplyCommand { cmdVerb = "applied" @@ -71,7 +73,7 @@ func (d *DefaultCommitStatusUpdater) UpdateProject(ctx models.ProjectCommandCont if projectID == "" { projectID = fmt.Sprintf("%s/%s", ctx.RepoRelDir, ctx.Workspace) } - src := fmt.Sprintf("atlantis/%s: %s", cmdName.String(), projectID) + src := fmt.Sprintf("%s/%s: %s", d.StatusName, cmdName.String(), projectID) var descripWords string switch status { case models.PendingCommitStatus: diff --git a/server/events/commit_status_updater_test.go b/server/events/commit_status_updater_test.go index 152df23c1c..582daf2586 100644 --- a/server/events/commit_status_updater_test.go +++ b/server/events/commit_status_updater_test.go @@ -66,7 +66,7 @@ func TestUpdateCombined(t *testing.T) { t.Run(c.expDescrip, func(t *testing.T) { RegisterMockTestingT(t) client := mocks.NewMockClient() - s := events.DefaultCommitStatusUpdater{Client: client} + s := events.DefaultCommitStatusUpdater{Client: client, StatusName: "atlantis"} err := s.UpdateCombined(models.Repo{}, models.PullRequest{}, c.status, c.command) Ok(t, err) @@ -132,11 +132,11 @@ func TestUpdateCombinedCount(t *testing.T) { t.Run(c.expDescrip, func(t *testing.T) { RegisterMockTestingT(t) client := mocks.NewMockClient() - s := events.DefaultCommitStatusUpdater{Client: client} + s := events.DefaultCommitStatusUpdater{Client: client, StatusName: "atlantis-test"} err := s.UpdateCombinedCount(models.Repo{}, models.PullRequest{}, c.status, c.command, c.numSuccess, c.numTotal) Ok(t, err) - expSrc := fmt.Sprintf("atlantis/%s", c.command) + expSrc := fmt.Sprintf("%s/%s", s.StatusName, c.command) client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, c.status, expSrc, c.expDescrip, "") }) } @@ -169,7 +169,7 @@ func TestDefaultCommitStatusUpdater_UpdateProjectSrc(t *testing.T) { for _, c := range cases { t.Run(c.expSrc, func(t *testing.T) { client := mocks.NewMockClient() - s := events.DefaultCommitStatusUpdater{Client: client} + s := events.DefaultCommitStatusUpdater{Client: client, StatusName: "atlantis"} err := s.UpdateProject(models.ProjectCommandContext{ ProjectName: c.projectName, RepoRelDir: c.repoRelDir, @@ -227,7 +227,7 @@ func TestDefaultCommitStatusUpdater_UpdateProject(t *testing.T) { for _, c := range cases { t.Run(c.expDescrip, func(t *testing.T) { client := mocks.NewMockClient() - s := events.DefaultCommitStatusUpdater{Client: client} + s := events.DefaultCommitStatusUpdater{Client: client, StatusName: "atlantis"} err := s.UpdateProject(models.ProjectCommandContext{ RepoRelDir: ".", Workspace: "default", @@ -240,3 +240,20 @@ func TestDefaultCommitStatusUpdater_UpdateProject(t *testing.T) { }) } } + +// Test that we can set the status name. +func TestDefaultCommitStatusUpdater_UpdateProjectCustomStatusName(t *testing.T) { + RegisterMockTestingT(t) + client := mocks.NewMockClient() + s := events.DefaultCommitStatusUpdater{Client: client, StatusName: "custom"} + err := s.UpdateProject(models.ProjectCommandContext{ + RepoRelDir: ".", + Workspace: "default", + }, + models.ApplyCommand, + models.SuccessCommitStatus, + "url") + Ok(t, err) + client.VerifyWasCalledOnce().UpdateStatus(models.Repo{}, models.PullRequest{}, + models.SuccessCommitStatus, "custom/apply: ./default", "Apply succeeded.", "url") +} diff --git a/server/events/matchers/models_pullrequest.go b/server/events/matchers/models_pullrequest.go index dd1fb0d4ee..37e4780130 100644 --- a/server/events/matchers/models_pullrequest.go +++ b/server/events/matchers/models_pullrequest.go @@ -3,6 +3,7 @@ package matchers import ( "reflect" + "github.com/petergtz/pegomock" models "github.com/runatlantis/atlantis/server/events/models" ) diff --git a/server/events/matchers/models_repo.go b/server/events/matchers/models_repo.go index 418f13cfcf..e985fd3a90 100644 --- a/server/events/matchers/models_repo.go +++ b/server/events/matchers/models_repo.go @@ -3,6 +3,7 @@ package matchers import ( "reflect" + "github.com/petergtz/pegomock" models "github.com/runatlantis/atlantis/server/events/models" ) diff --git a/server/events/matchers/ptr_to_logging_simplelogger.go b/server/events/matchers/ptr_to_logging_simplelogger.go index 04c72791bc..095fa65a72 100644 --- a/server/events/matchers/ptr_to_logging_simplelogger.go +++ b/server/events/matchers/ptr_to_logging_simplelogger.go @@ -3,6 +3,7 @@ package matchers import ( "reflect" + "github.com/petergtz/pegomock" logging "github.com/runatlantis/atlantis/server/logging" ) diff --git a/server/events/project_command_runner_test.go b/server/events/project_command_runner_test.go index 9729d8f9a8..0360d8ebcf 100644 --- a/server/events/project_command_runner_test.go +++ b/server/events/project_command_runner_test.go @@ -18,13 +18,12 @@ import ( "testing" "github.com/hashicorp/go-version" - "github.com/runatlantis/atlantis/server/events/runtime" - . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/events" "github.com/runatlantis/atlantis/server/events/mocks" "github.com/runatlantis/atlantis/server/events/mocks/matchers" "github.com/runatlantis/atlantis/server/events/models" + "github.com/runatlantis/atlantis/server/events/runtime" mocks2 "github.com/runatlantis/atlantis/server/events/runtime/mocks" tmocks "github.com/runatlantis/atlantis/server/events/terraform/mocks" "github.com/runatlantis/atlantis/server/events/yaml/valid" diff --git a/server/events/runtime/env_step_runner.go b/server/events/runtime/env_step_runner.go index 28a8d481f2..e8ba3246c4 100644 --- a/server/events/runtime/env_step_runner.go +++ b/server/events/runtime/env_step_runner.go @@ -1,8 +1,9 @@ package runtime import ( - "github.com/runatlantis/atlantis/server/events/models" "strings" + + "github.com/runatlantis/atlantis/server/events/models" ) // EnvStepRunner set environment variables. diff --git a/server/server.go b/server/server.go index 487522ad1e..af3522b290 100644 --- a/server/server.go +++ b/server/server.go @@ -211,7 +211,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { return nil, errors.Wrap(err, "initializing webhooks") } vcsClient := vcs.NewClientProxy(githubClient, gitlabClient, bitbucketCloudClient, bitbucketServerClient, azuredevopsClient) - commitStatusUpdater := &events.DefaultCommitStatusUpdater{Client: vcsClient} + commitStatusUpdater := &events.DefaultCommitStatusUpdater{Client: vcsClient, StatusName: userConfig.VCSStatusName} terraformClient, err := terraform.NewClient( logger, userConfig.DataDir, diff --git a/server/user_config.go b/server/user_config.go index a40614afe4..2697603adc 100644 --- a/server/user_config.go +++ b/server/user_config.go @@ -47,6 +47,7 @@ type UserConfig struct { TFDownloadURL string `mapstructure:"tf-download-url"` TFEHostname string `mapstructure:"tfe-hostname"` TFEToken string `mapstructure:"tfe-token"` + VCSStatusName string `mapstructure:"vcs-status-name"` DefaultTFVersion string `mapstructure:"default-tf-version"` Webhooks []WebhookConfig `mapstructure:"webhooks"` WriteGitCreds bool `mapstructure:"write-git-creds"`