Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f08084b
fix: upgrade linux dockerfile to debian 12
todthomson May 7, 2026
d42b992
fix: upgrade apt deps libicu72 and libssl3
todthomson May 7, 2026
da9a313
fix: add apt-utils to deps to avoid warning => debconf: delaying pack…
todthomson May 7, 2026
c327063
tidy: remove explicit install of things already installed by default
todthomson May 7, 2026
dedd1b8
fix: suppress stderr warning of dpkg interactive config
todthomson May 9, 2026
4b72ab1
revert: doesn't help
todthomson May 11, 2026
bd60a05
test: add E2E smoke test for Linux Tentacle Docker image
todthomson May 12, 2026
131f043
+semver: major
todthomson May 12, 2026
e6fb8e2
include `-y --no-install-recommends` even though it likely doesn't ma…
todthomson May 12, 2026
c80962f
fix: drop undeclared python3 dep from smoke test
todthomson May 12, 2026
6f577d3
fix: use mktemp for transient compose override
todthomson May 12, 2026
f22c0c2
fix: use mktemp for .env backup path
todthomson May 12, 2026
14f4c75
fix: put X's at end of mktemp templates for BSD portability
todthomson May 12, 2026
ebbb878
fix: use grep -qF for literal "Configuration successful." match
todthomson May 20, 2026
de069c6
docs: clarify that API_KEY is the sibling repo's dev-only sentinel
todthomson May 20, 2026
84550c7
fix: hard-fail when Debian 12 is missing from AdHocScript output
todthomson May 20, 2026
abea049
fix: use mktemp for upsert_env_var temp file
todthomson May 20, 2026
4d4cb43
feat: allow OCTOPUS_LICENSE_BASE64 to bypass 1Password lookup
todthomson May 20, 2026
98a2637
fix: look up worker by per-run TargetName instead of "highest Workers-N"
todthomson May 20, 2026
c6dc67e
fix: deregister worker in teardown to keep test idempotent
todthomson May 20, 2026
c242744
cleanup: combine two apt-get steps into one
todthomson May 20, 2026
09ba661
feat: make Linux Tentacle smoke test self-contained
todthomson May 27, 2026
9745577
feat: drop license requirement; default to Community Edition
todthomson May 27, 2026
27f5b3f
fix: loosen Tentacle registration wait from 60 to 180 iterations
todthomson May 27, 2026
8f39c71
refactor: rename smoke-test image to local-only tentacle-smoke:debian12
todthomson May 27, 2026
39a016e
fix: pin tentacle service to pull_policy: never
todthomson May 27, 2026
0aa4543
feat: add TeamCity wrapper for Linux Tentacle smoke test
todthomson May 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 7 additions & 20 deletions docker/linux/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
FROM debian:11-slim
FROM debian:12-slim
ENV ASPNETCORE_URLS=http://+:80 DOTNET_RUNNING_IN_CONTAINER=true
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
libc6 \
libgcc1 \
libgssapi-krb5-2 \
libicu67 \
libssl1.1 \
libstdc++6 \
zlib1g && \
libicu72 \
libssl3 \
xxd && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

ARG BUILD_NUMBER
ARG BUILD_DATE

RUN apt-get update && \
apt-get install -y \
curl \
dos2unix \
jq \
sudo \
xxd \
&& \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

EXPOSE 10933

WORKDIR /tmp
Expand All @@ -43,11 +30,11 @@ RUN /install-scripts/install-docker.sh
# Install Tentacle
COPY _artifacts/deb/tentacle_${BUILD_NUMBER}_amd64.deb /tmp/
RUN apt-get update && \
apt install ./tentacle_${BUILD_NUMBER}_amd64.deb && \
apt-get install -y --no-install-recommends ./tentacle_${BUILD_NUMBER}_amd64.deb && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
ln -s /opt/octopus/tentacle/Tentacle /usr/bin/tentacle

WORKDIR /

# We know this won't reduce the image size at all. It's just to make the filesystem a little tidier.
Expand Down
89 changes: 89 additions & 0 deletions scripts/smoke-test-linux-tentacle-teamcity.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
#
# TeamCity wrapper for scripts/smoke-test-linux-tentacle.sh.
#
# Emits TeamCity service messages so the smoke test surfaces as a single
# named integration test on a TeamCity build agent, with progress blocks
# per step (parsed from the inner script's `--- Step N: ... ---` markers)
# and a buildProblem on failure. The inner script's stdout/stderr is
# forwarded verbatim, so the TC messages are harmless noise when this
# wrapper is run locally.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INNER_SCRIPT="$SCRIPT_DIR/smoke-test-linux-tentacle.sh"
[[ -x "$INNER_SCRIPT" ]] || { echo "Missing executable: $INNER_SCRIPT" >&2; exit 1; }

SUITE_NAME="${TEAMCITY_SMOKE_SUITE_NAME:-LinuxTentacleSmoke}"
TEST_NAME="${TEAMCITY_SMOKE_TEST_NAME:-LinuxTentacleSmokeTest}"

# Service-message escaping per TeamCity's Build Script Interaction spec:
# |→|| '→|' newline→|n CR→|r [→|[ ]→|]
tc_escape() {
local s=$1
s=${s//|/||}
s=${s//\'/|\'}
s=${s//$'\n'/|n}
s=${s//$'\r'/|r}
s=${s//[/|[}
s=${s//]/|]}
printf '%s' "$s"
}

tc() { printf '##teamcity[%s]\n' "$*"; }

open_block=""
close_open_block() {
if [[ -n "$open_block" ]]; then
tc "blockClosed name='$open_block'"
open_block=""
fi
}

# The while-read loop consumes every line the inner script prints, so we
# stash its exit code in a temp file to recover it after the pipe drains.
exit_file=$(mktemp)
trap 'rm -f "$exit_file"' EXIT

ESC_SUITE=$(tc_escape "$SUITE_NAME")
ESC_TEST=$(tc_escape "$TEST_NAME")

start_epoch=$(date +%s)
tc "testSuiteStarted name='$ESC_SUITE'"
tc "testStarted name='$ESC_TEST' captureStandardOutput='true'"

# Process substitution keeps the loop in the current shell so $open_block
# survives across iterations. The `rc=0; … || rc=$?; echo "$rc" > …` form
# captures the inner script's exit code while keeping `set -e` happy — a
# bare `; echo $? > …` would never run, because `set -e` is inherited into
# the subshell and aborts it the moment the inner script exits non-zero.
while IFS= read -r line; do
printf '%s\n' "$line"
# Match the inner script's `[smoke] --- title ---` markers; the [^-]*
# gap tolerates ANSI colour bytes wrapped around `[smoke]` by log().
if [[ "$line" =~ \[smoke\][^-]*---\ (.+)\ ---$ ]]; then
title=$(tc_escape "${BASH_REMATCH[1]}")
close_open_block
tc "blockOpened name='$title'"
open_block="$title"
fi
done < <(rc=0; "$INNER_SCRIPT" 2>&1 || rc=$?; echo "$rc" > "$exit_file")

close_open_block

inner_exit=$(<"$exit_file")
inner_exit=${inner_exit:-1}
duration_ms=$(( ($(date +%s) - start_epoch) * 1000 ))

if [[ "$inner_exit" -ne 0 ]]; then
fail_msg=$(tc_escape "smoke-test-linux-tentacle.sh exited with code $inner_exit")
tc "testFailed name='$ESC_TEST' message='$fail_msg'"
tc "testFinished name='$ESC_TEST' duration='$duration_ms'"
tc "testSuiteFinished name='$ESC_SUITE'"
tc "buildProblem description='$fail_msg' identity='linux-tentacle-smoke'"
exit "$inner_exit"
fi

tc "testFinished name='$ESC_TEST' duration='$duration_ms'"
tc "testSuiteFinished name='$ESC_SUITE'"
69 changes: 69 additions & 0 deletions scripts/smoke-test-linux-tentacle.compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Self-contained docker compose for the Linux Tentacle smoke test (EFT-3311).
# Brings up MSSQL + Octopus Server + the Tentacle image under test on an
# internal network so the test does not need a sibling OctopusDeploy checkout.
#
# Driven by scripts/smoke-test-linux-tentacle.sh — env vars exported there
# (TENTACLE_IMAGE, TENTACLE_TAG, OCTOPUS_SERVER_TAG, SA_PASSWORD, ADMIN_PASSWORD,
# ADMIN_API_KEY, WORKER_TARGET_NAME) are interpolated below.
# OCTOPUS_SERVER_BASE64_LICENSE is optional: when empty, Octopus Server starts
# on the free Community Edition license (1 space, 1 worker, 5 projects), which
# is enough for this test.

name: octopustentacle-smoke

services:
mssql:
image: mcr.microsoft.com/mssql/server:2022-latest
platform: linux/amd64
environment:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: "${SA_PASSWORD}"
MSSQL_PID: "Developer"
healthcheck:
test:
- "CMD-SHELL"
- '/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$$MSSQL_SA_PASSWORD" -C -No -Q "SELECT 1" >/dev/null 2>&1 || exit 1'
interval: 5s
timeout: 5s
retries: 60
start_period: 30s

octopus-server:
image: octopusdeploy/octopusdeploy:${OCTOPUS_SERVER_TAG:-latest}
platform: linux/amd64
depends_on:
mssql:
condition: service_healthy
environment:
ACCEPT_EULA: "Y"
DB_CONNECTION_STRING: "Server=mssql,1433;Database=Octopus;User Id=sa;Password=${SA_PASSWORD};TrustServerCertificate=true;"
ADMIN_USERNAME: "admin"
ADMIN_PASSWORD: "${ADMIN_PASSWORD}"
ADMIN_API_KEY: "${ADMIN_API_KEY}"
OCTOPUS_SERVER_BASE64_LICENSE: "${OCTOPUS_SERVER_BASE64_LICENSE:-}"
ports:
- "8065:8080"

tentacle:
image: ${TENTACLE_IMAGE}:${TENTACLE_TAG}
# Fail loudly if the local build step was skipped — never silently pull
# from a registry. The script tags this image with a local-only name
# (no `octopusdeploy/` prefix), so a pull attempt would always be wrong.
pull_policy: never
platform: linux/amd64
depends_on:
- octopus-server
environment:
ACCEPT_EULA: "Y"
ServerApiKey: "${ADMIN_API_KEY}"
ServerUrl: "http://octopus-server:8080"
# Setting ServerPort puts the Tentacle into polling (TentacleActive) mode,
# which is what we want for a worker — no inbound listener required.
ServerPort: "10943"
TargetWorkerPool: "Default Worker Pool"
TargetName: "${WORKER_TARGET_NAME}"
Space: "Default"
# The Tentacle image's default entrypoint launches a dockerd sidecar,
# which requires --privileged. We don't need DinD for this smoke test;
# skipping it keeps the container running without elevated privileges.
DISABLE_DIND: "Y"
Loading