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/golingon/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/golingon/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 ¶
- Variables
- func Export(stack Exporter, opts ...ExportOption) error
- func ReferenceAsSingle[T Value[T]](ref Reference) T
- type Backend
- type BoolValue
- type DataSource
- type Dependencies
- type Dependency
- type ExportOption
- type Exporter
- type Lifecycle
- type LifecycleReplaceTriggeredBy
- type LifecyleIgnoreChanges
- type ListValue
- type MapValue
- type NumberValue
- type Provider
- type Reference
- type Referencer
- type Resource
- type SetValue
- type Stack
- type StackObjects
- type StringValue
- type Value
Examples ¶
- Package
- Package (StackConfig)
- Bool
- Lifecycle
- List (Bool)
- List (Index)
- List (Mixed)
- List (Number)
- List (Ref)
- List (Splat)
- List (SplatNested)
- List (String)
- Map (Mixed)
- Map (Number)
- Map (String)
- Number
- Set (Bool)
- Set (Index)
- Set (Mixed)
- Set (Number)
- Set (Ref)
- Set (Splat)
- Set (SplatNested)
- Set (String)
- String
- StringFormat
Constants ¶
This section is empty.
Variables ¶
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 ¶
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.
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 ¶
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 (BoolValue) AsNumber ¶
func (v BoolValue) AsNumber() NumberValue
func (BoolValue) AsString ¶
func (v BoolValue) AsString() StringValue
func (BoolValue) InternalRef ¶
func (BoolValue) InternalWithRef ¶
type DataSource ¶
DataSource represents a Terraform DataSource. 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 ¶
CastAsList takes a value (as a reference) and wraps it in a ListValue
func List ¶
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(ReferenceDataSource(&dummyDataSource{})) 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 ¶
ReferenceAsList creates a list reference
func (ListValue[T]) InternalRef ¶
func (ListValue[T]) InternalTokens ¶
func (ListValue[T]) InternalWithRef ¶
type MapValue ¶
type MapValue[T Value[T]] struct { // contains filtered or unexported fields }
func Map ¶
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 ¶
ReferenceAsMap returns a map value
func (MapValue[T]) InternalRef ¶
func (MapValue[T]) InternalWithRef ¶
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 DataSource to ReferenceDataSource.
func ReferenceDataSource ¶
func ReferenceDataSource(data DataSource) Reference
ReferenceDataSource takes a data resource and returns a Reference which is the address to that data resource in the Terraform configuration.
func ReferenceResource ¶
ReferenceResource takes a resource and returns a Reference which is the address to that resource in the Terraform configuration.
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 ReferenceAsSet ¶
ReferenceAsSet creates a list reference
func Set ¶
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.ToList().Index(0) fmt.Println(exampleTokensOrError(index))
Output: tolist(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(ReferenceDataSource(&dummyDataSource{})), ) 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]) InternalRef ¶
func (SetValue[T]) InternalWithRef ¶
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
type StackObjects ¶
type StackObjects struct { Backend Backend Providers []Provider Resources []Resource DataSources []DataSource }
StackObjects contains all the blocks that are extracted from a user-defined stack.
func ObjectsFromStack ¶
func ObjectsFromStack(stack Exporter) (*StackObjects, error)
ObjectsFromStack takes a terra stack and returns all the terra objects (resources, data sources, providers, and backend) that are defined in the stack.
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 StringFormat ¶
func StringFormat(s string, refs ...Referencer) StringValue
StringFormat can be used for string interpolation. It takes a string with %s placeholders and replaces those with the refs. E.g. given a variable `attr` which references an attribute `resource.id.attr`:
StringFormat("Hello ${%s}", attr) -> "Hello ${resource.id.attr}"
Example ¶
// Create a dummy resource and create a StringValue reference to it. // Ignore this and pretend you have your reference attribute as you // normally would, e.g. // ref.Attributes().Name() res := dummyResource{} ref := ReferenceAsSingle[StringValue](ReferenceResource(&res)) // Create a StringValue with a format string and the reference. s := StringFormat("${%s}", ref) fmt.Println(exampleTokensOrError(s))
Output: "${dummy.dummy}"
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 :)