terranova

package module
v0.0.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2021 License: Apache-2.0 Imports: 35 Imported by: 0

README

Actions Status Build Status codecov GoDoc License

Terranova

terranova

Terranova is a Go package that allows you to easily use the Terraform Go Packages instead of executing the Terraform binary. The version v0.0.2 of Terranova works with Terraform version v0.12.17.

For more information about Terranova and how to use use it, refer to the blog post Terranova: Using Terraform from Go.

How to use the Terranova package

Terranova works better as a Go module, if you don't have a go.mod file in your project, create it with go mod init [package full name]. Import Terranova in the Go code:

import (
  "github.com/johandry/terranova"
)

As soon as you execute a Go command such as go build, go test or go mod tidy it will be included in your go.mod file and downloaded.

If you are not using modules yet, using vendors or having the packages in $GOPATH, please, git clone the repository and create the vendor directory:

mkdir -p $GOPATH/src/github.com/johandry/
cd $GOPATH/src/github.com/johandry/
git clone --depth=1 https://github.com/johandry/terranova.git
GO111MODULE=on go mod vendor

After having the package, the high level use of Terranova is like follows:

  1. Create a Platform instance with the Terraform code to apply
  2. Add to the go.mod file, import and add (AddProvider()) the Terraform Provider(s) used in the code
  3. Add to the go.mod file, import and add (AddProvisioner()) the Terraform Provisioner (if any) used in the Terraform code.
  4. Add (Var() or BindVars()) the variables used in the Terraform code.
  5. (optional) Create (NewMiddleware()) a logger middleware with the default logger or a custom logger that implements the Logger interface.
  6. (optional) Create your custom Terraform Hooks and assign them to the Platform instance.
  7. Load the previous state of the infrastructure and keep it updated using PersistStateToFile().
  8. Apply the changes using the method Apply().

The following example shows how to create, scale or terminate AWS EC2 instances:

package main

import (
  "log"
  "os"

  "github.com/johandry/terranova"
  "github.com/johandry/terranova/logger"
  "github.com/terraform-providers/terraform-provider-aws/aws"
)

var code string

const stateFilename = "simple.tfstate"

func main() {
  count := 0
  keyName := "demo"

  log := log.New(os.Stderr, "", log.LstdFlags)
  logMiddleware := logger.NewMiddleware()
  defer logMiddleware.Close()

  platform, err := terranova.NewPlatform(code).
    SetMiddleware(logMiddleware).
    AddProvider("aws", aws.Provider()).
    Var("c", count).
    Var("key_name", keyName).
    PersistStateToFile(stateFilename)

  if err != nil {
    log.Fatalf("Fail to create the platform using state file %s. %s", stateFilename, err)
  }

  terminate := (count == 0)
  if err := platform.Apply(terminate); err != nil {
    log.Fatalf("Fail to apply the changes to the platform. %s", err)
  }
}

func init() {
  code = `
  variable "c"    { default = 2 }
  variable "key_name" {}
  provider "aws" {
    region        = "us-west-2"
  }
  resource "aws_instance" "server" {
    instance_type = "t2.micro"
    ami           = "ami-6e1a0117"
    count         = "${var.c}"
    key_name      = "${var.key_name}"
  }
`
}

Read the same example at terranova-examples/aws/simple/main.go and the blog post Terranova: Using Terraform from Go for a detail explanation of the code.

The git repository terranova-examples contain more examples of how to use Terranova with different clouds or providers.

Providers version

Terranova works with the latest version of Terraform (v0.12.12) but requires Terraform providers using the Legacy Terraform Plugin SDK instead of the newer Terraform Plugin SDK. If the required provider still uses the Legacy Terraform Plugin SDK select the latest release using the Terraform Plugin SDK. For more information read the Terraform Plugin SDK page in the Extending Terraform documentation.

These are the latest versions supported for some providers:

  • AWS: github.com/terraform-providers/terraform-provider-aws v1.60.1-0.20191003145700-f8707a46c6ec
  • OpenStack: github.com/terraform-providers/terraform-provider-openstack v1.23.0
  • vSphere: github.com/terraform-providers/terraform-provider-vsphere v1.13.0
  • Azure: github.com/terraform-providers/terraform-provider-azurerm v1.34.0
  • Null: github.com/terraform-providers/terraform-provider-null v1.0.1-0.20190430203517-8d3d85a60e20
  • Template: github.com/terraform-providers/terraform-provider-template v1.0.1-0.20190501175038-5333ad92003c
  • TLS: github.com/terraform-providers/terraform-provider-tls v1.2.1-0.20190816230231-0790c4b40281

Include the code line in the require section of your go.mod file for the provider you are importing, for example:

require (
  github.com/hashicorp/terraform v0.12.12
  github.com/johandry/terranova v0.0.3
  github.com/terraform-providers/terraform-provider-openstack v1.23.0
  github.com/terraform-providers/terraform-provider-vsphere v1.13.0
)

If you get an error for a provider that you are not directly using (i.e. TLS provider) include the required version in the replace section of the go.mod file, for example:

replace (
  github.com/terraform-providers/terraform-provider-tls => github.com/terraform-providers/terraform-provider-tls v1.2.1-0.20190816230231-0790c4b40281
)

If the required provider is not in this list, you can identify the version like this:

  1. Go to the GitHub project of the provider
  2. Locate the provider.go file located in the directory named like the provider name (i.e. was/provider.go)
  3. If the file imports terraform-plugin-sdk/terraform go to the previous git tag.
  4. Repeat step #3 until you find the latest tag importing terraform/terraform (i.e. the latest tag for AWS is v2.31.0)
  5. If the tag can be used in the go.mod file, just include it after the module name, for example: terraform-provider-vsphere v1.13.0
  6. If the tag cannot be used in the go.mod file, for example v2.31.0 for AWS:
    1. Find the release generated from the git tag (i.e. v2.31.0)
    2. Locate the hash number assigned to the release (i.e. f8707a4)
    3. Include the number in the go.mod file and execute go mod tidy or any other go command such as go test or go build.

If this is not working and need help to identify the right version, open an issue and you'll be help as soon as possible.

Logs

Using Terranova without a Log Middleware cause to print to Stderr all the Terraform logs, a lot, a lot of lines including traces. This may be uncomfortable or not needed. To discard the Terraform logs or filter them (print only the required log entries) you have to use the Log Middleware and (optionally) a custom Logger.

To create a Log Middleware use:

logMiddleware := logger.NewMiddleware()
defer logMiddleware.Close()

...

platform.SetMiddleware(logMiddleware)

You can decide when the Log Middleware starts intercepting the standard log with logMiddleware.Start(), if you don't the Log Middleware will start intercepting every line printed by the standard log when Terranova execute an action that makes Terraform to print something to the standard log. Every line intercepted by the Log Middleware is printed by the provided logger. This hijack ends when the Log Middleware is closed. To make the platform use the middleware, add it with SetMiddleware() to the platform.

A logger is an instance of the interface Logger. If the Log Middleware is created without parameter the default logger will be used, it prints the INFO, WARN and ERROR log entries of Terraform. To create your own logger check the examples in the Terranova Examples repository.

IMPORTANT: It's recommended to create your own instance of log to not use the standard log when the Log Middleware is in use. Everything that is printed using the standard log will be intercepted by the Log Middleware and processed by the Logger. So, use your own custom log or do something like this before creating the Log Middleware:

log := log.New(os.Stderr, "", log.LstdFlags)
...
log.Printf("[DEBUG] this line is not captured by the Log Middleware")

Sources

All this research was done reading the Terraform documentation and source code.

Please, feel free to comment, open Issues and Pull Requests, help us to improve Terranova.

Contribute and Support

A great way to contribute to the project is to send a detailed report when you encounter an issue/bug or when you are requesting a new feature. We always appreciate a well-written, thorough bug report, and will thank you for it!

The GitHub issue tracker is for bug reports and feature requests. If you already forked and modified Terranova with a new feature or fix, please, open a Pull Request at GitHub

General support can be found at Gopher's Slack #terraform channel or message me directly @johandry

Stars over time

Stargazers over time

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Platform

type Platform struct {
	Code          map[string]string
	Providers     map[addrs.Provider]providers.Factory
	Provisioners  map[string]provisioners.Factory
	Vars          map[string]interface{}
	State         *State
	Hooks         []terraform.Hook
	LogMiddleware *logger.Middleware

	ExpectedStats *Stats
	// contains filtered or unexported fields
}

Platform is the platform to be managed by Terraform

func NewPlatform

func NewPlatform(code string, hooks ...terraform.Hook) *Platform

NewPlatform return an instance of Platform with default values

func (*Platform) AddFile

func (p *Platform) AddFile(filename, code string) *Platform

AddFile adds Terraform code into a file. Such file could be in a directory, use os.PathSeparator as path separator.

func (*Platform) AddProvider

func (p *Platform) AddProvider(name string, provider terraform.ResourceProvider) *Platform

AddProvider adds a new provider to the providers list

func (*Platform) AddProvisioner

func (p *Platform) AddProvisioner(name string, provisioner terraform.ResourceProvisioner) *Platform

AddProvisioner adds a new provisioner to the provisioner list

func (*Platform) Apply

func (p *Platform) Apply(destroy bool) error

Apply brings the platform to the desired state. It'll destroy the platform when `destroy` is `true`.

func (*Platform) BindVars

func (p *Platform) BindVars(vars map[string]interface{}) *Platform

BindVars binds the map of variables to the Platform variables, to be used by Terraform

func (*Platform) Export

func (p *Platform) Export(dir string) error

Export save all the code to the given directory. The directory must exists and there should be code to export.

func (*Platform) OutputValueAsString

func (p *Platform) OutputValueAsString(name string) (string, error)

OutputValueAsString returns the value of the Terraform output parameter in the code

func (*Platform) PersistStateToFile

func (p *Platform) PersistStateToFile(filename string) (*Platform, error)

PersistStateToFile reads the state from the given file, if exists. Then will save the current state to the given file every time it changes during the Terraform actions.

func (*Platform) Plan

func (p *Platform) Plan(destroy bool) (*plans.Plan, error)

Plan returns execution plan for an existing configuration to apply to the platform.

func (*Platform) ReadState

func (p *Platform) ReadState(r io.Reader) (*Platform, error)

ReadState takes a io.Reader as input to read from it the Terraform state

func (*Platform) ReadStateFromFile

func (p *Platform) ReadStateFromFile(filename string) (*Platform, error)

ReadStateFromFile will load the Terraform state from a file and assign it to the Platform state.

func (*Platform) SetMiddleware

func (p *Platform) SetMiddleware(lm *logger.Middleware) *Platform

SetMiddleware assigns the given log middleware into the Platform

func (*Platform) Stats

func (p *Platform) Stats() *Stats

Stats return the current status from the count hook

func (*Platform) Var

func (p *Platform) Var(name string, value interface{}) *Platform

Var set a variable with it's value

func (*Platform) WriteState

func (p *Platform) WriteState(w io.Writer) (*Platform, error)

WriteState takes a io.Writer as input to write the Terraform state

func (*Platform) WriteStateToFile

func (p *Platform) WriteStateToFile(filename string) (*Platform, error)

WriteStateToFile save the state of the Terraform state to a file

type Provider

type Provider struct {
	// contains filtered or unexported fields
}

Provider implements the provider.Interface wrapping the legacy ResourceProvider but not using gRPC like terraform does

func NewProvider

func NewProvider(provider terraform.ResourceProvider) *Provider

NewProvider creates a Terranova Provider to wrap the given legacy ResourceProvider

func (*Provider) ApplyResourceChange

ApplyResourceChange implements the ApplyResourceChange from providers.Interface. Takes the planned state for a resource, which may yet contain unknown computed values, and applies the changes returning the final state.

func (*Provider) Close

func (p *Provider) Close() error

Close implements the Close from providers.Interface. It's to shuts down the plugin process but this is not a plugin, so nothing is done here

func (*Provider) Configure

Configure implements the Configure from providers.Interface. Configures and initialized the provider.

func (*Provider) GetSchema

func (p *Provider) GetSchema() (resp providers.GetSchemaResponse)

GetSchema implements the GetSchema from providers.Interface. Returns the complete schema for the provider.

func (*Provider) ImportResourceState

ImportResourceState implements the ImportResourceState from providers.Interface. Requests that the given resource be imported.

func (*Provider) PlanResourceChange

PlanResourceChange implements the PlanResourceChange from providers.Interface. Takes the current state and proposed state of a resource, and returns the planned final state.

func (*Provider) PrepareProviderConfig

PrepareProviderConfig implements the PrepareProviderConfig from providers.Interface. Allows the provider to validate the configuration values, and set or override any values with defaults.

func (*Provider) ReadDataSource

ReadDataSource implements the ReadDataSource from providers.Interface. Returns the data source's current state.

func (*Provider) ReadResource

ReadResource implements the ReadResource from providers.Interface. Refreshes a resource and returns its current state.

func (*Provider) Stop

func (p *Provider) Stop() error

Stop implements the Stop from providers.Interface. It is called when the provider should halt any in-flight actions.

Stop should not block waiting for in-flight actions to complete. It should take any action it wants and return immediately acknowledging it has received the stop request. Terraform will not make any further API calls to the provider after Stop is called.

The error returned, if non-nil, is assumed to mean that signaling the stop somehow failed and that the user should expect potentially waiting a longer period of time.

func (*Provider) UpgradeResourceState

UpgradeResourceState implements the UpgradeResourceState from providers.Interface. It is called when the state loader encounters an instance state whose schema version is less than the one reported by the currently-used version of the corresponding provider, and the upgraded result is used for any further processing.

func (*Provider) ValidateDataSourceConfig

ValidateDataSourceConfig implements the ValidateDataSourceConfig from providers.Interface. Allows the provider to validate the data source configuration values.

func (*Provider) ValidateResourceTypeConfig

ValidateResourceTypeConfig implements the ValidateResourceTypeConfig from providers.Interface. Allows the provider to validate the resource configuration values.

type Provisioner

type Provisioner struct {
	// contains filtered or unexported fields
}

Provisioner implements the provisioners.Interface wrapping the legacy terraform.ResourceProvisioner but not using gRPC like terraform does

func NewProvisioner

func NewProvisioner(provisioner terraform.ResourceProvisioner) *Provisioner

NewProvisioner creates a Terranova Provisioner to wrap the given legacy ResourceProvisioner

func (*Provisioner) Close

func (p *Provisioner) Close() error

Close implements Close from provisioners.Interface. It shuts down the plugin process if applicable.

func (*Provisioner) GetSchema

func (p *Provisioner) GetSchema() (resp provisioners.GetSchemaResponse)

GetSchema implements GetSchema from provisioners.Interface. It returns the schema for the provisioner configuration.

func (*Provisioner) ProvisionResource

ProvisionResource implements ProvisionResource from provisioners.Interface. It runs the provisioner with provided configuration. ProvisionResource blocks until the execution is complete. If the returned diagnostics contain any errors, the resource will be left in a tainted state.

func (*Provisioner) Stop

func (p *Provisioner) Stop() error

Stop implements Stop from provisioners.Interface. It is called to interrupt the provisioner.

Stop should not block waiting for in-flight actions to complete. It should take any action it wants and return immediately acknowledging it has received the stop request. Terraform will not make any further API calls to the provisioner after Stop is called.

The error returned, if non-nil, is assumed to mean that signaling the stop somehow failed and that the user should expect potentially waiting a longer period of time.

func (*Provisioner) ValidateProvisionerConfig

ValidateProvisionerConfig implements ValidateProvisionerConfig from provisioners.Interface. It allows the provisioner to validate the configuration values.

type State

type State = states.State

State is an alias for terraform.State

type Stats

type Stats struct {
	Add, Change, Destroy int
	// contains filtered or unexported fields
}

Stats encapsulate the statistics of changes to apply or applied

func NewStats

func NewStats() *Stats

NewStats creates an empty stats

func (*Stats) FromCountHook

func (s *Stats) FromCountHook(countHook *local.CountHook) *Stats

FromCountHook return stats from a given count hook

func (*Stats) FromPlan

func (s *Stats) FromPlan(plan *plans.Plan) *Stats

FromPlan return stats from a given plan

func (*Stats) Reset

func (s *Stats) Reset()

Reset resets, set everything to zeros, the current stats

func (*Stats) String

func (s *Stats) String() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL