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 ¶
- Variables
- func Export(stack Exporter, opts ...ExportOption) error
- func ReferenceAsSingle[T Value[T]](ref Reference) T
- func StackImportState(stack Exporter, tfState *tfjson.State) (bool, error)
- type Backend
- type BoolValue
- type DataResource
- 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 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
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.
func StackImportState ¶
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 ¶
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 DataResource ¶
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 ¶
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(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 ¶
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 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 ¶
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.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]) 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 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 :)