mirror of
https://github.com/coder/terraform-provider-envbuilder.git
synced 2025-07-24 20:47:50 +00:00
167 lines
5.1 KiB
Go
167 lines
5.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package provider
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
|
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
|
|
"github.com/mafredri/terraform-provider-envbuilder/testutil/registrytest"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/image"
|
|
"github.com/docker/docker/client"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
testContainerLabel = "terraform-provider-envbuilder-test"
|
|
)
|
|
|
|
// testAccProtoV6ProviderFactories are used to instantiate a provider during
|
|
// acceptance testing. The factory function will be invoked for every Terraform
|
|
// CLI command executed to create a provider server to which the CLI can
|
|
// reattach.
|
|
var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
|
|
"envbuilder": providerserver.NewProtocol6WithError(New("test")()),
|
|
}
|
|
|
|
func testAccPreCheck(t *testing.T) {
|
|
// You can add code here to run prior to any test case execution, for example assertions
|
|
// about the appropriate environment variables being set are common to see in a pre-check
|
|
// function.
|
|
}
|
|
|
|
type testDependencies struct {
|
|
BuilderImage string
|
|
RepoDir string
|
|
CacheRepo string
|
|
}
|
|
|
|
func setup(t testing.TB, files map[string]string) testDependencies {
|
|
t.Helper()
|
|
|
|
envbuilderImage := getEnvOrDefault("ENVBUILDER_IMAGE", "ghcr.io/coder/envbuilder-preview")
|
|
envbuilderVersion := getEnvOrDefault("ENVBUILDER_VERSION", "latest")
|
|
envbuilderImageRef := envbuilderImage + ":" + envbuilderVersion
|
|
|
|
// TODO: envbuilder creates /.envbuilder/bin/envbuilder owned by root:root which we are unable to clean up.
|
|
// This causes tests to fail.
|
|
repoDir := t.TempDir()
|
|
regDir := t.TempDir()
|
|
reg := registrytest.New(t, regDir)
|
|
writeFiles(t, files, repoDir)
|
|
return testDependencies{
|
|
BuilderImage: envbuilderImageRef,
|
|
CacheRepo: reg + "/test",
|
|
RepoDir: repoDir,
|
|
}
|
|
}
|
|
|
|
func seedCache(ctx context.Context, t testing.TB, deps testDependencies) {
|
|
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
|
require.NoError(t, err, "init docker client")
|
|
t.Cleanup(func() { _ = cli.Close() })
|
|
ensureImage(ctx, t, cli, deps.BuilderImage)
|
|
// Run envbuilder using this dir as a local layer cache
|
|
ctr, err := cli.ContainerCreate(ctx, &container.Config{
|
|
Image: deps.BuilderImage,
|
|
Env: []string{
|
|
"ENVBUILDER_CACHE_REPO=" + deps.CacheRepo,
|
|
"ENVBUILDER_DEVCONTAINER_DIR=" + deps.RepoDir,
|
|
"ENVBUILDER_EXIT_ON_BUILD_FAILURE=true",
|
|
"ENVBUILDER_INIT_SCRIPT=exit",
|
|
// FIXME: Enabling this options causes envbuilder to add its binary to the image under the path
|
|
// /.envbuilder/bin/envbuilder. This file will have ownership root:root and permissions 0o755.
|
|
// Because of this, t.Cleanup() will be unable to delete the temp dir, causing the test to fail.
|
|
// "ENVBUILDER_PUSH_IMAGE=true",
|
|
},
|
|
Labels: map[string]string{
|
|
testContainerLabel: "true",
|
|
}}, &container.HostConfig{
|
|
NetworkMode: container.NetworkMode("host"),
|
|
Binds: []string{deps.RepoDir + ":" + deps.RepoDir},
|
|
}, nil, nil, "")
|
|
require.NoError(t, err, "failed to run envbuilder to seed cache")
|
|
t.Cleanup(func() {
|
|
_ = cli.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{
|
|
RemoveVolumes: true,
|
|
Force: true,
|
|
})
|
|
})
|
|
err = cli.ContainerStart(ctx, ctr.ID, container.StartOptions{})
|
|
require.NoError(t, err)
|
|
|
|
rawLogs, err := cli.ContainerLogs(ctx, ctr.ID, container.LogsOptions{
|
|
ShowStdout: true,
|
|
ShowStderr: true,
|
|
Follow: true,
|
|
Timestamps: false,
|
|
})
|
|
require.NoError(t, err)
|
|
defer rawLogs.Close()
|
|
scanner := bufio.NewScanner(rawLogs)
|
|
SCANLOGS:
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
require.Fail(t, "envbuilder did not finish running in time")
|
|
default:
|
|
if !scanner.Scan() {
|
|
require.Fail(t, "envbuilder did not run successfully")
|
|
}
|
|
log := scanner.Text()
|
|
t.Logf("envbuilder: %s", log)
|
|
if strings.Contains(log, "=== Running the init command") {
|
|
break SCANLOGS
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func getEnvOrDefault(env, defVal string) string {
|
|
if val := os.Getenv(env); val != "" {
|
|
return val
|
|
}
|
|
return defVal
|
|
}
|
|
|
|
func writeFiles(t testing.TB, files map[string]string, destPath string) {
|
|
for relPath, content := range files {
|
|
absPath := filepath.Join(destPath, relPath)
|
|
d := filepath.Dir(absPath)
|
|
bs := []byte(content)
|
|
require.NoError(t, os.MkdirAll(d, 0o755))
|
|
require.NoError(t, os.WriteFile(absPath, bs, 0o644))
|
|
t.Logf("wrote %d bytes to %s", len(bs), absPath)
|
|
}
|
|
}
|
|
|
|
func ensureImage(ctx context.Context, t testing.TB, cli *client.Client, ref string) {
|
|
t.Helper()
|
|
|
|
t.Logf("ensuring image %q", ref)
|
|
images, err := cli.ImageList(ctx, image.ListOptions{})
|
|
require.NoError(t, err, "list images")
|
|
for _, img := range images {
|
|
if slices.Contains(img.RepoTags, ref) {
|
|
t.Logf("image %q found locally, not pulling", ref)
|
|
return
|
|
}
|
|
}
|
|
t.Logf("image %s not found locally, attempting to pull", ref)
|
|
resp, err := cli.ImagePull(ctx, ref, image.PullOptions{})
|
|
require.NoError(t, err)
|
|
_, err = io.ReadAll(resp)
|
|
require.NoError(t, err)
|
|
}
|