Skip to content

Commit eaab97a

Browse files
authored
test(mesh): bootstrap builder for tf templating (#5)
xrel: - Kong/kong-mesh#7697 - split from: Kong/terraform-provider-kong-mesh#13 - usage: https://github.com/Kong/terraform-provider-kong-mesh/pull/13/files#diff-3985d78eaab3779db8be1d6749202977ccdc1d7b20b61aa699fe8eb78103b35bR67 --------- Signed-off-by: slonka <slonka@users.noreply.github.com>
1 parent d518cb1 commit eaab97a

File tree

16 files changed

+536
-1
lines changed

16 files changed

+536
-1
lines changed

.github/workflows/test.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: test
2+
concurrency:
3+
group: test-${{ github.ref }}
4+
on:
5+
pull_request:
6+
7+
jobs:
8+
unit:
9+
runs-on: ubuntu-24.04
10+
timeout-minutes: 10
11+
steps:
12+
- name: Checkout Source Code
13+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
14+
- name: Setup go
15+
uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
16+
with:
17+
go-version: '^1.24'
18+
- run: make install-tools
19+
- run: make test-all

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ lint-all: ## lint all packages of the repository
99
.PHONY: lint-fix-all
1010
lint-fix-all:
1111
find . -name go.sum -exec dirname {} \; | xargs -I{} /bin/sh -c "cd {} && golangci-lint run --fix ./..."
12+
13+
.PHONY: test-all
14+
test-all: ## lint all packages of the repository
15+
find . -name go.sum -exec dirname {} \; | xargs -I{} /bin/sh -c "cd {} && go test ./... -v"

planmodifiers/suppress_zero_null/objectplanmodifier/supress_zero_null_modifier.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ func allNestedAttributesAreNull(obj basetypes.ObjectValue) bool {
6262
// we conclude that not all attributes are null.
6363
return false
6464
}
65-
6665
}
6766

6867
// If we finish iterating without finding a non-null attribute,

tfbuilder/builder.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package tfbuilder
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strings"
7+
"text/template"
8+
)
9+
10+
type ProviderType string
11+
12+
const (
13+
KongMesh ProviderType = "kong-mesh"
14+
Konnect ProviderType = "konnect"
15+
KonnectBeta ProviderType = "konnect-beta"
16+
)
17+
18+
type Builder struct {
19+
provider ProviderType
20+
providerProperty ProviderType // optional
21+
scheme string
22+
host string
23+
port int
24+
controlPlanes map[string]*ControlPlane
25+
meshes map[string]*MeshBuilder
26+
policies map[string]*PolicyBuilder
27+
}
28+
29+
func NewBuilder(provider ProviderType, scheme, host string, port int) *Builder {
30+
return &Builder{
31+
provider: provider,
32+
scheme: scheme,
33+
host: host,
34+
port: port,
35+
controlPlanes: make(map[string]*ControlPlane),
36+
meshes: make(map[string]*MeshBuilder),
37+
policies: make(map[string]*PolicyBuilder),
38+
}
39+
}
40+
41+
func (b *Builder) AddControlPlane(cp *ControlPlane) *Builder {
42+
b.controlPlanes[cp.ResourceName] = cp
43+
return b
44+
}
45+
46+
func (b *Builder) AddMesh(mesh *MeshBuilder) *Builder {
47+
b.meshes[mesh.ResourceName] = mesh
48+
return b
49+
}
50+
51+
func (b *Builder) RemoveMesh(name string) *Builder {
52+
delete(b.meshes, name)
53+
return b
54+
}
55+
56+
func (b *Builder) AddPolicy(p *PolicyBuilder) *Builder {
57+
b.policies[p.ResourceName] = p
58+
return b
59+
}
60+
61+
func (b *Builder) RemovePolicy(name string) *Builder {
62+
delete(b.policies, name)
63+
return b
64+
}
65+
66+
func (b *Builder) WithProviderProperty(providerProperty ProviderType) *Builder {
67+
b.providerProperty = providerProperty
68+
return b
69+
}
70+
71+
func (b *Builder) Build() string {
72+
var sb strings.Builder
73+
74+
sb.WriteString(b.renderTemplate("provider.tmpl", map[string]interface{}{
75+
"Provider": b.provider,
76+
"ProviderProperty": b.providerProperty,
77+
"Scheme": b.scheme,
78+
"Host": b.host,
79+
"Port": b.port,
80+
}))
81+
sb.WriteString("\n")
82+
83+
for _, cp := range b.controlPlanes {
84+
sb.WriteString(cp.Render(b))
85+
sb.WriteString("\n")
86+
}
87+
88+
for _, mesh := range b.meshes {
89+
sb.WriteString(mesh.Render(b))
90+
sb.WriteString("\n")
91+
}
92+
93+
for _, policy := range b.policies {
94+
sb.WriteString(policy.Render(b))
95+
sb.WriteString("\n")
96+
}
97+
98+
return sb.String()
99+
}
100+
101+
func (b *Builder) ResourceAddress(resourceType, resourceName string) string {
102+
return fmt.Sprintf("%s_%s.%s", b.provider, resourceType, resourceName)
103+
}
104+
105+
func (b *Builder) renderTemplate(file string, data interface{}) string {
106+
tmplBytes, err := templatesFS.ReadFile("templates/" + file)
107+
if err != nil {
108+
panic(fmt.Errorf("failed to read template %s: %w", file, err))
109+
}
110+
111+
tmpl, err := template.New(file).Parse(string(tmplBytes))
112+
if err != nil {
113+
panic(fmt.Errorf("failed to parse template: %w", err))
114+
}
115+
116+
var out bytes.Buffer
117+
if err := tmpl.Execute(&out, data); err != nil {
118+
panic(fmt.Errorf("failed to render template: %w", err))
119+
}
120+
return out.String()
121+
}

tfbuilder/builder_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package tfbuilder_test
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/Kong/shared-speakeasy/tfbuilder"
11+
)
12+
13+
func TestBuilder_KongMeshWithPolicy(t *testing.T) {
14+
builder := tfbuilder.NewBuilder(tfbuilder.KongMesh, "http", "localhost", 5681)
15+
16+
// Add mesh
17+
builder.AddMesh(
18+
tfbuilder.NewMeshBuilder("default", "mesh-1").
19+
WithSpec(`skip_creating_initial_policies = [ "*" ]`),
20+
)
21+
22+
// Add policy
23+
builder.AddPolicy(
24+
tfbuilder.NewPolicyBuilder("mesh_traffic_permission", "allow_all", "allow-all", "MeshTrafficPermission").
25+
WithMeshRef("kong-mesh_mesh.default.name").
26+
WithDependsOn("kong-mesh_mesh.default").
27+
WithLabels(map[string]string{
28+
"kuma.io/mesh": "kong-mesh_mesh.default.name",
29+
}).
30+
WithSpecHCL(tfbuilder.AllowAllTrafficPermissionSpec),
31+
)
32+
33+
actual := builder.Build()
34+
35+
goldenFile := filepath.Join("testdata", "expected_kong_mesh_with_policy.tf")
36+
37+
if updateGoldenFiles(t, goldenFile, actual) {
38+
return
39+
}
40+
41+
expected, err := os.ReadFile(goldenFile)
42+
require.NoError(t, err, "reading golden file")
43+
44+
require.Equal(t, string(expected), actual)
45+
}
46+
47+
func updateGoldenFiles(t *testing.T, goldenFile string, actual string) bool {
48+
if os.Getenv("UPDATE_GOLDEN_FILES") == "1" {
49+
err := os.WriteFile(goldenFile, []byte(actual), 0o600)
50+
require.NoError(t, err, "updating golden file")
51+
t.Logf("updated golden file: %s", goldenFile)
52+
return true
53+
}
54+
return false
55+
}

tfbuilder/control_plane.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package tfbuilder
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"text/template"
7+
)
8+
9+
type ControlPlane struct {
10+
ResourceName string
11+
Name string
12+
Description string
13+
}
14+
15+
func NewControlPlane(resourceName, name, description string) *ControlPlane {
16+
return &ControlPlane{
17+
ResourceName: resourceName,
18+
Name: name,
19+
Description: description,
20+
}
21+
}
22+
23+
func (cp *ControlPlane) Render(provider *Builder) string {
24+
data := map[string]interface{}{
25+
"Provider": provider.provider,
26+
"ProviderProperty": provider.providerProperty,
27+
"ResourceName": cp.ResourceName,
28+
"Name": cp.Name,
29+
"Description": cp.Description,
30+
}
31+
32+
tmplBytes, err := templatesFS.ReadFile("templates/control_plane.tmpl")
33+
if err != nil {
34+
panic(fmt.Errorf("failed to read control plane template: %w", err))
35+
}
36+
37+
tmpl, err := template.New("control_plane").Parse(string(tmplBytes))
38+
if err != nil {
39+
panic(err)
40+
}
41+
42+
var buf bytes.Buffer
43+
if err := tmpl.Execute(&buf, data); err != nil {
44+
panic(err)
45+
}
46+
47+
return buf.String()
48+
}

tfbuilder/go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/Kong/shared-speakeasy/tfbuilder
2+
3+
go 1.24.1
4+
5+
require github.com/stretchr/testify v1.10.0
6+
7+
require (
8+
github.com/davecgh/go-spew v1.1.1 // indirect
9+
github.com/pmezard/go-difflib v1.0.0 // indirect
10+
gopkg.in/yaml.v3 v3.0.1 // indirect
11+
)

tfbuilder/go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
6+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
8+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
9+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
10+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

tfbuilder/mesh.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package tfbuilder
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"text/template"
7+
)
8+
9+
type MeshBuilder struct {
10+
ResourceName string
11+
MeshName string
12+
DependsOn []string
13+
Spec string
14+
CPID string // Optional
15+
}
16+
17+
func NewMeshBuilder(resourceName, meshName string) *MeshBuilder {
18+
return &MeshBuilder{
19+
ResourceName: resourceName,
20+
MeshName: meshName,
21+
}
22+
}
23+
24+
func (m *MeshBuilder) WithSpec(spec string) *MeshBuilder {
25+
m.Spec = spec
26+
return m
27+
}
28+
29+
func (m *MeshBuilder) WithCPID(cpID string) *MeshBuilder {
30+
m.CPID = cpID
31+
return m
32+
}
33+
34+
func (m *MeshBuilder) WithDependsOn(deps ...string) *MeshBuilder {
35+
m.DependsOn = append(m.DependsOn, deps...)
36+
return m
37+
}
38+
39+
func (m *MeshBuilder) Render(provider *Builder) string {
40+
data := map[string]interface{}{
41+
"Provider": provider.provider,
42+
"ProviderProperty": provider.providerProperty,
43+
"ResourceName": m.ResourceName,
44+
"MeshName": m.MeshName,
45+
"DependsOn": m.DependsOn,
46+
"Spec": m.Spec,
47+
"CPID": m.CPID,
48+
}
49+
50+
tmplBytes, err := templatesFS.ReadFile("templates/mesh.tmpl")
51+
if err != nil {
52+
panic(fmt.Errorf("failed to read mesh template: %w", err))
53+
}
54+
55+
tmpl, err := template.New("mesh").Parse(string(tmplBytes))
56+
if err != nil {
57+
panic(err)
58+
}
59+
60+
var buf bytes.Buffer
61+
if err := tmpl.Execute(&buf, data); err != nil {
62+
panic(err)
63+
}
64+
65+
return buf.String()
66+
}

0 commit comments

Comments
 (0)