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
7445721
fix: upgrade linux dockerfile to debian 12
todthomson May 7, 2026
e876afa
fix: upgrade apt deps libicu72 and libssl3
todthomson May 7, 2026
bcac9b7
fix: add apt-utils to deps to avoid warning => debconf: delaying pack…
todthomson May 7, 2026
c80f2a5
tidy: remove explicit install of things already installed by default
todthomson May 7, 2026
d5e3cf5
fix: suppress stderr warning of dpkg interactive config
todthomson May 9, 2026
8171502
revert: doesn't help
todthomson May 11, 2026
9b197b2
test: add E2E smoke test for Linux Tentacle Docker image
todthomson May 12, 2026
95f0f1a
+semver: major
todthomson May 12, 2026
064f9a4
include `-y --no-install-recommends` even though it likely doesn't ma…
todthomson May 12, 2026
ad5faf3
fix: drop undeclared python3 dep from smoke test
todthomson May 12, 2026
10458ef
fix: use mktemp for transient compose override
todthomson May 12, 2026
30926e1
fix: use mktemp for .env backup path
todthomson May 12, 2026
9652f4b
fix: put X's at end of mktemp templates for BSD portability
todthomson May 12, 2026
c45a297
fix: use grep -qF for literal "Configuration successful." match
todthomson May 20, 2026
fd23ab8
docs: clarify that API_KEY is the sibling repo's dev-only sentinel
todthomson May 20, 2026
c75784f
fix: hard-fail when Debian 12 is missing from AdHocScript output
todthomson May 20, 2026
582a4f1
fix: use mktemp for upsert_env_var temp file
todthomson May 20, 2026
47578ba
feat: allow OCTOPUS_LICENSE_BASE64 to bypass 1Password lookup
todthomson May 20, 2026
fdb6cbb
fix: look up worker by per-run TargetName instead of "highest Workers-N"
todthomson May 20, 2026
437f08e
fix: deregister worker in teardown to keep test idempotent
todthomson May 20, 2026
b4882e9
cleanup: combine two apt-get steps into one
todthomson May 20, 2026
5582239
feat: make Linux Tentacle smoke test self-contained
todthomson May 27, 2026
0b9bc03
feat: drop license requirement; default to Community Edition
todthomson May 27, 2026
aba2bfc
fix: loosen Tentacle registration wait from 60 to 180 iterations
todthomson May 27, 2026
8d652fa
refactor: rename smoke-test image to local-only tentacle-smoke:debian12
todthomson May 27, 2026
549abd7
fix: pin tentacle service to pull_policy: never
todthomson May 27, 2026
26e4d90
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