From 074b532eb729714ec087396f4fb496393f1e8581 Mon Sep 17 00:00:00 2001 From: ajayshek Date: Tue, 1 Sep 2026 13:44:07 -0700 Subject: [PATCH 1/2] Create worker data_dir in edge install scripts Every edge-config.json sets data_dir to $ROOT_DIR/data, and the Vector-based worker's config validation hard-fails with exit 78 ("data_dir ... does not exist") if that directory isn't already present -- the worker does not create it itself. install.sh, install_darwin.sh, and install.ps1 only ever created the root, logs, and update directories, so every fresh install left the worker permanently unable to start. Add data_dir creation/ownership alongside the existing directories in all three installers. Root-caused and verified live on a fresh Linux ARM64 test VM: agent enrolled and received a real config, but the worker never started (exit status 78, empty stderr). Running the worker's own validate command surfaced the actual message ("data_dir ... does not exist"); creating the directory manually unblocked it immediately. Co-Authored-By: Claude Sonnet 5 --- edge/install.ps1 | 10 +++++++++- edge/install.sh | 14 +++++++++++--- edge/install_darwin.sh | 14 +++++++++++--- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/edge/install.ps1 b/edge/install.ps1 index 9586d65..2638c40 100644 --- a/edge/install.ps1 +++ b/edge/install.ps1 @@ -53,6 +53,13 @@ $LogDir = Get-EnvOrDefault "LOG_DIR" "$RootDir\logs" # match server.DefaultUpdateStagingDir in # internal/server/constant_windows.go ($RootDir\update). $UpdateDir = Get-EnvOrDefault "UPDATE_DIR" "$RootDir\update" +# DataDir is where the Vector-based worker persists its own state (file +# checkpoints, buffers, etc). Every edge-config.json sets data_dir to this +# path, and the worker's config validation hard-fails (exit 78) if it +# doesn't already exist -- the worker does not create it itself. Must +# match server.DefaultWorkerDataDir in internal/server/constant_windows.go +# ($RootDir\data). +$DataDir = Get-EnvOrDefault "DATA_DIR" "$RootDir\data" $TmpDir = Get-EnvOrDefault "TMP_DIR" "$env:TEMP\observo" $ZipFile = "$TmpDir\edge.zip" $ExtractDir = "$TmpDir\binaries_edge" @@ -339,7 +346,8 @@ function Move-BinariesToInstallDir { # $RootDir\ binaries + edge-config.json + effective.yaml # $RootDir\logs\ supervisor + worker + update-watcher logs # $RootDir\update\ staging dir + heartbeat (if used) + flag file - foreach ($d in @($RootDir, $LogDir, $UpdateDir)) { + # $RootDir\data\ worker data_dir (file checkpoints, buffers) + foreach ($d in @($RootDir, $LogDir, $UpdateDir, $DataDir)) { if (-not (Test-Path -Path $d)) { New-Item -ItemType Directory -Path $d -Force | Out-Null Write-Host "Created directory: $d" diff --git a/edge/install.sh b/edge/install.sh index b67ca60..7361649 100644 --- a/edge/install.sh +++ b/edge/install.sh @@ -54,6 +54,13 @@ LOG_DIR="${LOG_DIR:-$ROOT_DIR/logs}" # Must match server.DefaultUpdateStagingDir in # internal/server/constant_linux.go ($ROOT_DIR/update). UPDATE_DIR="${UPDATE_DIR:-$ROOT_DIR/update}" +# DATA_DIR is where the Vector-based worker persists its own state (file +# checkpoints, buffers, etc). Every edge-config.json sets data_dir to this +# path, and the worker's config validation hard-fails (exit 78) if it +# doesn't already exist -- the worker does not create it itself. Must +# match server.DefaultWorkerDataDir in internal/server/constant_linux.go +# ($ROOT_DIR/data). +DATA_DIR="${DATA_DIR:-$ROOT_DIR/data}" TMP_DIR="${TMP_DIR:-/tmp/observo}" TAR_FILE="$TMP_DIR/edge.tar.gz" EXTRACT_DIR="$TMP_DIR/binaries_edge" @@ -447,9 +454,10 @@ setup_directories() { # $ROOT_DIR/ binaries + edge-config.json + effective.yaml # $ROOT_DIR/logs/ edge-worker, supervisor, update-watcher logs # $ROOT_DIR/update/ staging dir + heartbeat socket + flag file + # $ROOT_DIR/data/ worker data_dir (file checkpoints, buffers) # Single chown -R later normalises ownership across the whole tree. echo "Setting up edge directory tree under $ROOT_DIR" - for d in "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR"; do + for d in "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" "$DATA_DIR"; do if [ ! -d "$d" ]; then echo " creating $d" sudo mkdir -p "$d" || { echo "Failed to create $d"; exit 1; } @@ -457,9 +465,9 @@ setup_directories() { done echo "Setting ownership to root:root (edge runs as root)" - sudo chown root:root "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" \ + sudo chown root:root "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" "$DATA_DIR" \ || { echo "Failed to set ownership on edge directories"; exit 1; } - sudo chmod 0755 "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" + sudo chmod 0755 "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" "$DATA_DIR" } create_systemd_service() { diff --git a/edge/install_darwin.sh b/edge/install_darwin.sh index de0adc1..73389e8 100755 --- a/edge/install_darwin.sh +++ b/edge/install_darwin.sh @@ -34,6 +34,13 @@ LOG_DIR="${LOG_DIR:-$ROOT_DIR/logs}" # Must match server.DefaultUpdateStagingDir in # internal/server/constant_darwin.go ($ROOT_DIR/update). UPDATE_DIR="${UPDATE_DIR:-$ROOT_DIR/update}" +# DATA_DIR is where the Vector-based worker persists its own state (file +# checkpoints, buffers, etc). Every edge-config.json sets data_dir to this +# path, and the worker's config validation hard-fails (exit 78) if it +# doesn't already exist -- the worker does not create it itself. Must +# match server.DefaultWorkerDataDir in internal/server/constant_darwin.go +# ($ROOT_DIR/data). +DATA_DIR="${DATA_DIR:-$ROOT_DIR/data}" TMP_DIR="${TMP_DIR:-/tmp/observo}" TAR_FILE="$TMP_DIR/edge.tar.gz" EXTRACT_DIR="$TMP_DIR/binaries_edge" @@ -252,9 +259,10 @@ install_binaries() { # $ROOT_DIR/ binaries + edge-config.json + effective.yaml # $ROOT_DIR/logs/ supervisor + worker + update-watcher logs # $ROOT_DIR/update/ staging dir + heartbeat socket + flag file - mkdir -p "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" - chown root:wheel "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" - chmod 0755 "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" + # $ROOT_DIR/data/ worker data_dir (file checkpoints, buffers) + mkdir -p "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" "$DATA_DIR" + chown root:wheel "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" "$DATA_DIR" + chmod 0755 "$ROOT_DIR" "$LOG_DIR" "$UPDATE_DIR" "$DATA_DIR" install_binary "$EDGE_BINARY_NAME" "$EDGE_EXECUTABLE" install_binary "$WATCHER_BINARY_NAME" "$WATCHER_EXECUTABLE" install_binary "$WORKER_BINARY_NAME" "$WORKER_EXECUTABLE_PATH" From 1778d238dafa0cfcf693648cd3740a6b9a954394 Mon Sep 17 00:00:00 2001 From: ajayshek Date: Tue, 1 Sep 2026 13:50:45 -0700 Subject: [PATCH 2/2] Always set edge_manager_tls_enabled to true in install scripts Replaces the URL-scheme-based derivation (wss://|https:// -> true, else false) with an unconditional true in all three install scripts. Every supported deployment terminates TLS at the edge-manager ingress, so the derivation was redundant in practice. Note this is a real behavior change for a plaintext-only edge-manager deployment (INSECURE_PLAINTEXT_OPAMP=true, ws://): the enrollment HTTP client will now unconditionally attempt https://, which has no fallback and fails fatally after 10 attempts (unlike the OpAMP client, which has a schemeFlip retry for exactly this case). Co-Authored-By: Claude Sonnet 5 --- edge/install.ps1 | 16 ++++++---------- edge/install.sh | 18 ++++++------------ edge/install_darwin.sh | 20 ++++++-------------- 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/edge/install.ps1 b/edge/install.ps1 index 2638c40..5ec26c3 100644 --- a/edge/install.ps1 +++ b/edge/install.ps1 @@ -246,16 +246,12 @@ function Decode-AndExtractConfig { exit 1 } - # edge_manager_tls_enabled is derived from the URL scheme rather than - # trusted from the payload: the enrollment HTTP client (EnsureEnrolled -> - # enrollEndpoint) reads this flag to decide http:// vs https://, with no - # fallback/retry if it guesses wrong (unlike the OpAMP client's - # schemeFlip). A wss:// (or https://) edge_manager_url with this flag - # left false/unset makes enrollment fail permanently against a TLS-only - # ingress -- fatal after 10 attempts, then crash-loop under the service - # manager. - $edgeManagerTlsEnabled = $EdgeManagerUrl -match '^(wss|https)://' - $config | Add-Member -NotePropertyName "edge_manager_tls_enabled" -NotePropertyValue $edgeManagerTlsEnabled -Force + # edge_manager_tls_enabled is always set true: every supported deployment + # terminates TLS at the edge-manager ingress, and the enrollment HTTP + # client (EnsureEnrolled -> enrollEndpoint) reads this flag to decide + # http:// vs https://, with no fallback/retry if it guesses wrong (unlike + # the OpAMP client's schemeFlip). + $config | Add-Member -NotePropertyName "edge_manager_tls_enabled" -NotePropertyValue $true -Force $DecodedWithTls = $config | ConvertTo-Json -Depth 10 # Write JSON (now carrying edge_manager_tls_enabled) to both current and diff --git a/edge/install.sh b/edge/install.sh index 7361649..c62934f 100644 --- a/edge/install.sh +++ b/edge/install.sh @@ -225,18 +225,12 @@ decode_and_extract_config() { PLATFORM=$(echo "$PAYLOAD" | jq -r '.platform') EDGE_MANAGER_URL=$(echo "$PAYLOAD" | jq -r '.edge_manager_url') - # edge_manager_tls_enabled is derived from the URL scheme rather than - # trusted from the payload: the enrollment HTTP client (EnsureEnrolled -> - # enrollEndpoint) reads this flag to decide http:// vs https://, with no - # fallback/retry if it guesses wrong (unlike the OpAMP client's - # schemeFlip). A wss:// (or https://) edge_manager_url with this flag - # left false/unset makes enrollment fail permanently against a TLS-only - # ingress -- fatal after 10 attempts, then crash-loop under systemd. - case "$EDGE_MANAGER_URL" in - wss://*|https://*) EDGE_MANAGER_TLS_ENABLED=true ;; - *) EDGE_MANAGER_TLS_ENABLED=false ;; - esac - PAYLOAD=$(echo "$PAYLOAD" | jq --argjson tls "$EDGE_MANAGER_TLS_ENABLED" '. + {edge_manager_tls_enabled: $tls}') + # edge_manager_tls_enabled is always set true: every supported deployment + # terminates TLS at the edge-manager ingress, and the enrollment HTTP + # client (EnsureEnrolled -> enrollEndpoint) reads this flag to decide + # http:// vs https://, with no fallback/retry if it guesses wrong (unlike + # the OpAMP client's schemeFlip). + PAYLOAD=$(echo "$PAYLOAD" | jq --argjson tls true '. + {edge_manager_tls_enabled: $tls}') mkdir -p "$CONFIG_DIR" echo "$PAYLOAD" > "$CONFIG_FILE" # Directly write payload to file diff --git a/edge/install_darwin.sh b/edge/install_darwin.sh index 73389e8..56b8af0 100755 --- a/edge/install_darwin.sh +++ b/edge/install_darwin.sh @@ -168,20 +168,12 @@ decode_and_extract_config() { PLATFORM=$(echo "$payload" | jq -r '.platform // empty') EDGE_MANAGER_URL=$(echo "$payload" | jq -r '.edge_manager_url // empty') - # edge_manager_tls_enabled is derived from the URL scheme rather than - # trusted from the payload: the enrollment HTTP client (EnsureEnrolled -> - # enrollEndpoint) reads this flag to decide http:// vs https://, with no - # fallback/retry if it guesses wrong (unlike the OpAMP client's - # schemeFlip). A wss:// (or https://) edge_manager_url with this flag - # left false/unset makes enrollment fail permanently against a TLS-only - # ingress -- fatal after 10 attempts, then crash-loop under the service - # manager. - local edge_manager_tls_enabled - case "$EDGE_MANAGER_URL" in - wss://*|https://*) edge_manager_tls_enabled=true ;; - *) edge_manager_tls_enabled=false ;; - esac - payload=$(echo "$payload" | jq --argjson tls "$edge_manager_tls_enabled" '. + {edge_manager_tls_enabled: $tls}') + # edge_manager_tls_enabled is always set true: every supported deployment + # terminates TLS at the edge-manager ingress, and the enrollment HTTP + # client (EnsureEnrolled -> enrollEndpoint) reads this flag to decide + # http:// vs https://, with no fallback/retry if it guesses wrong (unlike + # the OpAMP client's schemeFlip). + payload=$(echo "$payload" | jq --argjson tls true '. + {edge_manager_tls_enabled: $tls}') echo "$payload" > "$CONFIG_FILE" chmod 644 "$CONFIG_FILE"