Skip to content

Commit fa03540

Browse files
authored
fix(cli): stop managed EC2 demo starting zero containers (#298) (#302)
Signed-off-by: Kyle Hounslow <kylhouns@amazon.com>
1 parent ddbc490 commit fa03540

7 files changed

Lines changed: 110 additions & 5 deletions

File tree

β€Ž.envβ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ INCLUDE_COMPOSE_EXAMPLES=docker-compose.examples.yml
1111
INCLUDE_COMPOSE_LOCAL_OPENSEARCH=docker-compose.local-opensearch.yml
1212
INCLUDE_COMPOSE_LOCAL_OPENSEARCH_DASHBOARDS=docker-compose.local-opensearch-dashboards.yml
1313

14+
# Activates services that require a local backend (eval canary, otel-demo
15+
# monitors). These hardcode http://prometheus:9090 / https://opensearch:9200
16+
# with no managed-backend path. Managed-mode deploys leave COMPOSE_PROFILES
17+
# unset to prune them.
18+
COMPOSE_PROFILES=local-backends
19+
1420
# TODO: Change to opensearchproject after 3.7.0 official release
1521
OPENSEARCH_DOCKER_REPO=opensearchstaging
1622

β€Žaws/cli-installer/package-lock.jsonβ€Ž

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žaws/cli-installer/package.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opensearch-project/observability-stack",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"description": "CLI for the Observability Stack",
55
"type": "module",
66
"bin": "./bin/cli-installer.mjs",

β€Žaws/cli-installer/src/ec2-demo.mjsβ€Ž

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,20 @@ import {
1919
import {
2020
SSMClient, GetParameterCommand,
2121
} from '@aws-sdk/client-ssm';
22+
import { createRequire } from 'node:module';
2223
import { printStep, printSuccess, printWarning, printInfo, createSpinner } from './ui.mjs';
2324

25+
const require = createRequire(import.meta.url);
26+
const { version: PKG_VERSION } = require('../package.json');
27+
2428
const TAG_KEY = 'observability-stack:pipeline-name';
2529
const INSTANCE_TYPE = 't3.xlarge';
2630

31+
// The stack revision the EC2 instance clones. Defaults to the git tag matching
32+
// this CLI release so a pinned CLI deploys a pinned stack (no drift from main
33+
// HEAD at boot time). Override with OBS_STACK_REF for development.
34+
const STACK_REF = process.env.OBS_STACK_REF || `cli-installer-v${PKG_VERSION}`;
35+
2736
function tags(pipelineName, extra = {}) {
2837
return [
2938
{ Key: TAG_KEY, Value: pipelineName },
@@ -127,7 +136,13 @@ curl -SL "https://github.com/docker/buildx/releases/download/\${BUILDX_VERSION}/
127136
-o /usr/local/lib/docker/cli-plugins/docker-buildx
128137
chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
129138
130-
git clone --depth 1 https://github.com/opensearch-project/observability-stack.git /opt/obs-stack
139+
# Clone the stack at the pinned ref (see STACK_REF). Fall back to the default
140+
# branch if the ref is absent (e.g. an untagged dev build).
141+
OBS_STACK_REF="${STACK_REF}"
142+
if ! git clone --depth 1 --branch "\$OBS_STACK_REF" https://github.com/opensearch-project/observability-stack.git /opt/obs-stack; then
143+
echo "WARNING: ref \$OBS_STACK_REF not found, falling back to default branch"
144+
git clone --depth 1 https://github.com/opensearch-project/observability-stack.git /opt/obs-stack
145+
fi
131146
cd /opt/obs-stack
132147
133148
cat > docker-compose/otel-collector/config.yaml << 'COLLECTOREOF'
@@ -185,6 +200,11 @@ services:
185200
logging: *logging
186201
MANAGEDEOF
187202
203+
# Managed mode has no local opensearch/prometheus. Clear COMPOSE_PROFILES
204+
# (overriding the local-backends value committed in .env) to prune the services
205+
# that require those backends.
206+
export COMPOSE_PROFILES=
207+
188208
# Kafka's healthcheck can exceed compose's dependency grace window on first boot,
189209
# leaving kafka-dependent services in 'Created' state. Retry once β€” second pass
190210
# finds kafka healthy and starts the stragglers.

β€Žaws/cli-installer/test/unit.test.mjsβ€Ž

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,79 @@ describe('EC2 demo buildUserData', () => {
159159
assert.ok(decoded.includes('docker-compose'));
160160
assert.ok(decoded.includes('docker-buildx'));
161161
});
162+
163+
it('pins the stack clone to a ref instead of tracking main HEAD', () => {
164+
const decoded = Buffer.from(_buildUserData(cfg), 'base64').toString();
165+
assert.ok(decoded.includes('--branch "$OBS_STACK_REF"'));
166+
assert.ok(decoded.includes('cli-installer-v'));
167+
});
168+
169+
it('clears COMPOSE_PROFILES so local-backend services are pruned in managed mode', () => {
170+
const decoded = Buffer.from(_buildUserData(cfg), 'base64').toString();
171+
assert.ok(decoded.includes('export COMPOSE_PROFILES='));
172+
});
173+
});
174+
175+
// ── managed-mode compose project resolution ──────────────────────────────────
176+
// Regression guard for #298: the generated docker-compose.managed.yml must
177+
// resolve as a valid project with COMPOSE_PROFILES empty, with every
178+
// local-backend service pruned (managed mode defines no opensearch/prometheus).
179+
180+
import { execFileSync } from 'node:child_process';
181+
import { fileURLToPath } from 'node:url';
182+
import { writeFileSync, rmSync } from 'node:fs';
183+
import { join, dirname } from 'node:path';
184+
185+
function dockerComposeAvailable() {
186+
try {
187+
execFileSync('docker', ['compose', 'version'], { stdio: 'ignore' });
188+
return true;
189+
} catch {
190+
return false;
191+
}
192+
}
193+
194+
// Extract the docker-compose.managed.yml heredoc body from generated user-data.
195+
function extractManagedCompose(userDataB64) {
196+
const decoded = Buffer.from(userDataB64, 'base64').toString();
197+
const m = decoded.match(/docker-compose\.managed\.yml << .MANAGEDEOF.\n([\s\S]*?)\nMANAGEDEOF/);
198+
if (!m) throw new Error('managed compose heredoc not found in user-data');
199+
return m[1];
200+
}
201+
202+
describe('EC2 demo managed compose project', { skip: !dockerComposeAvailable() }, () => {
203+
const cfg = {
204+
pipelineName: 'test-pipeline',
205+
region: 'us-west-2',
206+
ingestEndpoints: ['test-pipeline-abc123.us-west-2.osis.amazonaws.com'],
207+
};
208+
// Repo root holds the included compose files; managed.yml's relative
209+
// include: paths resolve against the file's own directory.
210+
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
211+
const managedPath = join(repoRoot, 'docker-compose.managed.yml');
212+
213+
function configServices(profiles) {
214+
return execFileSync(
215+
'docker', ['compose', '-f', managedPath, 'config', '--services'],
216+
{ cwd: repoRoot, env: { ...process.env, COMPOSE_PROFILES: profiles }, encoding: 'utf8' },
217+
).split('\n').filter(Boolean);
218+
}
219+
220+
it('validates and prunes local-backend services when COMPOSE_PROFILES is empty', () => {
221+
writeFileSync(managedPath, extractManagedCompose(_buildUserData(cfg)));
222+
try {
223+
const services = configServices('');
224+
assert.ok(services.length > 0, 'expected the managed project to resolve to a non-empty service list');
225+
assert.ok(!services.includes('example-agent-eval-canary'),
226+
'eval canary should be pruned in managed mode (no local opensearch)');
227+
assert.ok(!services.includes('otel-demo-alerting-rules-monitors-init'),
228+
'otel-demo monitors-init should be pruned in managed mode (no local prometheus/opensearch)');
229+
assert.ok(services.includes('otel-collector'),
230+
'otel-collector should always be present');
231+
} finally {
232+
rmSync(managedPath, { force: true });
233+
}
234+
});
162235
});
163236

164237
// ── renderPipeline tests ─────────────────────────────────────────────────────

β€Ždocker-compose.examples.ymlβ€Ž

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,11 @@ services:
177177
memory: 128M
178178
logging: *logging
179179

180-
# Agent Eval Canary: periodically scores un-evaluated agent traces
180+
# Agent Eval Canary: periodically scores un-evaluated agent traces.
181+
# Requires a local OpenSearch backend; gated behind the local-backends
182+
# profile (see COMPOSE_PROFILES in .env).
181183
example-agent-eval-canary:
184+
profiles: ["local-backends"]
182185
build:
183186
context: ./docker-compose/agent-eval-canary
184187
dockerfile: Dockerfile

β€Ždocker-compose.otel-demo.ymlβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,10 @@ services:
774774
# failure. Named separately from the base container rather than overlaying it
775775
# because Docker Compose >= v2.38 rejects service-name overlays on
776776
# `include:`-imported resources. Both containers hit idempotent upsert APIs.
777+
# Requires local Cortex (prometheus) and OpenSearch backends; gated behind
778+
# the local-backends profile (see COMPOSE_PROFILES in .env).
777779
otel-demo-alerting-rules-monitors-init:
780+
profiles: ["local-backends"]
778781
image: python:3.11-alpine
779782
container_name: otel-demo-alerting-rules-monitors-init
780783
command:

0 commit comments

Comments
Β (0)