Skip to content

Conversation

octonautcal
Copy link

@octonautcal octonautcal commented Sep 11, 2025

Are you a customer of Octopus Deploy? Please contact our support team so we can triage your PR, so that we can make sure it's handled appropriately.

Background

The boostrapRunner executable is a Go binary that is built as part of the Tentacle image, and copied over to the script pod at runtime. It is used to wrap the script execution so the pod streams logs in a format that Tentacle can parse. It is not built as part of the worker-tools image because customers are free to specify their own worker-tools image, and we need to ensure the logs are in the correct format.

Problem

The current bootstrapRunner exe is tightly coupled with the architecture of the Tentacle container. The limitation exists because we only build the bootstrapRunner exe for the arch that the tentacle image is built on.

RUN go build -ldflags "-s -w" -o "bin/bootstrapRunner"

This becomes a problem when the Tentacle and script pods are running on different platform architectures - i.e. when the script pod is spun up on a different node. In such a scenario the script pod would receive a bootstrapRunner exe that it cannot execute.

Results

Testing the Architecture builds

For testing, I created a simple Dockerfile that was able to check if all of the architectures were able to build correctly. The simple Dockerfile is as below:

FROM golang:1.22 as bootstrapRunnerBuilder

COPY docker/kubernetes-agent-tentacle/bootstrapRunner/* /bootstrapRunner/
WORKDIR /bootstrapRunner

# Create bin directory
RUN mkdir -p bin

# Show build environment
RUN echo "=== BUILD ENVIRONMENT ===" && \
    echo "Go Version: $(go version)" && \
    echo "Build Date: $(date)" && \
    echo "Working Directory: $(pwd)" && \
    echo "Available Files:" && \
    ls -la && \
    echo "========================="

# Build all architectures
RUN echo "=== BUILDING MULTI-ARCH BINARIES ===" && \
    GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o "bin/bootstrapRunner-linux-amd64" && \
    GOOS=linux GOARCH=arm64 go build -ldflags "-s -w" -o "bin/bootstrapRunner-linux-arm64" && \
    GOOS=linux GOARCH=386 go build -ldflags "-s -w" -o "bin/bootstrapRunner-linux-386" && \
    GOOS=linux GOARCH=arm go build -ldflags "-s -w" -o "bin/bootstrapRunner-linux-arm" && \
    echo "=== BUILD COMPLETE ==="

# Copy selection script
COPY docker/kubernetes-agent-tentacle/bootstrapRunner/select-bootstrapRunner.sh bin/
RUN chmod +x bin/select-bootstrapRunner.sh

# Show final results
RUN echo "=== FINAL BUILD RESULTS ===" && \
    echo "All binaries created:" && \
    ls -lah bin/ && \
    echo "Binary sizes:" && \
    du -h bin/bootstrapRunner-* && \
    echo "Total size:" && \
    du -ch bin/bootstrapRunner-* | grep total && \
    echo "Binary verification (executable check):" && \
    test -x bin/bootstrapRunner-linux-amd64 && echo "✓ bootstrapRunner-linux-amd64 is executable" && \
    test -x bin/bootstrapRunner-linux-arm64 && echo "✓ bootstrapRunner-linux-arm64 is executable" && \
    test -x bin/bootstrapRunner-linux-386 && echo "✓ bootstrapRunner-linux-386 is executable" && \
    test -x bin/bootstrapRunner-linux-arm && echo "✓ bootstrapRunner-linux-arm is executable" && \
    echo "Selection script:" && \
    ls -lah bin/select-bootstrapRunner.sh && \
    echo "=============================="

# Create final stage for testing
FROM alpine:latest
COPY --from=bootstrapRunnerBuilder /bootstrapRunner/bin/* /evidence/
RUN echo "=== EVIDENCE COLLECTION ===" && \
    ls -lah /evidence/ && \
    du -ch /evidence/bootstrapRunner-* && \
    echo "========================="
EOF

When collecting the testing evidence, I was able to verify that all of the architectures were built correctly and were all executable:

Binary Verification Results

total 7M     
drwxr-xr-x    2 root     root        4.0K Sep 11 23:20 .
drwxr-xr-x    1 root     root        4.0K Sep 11 23:20 ..
-rwxr-xr-x    1 root     root        1.5M Sep 11 07:46 bootstrapRunner-linux-386
-rwxr-xr-x    1 root     root        1.6M Sep 11 07:46 bootstrapRunner-linux-amd64
-rwxr-xr-x    1 root     root        1.6M Sep 11 07:46 bootstrapRunner-linux-arm
-rwxr-xr-x    1 root     root        1.7M Sep 11 07:46 bootstrapRunner-linux-arm64
-rwxr-xr-x    1 root     root        1.4K Sep 10 06:16 select-bootstrapRunner.sh

Architecture Support Verification

Multi-Architecture Binaries Built Successfully:
-rwxr-xr-x    1 root     root       1597592 Sep 11 07:46 /evidence/bootstrapRunner-linux-386
-rwxr-xr-x    1 root     root       1704088 Sep 11 07:46 /evidence/bootstrapRunner-linux-amd64
-rwxr-xr-x    1 root     root       1704088 Sep 11 07:46 /evidence/bootstrapRunner-linux-arm
-rwxr-xr-x    1 root     root       1769624 Sep 11 07:46 /evidence/bootstrapRunner-linux-arm64
Binary executable verification:
✓ amd64 binary executable
✓ arm64 binary executable
✓ 386 binary executable
✓ arm binary executable

Testing the dynamic selection of architecture

Testing using the following script:

#!/bin/bash
set -eu

# Simulated architecture detection
ARCH="aarch64"
OS="linux"

# Map architecture names to Go architecture naming
case "$ARCH" in
    x86_64)
        GO_ARCH="amd64"
        ;;
    aarch64|arm64)
        GO_ARCH="arm64"
        ;;
    i386|i686)
        GO_ARCH="386"
        ;;
    armv7l|armv6l)
        GO_ARCH="arm"
        ;;
    *)
        echo "Error: Unsupported architecture: $ARCH" >&2
        echo "Supported architectures: x86_64, aarch64, arm64, i386, i686, armv7l, armv6l" >&2
        exit 1
        ;;
esac

# Construct binary name
BINARY_NAME="bootstrapRunner-${OS}-${GO_ARCH}"
BINARY_PATH="$(dirname "$0")/$BINARY_NAME"

# Output detection results
echo "Detected Architecture: $ARCH"
echo "Mapped to Go Arch: $GO_ARCH"
echo "Selected Binary: $BINARY_NAME"
echo "Binary Path: $BINARY_PATH"

# Check if the binary exists
if [ ! -f "$BINARY_PATH" ]; then
    echo "Error: Bootstrap runner binary not found for architecture ${OS}-${GO_ARCH}" >&2
    echo "Looking for: $BINARY_PATH" >&2
    echo "Available binaries:" >&2
    ls -1 "$(dirname "$0")"/bootstrapRunner-* 2>/dev/null || echo "  No bootstrap binaries found" >&2
    exit 1
fi

echo "✓ Binary found and verified"
echo "Test completed successfully for $ARCH → $GO_ARCH"

Results are:

[11:19:11] Starting select-bootstrapRunner.sh functionality tests
[11:19:11] Test environment: Darwin Cals-MBP 24.6.0 Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:40 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6041 arm64
[11:19:11] 
[11:19:11] 
[11:19:11] === TEST: Architecture mapping: x86_64 → amd64 ===
[11:19:11] Simulating architecture: x86_64
[11:19:11] Expected Go architecture: amd64
[11:19:12] Running architecture detection test...
[11:19:12] ✓ SUCCESS: Architecture detection worked correctly
[11:19:12] Output:
[11:19:12]   Detected Architecture: x86_64
[11:19:12]   Mapped to Go Arch: amd64
[11:19:12]   Selected Binary: bootstrapRunner-linux-amd64
[11:19:12]   Binary Path: ./bootstrapRunner-linux-amd64
[11:19:12]   ✓ Binary found and verified
[11:19:12]   Test completed successfully for x86_64 → amd64
[11:19:12] ✓ VERIFIED: Correct binary selected (bootstrapRunner-linux-amd64)
[11:19:12] 
[11:19:12] === TEST: Architecture mapping: aarch64 → arm64 ===
[11:19:12] Simulating architecture: aarch64
[11:19:12] Expected Go architecture: arm64
[11:19:12] Running architecture detection test...
[11:19:13] ✓ SUCCESS: Architecture detection worked correctly
[11:19:13] Output:
[11:19:13]   Detected Architecture: aarch64
[11:19:13]   Mapped to Go Arch: arm64
[11:19:13]   Selected Binary: bootstrapRunner-linux-arm64
[11:19:13]   Binary Path: ./bootstrapRunner-linux-arm64
[11:19:13]   ✓ Binary found and verified
[11:19:13]   Test completed successfully for aarch64 → arm64
[11:19:13] ✓ VERIFIED: Correct binary selected (bootstrapRunner-linux-arm64)
[11:19:13] 
[11:19:13] === TEST: Architecture mapping: arm64 → arm64 ===
[11:19:13] Simulating architecture: arm64
[11:19:13] Expected Go architecture: arm64
[11:19:13] Running architecture detection test...
[11:19:13] ✓ SUCCESS: Architecture detection worked correctly
[11:19:13] Output:
[11:19:13]   Detected Architecture: arm64
[11:19:13]   Mapped to Go Arch: arm64
[11:19:13]   Selected Binary: bootstrapRunner-linux-arm64
[11:19:13]   Binary Path: ./bootstrapRunner-linux-arm64
[11:19:13]   ✓ Binary found and verified
[11:19:13]   Test completed successfully for arm64 → arm64
[11:19:13] ✓ VERIFIED: Correct binary selected (bootstrapRunner-linux-arm64)
[11:19:13] 
[11:19:13] === TEST: Architecture mapping: i386 → 386 ===
[11:19:13] Simulating architecture: i386
[11:19:13] Expected Go architecture: 386
[11:19:13] Running architecture detection test...
[11:19:13] ✓ SUCCESS: Architecture detection worked correctly
[11:19:13] Output:
[11:19:13]   Detected Architecture: i386
[11:19:14]   Mapped to Go Arch: 386
[11:19:14]   Selected Binary: bootstrapRunner-linux-386
[11:19:14]   Binary Path: ./bootstrapRunner-linux-386
[11:19:14]   ✓ Binary found and verified
[11:19:14]   Test completed successfully for i386 → 386
[11:19:14] ✓ VERIFIED: Correct binary selected (bootstrapRunner-linux-386)
[11:19:14] 
[11:19:14] === TEST: Architecture mapping: i686 → 386 ===
[11:19:14] Simulating architecture: i686
[11:19:14] Expected Go architecture: 386
[11:19:14] Running architecture detection test...
[11:19:14] ✓ SUCCESS: Architecture detection worked correctly
[11:19:14] Output:
[11:19:14]   Detected Architecture: i686
[11:19:14]   Mapped to Go Arch: 386
[11:19:14]   Selected Binary: bootstrapRunner-linux-386
[11:19:14]   Binary Path: ./bootstrapRunner-linux-386
[11:19:14]   ✓ Binary found and verified
[11:19:14]   Test completed successfully for i686 → 386
[11:19:14] ✓ VERIFIED: Correct binary selected (bootstrapRunner-linux-386)
[11:19:14] 
[11:19:14] === TEST: Architecture mapping: armv7l → arm ===
[11:19:14] Simulating architecture: armv7l
[11:19:14] Expected Go architecture: arm
[11:19:14] Running architecture detection test...
[11:19:14] ✓ SUCCESS: Architecture detection worked correctly
[11:19:14] Output:
[11:19:14]   Detected Architecture: armv7l
[11:19:14]   Mapped to Go Arch: arm
[11:19:14]   Selected Binary: bootstrapRunner-linux-arm
[11:19:14]   Binary Path: ./bootstrapRunner-linux-arm
[11:19:14]   ✓ Binary found and verified
[11:19:14]   Test completed successfully for armv7l → arm
[11:19:14] ✓ VERIFIED: Correct binary selected (bootstrapRunner-linux-arm)
[11:19:14] 
[11:19:14] === TEST: Architecture mapping: armv6l → arm ===
[11:19:14] Simulating architecture: armv6l
[11:19:14] Expected Go architecture: arm
[11:19:14] Running architecture detection test...
[11:19:15] ✓ SUCCESS: Architecture detection worked correctly
[11:19:15] Output:
[11:19:15]   Detected Architecture: armv6l
[11:19:15]   Mapped to Go Arch: arm
[11:19:15]   Selected Binary: bootstrapRunner-linux-arm
[11:19:15]   Binary Path: ./bootstrapRunner-linux-arm
[11:19:15]   ✓ Binary found and verified
[11:19:15]   Test completed successfully for armv6l → arm
[11:19:15] ✓ VERIFIED: Correct binary selected (bootstrapRunner-linux-arm)
[11:19:15] 
[11:19:15] === TEST: Unsupported Architecture Error Handling ===
[11:19:15] Testing unsupported architecture handling...
[11:19:15] ✓ SUCCESS: Script correctly rejected unsupported architecture
[11:19:15] Error output:
[11:19:15]   Error: Unsupported architecture: unsupported_arch
[11:19:15]   Supported architectures: x86_64, aarch64, arm64, i386, i686, armv7l, armv6l
[11:19:15] 
[11:19:15] === TEST SUMMARY ===
[11:19:15] Tests passed: 8/8
[11:19:15] ✅ ALL TESTS PASSED - select-bootstrapRunner.sh works correctly

How to review this PR

Quality ✔️

Pre-requisites

  • I have read How we use GitHub Issues for help deciding when and where it's appropriate to make an issue.
  • I have considered informing or consulting the right people, according to the ownership map.
  • I have considered appropriate testing for my change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant