Skip to content

Commit

Permalink
Gitlab (runatlantis#190)
Browse files Browse the repository at this point in the history
Gitlab initial implementation
  • Loading branch information
lkysow committed Nov 16, 2017
1 parent dd39715 commit fa43feb
Show file tree
Hide file tree
Showing 121 changed files with 15,721 additions and 1,021 deletions.
8 changes: 7 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,7 @@
[[constraint]]
branch = "v2"
name = "gopkg.in/yaml.v2"

[[constraint]]
branch = "master"
name = "github.com/lkysow/go-gitlab"
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Package cmd holds all our cli commands.
// These are different from the commands that get run via GitHub comments
// These are different from the commands that get run via pull request comments.
package cmd

import (
Expand Down
66 changes: 53 additions & 13 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const (
GHTokenFlag = "gh-token"
GHUserFlag = "gh-user"
GHWebHookSecret = "gh-webhook-secret"
GitlabHostnameFlag = "gitlab-hostname"
GitlabTokenFlag = "gitlab-token"
GitlabUserFlag = "gitlab-user"
GitlabWebHookSecret = "gitlab-webhook-secret"
LogLevelFlag = "log-level"
PortFlag = "port"
RequireApprovalFlag = "require-approval"
Expand All @@ -49,19 +53,42 @@ var stringFlags = []stringFlag{
description: "Hostname of your Github Enterprise installation. If using github.com, no need to set.",
value: "github.com",
},
{
name: GHUserFlag,
description: "GitHub username of API user.",
},
{
name: GHTokenFlag,
description: "[REQUIRED] GitHub token of API user. Can also be specified via the ATLANTIS_GH_TOKEN environment variable.",
description: "GitHub token of API user. Can also be specified via the ATLANTIS_GH_TOKEN environment variable.",
env: "ATLANTIS_GH_TOKEN",
},
{
name: GHUserFlag,
description: "[REQUIRED] GitHub username of API user.",
name: GHWebHookSecret,
description: "Optional secret used to validate GitHub webhooks (see https://developer.github.com/webhooks/securing/)." +
" If not specified, Atlantis won't be able to validate that the incoming webhook call came from GitHub. " +
"Can also be specified via the ATLANTIS_GH_WEBHOOK_SECRET environment variable.",
env: "ATLANTIS_GH_WEBHOOK_SECRET",
},
{
name: GitlabHostnameFlag,
description: "Hostname of your GitLab Enterprise installation. If using gitlab.com, no need to set.",
value: "gitlab.com",
},
{
name: GHWebHookSecret,
description: "Optional secret used for GitHub webhooks (see https://developer.github.com/webhooks/securing/). If not specified, Atlantis won't validate the incoming webhook call.",
env: "ATLANTIS_GH_WEBHOOK_SECRET",
name: GitlabUserFlag,
description: "GitLab username of API user.",
},
{
name: GitlabTokenFlag,
description: "GitLab token of API user. Can also be specified via the ATLANTIS_GITLAB_TOKEN environment variable.",
env: "ATLANTIS_GITLAB_TOKEN",
},
{
name: GitlabWebHookSecret,
description: "Optional secret used to validate GitLab webhooks." +
" If not specified, Atlantis won't be able to validate that the incoming webhook call came from GitLab. " +
"Can also be specified via the ATLANTIS_GITLAB_WEBHOOK_SECRET environment variable.",
env: "ATLANTIS_GITLAB_WEBHOOK_SECRET",
},
{
name: LogLevelFlag,
Expand Down Expand Up @@ -206,7 +233,7 @@ func (s *ServerCmd) run() error {
if err := setDataDir(&config); err != nil {
return err
}
sanitizeGithubUser(&config)
trimAtSymbolFromUsers(&config)

// Config looks good. Start the server.
server, err := s.ServerCreator.NewServer(config)
Expand All @@ -221,11 +248,23 @@ func validate(config server.Config) error {
if logLevel != "debug" && logLevel != "info" && logLevel != "warn" && logLevel != "error" {
return errors.New("invalid log level: not one of debug, info, warn, error")
}
if config.GithubUser == "" {
return fmt.Errorf("--%s must be set", GHUserFlag)
vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GitlabUserFlag, GitlabTokenFlag)

// The following combinations are valid.
// 1. github user and token
// 2. gitlab user and token
// 3. all 4 set
// We validate using contradiction (I think).
if config.GithubUser != "" && config.GithubToken == "" || config.GithubToken != "" && config.GithubUser == "" {
return vcsErr
}
if config.GitlabUser != "" && config.GitlabToken == "" || config.GitlabToken != "" && config.GitlabUser == "" {
return vcsErr
}
if config.GithubToken == "" {
return fmt.Errorf("--%s must be set", GHTokenFlag)
// At this point, we know that there can't be a single user/token without
// its pair, but we haven't checked if any user/token is set at all.
if config.GithubUser == "" && config.GitlabUser == "" {
return vcsErr
}
return nil
}
Expand Down Expand Up @@ -256,9 +295,10 @@ func setDataDir(config *server.Config) error {
return nil
}

// sanitizeGithubUser trims @ from the front of the github username if it exists.
func sanitizeGithubUser(config *server.Config) {
// trimAtSymbolFromUsers trims @ from the front of the github and gitlab usernames
func trimAtSymbolFromUsers(config *server.Config) {
config.GithubUser = strings.TrimPrefix(config.GithubUser, "@")
config.GitlabUser = strings.TrimPrefix(config.GitlabUser, "@")
}

// withErrPrint prints out any errors to a terminal in red.
Expand Down
155 changes: 138 additions & 17 deletions cmd/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,57 +90,131 @@ func TestExecute_InvalidConfig(t *testing.T) {
Assert(t, strings.Contains(err.Error(), "unmarshal errors"), "should be an unmarshal error")
}

func TestExecute_Validation(t *testing.T) {
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_ValidateVCSConfig(t *testing.T) {
expErr := "--gh-user/--gh-token or --gitlab-user/--gitlab-token must be set"
cases := []struct {
description string
flags map[string]interface{}
expErr string
expectError bool
}{
{
"Should validate log level.",
"no config set",
nil,
true,
},
{
"just github token set",
map[string]interface{}{
cmd.LogLevelFlag: "invalid",
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.GHTokenFlag: "token",
},
"invalid log level: not one of debug, info, warn, error",
true,
},
{
"Should ensure github user is set.",
"just gitlab token set",
map[string]interface{}{
cmd.GHTokenFlag: "token",
cmd.GitlabTokenFlag: "token",
},
"--gh-user must be set",
true,
},
{
"Should ensure github token is set.",
"just github user set",
map[string]interface{}{
cmd.GHUserFlag: "user",
},
"--gh-token must be set",
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(testCase.description)
t.Log("Should validate vcs config when " + testCase.description)
c := setup(testCase.flags)
err := c.Execute()
Assert(t, err != nil, "should be an error")
Equals(t, testCase.expErr, err.Error())
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.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)
Expand All @@ -151,6 +225,7 @@ func TestExecute_Defaults(t *testing.T) {
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)
Expand Down Expand Up @@ -183,6 +258,18 @@ func TestExecute_GithubUser(t *testing.T) {
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{}{
Expand All @@ -192,6 +279,10 @@ func TestExecute_Flags(t *testing.T) {
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,
Expand All @@ -205,6 +296,10 @@ func TestExecute_Flags(t *testing.T) {
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)
Expand All @@ -219,6 +314,10 @@ 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`)
Expand All @@ -235,6 +334,10 @@ require-approval: true`)
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)
Expand Down Expand Up @@ -279,6 +382,24 @@ func TestExecute_FlagEnvVarOverride(t *testing.T) {
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 {
Expand Down
Loading

0 comments on commit fa43feb

Please sign in to comment.