Skip to content

Commit

Permalink
test: Refactor container integration tests to reduce flickers (#2104)
Browse files Browse the repository at this point in the history
  • Loading branch information
tippmar-nr authored Nov 29, 2023
1 parent 9effb5d commit a66bbfd
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
version: "3"

# The following must be set either in environment variables or via a .env file in the same folder as this file:
#
# AGENT_PATH host path to the Agent linux home folder - will map to /usr/local/newrelic-dotnet-agent in the container
# LOG_PATH host path for Agent logfile output - will map to /app/logs in the container
# DISTRO_TAG distro tag for build, not including the architecture suffix - possible values 7.0-bullseye-slim, 7.0-alpine, 7.0-jammy
# TARGET_ARCH the target architecture for the build and run -- either amd64 or arm64
# PORT external port for the smoketest API
# CONTAINER_NAME The name for the container
# PLATFORM The platform that the service runs on -- linux/amd64 or linux/arm64/v8
# DOTNET_VERSION The dotnet version number to use (7.0, 8.0, etc)
# TEST_DOCKERFILE The path and dockerfile to use for the service.
#
# and the usual suspects:
# NEW_RELIC_LICENSE_KEY
# NEW_RELIC_HOST
# NEW_RELIC_APP_NAME
#
#
# To build and run, execute `docker compose -f <path to docker-compose.yml> up`
# Alternatively, set COMPOSE_FILE environment variable to the path and omit the -f parameter

services:
UbuntuX64Kafka1TestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
depends_on:
- kafka-broker
environment:
- NEW_RELIC_KAFKA_TOPIC=${NEW_RELIC_KAFKA_TOPIC}
- NEW_RELIC_KAFKA_CONTAINER_NAME=${NEW_RELIC_KAFKA_CONTAINER_NAME}

UbuntuX64Kafka2TestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
depends_on:
- kafka-broker
environment:
- NEW_RELIC_KAFKA_TOPIC=${NEW_RELIC_KAFKA_TOPIC}
- NEW_RELIC_KAFKA_CONTAINER_NAME=${NEW_RELIC_KAFKA_CONTAINER_NAME}

kafka-broker:
image: confluentinc/cp-kafka:7.5.0
environment:
KAFKA_BROKER_ID: 1
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://${NEW_RELIC_KAFKA_CONTAINER_NAME}:29092,PLAINTEXT_HOST://${NEW_RELIC_KAFKA_CONTAINER_NAME}:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_NODE_ID: 1
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@${NEW_RELIC_KAFKA_CONTAINER_NAME}:29093
KAFKA_LISTENERS: PLAINTEXT://${NEW_RELIC_KAFKA_CONTAINER_NAME}:29092,CONTROLLER://${NEW_RELIC_KAFKA_CONTAINER_NAME}:29093,PLAINTEXT_HOST://:9092
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LOG_DIRS: /tmp/kraft-combined-logs
CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk


networks:
default:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_icc: "true"
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
version: "3"

# The following must be set either in environment variables or via a .env file in the same folder as this file:
#
# AGENT_PATH host path to the Agent linux home folder - will map to /usr/local/newrelic-dotnet-agent in the container
# LOG_PATH host path for Agent logfile output - will map to /app/logs in the container
# DISTRO_TAG distro tag for build, not including the architecture suffix - possible values 7.0-bullseye-slim, 7.0-alpine, 7.0-jammy
# TARGET_ARCH the target architecture for the build and run -- either amd64 or arm64
# PORT external port for the smoketest API
# CONTAINER_NAME The name for the container
# PLATFORM The platform that the service runs on -- linux/amd64 or linux/arm64/v8
# DOTNET_VERSION The dotnet version number to use (7.0, 8.0, etc)
# NETWORK_NAME The network name to use for containers in this app. Should be unique among all running instances.
# TEST_DOCKERFILE The path and dockerfile to use for the service.
#
# and the usual suspects:
# NEW_RELIC_LICENSE_KEY
# NEW_RELIC_HOST
# NEW_RELIC_APP_NAME
#
#
# To build and run, execute `docker compose -f <path to docker-compose.yml> up`
# Alternatively, set COMPOSE_FILE environment variable to the path and omit the -f parameter

services:
smoketestapp:
container_name: ${CONTAINER_NAME}
image: ${CONTAINER_NAME}
platform: ${PLATFORM}
build:
context: .
dockerfile: ${TEST_DOCKERFILE}
args:
DISTRO_TAG: ${DISTRO_TAG}
TARGET_ARCH: ${TARGET_ARCH}
NEW_RELIC_LICENSE_KEY: ${NEW_RELIC_LICENSE_KEY}
NEW_RELIC_APP_NAME: ${NEW_RELIC_APP_NAME}
NEW_RELIC_HOST: ${NEW_RELIC_HOST}
DOTNET_VERSION: ${DOTNET_VERSION}
APP_DOTNET_VERSION: ${APP_DOTNET_VERSION}
ports:
- "${PORT}:80"
volumes:
- ${AGENT_PATH}:/usr/local/newrelic-dotnet-agent # AGENT_PATH from .env, points to newrelichome_linux_x64
- ${LOG_PATH}:/app/logs # LOG_PATH from .env, should be a folder unique to this run of the smoketest app
Original file line number Diff line number Diff line change
Expand Up @@ -23,104 +23,53 @@ version: "3"
# Alternatively, set COMPOSE_FILE environment variable to the path and omit the -f parameter

services:
smoketestapp:
container_name: ${CONTAINER_NAME}
image: ${CONTAINER_NAME}
platform: ${PLATFORM}
build:
context: .
dockerfile: ${TEST_DOCKERFILE}
args:
DISTRO_TAG: ${DISTRO_TAG}
TARGET_ARCH: ${TARGET_ARCH}
NEW_RELIC_LICENSE_KEY: ${NEW_RELIC_LICENSE_KEY}
NEW_RELIC_APP_NAME: ${NEW_RELIC_APP_NAME}
NEW_RELIC_HOST: ${NEW_RELIC_HOST}
DOTNET_VERSION: ${DOTNET_VERSION}
APP_DOTNET_VERSION: ${APP_DOTNET_VERSION}
ports:
- "${PORT}:80"
volumes:
- ${AGENT_PATH}:/usr/local/newrelic-dotnet-agent # AGENT_PATH from .env, points to newrelichome_linux_x64
- ${LOG_PATH}:/app/logs # LOG_PATH from .env, should be a folder unique to this run of the smoketest app
DebianX64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
UbuntuX64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
AlpineX64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
DebianArm64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
UbuntuArm64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
CentosX64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
build:
dockerfile: SmokeTestApp/Dockerfile.centos
CentosArm64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
build:
dockerfile: SmokeTestApp/Dockerfile.centos
AmazonX64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
build:
dockerfile: SmokeTestApp/Dockerfile.amazon
AmazonArm64SmokeTestApp:
extends:
file: docker-compose-smoketestapp.yml
service: smoketestapp
build:
dockerfile: SmokeTestApp/Dockerfile.centos

UbuntuX64Kafka1TestApp:
extends:
service: smoketestapp
depends_on:
- kafka-broker
environment:
- NEW_RELIC_KAFKA_TOPIC=${NEW_RELIC_KAFKA_TOPIC}
- NEW_RELIC_KAFKA_CONTAINER_NAME=${NEW_RELIC_KAFKA_CONTAINER_NAME}

UbuntuX64Kafka2TestApp:
extends:
service: smoketestapp
depends_on:
- kafka-broker
environment:
- NEW_RELIC_KAFKA_TOPIC=${NEW_RELIC_KAFKA_TOPIC}
- NEW_RELIC_KAFKA_CONTAINER_NAME=${NEW_RELIC_KAFKA_CONTAINER_NAME}

kafka-broker:
image: confluentinc/cp-kafka:7.5.0
container_name: ${NEW_RELIC_KAFKA_CONTAINER_NAME}
environment:
KAFKA_BROKER_ID: 1
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://${NEW_RELIC_KAFKA_CONTAINER_NAME}:29092,PLAINTEXT_HOST://${NEW_RELIC_KAFKA_CONTAINER_NAME}:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_NODE_ID: 1
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@${NEW_RELIC_KAFKA_CONTAINER_NAME}:29093
KAFKA_LISTENERS: PLAINTEXT://${NEW_RELIC_KAFKA_CONTAINER_NAME}:29092,CONTROLLER://${NEW_RELIC_KAFKA_CONTAINER_NAME}:29093,PLAINTEXT_HOST://:9092
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_LOG_DIRS: /tmp/kraft-combined-logs
CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk



networks:
default:
name: ${NETWORK_NAME}
driver: bridge
driver_opts:
com.docker.network.bridge.enable_icc: "true"
2 changes: 2 additions & 0 deletions tests/Agent/IntegrationTests/ContainerIntegrationTests.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NewRelic.Api.Agent", "..\..
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_docker", "_docker", "{FB10922F-3CC6-4497-AF53-DF6808380258}"
ProjectSection(SolutionItems) = preProject
ContainerApplications\docker-compose-kafka.yml = ContainerApplications\docker-compose-kafka.yml
ContainerApplications\docker-compose-smoketestapp.yml = ContainerApplications\docker-compose-smoketestapp.yml
ContainerApplications\docker-compose.yml = ContainerApplications\docker-compose.yml
EndProjectSection
EndProject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace NewRelic.Agent.ContainerIntegrationTests.ContainerFixtures;
public class ContainerApplication : RemoteApplication
{
private readonly string _dockerfile;
private readonly string _dockerComposeFile;
private readonly string _dotnetVersion;
private readonly string _distroTag;
private readonly string _targetArch;
Expand All @@ -36,13 +37,16 @@ protected override string SourceApplicationDirectoryPath
}
}

public ContainerApplication(string applicationDirectoryName, string distroTag, Architecture containerArchitecture, string dotnetVersion, string dockerfile) : base(applicationType: ApplicationType.Container, isCoreApp: true)
public ContainerApplication(string applicationDirectoryName, string distroTag, Architecture containerArchitecture,
string dotnetVersion, string dockerfile, string dockerComposeFile = "docker-compose.yml") : base(applicationType: ApplicationType.Container, isCoreApp: true)
{
ApplicationDirectoryName = applicationDirectoryName;
_dockerComposeServiceName = applicationDirectoryName;
_distroTag = distroTag;
_dotnetVersion = dotnetVersion;
_dockerfile = dockerfile;
_dockerComposeFile = dockerComposeFile;

DockerDependencies = new List<string>();

switch (containerArchitecture)
Expand Down Expand Up @@ -75,7 +79,7 @@ public override void Start(string commandLineArguments, Dictionary<string, strin
{
CleanupContainer();

var arguments = $"compose up --abort-on-container-exit --force-recreate {_dockerComposeServiceName}";
var arguments = $"compose -f {_dockerComposeFile} -p {_dockerComposeServiceName.ToLower()} up --abort-on-container-exit --remove-orphans --force-recreate {_dockerComposeServiceName}";

var newRelicHomeDirectoryPath = DestinationNewRelicHomeDirectoryPath;
var profilerLogDirectoryPath = DefaultLogFileDirectoryPath;
Expand Down Expand Up @@ -179,30 +183,18 @@ public override void Shutdown()

// stop and remove the container, no need to kill RemoteProcess, as it will die when this command runs
// wait up to 5 seconds for the app to terminate gracefully before forcefully closing it
Process.Start("docker", $"container stop {ContainerName} -t 5");
Process.Start("docker", $"compose -p {_dockerComposeServiceName.ToLower()} down --rmi local --remove-orphans");

Thread.Sleep(TimeSpan.FromSeconds(5)); // give things a chance to settle before destroying the container

CleanupContainer();
}

private void CleanupContainer()
{
Console.WriteLine($"[{AppName} {DateTime.Now}] Cleaning up container and images related to {ContainerName} container.");
TestLogger?.WriteLine($"[{AppName}] Cleaning up container and images related to {ContainerName} container.");
// ensure there's no stray containers or images laying around
Process.Start("docker", $"container rm --force {ContainerName}");
Process.Start("docker", $"image rm --force {ContainerName}");

if (DockerDependencies.Count > 0)
{
foreach (var dep in DockerDependencies)
{
Console.WriteLine($"[{AppName} {DateTime.Now}] Removing dependent container: {dep}.");
TestLogger?.WriteLine($"[{AppName}] Removing dependent container: {dep}.");
Process.Start("docker", $"container rm --force {dep}");
}
}
Process.Start("docker", $"compose -p {_dockerComposeServiceName.ToLower()} down --rmi local --remove-orphans");


#if DEBUG
// Cleanup the networks with no attached containers. Mainly for testings on dev laptops - they can build up and block runs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ protected LinuxKafkaTestFixtureBase(
string distroTag,
ContainerApplication.Architecture containerArchitecture,
string dockerfile,
string dotnetVersion) :
base(new ContainerApplication(applicationDirectoryName, distroTag, containerArchitecture, dotnetVersion, dockerfile))
string dotnetVersion,
string dockerComposeFile = "docker-compose-kafka.yml") :
base(new ContainerApplication(applicationDirectoryName, distroTag, containerArchitecture, dotnetVersion, dockerfile, dockerComposeFile))
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public abstract class LinuxKafkaTest<T> : NewRelicIntegrationTest<T> where T : L
{
private const int TopicNameLength = 15;

internal string _topicName;
private readonly string _topicName;
private readonly T _fixture;

protected LinuxKafkaTest(T fixture, ITestOutputHelper output) : base(fixture)
Expand All @@ -27,8 +27,7 @@ protected LinuxKafkaTest(T fixture, ITestOutputHelper output) : base(fixture)
_fixture.TestLogger = output;

_topicName = GenerateTopic();
var brokerName = "broker" + _topicName;
((ContainerApplication)_fixture.RemoteApplication).DockerDependencies.Add(brokerName);
var brokerName = "kafka-broker";

_fixture.Actions(setupConfiguration: () =>
{
Expand Down Expand Up @@ -113,15 +112,13 @@ internal static string GenerateTopic()
}
}

[Collection("Sequential")]
public class UbuntuX64Kafka1Test : LinuxKafkaTest<UbuntuX64Kafka1TestFixture>
{
public UbuntuX64Kafka1Test(UbuntuX64Kafka1TestFixture fixture, ITestOutputHelper output) : base(fixture, output)
{
}
}

[Collection("Sequential")]
public class UbuntuX64Kafka2Test : LinuxKafkaTest<UbuntuX64Kafka2TestFixture>
{
public UbuntuX64Kafka2Test(UbuntuX64Kafka2TestFixture fixture, ITestOutputHelper output) : base(fixture, output)
Expand Down

0 comments on commit a66bbfd

Please sign in to comment.