Skip to content

Commit 3aa46a5

Browse files
authored
improve tests to avoid manually docker push (#481)
Signed-off-by: wbpcode <wbphub@gmail.com>
1 parent 7023af2 commit 3aa46a5

6 files changed

Lines changed: 70 additions & 107 deletions

File tree

cli/e2e/create_test.go

Lines changed: 27 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import (
1313
"os/exec"
1414
"path/filepath"
1515
"runtime"
16-
"strconv"
17-
"strings"
1816
"testing"
1917

2018
"github.com/stretchr/testify/require"
@@ -77,7 +75,15 @@ func TestCreateGoWithDockerSupport(t *testing.T) {
7775
require.NoError(t, err, "Makefile build_image target should be valid")
7876

7977
manifest := &extensions.Manifest{Name: "test-docker", Version: version, Type: extensions.TypeGo, CShared: true}
80-
pushOCIImageForDownload(t, extensionDir, "./Dockerfile", manifest)
78+
// Build and push the single-platform image via the Makefile's push_image target, which
79+
// emits the manifest-level OCI annotations required by boe download.
80+
// #nosec G204
81+
pushCmd := exec.CommandContext(ctx, "make", "push_image", "PLATFORMS=linux/"+runtime.GOARCH)
82+
pushCmd.Dir = extensionDir
83+
pushOutput, err := pushCmd.CombinedOutput()
84+
t.Logf("make push_image output: %s", string(pushOutput))
85+
require.NoError(t, err, "Makefile push_image target should be valid")
86+
8187
requireDownloadHasFiles(t, manifest, "manifest.yaml", "config.schema.json")
8288
})
8389
})
@@ -117,7 +123,15 @@ func TestCreateGoWithDockerSupport(t *testing.T) {
117123
require.NoError(t, err, "Makefile build_image_plugin target should be valid")
118124

119125
manifest := &extensions.Manifest{Name: "test-docker", Version: version, Type: extensions.TypeGo, CShared: false}
120-
pushOCIImageForDownload(t, extensionDir, "./Dockerfile.plugin", manifest)
126+
// Build and push the single-platform plugin image via the Makefile's push_image_plugin
127+
// target, which emits the manifest-level OCI annotations required by boe download.
128+
// #nosec G204
129+
pushCmd := exec.CommandContext(ctx, "make", "push_image_plugin", "PLATFORMS=linux/"+runtime.GOARCH)
130+
pushCmd.Dir = extensionDir
131+
pushOutput, err := pushCmd.CombinedOutput()
132+
t.Logf("make push_image_plugin output: %s", string(pushOutput))
133+
require.NoError(t, err, "Makefile push_image_plugin target should be valid")
134+
121135
requireDownloadHasFiles(t, manifest, "manifest.yaml", "config.schema.json")
122136
})
123137
})
@@ -178,63 +192,20 @@ func TestCreateRustNetworkAndListenerFilters(t *testing.T) {
178192
t.Logf("make build_image output: %s", string(output))
179193
require.NoError(t, err, "Makefile build_image target should be valid")
180194

181-
// Push local image to registry and check its annotations
182-
pushOCIImageForDownload(t, extensionDir, "./Dockerfile", manifest)
195+
// Build and push the single-platform image to the registry via the Makefile's push_image
196+
// target, then check its annotations are usable by boe download.
197+
// #nosec G204
198+
pushCmd := exec.CommandContext(ctx, "make", "push_image", "PLATFORMS=linux/"+runtime.GOARCH)
199+
pushCmd.Dir = extensionDir
200+
pushOutput, err := pushCmd.CombinedOutput()
201+
t.Logf("make push_image output: %s", string(pushOutput))
202+
require.NoError(t, err, "Makefile push_image target should be valid")
203+
183204
requireDownloadHasFiles(t, manifest, "manifest.yaml")
184205
})
185206
}
186207
}
187208

188-
// pushOCIImageForDownload pushes the extension image to the test registry in OCI format,
189-
// preserving the manifest annotations required by boe download.
190-
// It does not use the `make push_image` target because it is meant for multi-platform builds
191-
// and here we want to build for a single platform. We call directly buildx build with the
192-
// minimum set of annotations and config.
193-
func pushOCIImageForDownload(t *testing.T, extensionDir, dockerfile string, manifest *extensions.Manifest) {
194-
t.Helper()
195-
ctx := t.Context()
196-
197-
// Ensure boe-builder exists; the Makefile's create_builder target is idempotent.
198-
// #nosec G204
199-
createBuilderCmd := exec.CommandContext(ctx, "make", "create_builder")
200-
createBuilderCmd.Dir = extensionDir
201-
out, err := createBuilderCmd.CombinedOutput()
202-
t.Logf("make create_builder output: %s", string(out))
203-
require.NoError(t, err, "Should be able to ensure boe-builder exists")
204-
205-
args := []string{
206-
"buildx", "build",
207-
"--builder", "boe-builder",
208-
"--platform", fmt.Sprintf("linux/%s", runtime.GOARCH),
209-
"--output", "type=registry,oci-mediatypes=true,registry.insecure=true",
210-
"--provenance=false",
211-
"--tag", fmt.Sprintf("%s/extension-%s:%s", registryAddr, manifest.Name, manifest.Version),
212-
"-f", dockerfile,
213-
"--annotation", fmt.Sprintf("manifest:org.opencontainers.image.title=%s", manifest.Name),
214-
"--annotation", fmt.Sprintf("manifest:org.opencontainers.image.version=%s", manifest.Version),
215-
"--annotation", fmt.Sprintf("manifest:%s=%s", extensions.OCIAnnotationExtensionType, string(manifest.Type)),
216-
"--annotation", fmt.Sprintf("manifest:%s=%s", extensions.OCIAnnotationCShared, strconv.FormatBool(manifest.CShared)),
217-
}
218-
if manifest.Type == extensions.TypeGo {
219-
args = append(args, "--build-arg", fmt.Sprintf("NAME=%s", manifest.Name))
220-
}
221-
if len(manifest.FilterTypes) > 0 {
222-
filterTypes := make([]string, len(manifest.FilterTypes))
223-
for i, ft := range manifest.FilterTypes {
224-
filterTypes[i] = string(ft)
225-
}
226-
args = append(args, "--annotation", fmt.Sprintf("manifest:%s=%s", extensions.OCIAnnotationFilterType, strings.Join(filterTypes, ",")))
227-
}
228-
args = append(args, ".")
229-
230-
// #nosec G204
231-
pushCmd := exec.CommandContext(ctx, "docker", args...)
232-
pushCmd.Dir = extensionDir
233-
output, err := pushCmd.CombinedOutput()
234-
t.Logf("pushOCIImageForDownload output: %s", string(output))
235-
require.NoError(t, err, "Should be able to push OCI image to local registry")
236-
}
237-
238209
func fetchManifest(t *testing.T, registry, repository, reference string) {
239210
url := fmt.Sprintf("http://%s/v2/%s/manifests/%s", registry, repository, reference)
240211
// Set Accept header to request OCI media types to ensure the manifest is in OCI format

cli/e2e/run_test.go

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"net/http/httptest"
1414
"os"
1515
"os/exec"
16-
"path/filepath"
1716
"runtime"
1817
"strings"
1918
"testing"
@@ -326,7 +325,8 @@ func dummy() string {
326325
// corazaBuildTags mirrors BUILD_TAGS in extensions/composer/Makefile.common; the composer and its
327326
// TestGoPluginLoaderRemoteExtension exercises the raw goplugin-loader extension end to end:
328327
// 1. build the example Go plugin image and push it to the local test registry;
329-
// 2. build the composer-lite image and extract libcomposer.so into the local cache;
328+
// 2. build the composer-lite image and push it to the local test registry, so boe downloads
329+
// libcomposer.so from there into the local cache;
330330
// 3. run `boe run --extension goplugin-loader --config '{"name":...,"url":"oci://..."}'`
331331
// and assert the dynamically loaded plugin processes responses.
332332
//
@@ -358,32 +358,25 @@ func TestGoPluginLoaderRemoteExtension(t *testing.T) {
358358
dataHome := t.TempDir()
359359
t.Setenv("BOE_DATA_HOME", dataHome)
360360

361-
// The build_image targets below run on the active buildx builder. Select the default (docker
362-
// driver) builder: it builds via the Docker daemon, reusing the daemon's local base-image cache
363-
// and proxy/registry configuration, which keeps a single-platform local build (type=docker)
364-
// self-contained. We only need a local image to push, not a multi-platform registry export.
365-
runCmd(t, "", "docker", "buildx", "use", "default")
366-
367-
// Step 1: build the example Go plugin image (single-platform, --output type=docker) and push it
368-
// to the local registry. We use build_image rather than push_image: the latter does a
369-
// multi-platform registry export with index-level OCI annotations, which buildkit rejects for a
370-
// single-platform export ("index annotations not supported for single platform export").
371-
// build_image tags the image <HUB>/extension-<name>:<version>-<os>-<arch>, with HUB derived from
372-
// OCI_REGISTRY; we then push that tag (the daemon treats localhost/127.0.0.1 as insecure).
373-
pluginRef := fmt.Sprintf("%s/built-on-envoy/extension-example-go:%s-linux-%s", registryAddr, version, runtime.GOARCH)
374-
runCmd(t, composerDir, "make", "-f", "Makefile.plugin", "build_image",
361+
// Step 1: build the example Go plugin image for the local platform and push it to the local
362+
// registry via the Makefile's push_image target. PLATFORMS=linux/<arch> selects a single-platform
363+
// export, for which push_image emits manifest-level OCI annotations (no index annotations, which
364+
// buildkit rejects for single-platform). The target pushes directly (--output type=registry), so
365+
// no separate docker push is needed. It tags the image <HUB>/extension-<name>:<version>, with HUB
366+
// derived from OCI_REGISTRY; BOE_REGISTRY_INSECURE (set above) makes the export insecure.
367+
pluginRef := fmt.Sprintf("%s/built-on-envoy/extension-example-go:%s", registryAddr, version)
368+
runCmd(t, composerDir, "make", "-f", "Makefile.plugin", "push_image",
369+
"PLATFORMS=linux/"+runtime.GOARCH,
375370
"EXTENSION_PATH=example",
376371
"OCI_REGISTRY="+registryAddr,
377372
)
378-
runCmd(t, "", "docker", "push", pluginRef)
379373
pluginURL := "oci://" + pluginRef
380374

381-
// Step 2: build the composer-lite image and extract libcomposer.so into the local cache
382-
// at <dataHome>/extensions/dym/composer/<version>/libcomposer.so.
383-
runCmd(t, composerDir, "make", "build_image", "COMPOSER_LITE=true")
384-
composerImage := fmt.Sprintf("%s/composer-lite:%s-linux-%s", registryAddr, version, runtime.GOARCH)
385-
composerCacheDir := filepath.Join(dataHome, "extensions", "dym", "composer", version)
386-
extractFileFromImage(t, composerImage, "/libcomposer.so", filepath.Join(composerCacheDir, "libcomposer.so"))
375+
// Step 2: build the composer-lite image and push it to the local registry (as
376+
// <registry>/composer-lite:<version>). When the goplugin-loader runs in Step 3, boe downloads
377+
// libcomposer.so from there into <dataHome>/extensions/dym/composer/<version>/libcomposer.so.
378+
// This exercises the real composer download path instead of extracting the file manually.
379+
runCmd(t, composerDir, "make", "push_image", "COMPOSER_LITE=true", "PLATFORMS=linux/"+runtime.GOARCH)
387380

388381
// Step 3: run the goplugin-loader extension, pointing it at the pushed plugin image via
389382
// the user-supplied URL.
@@ -404,28 +397,6 @@ func TestGoPluginLoaderRemoteExtension(t *testing.T) {
404397
})
405398
}
406399

407-
// extractFileFromImage copies a single file out of a locally-built (loaded) Docker image into dst.
408-
func extractFileFromImage(t *testing.T, image, srcPath, dst string) {
409-
t.Helper()
410-
require.NoError(t, os.MkdirAll(filepath.Dir(dst), 0o750))
411-
412-
// The composer image is FROM scratch with no default command, so "docker create" needs an
413-
// explicit (dummy) command. It is never executed; we only use the container's filesystem.
414-
// #nosec G204 -- test-controlled image reference.
415-
created, err := exec.CommandContext(t.Context(), "docker", "create", image, "extract").Output()
416-
require.NoError(t, err, "docker create %s", image)
417-
containerID := strings.TrimSpace(string(created))
418-
t.Cleanup(func() {
419-
// #nosec G204 -- container ID produced by docker create above.
420-
_ = exec.Command("docker", "rm", "-f", containerID).Run()
421-
})
422-
423-
// #nosec G204 -- test-controlled container ID and paths.
424-
out, err := exec.CommandContext(t.Context(), "docker", "cp", containerID+":"+srcPath, dst).CombinedOutput()
425-
require.NoError(t, err, "docker cp %s: %s", srcPath, out)
426-
require.FileExists(t, dst)
427-
}
428-
429400
// runCmd runs name with args (in dir, if non-empty), logging the combined output and failing the
430401
// test on a non-zero exit.
431402
func runCmd(t *testing.T, dir, name string, args ...string) {

cli/internal/testing/docker.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import (
99
"context"
1010
"fmt"
1111
"math/rand"
12+
"os"
1213
"os/exec"
14+
"strings"
1315
"testing"
1416
"time"
1517

@@ -23,13 +25,29 @@ var rnd = rand.New(rand.NewSource(time.Now().UnixNano())) //nolint: gosec
2325
func CreateBuildxBuilder(ctx context.Context) (string, func(), error) {
2426
// Create a new builder instance that uses the custom buildkit configuration and host network.
2527
builderName := fmt.Sprintf("test-builder-%d", rnd.Int())
26-
// #nosec G204
27-
createBuilderCmd := exec.CommandContext(ctx, "docker", "buildx", "create",
28+
args := []string{
29+
"buildx", "create",
2830
"--name", builderName,
2931
"--use",
3032
"--driver", "docker-container",
3133
"--driver-opt", "network=host",
32-
)
34+
}
35+
// Forward proxy settings into the buildkit container. The buildx CLI does not propagate these
36+
// automatically to docker-container builders, so in proxied environments buildkit would dial
37+
// public registries (e.g. docker.io) directly and time out when pulling base images. Each
38+
// "env.KEY=value" token is wrapped in quotes so buildx's CSV driver-opt parser keeps
39+
// comma-separated values (such as NO_PROXY) intact.
40+
for _, key := range []string{"HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY"} {
41+
v := os.Getenv(key)
42+
if v == "" {
43+
v = os.Getenv(strings.ToLower(key))
44+
}
45+
if v != "" {
46+
args = append(args, "--driver-opt", fmt.Sprintf(`"env.%s=%s"`, key, v))
47+
}
48+
}
49+
// #nosec G204
50+
createBuilderCmd := exec.CommandContext(ctx, "docker", args...)
3351
if err := createBuilderCmd.Run(); err != nil {
3452
return "", nil, fmt.Errorf("failed to create buildx builder: %w", err)
3553
}

extensions/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ SOURCE := https://$(subst ghcr.io,github.com,$(HUB))
4747
# last development version.
4848
# We still keep the versioned development tag because the `boe run` command relies on those when
4949
# using the `--dev` flag.
50-
DEV_LATEST := $(firstword $(subst :, ,$(IMAGE))):dev
50+
# Strip the trailing ':<version>' tag from IMAGE (not every colon, so registry host:port is kept).
51+
DEV_LATEST := $(patsubst %:$(VERSION)$(IMAGE_NONCE),%,$(IMAGE)):dev
5152
DEV_TAG := $(if $(filter %-dev,$(VERSION)),--tag $(DEV_LATEST))
5253

5354
# Base OCI image annotations (shared by all image types)

extensions/composer/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ COMPOSER_BUILD_TAG := $(if $(BUILD_TAGS),-tags $(BUILD_TAGS))
5454
# last development version.
5555
# We still keep the versioned development tag because the `boe run` command relies on those when
5656
# using the `--dev` flag.
57-
DEV_LATEST := $(firstword $(subst :, ,$(IMAGE))):dev
57+
# Strip the trailing ':<version>' tag from IMAGE (not every colon, so registry host:port is kept).
58+
DEV_LATEST := $(patsubst %:$(VERSION)$(IMAGE_NONCE),%,$(IMAGE)):dev
5859
DEV_TAG := $(if $(filter %-dev,$(VERSION)),--tag $(DEV_LATEST))
5960

6061
OS := linux

extensions/composer/Makefile.plugin

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ SOURCE := https://$(subst ghcr.io,github.com,$(HUB))
5252
# last development version.
5353
# We still keep the versioned development tag because the `boe run` command relies on those when
5454
# using the `--dev` flag.
55-
DEV_LATEST := $(firstword $(subst :, ,$(IMAGE))):dev
55+
# Strip the trailing ':<version>' tag from IMAGE (not every colon, so registry host:port is kept).
56+
DEV_LATEST := $(patsubst %:$(VERSION)$(IMAGE_NONCE),%,$(IMAGE)):dev
5657
DEV_TAG := $(if $(filter %-dev,$(VERSION)),--tag $(DEV_LATEST))
5758

5859
ARCH := $(shell uname -m)

0 commit comments

Comments
 (0)