forked from hootsuite/atlantis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
221 lines (202 loc) · 6.04 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package cmd
import (
"fmt"
"os"
"strings"
"github.com/hootsuite/atlantis/server"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// To add a new flag you must
// 1. Add a const with the flag name (in alphabetic order)
// 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 (
atlantisURLFlag = "atlantis-url"
awsAssumeRoleFlag = "aws-assume-role-arn"
awsRegionFlag = "aws-region"
configFlag = "config"
dataDirFlag = "data-dir"
ghHostnameFlag = "gh-hostname"
ghTokenFlag = "gh-token"
ghUserFlag = "gh-user"
logLevelFlag = "log-level"
portFlag = "port"
requireApprovalFlag = "require-approval"
)
var stringFlags = []stringFlag{
{
name: atlantisURLFlag,
description: "URL that Atlantis can be reached at. Defaults to http://$(hostname):$port where $port is from --" + portFlag + ".",
},
{
name: awsAssumeRoleFlag,
description: "ARN of the role to assume when running Terraform against AWS. If not using assume role, no need to set.",
},
{
name: awsRegionFlag,
description: "Amazon region to use for assume role. If not setting --" + awsAssumeRoleFlag + " then ignore.",
value: "us-east-1",
},
{
name: configFlag,
description: "Path to config file.",
},
{
name: dataDirFlag,
description: "Path to directory to store Atlantis data.",
value: "~/.atlantis",
},
{
name: ghHostnameFlag,
description: "Hostname of your Github Enterprise installation. If using github.com, no need to set.",
value: "github.com",
},
{
name: ghTokenFlag,
description: "[REQUIRED] 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: logLevelFlag,
description: "Log level. Either debug, info, warn, or error.",
value: "info",
},
}
var boolFlags = []boolFlag{
{
name: requireApprovalFlag,
description: "Require pull requests to be \"Approved\" before allowing the apply command to be run.",
value: false,
},
}
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 file values are overridden by environment variables which in turn are overridden by flags.`,
SilenceErrors: true,
PreRunE: withErrPrint(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 errors.Wrapf(err, "invalid config: reading %s", configFile)
}
}
return nil
}),
RunE: withErrPrint(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
}
sanitizeGithubUser(&config)
// config looks good, start the server
server, err := server.NewServer(config)
if err != nil {
return errors.Wrap(err, "initializing server")
}
return server.Start()
}),
}
func init() {
// if a user passes in an invalid flag, tell them what the flag was
serverCmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error {
fmt.Fprintf(os.Stderr, "\033[31mError: %s\033[39m\n\n", err.Error())
return err
})
// 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.GithubToken == "" {
return fmt.Errorf("--%s must be set", ghTokenFlag)
}
return nil
}
// setAtlantisURL sets the externally accessible URL for atlantis.
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
}
// sanitizeGithubUser trims @ from the front of the github username if it exists.
func sanitizeGithubUser(config *server.ServerConfig) {
config.GithubUser = strings.TrimPrefix(config.GithubUser, "@")
}
// withErrPrint prints out any errors to a terminal in red.
func withErrPrint(f func(*cobra.Command, []string) error) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
if err := f(cmd, args); err != nil {
fmt.Fprintf(os.Stderr, "\033[31mError: %s\033[39m\n\n", err.Error())
return err
}
return nil
}
}