mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(coderd/templatebuilder): add exampleID->OS map and base template .tf.tmpl files (#26115)
Add the bundled `exampleID -> OS` Go map for Docker, Kubernetes, and AWS
EC2 Linux base templates. Create `.tf.tmpl` Go template files for each
within `coderd/templatebuilder/bases/`, along with `BaseRenderContext`
and `RenderBaseTemplate` rendering helpers.
The `.tf.tmpl` files are independent copies of the example templates
with module blocks (code-server, jetbrains) removed, since the template
builder composes modules separately into `modules.tf`. When
`ImageOptions` is provided, the container image field references the
Terraform parameter; otherwise it uses the hardcoded value via Go
template whitespace control (`{{-`).
Golden file snapshot tests verify rendered output stability with an
`-update` flag for regeneration.
Depends on #25909
> [!NOTE]
> This PR was authored by Coder Agents on behalf of @jeremyruppel.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package templatebuilder_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/templatebuilder"
|
||||
)
|
||||
|
||||
var updateGolden = flag.Bool("update", false, "update golden files")
|
||||
|
||||
func TestRenderBaseTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("UnknownBase", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := templatebuilder.RenderBaseTemplate("nonexistent", "main.tf.tmpl", templatebuilder.BaseRenderContext{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "unknown base template")
|
||||
})
|
||||
|
||||
t.Run("InvalidPath", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := templatebuilder.RenderBaseTemplate("docker", "nonexistent.tf.tmpl", templatebuilder.BaseRenderContext{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "not found")
|
||||
})
|
||||
|
||||
imageOpts := []templatebuilder.ImageOption{
|
||||
{Name: "Ubuntu", Value: "codercom/enterprise-base:ubuntu"},
|
||||
{Name: "Custom", Value: "custom/image:latest"},
|
||||
}
|
||||
|
||||
t.Run("DockerWithImageOptions", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
renderCtx := templatebuilder.BaseRenderContext{
|
||||
ContainerImage: "custom/image:latest",
|
||||
ImageOptions: imageOpts,
|
||||
}
|
||||
out, err := templatebuilder.RenderBaseTemplate("docker", "main.tf.tmpl", renderCtx)
|
||||
require.NoError(t, err)
|
||||
rendered := string(out)
|
||||
require.Contains(t, rendered, `data.coder_parameter.container_image.value`)
|
||||
require.Contains(t, rendered, `name = "Ubuntu"`)
|
||||
require.Contains(t, rendered, `name = "Custom"`)
|
||||
require.Contains(t, rendered, `coder_parameter`)
|
||||
})
|
||||
|
||||
t.Run("KubernetesWithImageOptions", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
renderCtx := templatebuilder.BaseRenderContext{
|
||||
ContainerImage: "custom/image:latest",
|
||||
ImageOptions: imageOpts,
|
||||
}
|
||||
out, err := templatebuilder.RenderBaseTemplate("kubernetes", "main.tf.tmpl", renderCtx)
|
||||
require.NoError(t, err)
|
||||
rendered := string(out)
|
||||
require.Contains(t, rendered, `data.coder_parameter.container_image.value`)
|
||||
require.Contains(t, rendered, `name = "Ubuntu"`)
|
||||
require.Contains(t, rendered, `coder_parameter`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBaseTemplateSnapshot(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// This test table must cover every known base template.
|
||||
// BaseTemplateIDs() is the source of truth; this list must match.
|
||||
tests := []struct {
|
||||
exampleID string
|
||||
}{
|
||||
{exampleID: "docker"},
|
||||
{exampleID: "kubernetes"},
|
||||
{exampleID: "aws-linux"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.exampleID, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
renderCtx := templatebuilder.DefaultBaseRenderContext(tc.exampleID)
|
||||
rendered, err := templatebuilder.RenderBaseTemplate(tc.exampleID, "main.tf.tmpl", renderCtx)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, rendered)
|
||||
|
||||
goldenPath := filepath.Join("testdata", tc.exampleID+".tf.golden")
|
||||
|
||||
if *updateGolden {
|
||||
err := os.MkdirAll("testdata", 0o755)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(goldenPath, rendered, 0o600)
|
||||
require.NoError(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
expected, err := os.ReadFile(goldenPath)
|
||||
require.NoError(t, err, "golden file %s not found; run with -update to create", goldenPath)
|
||||
require.Equal(t, string(expected), string(rendered),
|
||||
"rendered output for %s does not match golden file; run with -update to regenerate", tc.exampleID)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user