Make bootstrap re-runnable. README edits

This commit is contained in:
Luke Kysow
2017-08-01 22:37:20 -07:00
parent 6281704560
commit 433f8875c3
9 changed files with 93 additions and 66 deletions

View File

@@ -33,6 +33,9 @@ test-coverage:
dist: ## Package up everything in static/ using go-bindata-assetfs so it can be served by a single binary
go-bindata-assetfs -pkg server static/... && mv bindata_assetfs.go server
release: ## Create packages for a release
gox -os="darwin linux" -arch="amd64"
vendor-status:
@govendor status

View File

@@ -1,14 +1,24 @@
# atlantis
![atlantis-logo](./docs/atlantis-logo.png)
A unified workflow for collaborating on Terraform through GitHub.
[![CircleCI](https://circleci.com/gh/hootsuite/atlantis/tree/master.svg?style=shield&circle-token=08bf5b34233b0e168a9dd73e01cafdcf7dc4bf16)](https://circleci.com/gh/hootsuite/atlantis/tree/master)
A unified workflow for collaborating on Terraform through GitHub.
* [Features](#features)
* [Getting Started](#getting-started)
* [Production-Ready Deployment](#production-ready-deployment)
* [Configuration](#configuration)
* [AWS Credentials](#aws-credentials)
* [Environments](#environments)
* [Locking](#locking)
* [Glossary](#glossary)
* [Project](#project)
* [Environment](#environment)
## Features
➜ Collaborate on Terraform with your team
- Run terraform `plan` and `apply` **from GitHub pull requests** so everyone can review the output
![atlantis plan](https://github.com/hootsuite/atlantis/raw/master/docs/atlantis-plan.gif)
- **Lock environments** until pull requests are merged to prevent concurrent modification and confusion
➜ Developers can write Terraform safely
@@ -19,22 +29,6 @@ A unified workflow for collaborating on Terraform through GitHub.
- 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](#getting-started)
* [Production-Ready Deployment](#production-ready-deployment)
* [Install Terraform](#install-terraform)
* [Hosting Atlantis](#hosting-atlantis)
* [Add GitHub Webhook](#add-github-webhook)
* [Create a GitHub Token](#create-a-github-token)
* [Start Atlantis](#start-atlantis)
* [Testing Out Atlantis](#testing-out-atlantis)
* [Configuration](#configuration)
* [AWS Credentials](#aws-credentials)
* [Assume Role Session Names](#assume-role-session-names)
* [Environments](#environments)
* [Locking](#locking)
* [Glossary](#glossary)
* [Project](#project)
* [Environment](#environment)
## Getting Started
Download from https://github.com/hootsuite/atlantis/releases

View File

@@ -36,7 +36,7 @@ var pullRequestBody = "In this pull request we will learn how to use atlantis. T
"* Start by typing `atlantis help` in the comments.\n" +
"* Next, lets plan by typing `atlantis plan` in the comments. That will run a `terraform plan`.\n" +
"* Now lets apply that plan. Type `atlantis apply` in the comments. This will run a `terraform apply`.\n" +
"\nThank you for using atlantis. For more info on running atlantis in production please follow: https://atlantis.run/link/to/doc"
"\nThank you for trying out atlantis. For more info on running atlantis in production see https://github.com/hootsuite/atlantis"
func Start() error {
s := spinner.New(spinner.CharSets[14], 100*time.Millisecond)
@@ -133,11 +133,16 @@ Follow these instructions to create a token (we don't store any tokens):
time.Sleep(2 * time.Second)
s.Stop()
colorstring.Println("\n[green]=> started tunnel!")
tunnelURL, err := getTunnelAddr()
if err != nil {
return errors.Wrapf(err, "getting tunnel url")
}
s.Stop()
// start atlantis server
colorstring.Printf("[white]=> starting atlantis server ")
s.Start()
atlantisCmd, err := executeCmd("./atlantis", []string{"server", "--gh-user", githubUsername, "--gh-token", githubToken, "--data-dir", "/tmp/atlantis/data"})
atlantisCmd, err := executeCmd(os.Args[0], []string{"server", "--gh-user", githubUsername, "--gh-token", githubToken, "--data-dir", "/tmp/atlantis/data", "--atlantis-url", tunnelURL})
if err != nil {
return errors.Wrapf(err, "creating atlantis server")
}
@@ -148,12 +153,6 @@ Follow these instructions to create a token (we don't store any tokens):
}()
// if this function returns atlantis server should be stopped
defer atlantisCmd.Process.Kill()
tunnelURL, err := getTunnelAddr()
if err != nil {
return errors.Wrapf(err, "getting tunnel url")
}
s.Stop()
colorstring.Printf("\n[green]=> atlantis server is now securely exposed at [bold][underline]%s", tunnelURL)
fmt.Println("")

View File

@@ -8,7 +8,6 @@ import (
"github.com/google/go-github/github"
)
var githubHostname = "http://api.github.com"
var githubUsername string
var githubToken string
@@ -61,8 +60,22 @@ func (g *Client) CreateWebhook(ownerName string, repoName string, hookURL string
return nil
}
// CreatePullRequest creates a github pull request with custom title and description
// CreatePullRequest creates a github pull request with custom title and description.
// It first checks if there's already a pull request open for this branch
func (g *Client) CreatePullRequest(ownerName string, repoName string, head string, base string) (string, error) {
// first check if the pull request already exists
pulls, _, err := g.client.PullRequests.List(g.ctx, ownerName, repoName, nil)
if err != nil {
return "", err
}
for _, pull := range pulls {
if pull.Head.GetRef() == head && pull.Base.GetRef() == base {
return pull.GetHTMLURL(), nil
}
}
// if not, create it
newPullRequest := &github.NewPullRequest{
Title: github.String("Welcome to Atlantis!"),
Head: github.String(head),

View File

@@ -33,7 +33,7 @@ const (
var stringFlags = []stringFlag{
{
name: atlantisURLFlag,
description: "Url that Atlantis can be reached at. Defaults to http://$(hostname):$port where $port comes from the port flag.",
description: "Url that Atlantis can be reached at. Defaults to http://$(hostname):$port where $port is from --" + portFlag + ".",
},
{
name: awsAssumeRoleFlag,
@@ -41,7 +41,7 @@ var stringFlags = []stringFlag{
},
{
name: awsRegionFlag,
description: "The Amazon region to connect to for API actions.",
description: "Amazon region to use for assume role. If not setting --" + awsAssumeRoleFlag + " then ignore.",
value: "us-east-1",
},
{

View File

@@ -125,22 +125,28 @@ func (a *ApplyExecutor) apply(ctx *CommandContext, repoDir string, plan models.P
applyExtraArgs = config.GetExtraArguments(ctx.Command.Name.String())
}
awsSession, err := a.awsConfig.CreateSession(ctx.User.Username)
if err != nil {
return ProjectResult{Error: err}
}
creds, err := awsSession.Config.Credentials.Get()
if err != nil {
err = errors.Wrap(err, "getting aws credentials")
ctx.Log.Err(err.Error())
return ProjectResult{Error: err}
}
ctx.Log.Info("created aws session")
// todo: de-duplicate this section between plan and apply
var credsEnvVars []string
// If awsConfig is nil we know that we're not using assume role and so
// don't need to do an AWS calls ourselves
if a.awsConfig != nil {
awsSession, err := a.awsConfig.CreateSession(ctx.User.Username)
if err != nil {
return ProjectResult{Error: err}
}
creds, err := awsSession.Config.Credentials.Get()
if err != nil {
err = errors.Wrap(err, "getting aws credentials")
ctx.Log.Err(err.Error())
return ProjectResult{Error: err}
}
ctx.Log.Info("created aws session")
credsEnvVars := []string{
fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", creds.AccessKeyID),
fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", creds.SecretAccessKey),
fmt.Sprintf("AWS_SESSION_TOKEN=%s", creds.SessionToken),
credsEnvVars = []string{
fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", creds.AccessKeyID),
fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", creds.SecretAccessKey),
fmt.Sprintf("AWS_SESSION_TOKEN=%s", creds.SessionToken),
}
}
// check if terraform version is >= 0.9.0

View File

@@ -80,10 +80,10 @@ func (g *GithubCommentRenderer) renderProjectResults(pathResults []ProjectResult
if result.Error != nil {
results[result.Path] = g.renderTemplate(errTmpl, struct {
Command string
Error string
Error string
}{
Command: common.Command,
Error: result.Error.Error(),
Error: result.Error.Error(),
})
} else if result.Failure != "" {
results[result.Path] = g.renderTemplate(failureTmpl, struct {

View File

@@ -119,23 +119,29 @@ func (p *PlanExecutor) plan(ctx *CommandContext, repoDir string, project models.
planExtraArgs = config.GetExtraArguments(ctx.Command.Name.String())
}
awsSession, err := p.awsConfig.CreateSession(ctx.User.Username)
if err != nil {
ctx.Log.Err(err.Error())
return ProjectResult{Error: err}
}
creds, err := awsSession.Config.Credentials.Get()
if err != nil {
err = errors.Wrap(err, "getting aws credentials")
ctx.Log.Err(err.Error())
return ProjectResult{Error: err}
}
ctx.Log.Info("created aws session")
// todo: de-duplicate this section between plan and apply
var credsEnvVars []string
// If awsConfig is nil we know that we're not using assume role and so
// don't need to do an AWS calls ourselves
if p.awsConfig != nil {
awsSession, err := p.awsConfig.CreateSession(ctx.User.Username)
if err != nil {
ctx.Log.Err(err.Error())
return ProjectResult{Error: err}
}
creds, err := awsSession.Config.Credentials.Get()
if err != nil {
err = errors.Wrap(err, "getting aws credentials")
ctx.Log.Err(err.Error())
return ProjectResult{Error: err}
}
ctx.Log.Info("created aws session")
credsEnvVars := []string{
fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", creds.AccessKeyID),
fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", creds.SecretAccessKey),
fmt.Sprintf("AWS_SESSION_TOKEN=%s", creds.SessionToken),
credsEnvVars = []string{
fmt.Sprintf("AWS_ACCESS_KEY_ID=%s", creds.AccessKeyID),
fmt.Sprintf("AWS_SECRET_ACCESS_KEY=%s", creds.SecretAccessKey),
fmt.Sprintf("AWS_SESSION_TOKEN=%s", creds.SessionToken),
}
}
// check if terraform version is >= 0.9.0

View File

@@ -87,9 +87,15 @@ func NewServer(config ServerConfig) (*Server, error) {
return nil, errors.Wrap(err, "initializing terraform")
}
githubComments := &GithubCommentRenderer{}
awsConfig := &aws.Config{
Region: config.AWSRegion,
RoleARN: config.AssumeRole,
// a nil awsConfig indicates that we won't be doing any AWS
// config in Atlantis
var awsConfig *aws.Config
if config.AssumeRole != "" {
awsConfig = &aws.Config{
Region: config.AWSRegion,
RoleARN: config.AssumeRole,
}
}
boltdb, err := boltdb.New(config.DataDir)