Skip to content

Instantly share code, notes, and snippets.

@gosuri
Created September 4, 2015 18:28
Show Gist options
  • Save gosuri/a1233ad6197e45d670b3 to your computer and use it in GitHub Desktop.
Save gosuri/a1233ad6197e45d670b3 to your computer and use it in GitHub Desktop.
Using terraform with go
package terraformer
import (
"bytes"
"errors"
"path"
"path/filepath"
"...runtime"
"...store"
"code.google.com/p/go-uuid/uuid"
"github.com/hashicorp/terraform/builtin/providers/aws"
"github.com/hashicorp/terraform/builtin/providers/null"
"github.com/hashicorp/terraform/builtin/providers/template"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/terraform"
)
var (
DefaultConfigPath = "terraform"
)
// Terraformer represents the cluster terraformer
type Terraformer struct {
cluster *runtime.Cluster
store *store.Store
configPath string
vars map[string]string
state *terraform.State
}
// New returns an instance of terraformer
func New(cluster *runtime.Cluster, store *store.Store, configPath string) *Terraformer {
if len(configPath) == 0 {
configPath = DefaultConfigPath
}
return &Terraformer{
cluster: cluster,
store: store,
configPath: configPath,
}
}
// Run runs terraform
func (t *Terraformer) Run(destroy bool) error {
t.BindVars()
err := t.ReadState()
if err != nil {
return err
}
if LiveMode {
err = t.Apply(destroy)
if err != nil {
return err
}
}
return t.SaveState()
}
// BindVars binds the neccessary variables to the terraform context
func (t *Terraformer) BindVars() error {
cpath, err := filepath.Abs(t.configPath)
if err != nil {
return err
}
t.Var("aws_key", t.cluster.AwsKey)
t.Var("aws_secret", t.cluster.AwsSecret)
t.Var("aws_az", t.cluster.AwsAz())
// ...
return err
}
// ReadState reads the cluster state from the store
func (t *Terraformer) ReadState() error {
if t.cluster == nil {
return errors.New("cluster cannot be nil")
}
cs, err := t.store.ClusterStates().Get(t.cluster.Key(), false)
if err != nil && err != store.ErrNotFound {
return err
}
if cs != nil && len(cs.State) > 0 {
buf := bytes.NewBufferString(cs.State)
t.state, err = terraform.ReadState(buf)
}
return nil
}
// SaveState saves the cluster state to the store
func (t *Terraformer) SaveState() error {
if t.cluster == nil {
return errors.New("cluster cannot be nil")
}
cs := &runtime.ClusterState{ClusterId: t.cluster.Key()}
var buf bytes.Buffer
if err := terraform.WriteState(t.State(), &buf); err != nil {
return err
}
cs.State = bytes.NewBuffer(buf.Bytes()).String()
return t.store.ClusterStates().Save(cs)
}
// Context returns the context to run terraform tool
func (t *Terraformer) Context(destroy bool) (*terraform.Context, error) {
ctxOpts := &terraform.ContextOpts{
Destroy: destroy,
State: t.State(),
Variables: t.vars,
}
storage := &module.FolderStorage{
StorageDir: filepath.Join(t.configPath, "modules"),
}
mod, err := module.NewTreeModule("", t.configPath)
if err != nil {
return nil, err
}
if err := mod.Load(storage, module.GetModeNone); err != nil {
return nil, err
}
ctxOpts.Module = mod
ctxOpts.Providers = map[string]terraform.ResourceProviderFactory{
"aws": func() (terraform.ResourceProvider, error) {
return aws.Provider(), nil
},
"template": func() (terraform.ResourceProvider, error) {
return template.Provider(), nil
},
"null": func() (terraform.ResourceProvider, error) {
return null.Provider(), nil
},
}
return terraform.NewContext(ctxOpts), nil
}
// Apply brings the cluster to the desired state, it builds and changes real
// infrastructure. It destroys the cluster when destroy is true
func (t *Terraformer) Apply(destroy bool) error {
ctx, err := t.Context(destroy)
if err != nil {
return err
}
_, err = ctx.Plan()
if err != nil {
return err
}
_, err = ctx.Refresh()
if err != nil {
return err
}
t.state, err = ctx.Apply()
if err != nil {
return err
}
return nil
}
// State returns the cluster state
func (t *Terraformer) State() *terraform.State {
if t.state == nil {
t.state = terraform.NewState()
}
return t.state
}
// Var sets the variable with the value
func (t *Terraformer) Var(name, value string) {
if len(t.vars) == 0 {
t.vars = map[string]string{}
}
t.vars[name] = value
}
// GetVar returns the value associated with the variable name
func (t *Terraformer) GetVar(name string) string {
return t.vars[name]
}
func (t *Terraformer) Vars() map[string]string {
return t.vars
}
func (t *Terraformer) Cluster() *runtime.Cluster {
return t.cluster
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment