Add Getting Started instructions. Rename gh-password to gh-token

This commit is contained in:
Luke Kysow
2017-07-27 13:51:48 -07:00
parent 4b3aaa2679
commit e728d2548b
7 changed files with 125 additions and 24 deletions

109
README.md
View File

@@ -4,13 +4,108 @@
A unified workflow for collaborating on Terraform through GitHub.
## Features
-
➜ Collaborate on Terraform with your team
- Run terraform `plan` and `apply` **from GitHub pull requests** so everyone can review the output
- todo: gif
- **Lock environments** until pull requests are merged to prevent concurrent modification and confusion
➜ Developers can write Terraform safely
- **No need to distribute AWS credentials** to your whole team! Developers can submit Terraform changes and run `plan` and `apply` directly from the pull request
- Optionally, require a **review and approval** prior to running `apply`
➜ Also
- No more **copy-pasted code across environments**. Atlantis supports using an `env/{env}.tfvars` file per environment so you can write your base configuration once
- Support **multiple versions of Terraform** with a simple project config file
## Getting Started
// todo: atlantis bootstrap workflow
Atlantis runs as a server that receives GitHub webhooks. Once it's running and hooked up with GitHub, you can interact with it directly through GitHub comments.
### First Download Atlantis
Download from https://github.com/hootsuite/atlantis/releases
### Start with `atlantis bootstrap` (recommended)
Run `atlantis bootstrap` to get started quickly with Atlantis.
If you want to manually run through all the steps that `bootstrap` performs, keep reading.
### Start Manually
To manually get started with Atlantis, you'll need to
- install `terraform` into your `$PATH`
- download from https://www.terraform.io/downloads.html
- `unzip path/to/terraform_*.zip -d /usr/local/bin`
- check that it's installed by running `terraform version`
- Atlantis needs to be reachable on an IP address or hostname that github.com can access. By default, Atlantis runs on port `4141` (this can be changed with the `--port` flag). You can install `ngrok` to make exposing Atlantis easy for testing purposes
- download from https://ngrok.com/download
- `unzip path/to/ngrok*.zip -d /usr/local/bin`
- start ngrok with `ngrok http 4141`
- Create a GitHub personal access token for Atlantis to use GitHub's API
- follow [https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/#creating-a-token)
- copy the access token to your clipboard
- now you're ready to start Atlantis! Run:
```
$ atlantis server --atlantis-url $URL --gh-username $USERNAME --gh-token $TOKEN
2049/10/6 00:00:00 [WARN] server: Atlantis started - listening on port 4141
```
- where `$URL` is the URL that Atlantis can be reached at. If using `ngrok` it will be something like `https://68da2fdd.ngrok.io`
- where `$USERNAME` is your GitHub username
- where `$TOKEN` is the access token you created
Now that Atlantis is running, it's time to test it out. You'll need to set up a pull request first
- Fork https://github.com/hootsuite/atlantis-example to your user
- Add Atlantis as a webhook to the forked repo
- navigate to `{your-repo-url}/settings/hooks/new`, ex. https://github.com/hootsuite/atlantis-example/settings/hooks/new
- set **Payload URL** to `$URL/events` where `$URL` is what you used above, ex. `https://68da2fdd.ngrok.io/events`. **Be sure to add `/events` to the end**
- set **Content type** to `application/json`
- leave **Secret** blank
- select **Let me select individual events**
- check the boxes
- **Pull request review**
- **Push**
- **Issue comment**
- **Pull request**
- leave **Active** checked
- click **Add webhook**
- Now that Atlantis can receive events you should be able to comment on a pull request to trigger Atlantis. Let's create a pull request
- Navigate to `{your-repo-url}/branches`, ex. https://github.com/hootsuite/atlantis-example/branches
- click the **New pull request** button next to the `example` branch
- Change the `base` to `{your-repo}/master`
- click **Create pull request**
- Finally we're ready to talk to Atlantis!
- Create a comment `atlantis help` to see what commands you can run from the pull request
- `atlantis plan` will run `terraform plan` behind the scenes. You should see the output commented back on the pull request. You should also see some logs show up where you're running `atlantis server`
- You could also type `atlantis apply` but since you may not have AWS credentials set up this probably won't work TODO: VERIFY THIS
You're done! If you're ready to set up Atlantis for a production deployment, see [Production-Ready Deployment](#Production-Ready+Deployment)
## Production-Ready Deployment
## Configuration
Atlantis configuration can be specified via cli flags or a yaml config file.
Atlantis configuration can be specified via command line flags or a YAML config file.
```
$ atlantis server --help
...
Usage:
atlantis server [flags]
Flags:
--atlantis-url string Url that Atlantis can be reached at. Defaults to http://$(hostname):$port where $port comes from the port flag.
--aws-assume-role-arn string ARN of the role to assume when running Terraform against AWS. If not using assume role, no need to set.
--aws-region string The Amazon region to connect to for API actions. (default "us-east-1")
--config string Path to config file.
--data-dir string Path to directory to store Atlantis data. (default "~/.atlantis")
--gh-hostname string Hostname of your Github Enterprise installation. If using github.com, no need to set. (default "github.com")
--gh-password string [REQUIRED] GitHub password of API user. Can also be specified via the ATLANTIS_GH_PASSWORD environment variable.
--gh-user string [REQUIRED] GitHub username of API user.
-h, --help help for server
--log-level string Log level. Either debug, info, warn, or error. (default "warn")
--port int Port to bind to. (default 4141)
--require-approval Require pull requests to be "Approved" before allowing the apply command to be run. (default true)
```
The `gh-password` flag can also be specified via an `ATLANTIS_GH_PASSWORD` environment variable.
Config file values are overridden by environment variables which in turn are overridden by flags.
@@ -35,7 +130,10 @@ to assume a role (specified by the `--aws-assume-role-arn` flag) and **dynamical
name the session** with the GitHub username of whoever is running `atlantis apply`.
To take advantage of this feature, simply set the `--aws-assume-role-arn` flag.
## Project Locking
## Environments
## Locking
When `plan` is run, the project and environment are Locked until an `apply` succeeds and the pull request is merged.
This protects against concurrent modifications to the same set of infrastructure and prevents
users from seeing a `plan` that will be invalid if another pull request is merged.
@@ -43,9 +141,12 @@ users from seeing a `plan` that will be invalid if another pull request is merge
To unlock the project and environment without completing an `apply`, click the link
at the bottom of each plan to discard the plan and delete the lock.
## `atlantis.yaml` Config File
## Glossary
#### Project
A Terraform project. Multiple projects can be in a single GitHub repo.
#### Environment
A Terraform environment. See [terraform docs](https://www.terraform.io/docs/state/environments.html) for more information.

View File

@@ -56,14 +56,14 @@ Follow these instructions to create a token (we don't store any tokens):
- add "repo" scope
- copy the access token
`)
// read github password, check for error later
// read github token, check for error later
colorstring.Print("[white][bold]GitHub access token (will be hidden): ")
githubPassword, _ = readPassword()
githubToken, _ = readPassword()
// create github client
tp := github.BasicAuthTransport{
Username: strings.TrimSpace(githubUsername),
Password: strings.TrimSpace(githubPassword),
Password: strings.TrimSpace(githubToken),
}
githubClient := &Client{client: github.NewClient(tp.Client()), ctx: context.Background()}
@@ -137,7 +137,7 @@ Follow these instructions to create a token (we don't store any tokens):
// start atlantis server
colorstring.Printf("[white]=> starting atlantis server ")
s.Start()
atlantisCmd, err := executeCmd("./atlantis", []string{"server", "--gh-user", githubUsername, "--gh-password", githubPassword, "--data-dir", "/tmp/atlantis/data"})
atlantisCmd, err := executeCmd("./atlantis", []string{"server", "--gh-user", githubUsername, "--gh-token", githubToken, "--data-dir", "/tmp/atlantis/data"})
if err != nil {
return errors.Wrapf(err, "creating atlantis server")
}

View File

@@ -10,7 +10,7 @@ import (
var githubHostname = "http://api.github.com"
var githubUsername string
var githubPassword string
var githubToken string
// Client used for github interactions
type Client struct {

View File

@@ -26,7 +26,7 @@ test:
# Run e2e tests
- cd "${WORKDIR}" && ./scripts/e2e-deps.sh
# Start atlantis server
- cd "${WORKDIR}/e2e" && ./atlantis server --gh-user="$GITHUB_USERNAME" --gh-password="$GITHUB_PASSWORD" --data-dir="/tmp" --require-approval=false --log-level="debug" &> /tmp/atlantis-server.log:
- cd "${WORKDIR}/e2e" && ./atlantis server --gh-user="$GITHUB_USERNAME" --gh-token="$GITHUB_TOKEN" --data-dir="/tmp" --require-approval=false --log-level="debug" &> /tmp/atlantis-server.log:
background: true
- sleep 2
- cd "${WORKDIR}/e2e" && ./ngrok http 4141:

View File

@@ -23,7 +23,7 @@ const (
configFlag = "config"
dataDirFlag = "data-dir"
ghHostnameFlag = "gh-hostname"
ghPasswordFlag = "gh-password"
ghTokenFlag = "gh-token"
ghUserFlag = "gh-user"
logLevelFlag = "log-level"
portFlag = "port"
@@ -59,9 +59,9 @@ var stringFlags = []stringFlag{
value: "github.com",
},
{
name: ghPasswordFlag,
description: "[REQUIRED] GitHub password of API user. Can also be specified via the ATLANTIS_GH_PASSWORD environment variable.",
env: "ATLANTIS_GH_PASSWORD",
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,
@@ -70,7 +70,7 @@ var stringFlags = []stringFlag{
{
name: logLevelFlag,
description: "Log level. Either debug, info, warn, or error.",
value: "warn",
value: "info",
},
}
var boolFlags = []boolFlag{
@@ -186,8 +186,8 @@ func validate(config server.ServerConfig) 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.GithubToken == "" {
return fmt.Errorf("--%s must be set", ghTokenFlag)
}
return nil
}

View File

@@ -30,9 +30,9 @@ func main() {
if githubUsername == "" {
log.Fatalf("GITHUB_USERNAME cannot be empty")
}
githubPassword := os.Getenv("GITHUB_PASSWORD")
if githubPassword == "" {
log.Fatalf("GITHUB_PASSWORD cannot be empty")
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken == "" {
log.Fatalf("GITHUB_TOKEN cannot be empty")
}
atlantisURL := os.Getenv("ATLANTIS_URL")
if atlantisURL == "" {
@@ -49,7 +49,7 @@ func main() {
repoName = "atlantis-tests"
}
// using https to clone the repo
repoUrl := fmt.Sprintf("https://%s:%s@github.com/%s/%s.git", githubUsername, githubPassword, ownerName, repoName)
repoUrl := fmt.Sprintf("https://%s:%s@github.com/%s/%s.git", githubUsername, githubToken, ownerName, repoName)
cloneDirRoot := os.Getenv("CLONE_DIR")
if cloneDirRoot == "" {
cloneDirRoot = "/tmp/atlantis-tests"
@@ -65,7 +65,7 @@ func main() {
// create github client
tp := github.BasicAuthTransport{
Username: strings.TrimSpace(githubUsername),
Password: strings.TrimSpace(githubPassword),
Password: strings.TrimSpace(githubToken),
}
ghClient := github.NewClient(tp.Client())

View File

@@ -50,7 +50,7 @@ type ServerConfig struct {
AtlantisURL string `mapstructure:"atlantis-url"`
DataDir string `mapstructure:"data-dir"`
GithubHostname string `mapstructure:"gh-hostname"`
GithubPassword string `mapstructure:"gh-password"`
GithubToken string `mapstructure:"gh-token"`
GithubUser string `mapstructure:"gh-user"`
LogLevel string `mapstructure:"log-level"`
Port int `mapstructure:"port"`
@@ -77,7 +77,7 @@ func NewServer(config ServerConfig) (*Server, error) {
config.DataDir = expanded
}
githubClient, err := github.NewClient(config.GithubHostname, config.GithubUser, config.GithubPassword)
githubClient, err := github.NewClient(config.GithubHostname, config.GithubUser, config.GithubToken)
if err != nil {
return nil, err
}