mirror of
https://github.com/thegeeklab/wp-opentofu.git
synced 2024-11-22 10:40:39 +00:00
Initial Working 0.5 version
This commit is contained in:
parent
714d496249
commit
0208bfdc19
4
DOCS.md
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
|
||||||
|
@ -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" && \
|
||||||
|
257
main.go
257
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"
|
"github.com/joho/godotenv"
|
||||||
"time"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var version string // build number set at compile-time
|
||||||
buildCommit string
|
|
||||||
)
|
|
||||||
|
|
||||||
type terraform struct {
|
|
||||||
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"
|
||||||
|
app.Usage = "terraform plugin"
|
||||||
|
app.Action = run
|
||||||
|
app.Version = version
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
|
||||||
workspace := plugin.Workspace{}
|
//
|
||||||
vargs := terraform{}
|
// plugin args
|
||||||
|
//
|
||||||
|
|
||||||
plugin.Param("workspace", &workspace)
|
cli.BoolFlag{
|
||||||
plugin.Param("vargs", &vargs)
|
Name: "terraform.plan",
|
||||||
plugin.MustParse()
|
Usage: "calculates a plan but does NOT apply it",
|
||||||
|
EnvVar: "PLUGIN_PLAN",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "terraform.remote",
|
||||||
|
Usage: "contains the configuration for the Terraform remote state tracking",
|
||||||
|
EnvVar: "PLUGIN_REMOTE",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "terraform.vars",
|
||||||
|
Usage: "a map of variables to pass to the Terraform `plan` and `apply` commands. Each value is passed as a `<key>=<value>` option",
|
||||||
|
EnvVar: "PLUGIN_VARS",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
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",
|
||||||
|
EnvVar: "PLUGIN_SECRETS",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "terraform.ca_cert",
|
||||||
|
Usage: "ca cert to add to your environment to allow terraform to use internal/private resources",
|
||||||
|
EnvVar: "PLUGIN_CA_CERT",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "terraform.sensitive",
|
||||||
|
Usage: "whether or not to suppress terraform commands to stdout",
|
||||||
|
EnvVar: "PLUGIN_SENSITIVE",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "terraform.role_arn_to_assume",
|
||||||
|
Usage: "A role to assume before running the terraform commands",
|
||||||
|
EnvVar: "PLUGIN_ROLE_ARN_TO_ASSUME",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "terraform.root_dir",
|
||||||
|
Usage: "The root directory where the terraform files live. When unset, the top level directory will be assumed",
|
||||||
|
EnvVar: "PLUGIN_ROOT_DIR",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "terraform.parallelism",
|
||||||
|
Usage: "The number of concurrent operations as Terraform walks its graph",
|
||||||
|
EnvVar: "PLUGIN_PARALLELISM",
|
||||||
|
},
|
||||||
|
|
||||||
if vargs.RoleARN != "" {
|
cli.StringFlag{
|
||||||
assumeRole(vargs.RoleARN)
|
Name: "env-file",
|
||||||
|
Usage: "source env file",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var commands []*exec.Cmd
|
if err := app.Run(os.Args); err != nil {
|
||||||
remote := vargs.Remote
|
logrus.Fatal(err)
|
||||||
if vargs.Cacert != "" {
|
|
||||||
commands = append(commands, installCaCert(vargs.Cacert))
|
|
||||||
}
|
}
|
||||||
if remote.Backend != "" {
|
|
||||||
commands = append(commands, deleteCache())
|
|
||||||
commands = append(commands, remoteConfigCommand(remote))
|
|
||||||
}
|
|
||||||
commands = append(commands, getModules())
|
|
||||||
commands = append(commands, planCommand(vargs.Vars, vargs.Parallelism))
|
|
||||||
if !vargs.Plan {
|
|
||||||
commands = append(commands, applyCommand(vargs.Parallelism))
|
|
||||||
}
|
|
||||||
commands = append(commands, deleteCache())
|
|
||||||
|
|
||||||
for _, c := range commands {
|
|
||||||
c.Env = os.Environ()
|
|
||||||
c.Dir = workspace.Path
|
|
||||||
if c.Dir == "" {
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
if err == nil {
|
|
||||||
c.Dir = wd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if vargs.RootDir != "" {
|
|
||||||
c.Dir = c.Dir + "/" + vargs.RootDir
|
|
||||||
}
|
|
||||||
c.Stdout = os.Stdout
|
|
||||||
c.Stderr = os.Stderr
|
|
||||||
if !vargs.Sensitive {
|
|
||||||
trace(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.Run()
|
func run(c *cli.Context) error {
|
||||||
if err != nil {
|
if c.String("env-file") != "" {
|
||||||
fmt.Println("Error!")
|
_ = godotenv.Load(c.String("env-file"))
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
fmt.Println("Command completed successfully")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remote := Remote{}
|
||||||
|
json.Unmarshal([]byte(c.String("terraform.remote")), &remote)
|
||||||
|
|
||||||
|
var vars map[string]string
|
||||||
|
if err := json.Unmarshal([]byte(c.String("terraform.vars")), &vars); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var secrets map[string]string
|
||||||
|
if err := json.Unmarshal([]byte(c.String("terraform.secrets")), &secrets); err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func installCaCert(cacert string) *exec.Cmd {
|
plugin := Plugin{
|
||||||
ioutil.WriteFile("/usr/local/share/ca-certificates/ca_cert.crt", []byte(cacert), 0644)
|
Config: Config{
|
||||||
return exec.Command(
|
Remote: remote,
|
||||||
"update-ca-certificates",
|
Plan: c.Bool("terraform.plan"),
|
||||||
)
|
Vars: vars,
|
||||||
|
Secrets: secrets,
|
||||||
|
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"),
|
||||||
|
Parallelism: c.Int("terraform.parallelism"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteCache() *exec.Cmd {
|
return plugin.Exec()
|
||||||
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, 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))
|
|
||||||
}
|
|
||||||
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
Normal file
190
plugin.go
Normal file
@ -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…
Reference in New Issue
Block a user