mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-25 10:40:02 +08:00
* Add `lib/tfgen` package for generating resource Terraform code
There are a number of different people working on features that will improve our
IaC (Infrastructure-as-Code) story. For example, the Machine & Workload Identity
team plan to build a set of wizards to enable the user to deploy `tbot` for
various CI/CD use-cases, the output of which will be a set of Terraform resources.
Our Terraform resource schemas are generated from protobuf definitions using the
`protoc-gen-terraform` plugin. As they can be arbitrarily complex (e.g. messages
inside lists inside messages inside maps etc.) using simple text templating can
be cumbersome and error-prone, and you need to carefully escape strings, etc.
This PR introduces a new package `lib/tfgen` which can generate Terraform code
programatically from resource structs (think `json.Marshal` but for Terraform
code).
Example usage:
role, err := types.NewRole("my-role", types.RoleSpecV6{/* ... */})
terraformCode, err := tfgen.Generate(role)
You can even inject comments on specific fields:
terraformCode, err := tfgen.Generate(
role,
tfgen.WithFieldComment("spec.allow", "Add your rules here..."),
)
For full examples of the output, see the golden files in `lib/tfgen/testdata`.
How it works:
It uses reflection - `protoreflect` for "modern" resources and Go's `reflect`
package for legacy/gogo-proto resources - to walk the message structure and
extract the fields into an intermediate representation.
Then it walks this intermediate representation and uses the Go HCL library to
emit Terraform code (ensuring proper escaping and formatting).
* Push protobuf type assertion up so we can constrain ReflectLegacy
* Clarify when to use ReflectModern or ReflectLegacy
* Handle embeded structs and other known difficult fields
60 lines
1.7 KiB
Go
60 lines
1.7 KiB
Go
/*
|
|
* Teleport
|
|
* Copyright (C) 2025 Gravitational, Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package transform
|
|
|
|
import "github.com/gravitational/teleport/lib/tfgen/internal"
|
|
|
|
// BotTraits transforms the standard representation of a slice of
|
|
// machineidv1.Trait values into the map[string][]string form we
|
|
// expect in the Terraform provider.
|
|
//
|
|
// Before:
|
|
//
|
|
// [{name = "logins", values = ["root", "ubuntu"]}]
|
|
//
|
|
// After:
|
|
//
|
|
// {logins = ["root", "ubuntu"]}
|
|
//
|
|
// Example usage:
|
|
//
|
|
// tfgen.Generate(bot, tfgen.WithFieldTransform("spec.traits", transform.BotTraits))
|
|
func BotTraits(value *internal.Value) (*internal.Value, error) {
|
|
traitsMap := make(map[any]*internal.Value)
|
|
|
|
for _, elem := range value.List().Elems {
|
|
message := elem.Message()
|
|
|
|
name := message.AttributeNamed("name").Value.String()
|
|
values := message.AttributeNamed("values").Value.List()
|
|
|
|
traitsMap[name] = &internal.Value{
|
|
Type: internal.TypeList,
|
|
Value: values,
|
|
}
|
|
}
|
|
|
|
return &internal.Value{
|
|
Type: internal.TypeMap,
|
|
Value: &internal.MapValue{
|
|
Elems: traitsMap,
|
|
},
|
|
}, nil
|
|
}
|