Skip to content

Commit d749dee

Browse files
slonkaCopilotCopilot
authored
feat(builder): make builder more generic (#49)
## Pull request overview This PR makes the builder more generic by introducing a new `hclbuilder` package that provides a type-safe, fluent API for building and modifying HCL configurations. This is a significant enhancement that moves away from template-based approaches to using the official HashiCorp HCL write library. **Key changes:** - Adds new `hclbuilder` package with comprehensive HCL manipulation capabilities - Updates dependencies (hcl/v2, go-cty, go-wordwrap versions) - Minor code formatting improvements (whitespace cleanup) xrel: #47 https://github.com/Kong/terraform-provider-kong-mesh/pull/98/files --------- Signed-off-by: slonka <slonka@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: slonka <2753650+slonka@users.noreply.github.com>
1 parent a5e95ac commit d749dee

File tree

86 files changed

+2860
-12
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+2860
-12
lines changed

hclbuilder/README.md

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# hclbuilder
2+
3+
Generic HCL builder for programmatically creating and modifying HCL configurations.
4+
5+
## Overview
6+
7+
`hclbuilder` provides a fluent API for building and modifying HCL (HashiCorp Configuration Language) files. Unlike the template-based `tfbuilder` package, this uses the official `hashicorp/hcl/v2/hclwrite` library for type-safe HCL manipulation.
8+
9+
## Installation
10+
11+
```bash
12+
go get github.com/Kong/shared-speakeasy/hclbuilder
13+
```
14+
15+
## Usage
16+
17+
### Create from scratch
18+
19+
```go
20+
import "github.com/Kong/shared-speakeasy/hclbuilder"
21+
22+
builder := hclbuilder.New()
23+
builder.SetBlock("resource.kong-mesh_mesh.default", map[string]any{
24+
"type": "Mesh",
25+
"name": "mesh-1",
26+
"skip_creating_initial_policies": []string{"*"},
27+
})
28+
builder.SetBlock("resource.kong-mesh_mesh_traffic_permission.allow_all", map[string]any{
29+
"type": "MeshTrafficPermission",
30+
"name": "allow-all",
31+
"mesh": "kong-mesh_mesh.default.name",
32+
"spec": map[string]any{
33+
"from": []any{
34+
map[string]any{
35+
"target_ref": map[string]any{
36+
"kind": "Mesh",
37+
},
38+
},
39+
},
40+
},
41+
})
42+
43+
hcl := builder.Build()
44+
```
45+
46+
### Load from file
47+
48+
```go
49+
builder, err := hclbuilder.FromFile("main.tf")
50+
if err != nil {
51+
log.Fatal(err)
52+
}
53+
54+
// Modify the loaded configuration
55+
builder.SetAttribute("resource.kong-mesh_mesh.default.name", "mesh-2")
56+
builder.RemoveBlock("resource.kong-mesh_mesh_traffic_permission.old_policy")
57+
58+
// Write back to file
59+
err = builder.WriteFile("main.tf")
60+
```
61+
62+
### Parse from string
63+
64+
```go
65+
hclBlock := `
66+
resource "kong-mesh_mesh" "default" {
67+
name = "mesh-1"
68+
type = "Mesh"
69+
}
70+
`
71+
builder, err := hclbuilder.FromString(hclBlock)
72+
if err != nil {
73+
log.Fatal(err)
74+
}
75+
// Use builder as needed
76+
```
77+
78+
### Set attributes
79+
80+
```go
81+
// Path format: "block_type.block_label.attribute_name"
82+
builder.SetAttribute("resource.kong-mesh_mesh.default.name", "mesh-1")
83+
builder.SetAttribute("resource.kong-mesh_mesh.default.type", "Mesh")
84+
builder.SetAttribute("resource.kong-mesh_mesh_traffic_permission.allow_all.mesh", "kong-mesh_mesh.default.name")
85+
```
86+
87+
### Create blocks
88+
89+
```go
90+
// Simple block
91+
builder.SetBlock("resource.kong-mesh_mesh.default", map[string]any{
92+
"type": "Mesh",
93+
"name": "mesh-1",
94+
"skip_creating_initial_policies": []string{"*"},
95+
})
96+
97+
// Nested blocks (maps are treated as nested blocks)
98+
builder.SetBlock("resource.kong-mesh_mesh_traffic_permission.allow_all", map[string]any{
99+
"type": "MeshTrafficPermission",
100+
"name": "allow-all",
101+
"mesh": "kong-mesh_mesh.default.name",
102+
"spec": map[string]any{
103+
"from": []any{
104+
map[string]any{
105+
"target_ref": map[string]any{
106+
"kind": "Mesh",
107+
},
108+
},
109+
},
110+
},
111+
})
112+
```
113+
114+
### Remove attributes and blocks
115+
116+
```go
117+
builder.RemoveAttribute("resource.kong-mesh_mesh.default.skip_creating_initial_policies")
118+
builder.RemoveBlock("resource.kong-mesh_mesh_traffic_permission.old_policy")
119+
```
120+
121+
## API
122+
123+
### Constructor Functions
124+
125+
- `New() *Builder` - Create empty builder
126+
- `FromFile(path string) (*Builder, error)` - Load from HCL file
127+
- `FromString(content string) (*Builder, error)` - Parse HCL from string
128+
129+
### Methods
130+
131+
- `Build() string` - Generate HCL string
132+
- `WriteFile(path string) error` - Write to file
133+
- `SetAttribute(path string, value any)` - Set attribute value
134+
- `SetBlock(path string, attributes map[string]any)` - Create/replace block
135+
- `RemoveAttribute(path string)` - Remove attribute
136+
- `RemoveBlock(path string)` - Remove block
137+
138+
### Path Format
139+
140+
Paths use dot notation:
141+
- Attributes: `"block_type.block_label.attribute_name"`
142+
- Blocks: `"block_type.block_label1.block_label2"`
143+
144+
Examples:
145+
- `"variable.mesh_name.default"``variable "mesh_name" { default = ... }`
146+
- `"resource.kong-mesh_mesh.default"``resource "kong-mesh_mesh" "default" { ... }`
147+
148+
## Testing
149+
150+
```bash
151+
# Run tests
152+
go test ./...
153+
154+
# Update golden files
155+
UPDATE_GOLDEN_FILES=1 go test ./...
156+
```
157+
158+
## Test Helpers
159+
160+
The package includes test helpers for Terraform provider testing:
161+
162+
### TestBuilder
163+
164+
Provider-specific builder for generating test configurations:
165+
166+
```go
167+
import "github.com/Kong/shared-speakeasy/hclbuilder"
168+
169+
// Create a test builder
170+
builder := hclbuilder.NewTestBuilder(hclbuilder.KongMesh)
171+
172+
// Add resources
173+
builder.AddMesh("mesh-1", "default", map[string]any{
174+
"skip_creating_initial_policies": []string{"*"},
175+
})
176+
177+
// Generate HCL
178+
config := builder.Build()
179+
```
180+
181+
### Test Case Functions
182+
183+
Reusable test cases for common scenarios:
184+
185+
- `CreateMeshAndModifyFields` - Tests mesh creation and field modifications
186+
- `CreatePolicyAndModifyFields` - Tests policy creation and field modifications
187+
- `NotImportedResourceShouldError` - Tests error handling for non-imported resources
188+
189+
See `test_cases.go` for details.
190+
191+
## Differences from tfbuilder
192+
193+
- **Generic**: Not tied to specific Terraform providers
194+
- **Type-safe**: Uses `hclwrite` library instead of templates
195+
- **File I/O**: Can read and modify existing HCL files
196+
- **Simpler API**: Unified interface for all HCL manipulation

0 commit comments

Comments
 (0)