Browse Source

Initial Working 0.5 version

pull/26/head
Jacob McCann 8 years ago
parent
commit
0208bfdc19
  1. 4
      DOCS.md
  2. 2
      Dockerfile
  3. 283
      main.go
  4. 190
      plugin.go

4
DOCS.md

@ -6,6 +6,8 @@ Use the Terraform plugin to apply the infrastructure configuration contained wit
* `config` - a map of configuration parameters for the remote state backend. Each value is passed as a `-backend-config=<key>=<value>` option. * `config` - a map of configuration parameters for the remote state backend. Each value is passed as a `-backend-config=<key>=<value>` option.
* `vars` - a map of variables to pass to the Terraform `plan` and `apply` commands. Each value is passed as a `-var * `vars` - a map of variables to pass to the Terraform `plan` and `apply` commands. Each value is passed as a `-var
<key>=<value>` option. <key>=<value>` option.
* `secrets` - a map of variables to pass to the Terraform `plan` and `apply` commands. Each value is passed as a `-var
<key>=<ENVVAR>` option. The `ENVVAR` is read as the key/pair value.
* `ca_cert` - ca cert to add to your environment to allow terraform to use internal/private resources * `ca_cert` - ca cert to add to your environment to allow terraform to use internal/private resources
* `sensitive` (default: `false`) - Whether or not to suppress terraform commands to stdout. * `sensitive` (default: `false`) - Whether or not to suppress terraform commands to stdout.
* `role_arn_to_assume` - A role to assume before running the terraform commands. * `role_arn_to_assume` - A role to assume before running the terraform commands.
@ -27,6 +29,8 @@ deploy:
vars: vars:
app_name: my-project app_name: my-project
app_version: 1.0.0 app_version: 1.0.0
secrets:
my_secret: TERRAFORM_SECRET
``` ```
# Advanced Configuration # Advanced Configuration

2
Dockerfile

@ -6,7 +6,7 @@
FROM gliderlabs/alpine:3.2 FROM gliderlabs/alpine:3.2
RUN apk-install ca-certificates git RUN apk-install ca-certificates git
ENV TERRAFORM_VERSION 0.6.16 ENV TERRAFORM_VERSION 0.7.5
RUN apk update && \ RUN apk update && \
wget -q "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.21-r2/glibc-2.21-r2.apk" && \ wget -q "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.21-r2/glibc-2.21-r2.apk" && \

283
main.go

@ -1,188 +1,115 @@
package main package main
import ( import (
"fmt" "encoding/json"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/drone/drone-plugin-go/plugin"
"io/ioutil"
"os" "os"
"os/exec"
"strings"
"time"
)
var ( "github.com/joho/godotenv"
buildCommit string "github.com/Sirupsen/logrus"
"github.com/urfave/cli"
) )
type terraform struct { var version string // build number set at compile-time
Remote remote `json:"remote"`
Plan bool `json:"plan"`
Vars map[string]string `json:"vars"`
Cacert string `json:"ca_cert"`
Sensitive bool `json:"sensitive"`
RoleARN string `json:"role_arn_to_assume"`
RootDir string `json:"root_dir"`
Parallelism int `json:"parallelism"`
}
type remote struct {
Backend string `json:"backend"`
Config map[string]string `json:"config"`
}
func main() { func main() {
fmt.Printf("Drone Terraform Plugin built from %s\n", buildCommit) app := cli.NewApp()
app.Name = "terraform plugin"
workspace := plugin.Workspace{} app.Usage = "terraform plugin"
vargs := terraform{} app.Action = run
app.Version = version
plugin.Param("workspace", &workspace) app.Flags = []cli.Flag{
plugin.Param("vargs", &vargs)
plugin.MustParse() //
// plugin args
if vargs.RoleARN != "" { //
assumeRole(vargs.RoleARN)
} cli.BoolFlag{
Name: "terraform.plan",
var commands []*exec.Cmd Usage: "calculates a plan but does NOT apply it",
remote := vargs.Remote EnvVar: "PLUGIN_PLAN",
if vargs.Cacert != "" { },
commands = append(commands, installCaCert(vargs.Cacert)) cli.StringFlag{
} Name: "terraform.remote",
if remote.Backend != "" { Usage: "contains the configuration for the Terraform remote state tracking",
commands = append(commands, deleteCache()) EnvVar: "PLUGIN_REMOTE",
commands = append(commands, remoteConfigCommand(remote)) },
} cli.StringFlag{
commands = append(commands, getModules()) Name: "terraform.vars",
commands = append(commands, planCommand(vargs.Vars, vargs.Parallelism)) Usage: "a map of variables to pass to the Terraform `plan` and `apply` commands. Each value is passed as a `<key>=<value>` option",
if !vargs.Plan { EnvVar: "PLUGIN_VARS",
commands = append(commands, applyCommand(vargs.Parallelism)) },
} cli.StringFlag{
commands = append(commands, deleteCache()) Name: "terraform.secrets",
Usage: "a map of secrets to pass to the Terraform `plan` and `apply` commands. Each value is passed as a `<key>=<ENV>` option",
for _, c := range commands { EnvVar: "PLUGIN_SECRETS",
c.Env = os.Environ() },
c.Dir = workspace.Path cli.StringFlag{
if c.Dir == "" { Name: "terraform.ca_cert",
wd, err := os.Getwd() Usage: "ca cert to add to your environment to allow terraform to use internal/private resources",
if err == nil { EnvVar: "PLUGIN_CA_CERT",
c.Dir = wd },
} cli.BoolFlag{
} Name: "terraform.sensitive",
if vargs.RootDir != "" { Usage: "whether or not to suppress terraform commands to stdout",
c.Dir = c.Dir + "/" + vargs.RootDir EnvVar: "PLUGIN_SENSITIVE",
} },
c.Stdout = os.Stdout cli.StringFlag{
c.Stderr = os.Stderr Name: "terraform.role_arn_to_assume",
if !vargs.Sensitive { Usage: "A role to assume before running the terraform commands",
trace(c) EnvVar: "PLUGIN_ROLE_ARN_TO_ASSUME",
} },
cli.StringFlag{
err := c.Run() Name: "terraform.root_dir",
if err != nil { Usage: "The root directory where the terraform files live. When unset, the top level directory will be assumed",
fmt.Println("Error!") EnvVar: "PLUGIN_ROOT_DIR",
fmt.Println(err) },
os.Exit(1) cli.IntFlag{
} Name: "terraform.parallelism",
fmt.Println("Command completed successfully") Usage: "The number of concurrent operations as Terraform walks its graph",
} EnvVar: "PLUGIN_PARALLELISM",
},
}
cli.StringFlag{
func installCaCert(cacert string) *exec.Cmd { Name: "env-file",
ioutil.WriteFile("/usr/local/share/ca-certificates/ca_cert.crt", []byte(cacert), 0644) Usage: "source env file",
return exec.Command( },
"update-ca-certificates", }
)
} if err := app.Run(os.Args); err != nil {
logrus.Fatal(err)
func deleteCache() *exec.Cmd { }
return exec.Command( }
"rm",
"-rf", func run(c *cli.Context) error {
".terraform", if c.String("env-file") != "" {
) _ = godotenv.Load(c.String("env-file"))
} }
func remoteConfigCommand(config remote) *exec.Cmd { remote := Remote{}
args := []string{ json.Unmarshal([]byte(c.String("terraform.remote")), &remote)
"remote",
"config", var vars map[string]string
fmt.Sprintf("-backend=%s", config.Backend), if err := json.Unmarshal([]byte(c.String("terraform.vars")), &vars); err != nil {
} panic(err)
for k, v := range config.Config { }
args = append(args, fmt.Sprintf("-backend-config=%s=%s", k, v)) var secrets map[string]string
} if err := json.Unmarshal([]byte(c.String("terraform.secrets")), &secrets); err != nil {
return exec.Command( panic(err)
"terraform", }
args...,
) plugin := Plugin{
} Config: Config{
Remote: remote,
func getModules() *exec.Cmd { Plan: c.Bool("terraform.plan"),
return exec.Command( Vars: vars,
"terraform", Secrets: secrets,
"get", Cacert: c.String("terraform.ca_cert"),
) Sensitive: c.Bool("terraform.sensitive"),
} RoleARN: c.String("terraform.role_arn_to_assume"),
RootDir: c.String("terraform.root_dir"),
func planCommand(variables map[string]string, parallelism int) *exec.Cmd { Parallelism: c.Int("terraform.parallelism"),
args := []string{ },
"plan", }
"-out=plan.tfout",
} return plugin.Exec()
for k, v := range variables {
args = append(args, "-var")
args = append(args, fmt.Sprintf("%s=%s", k, v))
}
if parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", parallelism))
}
return exec.Command(
"terraform",
args...,
)
}
func applyCommand(parallelism int) *exec.Cmd {
args := []string{
"apply",
}
if parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", parallelism))
}
args = append(args, "plan.tfout")
return exec.Command(
"terraform",
args...,
)
}
func assumeRole(roleArn string) {
client := sts.New(session.New())
duration := time.Hour * 1
stsProvider := &stscreds.AssumeRoleProvider{
Client: client,
Duration: duration,
RoleARN: roleArn,
RoleSessionName: "drone",
}
value, err := credentials.NewCredentials(stsProvider).Get()
if err != nil {
fmt.Println("Error assuming role!")
fmt.Println(err)
os.Exit(1)
}
os.Setenv("AWS_ACCESS_KEY_ID", value.AccessKeyID)
os.Setenv("AWS_SECRET_ACCESS_KEY", value.SecretAccessKey)
os.Setenv("AWS_SESSION_TOKEN", value.SessionToken)
}
func trace(cmd *exec.Cmd) {
fmt.Println("$", strings.Join(cmd.Args, " "))
} }

190
plugin.go

@ -0,0 +1,190 @@
package main
import (
"fmt"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"io/ioutil"
"os"
"os/exec"
"strings"
"time"
)
var (
buildCommit string
)
type (
Config struct {
Remote Remote
Plan bool
Vars map[string]string
Secrets map[string]string
Cacert string
Sensitive bool
RoleARN string
RootDir string
Parallelism int
}
Remote struct {
Backend string `json:"backend"`
Config map[string]string `json:"config"`
}
Plugin struct {
Config Config
}
)
func (p Plugin) Exec() error {
fmt.Printf("Drone Terraform Plugin built from %s\n", buildCommit)
if p.Config.RoleARN != "" {
assumeRole(p.Config.RoleARN)
}
var commands []*exec.Cmd
remote := p.Config.Remote
if p.Config.Cacert != "" {
commands = append(commands, installCaCert(p.Config.Cacert))
}
if remote.Backend != "" {
commands = append(commands, deleteCache())
commands = append(commands, remoteConfigCommand(remote))
}
commands = append(commands, getModules())
commands = append(commands, planCommand(p.Config.Vars, p.Config.Secrets, p.Config.Parallelism))
if !p.Config.Plan {
commands = append(commands, applyCommand(p.Config.Parallelism))
}
commands = append(commands, deleteCache())
for _, c := range commands {
if c.Dir == "" {
wd, err := os.Getwd()
if err == nil {
c.Dir = wd
}
}
if p.Config.RootDir != "" {
c.Dir = c.Dir + "/" + p.Config.RootDir
}
c.Stdout = os.Stdout
c.Stderr = os.Stderr
if !p.Config.Sensitive {
trace(c)
}
err := c.Run()
if err != nil {
fmt.Println("Error!")
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Command completed successfully")
}
return nil
}
func installCaCert(cacert string) *exec.Cmd {
ioutil.WriteFile("/usr/local/share/ca-certificates/ca_cert.crt", []byte(cacert), 0644)
return exec.Command(
"update-ca-certificates",
)
}
func deleteCache() *exec.Cmd {
return exec.Command(
"rm",
"-rf",
".terraform",
)
}
func remoteConfigCommand(config Remote) *exec.Cmd {
args := []string{
"remote",
"config",
fmt.Sprintf("-backend=%s", config.Backend),
}
for k, v := range config.Config {
args = append(args, fmt.Sprintf("-backend-config=%s=%s", k, v))
}
return exec.Command(
"terraform",
args...,
)
}
func getModules() *exec.Cmd {
return exec.Command(
"terraform",
"get",
)
}
func planCommand(variables map[string]string, secrets map[string]string, parallelism int) *exec.Cmd {
args := []string{
"plan",
"-out=plan.tfout",
}
for k, v := range variables {
args = append(args, "-var")
args = append(args, fmt.Sprintf("%s=%s", k, v))
}
for k, v := range secrets {
args = append(args, "-var")
args = append(args, fmt.Sprintf("%s=%s", k, os.Getenv(v)))
}
if parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", parallelism))
}
return exec.Command(
"terraform",
args...,
)
}
func applyCommand(parallelism int) *exec.Cmd {
args := []string{
"apply",
}
if parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", parallelism))
}
args = append(args, "plan.tfout")
return exec.Command(
"terraform",
args...,
)
}
func assumeRole(roleArn string) {
client := sts.New(session.New())
duration := time.Hour * 1
stsProvider := &stscreds.AssumeRoleProvider{
Client: client,
Duration: duration,
RoleARN: roleArn,
RoleSessionName: "drone",
}
value, err := credentials.NewCredentials(stsProvider).Get()
if err != nil {
fmt.Println("Error assuming role!")
fmt.Println(err)
os.Exit(1)
}
os.Setenv("AWS_ACCESS_KEY_ID", value.AccessKeyID)
os.Setenv("AWS_SECRET_ACCESS_KEY", value.SecretAccessKey)
os.Setenv("AWS_SESSION_TOKEN", value.SessionToken)
}
func trace(cmd *exec.Cmd) {
fmt.Println("$", strings.Join(cmd.Args, " "))
}
Loading…
Cancel
Save