Skip to content

Commit

Permalink
Merge pull request #91 from camunda-community-hub/np-agent
Browse files Browse the repository at this point in the history
Add a standalone, containerized agent
  • Loading branch information
ChrisKujawa authored Jan 21, 2022
2 parents b554246 + 3c77d47 commit 78498c9
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/ci-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,10 @@ jobs:
asset_path: ${{ steps.release.outputs.artifacts_archive_path }}
asset_name: ${{ steps.release.outputs.artifacts_archive_path }}
asset_content_type: application/zip
- if: github.event.release
name: Build & Push Docker images
id: docker
timeout-minutes: 10
run: |
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin
mvn -B -Pdocker -DskipTests -DdockerGoal=build -DdockerImageTag=${{ github.event.release.tag_name }} package
148 changes: 148 additions & 0 deletions agent/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<artifactId>agent</artifactId>
<packaging>jar</packaging>

<name>EZE - Embedded Zeebe Engine Agent</name>

<parent>
<groupId>org.camunda.community</groupId>
<artifactId>eze-root</artifactId>
<relativePath>../pom.xml</relativePath>
<version>0.7.1-SNAPSHOT</version>
</parent>

<dependencies>
<dependency>
<groupId>org.camunda.community</groupId>
<artifactId>eze</artifactId>
<version>${project.version}</version>
</dependency>

<!-- required as otherwise the engine cannot be instantiated due to a missing class -->
<dependency>
<groupId>io.camunda</groupId>
<artifactId>zeebe-exporter-api</artifactId>
</dependency>

<!-- cli -->
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
</dependency>

<dependency>
<groupId>org.agrona</groupId>
<artifactId>agrona</artifactId>
</dependency>

<!-- logging; adopt the implementation included in eze -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.camunda</groupId>
<artifactId>zeebe-client-java</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
<agent.finalName>eze</agent.finalName>
<agent.mainClass>org.camunda.community.eze.agent.Agent</agent.mainClass>
</properties>

<build>
<finalName>${agent.finalName}</finalName>
<plugins>
<!-- process picocli annotations -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>info.picocli</groupId>
<artifactId>picocli-codegen</artifactId>
<version>${picocli.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>

<profiles>
<!-- generates a Docker image as part of the build process; this configuration expects
authentication to have been set up beforehand -->
<profile>
<id>docker</id>

<properties>
<dockerImageTag>${project.version}</dockerImageTag>

<!-- set to build to build AND push; leave to dockerBuild to only build locally -->
<dockerGoal>dockerBuild</dockerGoal>
</properties>

<build>
<plugins>
<!-- Build a docker image -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>

<configuration>
<from>
<!-- smallest Java 17 base image; Temurin does not produce Java 17 JRE only builds -->
<image>
azul/zulu-openjdk-alpine:17-jre-headless@sha256:ca58039e2aa75651ab6e6558816202e85187675d8ee64f6dc7fc8d44cb6ffef3
</image>
</from>
<to>
<image>ghcr.io/camunda-community-hub/${project.build.finalName}</image>
<tags>${dockerImageTag}</tags>
</to>
<container>
<mainClass>${agent.mainClass}</mainClass>
<ports>
<port>26500</port>
</ports>
</container>
</configuration>

<executions>
<execution>
<phase>package</phase>
<goals>
<goal>${dockerGoal}</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
73 changes: 73 additions & 0 deletions agent/src/main/java/org/camunda/community/eze/agent/Agent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Zeebe Community License 1.1. You may not use this file
* except in compliance with the Zeebe Community License 1.1.
*/
package org.camunda.community.eze.agent;

import java.util.Collections;
import java.util.concurrent.Callable;
import org.agrona.concurrent.ShutdownSignalBarrier;
import org.camunda.community.eze.EngineFactory;
import org.camunda.community.eze.ZeebeEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
import picocli.CommandLine.Command;

/**
* A thin wrapper around a {@link ZeebeEngine} instance. For potential exit codes, see {@link
* ExitCode}.
*/
@Command(name = "agent", mixinStandardHelpOptions = true, description = "Standalone EZE agent")
public final class Agent implements Callable<Integer> {

private final Logger logger;

public Agent() {
logger = LoggerFactory.getLogger(getClass());
}

@Override
public Integer call() {
final var shutdownBarrier = new ShutdownSignalBarrier();
final ZeebeEngine engine;

logger.info("Starting EZE agent...");
try {
engine = EngineFactory.INSTANCE.create(Collections.emptyList());
} catch (final Exception e) {
logger.error("Failed to create an instance of the embedded engine", e);
return ExitCode.CREATE_ERROR.getCode();
}

try {
engine.start();
} catch (final Exception e) {
logger.error("Failed to start EZE agent", e);
return ExitCode.START_ERROR.getCode();
}

logger.info("EZE agent started at {}", engine.getGatewayAddress());
ExitCode exitCode = ExitCode.OK;
shutdownBarrier.await();

logger.info("Shutting down EZE agent...");
try {
engine.stop();
} catch (final Exception e) {
logger.warn("Failed to gracefully shutdown the embedded engine", e);
exitCode = ExitCode.SHUTDOWN_ERROR;
}

logger.info("Shutdown EZE agent with status: {}", exitCode);
return exitCode.getCode();
}

public static void main(final String[] args) {
final int exitCode = new CommandLine(new Agent()).execute(args);
System.exit(exitCode);
}
}
33 changes: 33 additions & 0 deletions agent/src/main/java/org/camunda/community/eze/agent/ExitCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Zeebe Community License 1.1. You may not use this file
* except in compliance with the Zeebe Community License 1.1.
*/
package org.camunda.community.eze.agent;

/**
* A list of possible exit status codes returned by the Agent. These can be consumed by scripts or
* container orchestrators to react appropriately based on the error.
*/
enum ExitCode {
/** Indicates the application started and shutdown gracefully without errors */
OK(0),
/** Indicates the agent failed to create an instance of EZE */
CREATE_ERROR(128),
/** Indicates the agent failed to start the EZE instance */
START_ERROR(129),
/** Indicates the agent started the engine, but failed to gracefully shut it down */
SHUTDOWN_ERROR(131);

private final int code;

ExitCode(final int code) {
this.code = code;
}

int getCode() {
return code;
}
}
16 changes: 16 additions & 0 deletions agent/src/main/resources/log4j2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">

<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>

<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>

</Configuration>
49 changes: 49 additions & 0 deletions agent/src/test/java/org/camunda/community/eze/agent/AgentTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.camunda.community.eze.agent;

import static org.assertj.core.api.Assertions.assertThat;

import io.camunda.zeebe.client.ZeebeClient;
import io.camunda.zeebe.client.api.response.Topology;
import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.camunda.community.eze.ZeebeEngineImpl;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import sun.misc.Signal;

final class AgentTest {
private ExecutorService agentThreadContext;

@AfterEach
void afterEach() {
if (agentThreadContext != null) {
agentThreadContext.shutdownNow();
}
}

@Test
void shouldStartEngine() {
// given
final var agent = new Agent();
agentThreadContext = Executors.newSingleThreadExecutor();

// when
final var exitCodeFuture = agentThreadContext.submit(agent);
final Topology topology;
try (final var client =
ZeebeClient.newClientBuilder()
.usePlaintext()
.gatewayAddress("localhost:" + ZeebeEngineImpl.PORT)
.build()) {
topology = client.newTopologyRequest().send().join();
}
Signal.raise(new Signal("INT"));

// then
assertThat(topology.getClusterSize()).isEqualTo(1);
assertThat(exitCodeFuture)
.succeedsWithin(Duration.ofSeconds(10))
.isEqualTo(ExitCode.OK.getCode());
}
}
28 changes: 28 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
</parent>

<modules>
<module>agent</module>
<module>eze</module>
<module>junit-extension</module>
</modules>
Expand Down Expand Up @@ -54,6 +55,7 @@
<plugin.version.jacoco>0.8.7</plugin.version.jacoco>
<plugin.version.fmt>2.13</plugin.version.fmt>
<plugin.version.license>4.1</plugin.version.license>
<plugin.version.jib>3.2.0</plugin.version.jib>
<assertj.version>3.22.0</assertj.version>
<awaitility.version>4.1.1</awaitility.version>
<log4j.version>2.17.1</log4j.version>
Expand All @@ -63,6 +65,8 @@
<protobuf-java.version>3.19.3</protobuf-java.version>
<jackson.version>2.13.1</jackson.version>
<scala-library.version>2.13.8</scala-library.version>
<picocli.version>4.6.2</picocli.version>
<agrona.version>1.14.0</agrona.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -214,10 +218,34 @@
<artifactId>scala-library</artifactId>
<version>${scala-library.version}</version>
</dependency>

<!-- Agent dependencies -->
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>${picocli.version}</version>
</dependency>

<dependency>
<groupId>org.agrona</groupId>
<artifactId>agrona</artifactId>
<version>${agrona.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<pluginManagement>
<plugins>
<!-- used to package Docker images, primarily for the Agent -->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${plugin.version.jib}</version>
</plugin>
</plugins>
</pluginManagement>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down

0 comments on commit 78498c9

Please sign in to comment.