Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
5 changes: 4 additions & 1 deletion .github/workflows/component_onhost_e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ permissions:
env:
HOST_E2E_INFRA_AGENT_VERSION: "1.74.3"
HOST_E2E_NRDOT_VERSION: "1.14.0"
HOST_E2E_PRELOAD_VERSION: "0.1.0"

jobs:
build-packages:
Expand All @@ -53,6 +54,7 @@ jobs:
- infra-agent
- nrdot-agent
- proxy
- preload-agent
steps:
- name: Record job start time
run: echo "JOB_START_TIME=$(date +%s)" >> "$GITHUB_ENV"
Expand All @@ -79,7 +81,8 @@ jobs:
--agent-control-version "0.900.${{ github.run_id }}" \
--recipes-repo-branch "main" \
--infra-agent-version "${{ env.HOST_E2E_INFRA_AGENT_VERSION }}" \
--nrdot-version "${{ env.HOST_E2E_NRDOT_VERSION }}"
--nrdot-version "${{ env.HOST_E2E_NRDOT_VERSION }}" \
--preload-version "${{ env.HOST_E2E_PRELOAD_VERSION }}"

- name: Report test result to New Relic
if: always()
Expand Down

This file was deleted.

2 changes: 2 additions & 0 deletions test/e2e-runner/src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub fn local_config_path(agent_id: &str) -> String {

const DEFAULT_LOG_PATH: &str = "/var/log/newrelic-agent-control/agent-control/";

pub const AGENT_CONTROL_DATA_DIR: &str = "/var/lib/newrelic-agent-control";

const SERVICE_NAME: &str = "newrelic-agent-control";

/// Run Linux e2e corresponding scenario which will panic on failure
Expand Down
120 changes: 88 additions & 32 deletions test/e2e-runner/src/linux/scenarios/preload_agent.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
use crate::common::config::{DEBUG_LOGGING_CONFIG, update_config, write_agent_local_config};
use crate::common::file::write;
use crate::common::on_drop::CleanUp;
use crate::common::test::{TestResult, retry_panic};
use crate::common::{InstallationArgs, RecipeData};
use crate::{
linux::{
self,
install::{install_agent_control_from_recipe, tear_down_test},
bash::exec_bash_command
},
use crate::linux::{
self,
bash::exec_bash_command,
install::{install_agent_control_from_recipe, tear_down_test},
};
use std::time::Duration;
use tracing::{debug, info};

/// Directory where Agent Control loads dynamic (custom) agent type definitions.
const DYNAMIC_AGENT_TYPES_DIR: &str = "/etc/newrelic-agent-control/dynamic-agent-types";

/// Expected package installation directory for the preload agent.
fn preload_package_dir() -> String {
format!(
"{}/packages/nr-preload/stored_packages/preload-agent",
linux::AGENT_CONTROL_DATA_DIR
)
}

pub fn test_installation_with_preload_agent(args: InstallationArgs) {
let preload_version = args
.preload_version
.clone()
.expect("--preload-agent-version is required for this scenario");

let staging = matches!(args.nr_region.to_lowercase().as_str(), "staging");
.expect("--preload-version is required for this scenario");

let recipe_data = RecipeData {
args,
Expand All @@ -35,6 +45,40 @@ pub fn test_installation_with_preload_agent(args: InstallationArgs) {

let preload_agent_id = "nr-preload";

info!("Writing custom preload agent type definition");
let custom_agent_type_path = format!("{DYNAMIC_AGENT_TYPES_DIR}/preload.yaml");
let custom_agent_type = r#"namespace: newrelic

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude found this issue:

🔴 Bug: ${{ ... }} won't be substituted by Agent Control

test/e2e-runner/src/linux/scenarios/preload_agent.rs:50-77 — the YAML is a plain r#"..."# raw string, not a format! template, so the doubled braces are not escapes — they end up literally in the file:

repository: ${{nr-var:oci.repository}}
version: ${{nr-var:version}}

Compare with agent-control/tests/on_host/scenarios/oci_auth.rs:43-54, where ${{...}} is inside format!(...) and correctly resolves to ${...}. AC's template engine expects the single-brace form
(${nr-var:version}), so the inlined agent type will fail to resolve version and the OCI download won't get a valid tag.

Fix: use single braces (${nr-var:...}) since this is a raw string, or wrap the literal in format! if you want to keep the doubled form for visual consistency with the other tests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed e91cd32

name: com.newrelic.preload
version: 0.1.0
variables:
linux:
oci:
repository:
description: "Package repository name"
type: string
required: false
default: newrelic/preload-agent-artifacts
variants:
ac_config_field: "oci_repository_urls"
values: ["newrelic/preload-agent-artifacts"]
version:
description: "Agent version"
type: string
required: true
deployment:
linux:
packages:
preload-agent:
download:
oci:
repository: ${{nr-var:oci.repository}}
version: ${{nr-var:version}}
public_key_url: https://publickeys.newrelic.com/g/agent-control-oci/global/nrpreloadagent/jwks.json
"#;
exec_bash_command(&format!("mkdir -p {DYNAMIC_AGENT_TYPES_DIR}"))
.unwrap_or_else(|err| panic!("Failed to create dynamic agent types directory: {err}"));
write(&custom_agent_type_path, custom_agent_type);

info!("Setup Agent Control config");
update_config(
linux::DEFAULT_AC_CONFIG_PATH,
Expand All @@ -51,35 +95,47 @@ agents:

write_agent_local_config(
&linux::local_config_path(preload_agent_id),
// Correct Config?
format! {r#"
fleet_id: alphanumeric_id # needed anymore?
apm_language: java
agent_version: 8.13.0
application_names:
- my-app
- functions
- lib
- bin
new_relic_license_key: '{{{{NEW_RELIC_LICENSE_KEY}}}}'
staging: {staging}
version: {preload_version}"#},
version: {preload_version}"#},
);

linux::service::restart_service(linux::SERVICE_NAME);

// ToDo update with actual path
let ld_preload_path = "path_to_ld_preload";
let install_command = format!(r#"echo "{ld_preload_path}" >> /ec/ld.so.preload"#);
let output = exec_bash_command(&install_command)
.unwrap_or_else(|err| panic!("Editing /ec/ld.so.preload failed: {err}"));
debug!("echo output:\n{output}");
info!("Waiting for preload OCI package to be downloaded and extracted");
let package_dir = preload_package_dir();
let retries = 60;
retry_panic(
retries,
Duration::from_secs(10),
"preload package download assertion",
|| assert_preload_package_downloaded(&package_dir),
);

linux::service::restart_service(linux::SERVICE_NAME);
info!("Searching for shared library inside extracted package");
let find_so_command = format!(r#"find {package_dir} -type f -name "*.so" | head -n 1"#);
let so_path = exec_bash_command(&find_so_command)
.unwrap_or_else(|err| panic!("Failed to find shared library in package: {err}"));
let so_path = so_path.lines().last().unwrap_or("").trim().to_string();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also from Claude;

🔴 Bug: so_path extraction picks up the wrapper text, not the actual path

test/e2e-runner/src/linux/scenarios/preload_agent.rs:115-122:

let so_path = exec_bash_command(&find_so_command)...;
let so_path = so_path.lines().last().unwrap_or("").trim().to_string();

exec_bash_command (test/e2e-runner/src/linux/bash.rs:22) does not return raw stdout — it returns a wrapped, multi-line string:
command

success
Stdout:
Stderr:

The Stderr: line is always last, so so_path ends up as "Stderr:" (or empty after the colon depending on trim). The if so_path.is_empty() guard does not catch this, and the next step appends Stderr: to
/etc/ld.so.preload. None of the existing callers parse stdout from exec_bash_command — this is the first one, and it doesn't work.

Fix options (pick one):

  • Add a sibling helper that returns just stdout (e.g., exec_bash_command_stdout), then use it here.
  • Run find ... -print -quit on the host and have the helper write its result to a file you read_to_string.
  • Parse the wrapper: extract the line starting with Stdout: and strip the prefix — least preferred since it bakes in a fragile dependency on the wrapper format.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the strategy used here: fec0466

if so_path.is_empty() {
panic!("No .so file found in extracted preload package at {package_dir}");
}
info!("Found shared library: {so_path}");

let ls_command = format!("ls {ld_preload_path}");
let output = exec_bash_command(&ls_command)
.unwrap_or_else(|err| panic!("Installation failed: {err}"));
debug!("ls output:\n{output}");
info!("Installing shared library into /etc/ld.so.preload");
let install_command = format!(r#"echo "{so_path}" >> /etc/ld.so.preload"#);
let output = exec_bash_command(&install_command)
.unwrap_or_else(|err| panic!("Editing /etc/ld.so.preload failed: {err}"));
debug!("Install output:\n{output}");

info!("Test completed successfully");
}

fn assert_preload_package_downloaded(package_dir: &str) -> TestResult<()> {
let output = exec_bash_command(&format!("ls -d {package_dir}"))?;
if output.contains("No such file") || output.contains("cannot access") {
return Err(format!("Preload package directory not found yet at {package_dir}").into());
}
let listing = exec_bash_command(&format!("ls -la {package_dir}"))?;
debug!("Package listing:\n{listing}");
Ok(())
}
3 changes: 2 additions & 1 deletion test/e2e-runner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ enum LinuxScenarios {
EBPFAgent(InstallationArgs),
/// Local installation of Agent Control with NRDot. It checks that nr-dot eventually reports data.
NrdotAgent(InstallationArgs),
/// Local installation of Agent Control with Preload Agent. It checks that the preload-agent eventually reports data.
/// Local installation of Agent Control with a custom preload agent type. Verifies that the
/// OCI artifact is downloaded and present in the environment.
PreloadAgent(InstallationArgs),
/// Checks that remote configuration for a sub-agent has been applied.
RemoteConfig(InstallationArgs),
Expand Down
Loading