mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-31 09:08:44 +00:00
* set version in one place * rename ExecutionContext to CommandContext * delete unneeded PullRequestContext since it just duplicated what is in CommandContext * split fields inside CommandContext into Repo, PullRequest, and User models * clean up unused fields * remove base_executor stuff which was trying to do inheritance in go and didn't get us anywhere * clean up unused code and assets * created a locking.Client that creates the Keys that are used on the front-end. This allows the backends to store the locks any way they like as long as they support the interface
228 lines
6.3 KiB
Go
228 lines
6.3 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/hootsuite/atlantis/server"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
"os"
|
|
)
|
|
|
|
// To add a new flag you must
|
|
// 1. Add a const with the flag name
|
|
// 2. Add a new field to server.ServerConfig and set the mapstructure tag equal to the flag name
|
|
// 3. Add your flag's description etc. to the stringFlags, intFlags, or boolFlags slices
|
|
const (
|
|
ghUserFlag = "gh-user"
|
|
ghPasswordFlag = "gh-password"
|
|
ghHostnameFlag = "gh-hostname"
|
|
sshKeyFlag = "ssh-key"
|
|
portFlag = "port"
|
|
awsAssumeRoleFlag = "aws-assume-role-arn"
|
|
scratchDirFlag = "scratch-dir"
|
|
awsRegionFlag = "aws-region"
|
|
s3BucketFlag = "s3-bucket"
|
|
logLevelFlag = "log-level"
|
|
configFlag = "config"
|
|
requireApprovalFlag = "require-approval"
|
|
atlantisURLFlag = "atlantis-url"
|
|
dataDirFlag = "data-dir"
|
|
lockingBackendFlag = "locking-backend"
|
|
lockingTableFlag = "locking-dynamodb-table"
|
|
)
|
|
|
|
var stringFlags = []stringFlag{
|
|
{
|
|
name: ghHostnameFlag,
|
|
description: "Hostname of Github installation.",
|
|
value: "api.github.com",
|
|
},
|
|
{
|
|
name: logLevelFlag,
|
|
description: "Log level. Either debug, info, warn, or error.",
|
|
value: "warn",
|
|
},
|
|
{
|
|
name: sshKeyFlag,
|
|
description: "Path to SSH key used for GitHub.",
|
|
},
|
|
{
|
|
name: awsAssumeRoleFlag,
|
|
description: "The Amazon Resource Name (`arn`) to assume when running Terraform commands. If not specified, will use AWS credentials via environment variables, or credentials files.",
|
|
},
|
|
{
|
|
name: scratchDirFlag,
|
|
description: "Path to directory to use as a temporary workspace for checking out repos.",
|
|
value: "/tmp/atlantis",
|
|
},
|
|
{
|
|
name: awsRegionFlag,
|
|
description: "The Amazon region to connect to for API actions.",
|
|
value: "us-east-1",
|
|
},
|
|
{
|
|
name: s3BucketFlag,
|
|
description: "The S3 bucket name to store atlantis data (terraform plans, terraform state, etc).",
|
|
value: "atlantis",
|
|
},
|
|
{
|
|
name: atlantisURLFlag,
|
|
description: "Url that Atlantis can be reached at. Defaults to http://$(hostname):$port where $port is the port flag.",
|
|
},
|
|
{
|
|
name: dataDirFlag,
|
|
description: "Path to directory to store Atlantis data.",
|
|
value: "/var/lib/atlantis",
|
|
},
|
|
{
|
|
name: lockingBackendFlag,
|
|
description: "Which backend to use for locking. file or dynamodb.",
|
|
value: "file",
|
|
},
|
|
{
|
|
name: lockingTableFlag,
|
|
description: "Name of table in DynamoDB to use for locking. Only read if locking-backend is set to dynamodb.",
|
|
value: "atlantis-locks",
|
|
},
|
|
{
|
|
name: configFlag,
|
|
description: "Config file.",
|
|
},
|
|
{
|
|
name: ghUserFlag,
|
|
description: "GitHub username of API user. Can also be specified via the ATLANTIS_GH_USER environment variable.",
|
|
env: "ATLANTIS_GH_USER",
|
|
},
|
|
{
|
|
name: ghPasswordFlag,
|
|
description: "GitHub password of API user. Can also be specified via the ATLANTIS_GH_PASSWORD environment variable.",
|
|
env: "ATLANTIS_GH_PASSWORD",
|
|
},
|
|
}
|
|
var boolFlags = []boolFlag{
|
|
{
|
|
name: requireApprovalFlag,
|
|
description: "Require pull requests to be \"Approved\" before allowing the apply command to be run.",
|
|
value: true,
|
|
},
|
|
}
|
|
var intFlags = []intFlag{
|
|
{
|
|
name: portFlag,
|
|
description: "Port to bind to.",
|
|
value: 4141,
|
|
},
|
|
}
|
|
|
|
type stringFlag struct {
|
|
name string
|
|
description string
|
|
value string
|
|
env string
|
|
}
|
|
type intFlag struct {
|
|
name string
|
|
description string
|
|
value int
|
|
}
|
|
type boolFlag struct {
|
|
name string
|
|
description string
|
|
value bool
|
|
}
|
|
|
|
var serverCmd = &cobra.Command{
|
|
Use: "server",
|
|
Short: "Start the atlantis server",
|
|
Long: `Start the atlantis server
|
|
|
|
Flags can also be set in a yaml config file (see --` + configFlag + `).
|
|
Config values are overridden by environment variables which in turn are overridden by flags.`,
|
|
SilenceUsage: true,
|
|
|
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
|
// if passed a config file then try and load it
|
|
configFile := viper.GetString(configFlag)
|
|
if configFile != "" {
|
|
viper.SetConfigFile(configFile)
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
return fmt.Errorf("invalid config: reading %s: %s", configFile, err)
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
var config server.ServerConfig
|
|
if err := viper.Unmarshal(&config); err != nil {
|
|
return err
|
|
}
|
|
if err := validate(config); err != nil {
|
|
return err
|
|
}
|
|
if err := setAtlantisURL(&config); err != nil {
|
|
return err
|
|
}
|
|
|
|
// config looks good, start the server
|
|
server, err := server.NewServer(config)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Failed to initialize server")
|
|
}
|
|
return server.Start()
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
// set string flags
|
|
for _, f := range stringFlags {
|
|
serverCmd.Flags().String(f.name, f.value, f.description)
|
|
if f.env != "" {
|
|
viper.BindEnv(f.name, f.env)
|
|
}
|
|
viper.BindPFlag(f.name, serverCmd.Flags().Lookup(f.name))
|
|
}
|
|
|
|
// set int flags
|
|
for _, f := range intFlags {
|
|
serverCmd.Flags().Int(f.name, f.value, f.description)
|
|
viper.BindPFlag(f.name, serverCmd.Flags().Lookup(f.name))
|
|
}
|
|
|
|
// set bool flags
|
|
for _, f := range boolFlags {
|
|
serverCmd.Flags().Bool(f.name, f.value, f.description)
|
|
viper.BindPFlag(f.name, serverCmd.Flags().Lookup(f.name))
|
|
}
|
|
|
|
RootCmd.AddCommand(serverCmd)
|
|
}
|
|
|
|
func validate(config server.ServerConfig) error {
|
|
logLevel := config.LogLevel
|
|
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)
|
|
}
|
|
if config.GitHubPassword == "" {
|
|
return fmt.Errorf("%s must be set", ghPasswordFlag)
|
|
}
|
|
if config.LockingBackend != server.LockingFileBackend && config.LockingBackend != server.LockingDynamoDBBackend {
|
|
return fmt.Errorf("unsupported locking backend %q: not one of %q or %q", config.LockingBackend, server.LockingFileBackend, server.LockingDynamoDBBackend)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setAtlantisURL(config *server.ServerConfig) error {
|
|
if config.AtlantisURL == "" {
|
|
hostname, err := os.Hostname()
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to determine hostname: %v", err)
|
|
}
|
|
config.AtlantisURL = fmt.Sprintf("http://%s:%d", hostname, config.Port)
|
|
}
|
|
return nil
|
|
}
|