mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-08-01 11:58:48 +00:00
Operating on forked pull requests is dangerous if it's a public repo because anyone can make a pull request to a public repo. We default to false like CircleCI and TravisCI who don't allow credentials to be available on fork PRs.
495 lines
13 KiB
Go
495 lines
13 KiB
Go
package cmd_test
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mitchellh/go-homedir"
|
|
"github.com/runatlantis/atlantis/cmd"
|
|
"github.com/runatlantis/atlantis/server"
|
|
. "github.com/runatlantis/atlantis/testing"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// passedConfig is set to whatever config ended up being passed to NewServer.
|
|
// Used for testing.
|
|
var passedConfig server.Config
|
|
|
|
type ServerCreatorMock struct{}
|
|
|
|
func (s *ServerCreatorMock) NewServer(config server.Config, flagNames server.FlagNames) (cmd.ServerStarter, error) {
|
|
passedConfig = config
|
|
return &ServerStarterMock{}, nil
|
|
}
|
|
|
|
type ServerStarterMock struct{}
|
|
|
|
func (s *ServerStarterMock) Start() error {
|
|
return nil
|
|
}
|
|
|
|
func TestExecute_NoConfigFlag(t *testing.T) {
|
|
t.Log("If there is no config flag specified Execute should return nil.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.ConfigFlag: "",
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
}
|
|
|
|
func TestExecute_ConfigFileExtension(t *testing.T) {
|
|
t.Log("If the config file doesn't have an extension then error.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.ConfigFlag: "does-not-exist",
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
})
|
|
err := c.Execute()
|
|
Equals(t, "invalid config: reading does-not-exist: Unsupported Config Type \"\"", err.Error())
|
|
}
|
|
|
|
func TestExecute_ConfigFileMissing(t *testing.T) {
|
|
t.Log("If the config file doesn't exist then error.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.ConfigFlag: "does-not-exist.yaml",
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
})
|
|
err := c.Execute()
|
|
Equals(t, "invalid config: reading does-not-exist.yaml: open does-not-exist.yaml: no such file or directory", err.Error())
|
|
}
|
|
|
|
func TestExecute_ConfigFileExists(t *testing.T) {
|
|
t.Log("If the config file exists then there should be no error.")
|
|
tmpFile := tempFile(t, "")
|
|
defer os.Remove(tmpFile) // nolint: errcheck
|
|
c := setup(map[string]interface{}{
|
|
cmd.ConfigFlag: tmpFile,
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
}
|
|
|
|
func TestExecute_InvalidConfig(t *testing.T) {
|
|
t.Log("If the config file contains invalid yaml there should be an error.")
|
|
tmpFile := tempFile(t, "invalidyaml")
|
|
defer os.Remove(tmpFile) // nolint: errcheck
|
|
c := setup(map[string]interface{}{
|
|
cmd.ConfigFlag: tmpFile,
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
})
|
|
err := c.Execute()
|
|
Assert(t, strings.Contains(err.Error(), "unmarshal errors"), "should be an unmarshal error")
|
|
}
|
|
|
|
func TestExecute_ValidateLogLevel(t *testing.T) {
|
|
t.Log("Should validate log level.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.LogLevelFlag: "invalid",
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
})
|
|
err := c.Execute()
|
|
Assert(t, err != nil, "should be an error")
|
|
Equals(t, "invalid log level: not one of debug, info, warn, error", err.Error())
|
|
}
|
|
|
|
func TestExecute_ValidateSSLConfig(t *testing.T) {
|
|
expErr := "--ssl-key-file and --ssl-cert-file are both required for ssl"
|
|
cases := []struct {
|
|
description string
|
|
flags map[string]interface{}
|
|
expectError bool
|
|
}{
|
|
{
|
|
"neither option set",
|
|
make(map[string]interface{}),
|
|
false,
|
|
},
|
|
{
|
|
"just ssl-key-file set",
|
|
map[string]interface{}{
|
|
cmd.SSLKeyFileFlag: "file",
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"just ssl-cert-file set",
|
|
map[string]interface{}{
|
|
cmd.SSLCertFileFlag: "flag",
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"both flags set",
|
|
map[string]interface{}{
|
|
cmd.SSLCertFileFlag: "cert",
|
|
cmd.SSLKeyFileFlag: "key",
|
|
},
|
|
false,
|
|
},
|
|
}
|
|
for _, testCase := range cases {
|
|
t.Log("Should validate ssl config when " + testCase.description)
|
|
// Add in required flags.
|
|
testCase.flags[cmd.GHUserFlag] = "user"
|
|
testCase.flags[cmd.GHTokenFlag] = "token"
|
|
|
|
c := setup(testCase.flags)
|
|
err := c.Execute()
|
|
if testCase.expectError {
|
|
Assert(t, err != nil, "should be an error")
|
|
Equals(t, expErr, err.Error())
|
|
} else {
|
|
Ok(t, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExecute_ValidateVCSConfig(t *testing.T) {
|
|
expErr := "--gh-user and --gh-token or --gitlab-user and --gitlab-token must be set"
|
|
cases := []struct {
|
|
description string
|
|
flags map[string]interface{}
|
|
expectError bool
|
|
}{
|
|
{
|
|
"no config set",
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"just github token set",
|
|
map[string]interface{}{
|
|
cmd.GHTokenFlag: "token",
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"just gitlab token set",
|
|
map[string]interface{}{
|
|
cmd.GitlabTokenFlag: "token",
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"just github user set",
|
|
map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"just gitlab user set",
|
|
map[string]interface{}{
|
|
cmd.GitlabUserFlag: "user",
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"github user and gitlab token set",
|
|
map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GitlabTokenFlag: "token",
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"gitlab user and github token set",
|
|
map[string]interface{}{
|
|
cmd.GitlabUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"github user and github token set and should be successful",
|
|
map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"gitlab user and gitlab token set and should be successful",
|
|
map[string]interface{}{
|
|
cmd.GitlabUserFlag: "user",
|
|
cmd.GitlabTokenFlag: "token",
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"github and gitlab user and github and gitlab token set and should be successful",
|
|
map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
cmd.GitlabUserFlag: "user",
|
|
cmd.GitlabTokenFlag: "token",
|
|
},
|
|
false,
|
|
},
|
|
}
|
|
for _, testCase := range cases {
|
|
t.Log("Should validate vcs config when " + testCase.description)
|
|
c := setup(testCase.flags)
|
|
err := c.Execute()
|
|
if testCase.expectError {
|
|
Assert(t, err != nil, "should be an error")
|
|
Equals(t, expErr, err.Error())
|
|
} else {
|
|
Ok(t, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExecute_Defaults(t *testing.T) {
|
|
t.Log("Should set the defaults for all unspecified flags.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
cmd.GitlabUserFlag: "gitlab-user",
|
|
cmd.GitlabTokenFlag: "gitlab-token",
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
|
|
Equals(t, "user", passedConfig.GithubUser)
|
|
Equals(t, "token", passedConfig.GithubToken)
|
|
Equals(t, "", passedConfig.GithubWebHookSecret)
|
|
Equals(t, "gitlab-user", passedConfig.GitlabUser)
|
|
Equals(t, "gitlab-token", passedConfig.GitlabToken)
|
|
Equals(t, "", passedConfig.GitlabWebHookSecret)
|
|
// Get our hostname since that's what gets defaulted to
|
|
hostname, err := os.Hostname()
|
|
Ok(t, err)
|
|
Equals(t, "http://"+hostname+":4141", passedConfig.AtlantisURL)
|
|
|
|
// Get our home dir since that's what gets defaulted to
|
|
dataDir, err := homedir.Expand("~/.atlantis")
|
|
Ok(t, err)
|
|
Equals(t, dataDir, passedConfig.DataDir)
|
|
Equals(t, "github.com", passedConfig.GithubHostname)
|
|
Equals(t, "gitlab.com", passedConfig.GitlabHostname)
|
|
Equals(t, "info", passedConfig.LogLevel)
|
|
Equals(t, false, passedConfig.RequireApproval)
|
|
Equals(t, 4141, passedConfig.Port)
|
|
}
|
|
|
|
func TestExecute_ExpandHomeInDataDir(t *testing.T) {
|
|
t.Log("If ~ is used as a data-dir path, should expand to absolute home path")
|
|
c := setup(map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
cmd.DataDirFlag: "~/this/is/a/path",
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
|
|
home, err := homedir.Dir()
|
|
Ok(t, err)
|
|
Equals(t, home+"/this/is/a/path", passedConfig.DataDir)
|
|
}
|
|
|
|
func TestExecute_RelativeDataDir(t *testing.T) {
|
|
t.Log("Should convert relative dir to absolute.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
cmd.DataDirFlag: "../",
|
|
})
|
|
|
|
// Figure out what ../ should be as an absolute path.
|
|
expectedAbsolutePath, err := filepath.Abs("../")
|
|
Ok(t, err)
|
|
|
|
err = c.Execute()
|
|
Ok(t, err)
|
|
Equals(t, expectedAbsolutePath, passedConfig.DataDir)
|
|
}
|
|
|
|
func TestExecute_GithubUser(t *testing.T) {
|
|
t.Log("Should remove the @ from the github username if it's passed.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.GHUserFlag: "@user",
|
|
cmd.GHTokenFlag: "token",
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
|
|
Equals(t, "user", passedConfig.GithubUser)
|
|
}
|
|
|
|
func TestExecute_GitlabUser(t *testing.T) {
|
|
t.Log("Should remove the @ from the gitlab username if it's passed.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.GitlabUserFlag: "@user",
|
|
cmd.GitlabTokenFlag: "token",
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
|
|
Equals(t, "user", passedConfig.GitlabUser)
|
|
}
|
|
|
|
func TestExecute_Flags(t *testing.T) {
|
|
t.Log("Should use all flags that are set.")
|
|
c := setup(map[string]interface{}{
|
|
cmd.AtlantisURLFlag: "url",
|
|
cmd.DataDirFlag: "/path",
|
|
cmd.GHHostnameFlag: "ghhostname",
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "token",
|
|
cmd.GHWebHookSecret: "secret",
|
|
cmd.GitlabHostnameFlag: "gitlab-hostname",
|
|
cmd.GitlabUserFlag: "gitlab-user",
|
|
cmd.GitlabTokenFlag: "gitlab-token",
|
|
cmd.GitlabWebHookSecret: "gitlab-secret",
|
|
cmd.LogLevelFlag: "debug",
|
|
cmd.PortFlag: 8181,
|
|
cmd.RequireApprovalFlag: true,
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
|
|
Equals(t, "url", passedConfig.AtlantisURL)
|
|
Equals(t, "/path", passedConfig.DataDir)
|
|
Equals(t, "ghhostname", passedConfig.GithubHostname)
|
|
Equals(t, "user", passedConfig.GithubUser)
|
|
Equals(t, "token", passedConfig.GithubToken)
|
|
Equals(t, "secret", passedConfig.GithubWebHookSecret)
|
|
Equals(t, "gitlab-hostname", passedConfig.GitlabHostname)
|
|
Equals(t, "gitlab-user", passedConfig.GitlabUser)
|
|
Equals(t, "gitlab-token", passedConfig.GitlabToken)
|
|
Equals(t, "gitlab-secret", passedConfig.GitlabWebHookSecret)
|
|
Equals(t, "debug", passedConfig.LogLevel)
|
|
Equals(t, 8181, passedConfig.Port)
|
|
Equals(t, true, passedConfig.RequireApproval)
|
|
}
|
|
|
|
func TestExecute_ConfigFile(t *testing.T) {
|
|
t.Log("Should use all the values from the config file.")
|
|
tmpFile := tempFile(t, `---
|
|
atlantis-url: "url"
|
|
data-dir: "/path"
|
|
gh-hostname: "ghhostname"
|
|
gh-user: "user"
|
|
gh-token: "token"
|
|
gh-webhook-secret: "secret"
|
|
gitlab-hostname: "gitlab-hostname"
|
|
gitlab-user: "gitlab-user"
|
|
gitlab-token: "gitlab-token"
|
|
gitlab-webhook-secret: "gitlab-secret"
|
|
log-level: "debug"
|
|
port: 8181
|
|
require-approval: true`)
|
|
defer os.Remove(tmpFile) // nolint: errcheck
|
|
c := setup(map[string]interface{}{
|
|
cmd.ConfigFlag: tmpFile,
|
|
})
|
|
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
Equals(t, "url", passedConfig.AtlantisURL)
|
|
Equals(t, "/path", passedConfig.DataDir)
|
|
Equals(t, "ghhostname", passedConfig.GithubHostname)
|
|
Equals(t, "user", passedConfig.GithubUser)
|
|
Equals(t, "token", passedConfig.GithubToken)
|
|
Equals(t, "secret", passedConfig.GithubWebHookSecret)
|
|
Equals(t, "gitlab-hostname", passedConfig.GitlabHostname)
|
|
Equals(t, "gitlab-user", passedConfig.GitlabUser)
|
|
Equals(t, "gitlab-token", passedConfig.GitlabToken)
|
|
Equals(t, "gitlab-secret", passedConfig.GitlabWebHookSecret)
|
|
Equals(t, "debug", passedConfig.LogLevel)
|
|
Equals(t, 8181, passedConfig.Port)
|
|
Equals(t, true, passedConfig.RequireApproval)
|
|
}
|
|
|
|
func TestExecute_EnvironmentOverride(t *testing.T) {
|
|
t.Log("Environment variables should override config file flags.")
|
|
tmpFile := tempFile(t, "gh-user: config\ngh-token: config2")
|
|
defer os.Remove(tmpFile) // nolint: errcheck
|
|
os.Setenv("ATLANTIS_GH_TOKEN", "override") // nolint: errcheck
|
|
c := setup(map[string]interface{}{
|
|
cmd.ConfigFlag: tmpFile,
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
Equals(t, "override", passedConfig.GithubToken)
|
|
}
|
|
|
|
func TestExecute_FlagConfigOverride(t *testing.T) {
|
|
t.Log("Flags should override config file flags.")
|
|
os.Setenv("ATLANTIS_GH_TOKEN", "env-var") // nolint: errcheck
|
|
c := setup(map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GHTokenFlag: "override",
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
Equals(t, "override", passedConfig.GithubToken)
|
|
}
|
|
|
|
func TestExecute_FlagEnvVarOverride(t *testing.T) {
|
|
t.Log("Flags should override environment variables.")
|
|
tmpFile := tempFile(t, "gh-user: config\ngh-token: config2")
|
|
defer os.Remove(tmpFile) // nolint: errcheck
|
|
c := setup(map[string]interface{}{
|
|
cmd.ConfigFlag: tmpFile,
|
|
cmd.GHTokenFlag: "override",
|
|
})
|
|
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
Equals(t, "override", passedConfig.GithubToken)
|
|
}
|
|
|
|
func TestExecute_EnvVars(t *testing.T) {
|
|
t.Log("Setting flags by env var should work.")
|
|
os.Setenv("ATLANTIS_GH_TOKEN", "gh-token") // nolint: errcheck
|
|
os.Setenv("ATLANTIS_GH_WEBHOOK_SECRET", "gh-webhook") // nolint: errcheck
|
|
os.Setenv("ATLANTIS_GITLAB_TOKEN", "gitlab-token") // nolint: errcheck
|
|
os.Setenv("ATLANTIS_GITLAB_WEBHOOK_SECRET", "gitlab-webhook") // nolint: errcheck
|
|
c := setup(map[string]interface{}{
|
|
cmd.GHUserFlag: "user",
|
|
cmd.GitlabUserFlag: "user",
|
|
})
|
|
err := c.Execute()
|
|
Ok(t, err)
|
|
Equals(t, "gh-token", passedConfig.GithubToken)
|
|
Equals(t, "gh-webhook", passedConfig.GithubWebHookSecret)
|
|
Equals(t, "gitlab-token", passedConfig.GitlabToken)
|
|
Equals(t, "gitlab-webhook", passedConfig.GitlabWebHookSecret)
|
|
}
|
|
|
|
func setup(flags map[string]interface{}) *cobra.Command {
|
|
viper := viper.New()
|
|
for k, v := range flags {
|
|
viper.Set(k, v)
|
|
}
|
|
c := &cmd.ServerCmd{
|
|
ServerCreator: &ServerCreatorMock{},
|
|
Viper: viper,
|
|
SilenceOutput: true,
|
|
}
|
|
return c.Init()
|
|
}
|
|
|
|
func tempFile(t *testing.T, contents string) string {
|
|
f, err := ioutil.TempFile("", "")
|
|
Ok(t, err)
|
|
newName := f.Name() + ".yaml"
|
|
err = os.Rename(f.Name(), newName)
|
|
Ok(t, err)
|
|
ioutil.WriteFile(newName, []byte(contents), 0644) // nolint: errcheck
|
|
return newName
|
|
}
|