terra

package
v0.0.0-...-862afa6 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2023 License: Apache-2.0 Imports: 16 Imported by: 72,950

Documentation

Overview

Package terra provides the core functionality for working with Terraform. It is used by both the user written code and the generated code, including a minimal type system and exporting to Terraform configuration capabilities.

Example
package main

import (
	"bytes"
	"fmt"
	"log/slog"
	"os"

	"github.com/volvo-cars/lingon/pkg/terra"
)

// EmptyStack shows how to create a Terraform stack.
// The only catch is that this one is empty!
type EmptyStack struct {
	// Embed terra.Stack to implement the terra.Exporter interface
	terra.Stack

	// Add some resources here from our generated code, e.g.
	// VPC aws.vpc `validate:"required"
}

func main() {
	stack := EmptyStack{}

	// Typically you would use terra.Export() and write to a file. We will
	// write to a buffer for test purposes
	var b bytes.Buffer
	if err := terra.Export(&stack, terra.WithExportWriter(&b)); err != nil {
		slog.Error("exporting stack", "err", err.Error())
		os.Exit(1)
	}
	fmt.Println(b.String())

}
Output:

terraform {
}
Example (StackConfig)
package main

import (
	"bytes"
	"fmt"
	"io"
	"log/slog"
	"os"

	"github.com/volvo-cars/lingon/pkg/terra"
)

// StackConfig defines a reusable stack configuration containing things like
// the backend and any providers that need to be initialised
type StackConfig struct {
	terra.Stack
	Backend  *BackendS3
	Provider *DummyProvider
}

var _ terra.Backend = (*BackendS3)(nil)

// BackendS3 defines a backend configuration for our stacks to use.
// For backends, the `hcl` struct tags are required.
type BackendS3 struct {
	Bucket string `hcl:"bucket" validate:"required"`
	Key    string `hcl:"key" validate:"required"`
	// Add any other options needed
}

func (b *BackendS3) BackendType() string {
	return "s3"
}

type MyStack struct {
	// Embed our reusable custom StackConfig
	StackConfig
	// Add a dummy resource
	Resource *DummyResource `validate:"required"`
}

func main() {
	stack := MyStack{
		StackConfig: StackConfig{
			Backend: &BackendS3{
				Bucket: "my-s3-bucket",
				Key:    "some/path/to/state",
			},
			Provider: &DummyProvider{},
		},
		Resource: &DummyResource{},
	}

	// Typically you would use terra.Export() and write to a file. We will
	// write to a buffer for test purposes
	var b bytes.Buffer
	if err := terra.Export(&stack, terra.WithExportWriter(&b)); err != nil {
		slog.Error("exporting stack", "err", err.Error())
		os.Exit(1)
	}
	fmt.Println(b.String())
}

var _ terra.Provider = (*DummyProvider)(nil)

type DummyProvider struct{}

func (m DummyProvider) LocalName() string {
	return "dummy"
}

func (m DummyProvider) Source() string {
	return "dummy/dummy"
}

func (m DummyProvider) Version() string {
	return "0"
}

func (m DummyProvider) Configuration() interface{} {
	return struct{}{}
}

var _ terra.Resource = (*DummyResource)(nil)

// DummyResource implements a dummy resource.
// Users do not need to write resources themselves,
// they should be generated using terragen.
type DummyResource struct{}

func (m DummyResource) Type() string {
	return "dummy_resource"
}

func (m DummyResource) LocalName() string {
	return "dummy"
}

func (m DummyResource) Configuration() interface{} {
	return struct{}{}
}

func (m DummyResource) Dependencies() terra.Dependencies {
	return nil
}

func (m DummyResource) LifecycleManagement() *terra.Lifecycle {
	return nil
}

func (m DummyResource) ImportState(attributes io.Reader) error {
	return nil
}
Output:

terraform {
  backend "s3" {
    bucket = "my-s3-bucket"
    key    = "some/path/to/state"
  }
  required_providers {
    dummy = {
      source  = "dummy/dummy"
      version = "0"
    }
  }
}

// Provider blocks
provider "dummy" {
}

// Resource blocks
resource "dummy_resource" "dummy" {
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrNoBackendBlock        = errors.New("stack must have a backend block")
	ErrMultipleBackendBlocks = errors.New("stack cannot have multiple backend blocks")
	ErrNoProviderBlock       = errors.New("stack must have a provider block")
	ErrNotExportedField      = errors.New("stack has non-exported (private) field")
	ErrUnknownPublicField    = errors.New("unknown public field")
)

Functions

func Export

func Export(stack Exporter, opts ...ExportOption) error

Export encodes Exporter to Terraform configurations

func ReferenceAsSingle

func ReferenceAsSingle[T Value[T]](ref Reference) T

ReferenceAsSingle creates an instance of T with the given reference. It is a helper method for the generated code to use, to make it consistent with creating maps, sets, etc.

func StackImportState

func StackImportState(stack Exporter, tfState *tfjson.State) (bool, error)

StackImportState imports the Terraform state into the Terraform Stack. A bool is returned indicating whether all the resources have state. If the bool is true, every resource in the stack will have some state. If the bool is false, the state is incomplete meaning some resources may have state.

Types

type Backend

type Backend interface {
	BackendType() string
}

Backend represents a Terraform Backend. Users will define their backends to implement this interface

type BoolValue

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

func Bool

func Bool(b bool) BoolValue
Example
b := Bool(true)
toks, err := b.InternalTokens()
if err != nil {
	slog.Error("getting tokens", "err", err)
	return
}
fmt.Println(string(toks.Bytes()))
Output:

true

func ReferenceAsBool

func ReferenceAsBool(ref Reference) BoolValue

func (BoolValue) AsNumber

func (v BoolValue) AsNumber() NumberValue

func (BoolValue) AsString

func (v BoolValue) AsString() StringValue

func (BoolValue) InternalRef

func (v BoolValue) InternalRef() (Reference, error)

func (BoolValue) InternalTokens

func (v BoolValue) InternalTokens() (hclwrite.Tokens, error)

func (BoolValue) InternalWithRef

func (v BoolValue) InternalWithRef(ref Reference) BoolValue

type DataResource

type DataResource interface {
	DataSource() string
	LocalName() string
	Configuration() interface{}
}

DataResource represents a Terraform DataResource. The generated Go structs from a Terraform provider data resource will implement this interface

type Dependencies

type Dependencies []Dependency

func (Dependencies) InternalTokens

func (d Dependencies) InternalTokens() (hclwrite.Tokens, error)

type Dependency

type Dependency interface {
	DependOn() Reference
}

Dependency represents a Terraform dependency using the depends_on meta-argument

func DependsOn

func DependsOn(dependencies ...Dependency) []Dependency

DependsOn returns a list of dependencies

type ExportOption

type ExportOption func(*gotf)

ExportOption is used to configure the conversion from Go code to Terraform configurations. Use the helper functions WithExportXXX to configure the export.

func WithExportOutputDirectory

func WithExportOutputDirectory(dir string) ExportOption

WithExportOutputDirectory writes the generated Terraform configuration to the given output directory.

func WithExportWriter

func WithExportWriter(w io.Writer) ExportOption

WithExportWriter writes the generated Terraform configuration to io.Writer.

type Exporter

type Exporter interface {
	// Terriyaki is the original name of this project when it was being built, and is used to
	// explicitly mark a struct as implementing the Exporter interface
	Terriyaki()
}
	IAMRole    aws.IamRole
	EKSCluster aws.EksCluster
	...
}

type Lifecycle

type Lifecycle struct {
	CreateBeforeDestroy BoolValue                   `hcl:"create_before_destroy,attr"`
	PreventDestroy      BoolValue                   `hcl:"prevent_destroy,attr"`
	IgnoreChanges       LifecyleIgnoreChanges       `hcl:"ignore_changes,attr"`
	ReplaceTriggeredBy  LifecycleReplaceTriggeredBy `hcl:"replace_triggered_by,attr"`
}
Example
type lifecycleBlock struct {
	Lifecycle *Lifecycle `hcl:"lifecycle,block"`
}
tagsRef := ReferenceAsString(
	ReferenceResource(&dummyResource{}).Append(
		"tags",
	),
)
d := lifecycleBlock{
	Lifecycle: &Lifecycle{
		CreateBeforeDestroy: Bool(true),
		PreventDestroy:      Bool(true),
		IgnoreChanges:       IgnoreChanges(tagsRef),
		ReplaceTriggeredBy:  ReplaceTriggeredBy(tagsRef),
	},
}

var b bytes.Buffer
if err := hcl.EncodeRaw(&b, d); err != nil {
	slog.Error("encoding lifecycle", "err", err)
	return
}

fmt.Println(b.String())
Output:

lifecycle {
  create_before_destroy = true
  prevent_destroy       = true
  ignore_changes        = [tags]
  replace_triggered_by  = [dummy.dummy.tags]
}

type LifecycleReplaceTriggeredBy

type LifecycleReplaceTriggeredBy []Reference

LifecycleReplaceTriggeredBy is a list of references to attributes that we want to trigger a replacement on if those attributes themselves are replaced.

func ReplaceTriggeredBy

func ReplaceTriggeredBy(attrs ...Referencer) LifecycleReplaceTriggeredBy

ReplaceTriggeredBy takes a list of object attributes to add to the `replace_triggered_by` list for the lifecycle of a resource.

func (LifecycleReplaceTriggeredBy) InternalTokens

func (r LifecycleReplaceTriggeredBy) InternalTokens() (hclwrite.Tokens, error)

InternalTokens returns the HCL tokens for the `replace_triggered_by` list

type LifecyleIgnoreChanges

type LifecyleIgnoreChanges []Reference

LifecyleIgnoreChanges is a list of references to attributes that we want to ignore in the `lifecycle` block

func IgnoreChanges

func IgnoreChanges(attrs ...Referencer) LifecyleIgnoreChanges

IgnoreChanges takes a list of object attributes to include in the `ignore_changes` list for the lifecycle of a resource.

func (LifecyleIgnoreChanges) InternalTokens

func (l LifecyleIgnoreChanges) InternalTokens() (hclwrite.Tokens, error)

InternalTokens only returns the relative address of a reference. This is due to the specification for the `ignore_changes` list inside the `lifecycle` block. E.g.

resource "aws_instance" "example" {
  # ...
   lifecycle {
    ignore_changes = [
      # Use a relative reference to the tags,
      # i.e. not `aws_instance.example.tags`
      tags,
    ]
  }
}

type ListValue

type ListValue[T Value[T]] struct {
	// contains filtered or unexported fields
}

func CastAsList

func CastAsList[T Value[T]](value T) ListValue[T]

CastAsList takes a value (as a reference) and wraps it in a ListValue

func List

func List[T Value[T]](values ...T) ListValue[T]

List returns a list value

Example (Bool)
s := List(
	Bool(false),
	Bool(true),
)
fmt.Println(exampleTokensOrError(s))
Output:

[false, true]
Example (Index)
// Create a reference list of string and Splat() it
l := ReferenceAsList[StringValue](
	ReferenceResource(&dummyResource{}),
)
index := l.Index(0)
fmt.Println(exampleTokensOrError(index))
Output:

dummy.dummy[0]
Example (Mixed)
s := List(
	String("a"),
	Number(1).AsString(),
	ReferenceAsString(ReferenceResource(&dummyResource{})),
)
fmt.Println(exampleTokensOrError(s))
Output:

["a", "1", dummy.dummy]
Example (Number)
s := List(
	Number(0),
	Number(1),
)
fmt.Println(exampleTokensOrError(s))
Output:

[0, 1]
Example (Ref)
// Create some dummy references
refA := ReferenceAsString(ReferenceResource(&dummyResource{}))
refB := ReferenceAsString(ReferenceDataResource(&dummyDataResource{}))

s := List(refA, refB)
fmt.Println(exampleTokensOrError(s))
Output:

[dummy.dummy, data.dummy.dummy]
Example (Splat)
// Create a reference list of string and Splat() it
l := ReferenceAsList[StringValue](
	ReferenceResource(&dummyResource{}),
)
splat := l.Splat()
// Convert "splatted" list back to a List
var ls ListValue[StringValue] //nolint:gosimple
ls = CastAsList(splat)
fmt.Println(exampleTokensOrError(ls))
Output:

dummy.dummy[*]
Example (SplatNested)
// Create a reference list of a list of string and Splat() it
l := ReferenceAsList[ListValue[StringValue]](
	ReferenceResource(&dummyResource{}),
)
splat := l.Splat()
// Convert "splatted" list back to a List of List
var ls ListValue[ListValue[StringValue]] //nolint:gosimple
ls = CastAsList(
	splat,
)
fmt.Println(exampleTokensOrError(ls))
Output:

dummy.dummy[*]
Example (String)
s := List(
	String("a"),
	String("b"),
)
fmt.Println(exampleTokensOrError(s))
Output:

["a", "b"]

func ListString

func ListString(values ...string) ListValue[StringValue]

ListString returns a list value containing the given string values

func ReferenceAsList

func ReferenceAsList[T Value[T]](ref Reference) ListValue[T]

ReferenceAsList creates a list reference

func (ListValue[T]) Index

func (v ListValue[T]) Index(i int) T

func (ListValue[T]) InternalRef

func (v ListValue[T]) InternalRef() (Reference, error)

func (ListValue[T]) InternalTokens

func (v ListValue[T]) InternalTokens() (hclwrite.Tokens, error)

func (ListValue[T]) InternalWithRef

func (v ListValue[T]) InternalWithRef(ref Reference) ListValue[T]

func (ListValue[T]) Splat

func (v ListValue[T]) Splat() T

type MapValue

type MapValue[T Value[T]] struct {
	// contains filtered or unexported fields
}

func CastAsMap

func CastAsMap[T Value[T]](value T) MapValue[T]

CastAsMap takes a value (as a reference) and wraps it in a MapValue

func Map

func Map[T Value[T]](value map[string]T) MapValue[T]

Map returns a map value

Example (Mixed)
s := Map(
	map[string]StringValue{
		"a": String("a"),
		"b": ReferenceAsString(ReferenceResource(&dummyResource{})),
	},
)

toks, err := s.InternalTokens()
if err != nil {
	slog.Error("getting tokens", "err", err)
	return
}
fmt.Println(string(toks.Bytes()))
Output:

{
  "a" = "a"
  "b" = dummy.dummy
}
Example (Number)
s := Map(
	map[string]NumberValue{
		"0": Number(0),
		"1": Number(1),
	},
)

toks, err := s.InternalTokens()
if err != nil {
	slog.Error("getting tokens", "err", err)
	return
}
fmt.Println(string(toks.Bytes()))
Output:

{
  "0" = 0
  "1" = 1
}
Example (String)
s := Map(
	map[string]StringValue{
		"a": String("a"),
		"b": String("b"),
	},
)

toks, err := s.InternalTokens()
if err != nil {
	slog.Error("getting tokens", "err", err)
	return
}
fmt.Println(string(toks.Bytes()))
Output:

{
  "a" = "a"
  "b" = "b"
}

func MapString

func MapString(value map[string]string) MapValue[StringValue]

MapString returns a map value containing the given string values

func ReferenceAsMap

func ReferenceAsMap[T Value[T]](ref Reference) MapValue[T]

ReferenceAsMap returns a map value

func (MapValue[T]) InternalRef

func (v MapValue[T]) InternalRef() (Reference, error)

func (MapValue[T]) InternalTokens

func (v MapValue[T]) InternalTokens() (hclwrite.Tokens, error)

func (MapValue[T]) InternalWithRef

func (v MapValue[T]) InternalWithRef(ref Reference) MapValue[T]

func (MapValue[T]) Key

func (v MapValue[T]) Key(s string) T

type NumberValue

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

func Number

func Number(i int) NumberValue
Example
n := Number(1)
toks, err := n.InternalTokens()
if err != nil {
	slog.Error("getting tokens", "err", err)
	return
}
fmt.Println(string(toks.Bytes()))
Output:

1

func ReferenceAsNumber

func ReferenceAsNumber(ref Reference) NumberValue

func (NumberValue) AsBool

func (v NumberValue) AsBool() BoolValue

func (NumberValue) AsString

func (v NumberValue) AsString() StringValue

func (NumberValue) InternalRef

func (v NumberValue) InternalRef() (Reference, error)

func (NumberValue) InternalTokens

func (v NumberValue) InternalTokens() (hclwrite.Tokens, error)

func (NumberValue) InternalWithRef

func (v NumberValue) InternalWithRef(ref Reference) NumberValue

type Provider

type Provider interface {
	LocalName() string
	Source() string
	Version() string
	Configuration() interface{}
}

Provider represents a Terraform Provider. The generated Go structs from a Terraform provider configuration will implement this interface

type Reference

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

Reference represents a reference to some value in a Terraform configuration. The reference might include things like indices (i.e. [0]), nested objects or even the splat operator (i.e. [*]).

A reference can be created by passing a Resource to ReferenceResource or passing a DataResource to ReferenceDataResource.

func ReferenceDataResource

func ReferenceDataResource(data DataResource) Reference

ReferenceDataResource takes a data resource and returns a Reference which is the address to that data resource in the Terraform configuration.

func ReferenceResource

func ReferenceResource(res Resource) Reference

ReferenceResource takes a resource and returns a Reference which is the address to that resource in the Terraform configuration.

func (Reference) Append

func (r Reference) Append(name string) Reference

Append appends the given string to the reference

func (Reference) InternalTokens

func (r Reference) InternalTokens() (hclwrite.Tokens, error)

InternalTokens returns the tokens to represent this reference in Terraform configurations

type Referencer

type Referencer interface {
	// InternalRef returns a copy of the reference stored, if any.
	// If the Value T is not a reference, this method should return an error to
	// avoid any nasty hidden errors (i.e.
	// silently converting a value to a reference).
	//
	// Internal: users should **not** use this!
	InternalRef() (Reference, error)
}

type Resource

type Resource interface {
	// Type returns the resource type, e.g. aws_iam_role
	Type() string
	// LocalName returns the unique name of the resource as it will be stored in the state
	LocalName() string
	// Configuration returns the arguments for the resource
	Configuration() interface{}
	// Dependencies returns the list of resources that this resource depends_on
	Dependencies() Dependencies
	// LifecycleManagement returns the lifecycle configuration for this resource
	LifecycleManagement() *Lifecycle
	// ImportState takes the given attributes value map (from a Terraform state) and imports it
	// into this resource
	ImportState(attributes io.Reader) error
}

Resource represents a Terraform Resource. The generated Go structs from a Terraform provider resource will implement this interface and be used when exported to Terraform Configuration

type SetValue

type SetValue[T Value[T]] struct {
	// contains filtered or unexported fields
}

func CastAsSet

func CastAsSet[T Value[T]](value T) SetValue[T]

CastAsSet takes a value (as a reference) and wraps it in a SetValue

func ReferenceAsSet

func ReferenceAsSet[T Value[T]](ref Reference) SetValue[T]

ReferenceAsSet creates a list reference

func Set

func Set[T Value[T]](values ...T) SetValue[T]

Set returns a list value

Example (Bool)
s := Set(
	Bool(false),
	Bool(true),
)

fmt.Println(exampleTokensOrError(s))
Output:

[false, true]
Example (Index)
// Create a reference set of string and Splat() it
l := ReferenceAsSet[StringValue](
	ReferenceResource(&dummyResource{}),
)
index := l.Index(0)
fmt.Println(exampleTokensOrError(index))
Output:

dummy.dummy[0]
Example (Mixed)
s := Set(
	String("a"),
	ReferenceAsString(ReferenceResource(&dummyResource{})),
)

fmt.Println(exampleTokensOrError(s))
Output:

["a", dummy.dummy]
Example (Number)
s := Set(
	Number(0),
	Number(1),
)

fmt.Println(exampleTokensOrError(s))
Output:

[0, 1]
Example (Ref)
s := Set(
	ReferenceAsString(ReferenceResource(&dummyResource{})),
	ReferenceAsString(ReferenceDataResource(&dummyDataResource{})),
)

fmt.Println(exampleTokensOrError(s))
Output:

[dummy.dummy, data.dummy.dummy]
Example (Splat)
// Create a reference set of string and Splat() it
l := ReferenceAsSet[StringValue](
	ReferenceResource(&dummyResource{}),
)
splat := l.Splat()
// Convert "splatted" set back to a Set
var ls SetValue[StringValue] //nolint:gosimple
ls = CastAsSet(splat)
fmt.Println(exampleTokensOrError(ls))
Output:

dummy.dummy[*]
Example (SplatNested)
// Create a reference set of a set of string and Splat() it
l := ReferenceAsSet[SetValue[StringValue]](
	ReferenceResource(&dummyResource{}),
)
splat := l.Splat()
// Convert "splatted" set back to a Set of Set
var ls SetValue[SetValue[StringValue]] //nolint:gosimple
ls = CastAsSet(
	splat,
)
fmt.Println(exampleTokensOrError(ls))
Output:

dummy.dummy[*]
Example (String)
s := Set(
	String("a"),
	String("b"),
)

fmt.Println(exampleTokensOrError(s))
Output:

["a", "b"]

func SetString

func SetString(values ...string) SetValue[StringValue]

SetString returns a list value containing the given string values

func (SetValue[T]) Index

func (v SetValue[T]) Index(i int) T

func (SetValue[T]) InternalRef

func (v SetValue[T]) InternalRef() (Reference, error)

func (SetValue[T]) InternalTokens

func (v SetValue[T]) InternalTokens() (hclwrite.Tokens, error)

func (SetValue[T]) InternalWithRef

func (v SetValue[T]) InternalWithRef(ref Reference) SetValue[T]

func (SetValue[T]) Splat

func (v SetValue[T]) Splat() T

type Stack

type Stack struct{}

Stack minimally implements the Exporter interface and can be embedded into user-defined stacks to make them implement the Exporter interface

func (*Stack) Terriyaki

func (*Stack) Terriyaki()

type StringValue

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

func ReferenceAsString

func ReferenceAsString(ref Reference) StringValue

func String

func String(s string) StringValue
Example
s := String("hello world")
fmt.Println(exampleTokensOrError(s))
Output:

"hello world"

func (StringValue) AsBool

func (v StringValue) AsBool() BoolValue

func (StringValue) AsNumber

func (v StringValue) AsNumber() NumberValue

func (StringValue) InternalRef

func (v StringValue) InternalRef() (Reference, error)

func (StringValue) InternalTokens

func (v StringValue) InternalTokens() (hclwrite.Tokens, error)

func (StringValue) InternalWithRef

func (v StringValue) InternalWithRef(ref Reference) StringValue

type Value

type Value[T any] interface {
	tkihcl.Tokenizer
	Referencer
	// InternalWithRef returns a copy of this type T with the provided
	// reference. This is used internally to create a reference to attributes
	// within sets, lists,
	// maps and custom attributes types in the generated code.
	//
	// Internal: users should **not** use this!
	InternalWithRef(Reference) T
}

Value represents the most generic type in terra's minimal type system. Every other type must implement this type. It is never expected that a user should have to interact with this type directly.

Value exposes some functions prefixed with "Internal". It is not possible to make these methods non-public, as generated code relies on this, but consumers of this package should **not** use these methods, please :)

Jump to

Keyboard shortcuts

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