Name server serves as a single, reliable source for EPICS and ACNET control system component information such as device definitions, property configurations, and associated metadata.
- Acorn Nameserver prototype
This prototype does the following:
- Hosts component information about the control system devices, including nodes, locations, alarm and access control properties, etc...
- Uses secure gRPC as the protocol for service-to-service communication
- Uses PostgreSQL for storage
- Uses OpenID Connect Role-based Access Control for authentication and authorization that can be integrated with Keycloak
- Database schema version control and automated database deployments
- Provides health check and reflection services
- Packages the application to a container
- Provides a client tool to interact with the nameserver
See the Nameserver design document for more information
This project uses Quarkus, a Java framework geared towards building Kubernetes native Java applications.
Due to this fact, using this framework makes it easy to add on common performance optimizations, implement common protocols, and integrate with other common services.
For more information, go to quarkus.io
For prerequisites for Quarkus, refer to the prerequisite lists in the Creating your first application and Your second Quarkus application tutorials. Specifically, the following need to be installed:
- JDK 17+ installed with JAVA_HOME configured appropriately
- Apache Maven 3.9.9
- A working container runtime (Docker or Podman) is needed to run in dev mode
NOTE: Application was tested using Podman. As such, instructions below use Podman. To use Docker, replace podman
with docker
For the test client, you need the following installed:
Run the following to install the plugin for generating Go code (see docs):
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
Server was tested on Linux operating system.
- Go to
nameserver
folder, this is the server project directory - Go to
src/main/resources/ssl
and run./gen-certs.sh
. This will generate a self-signed certificate that can be used for testing. - Run your application in dev mode by running:
./mvnw quarkus:dev
This mode will allow live editing. The server will by default listen from port 8443. The URL and port it's listening on will be printed on the console with a message like this:
acorn-nameserver 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.18.3) started in 32.681s. Listening on: https://0.0.0.0:8443
Dev mode also automatically starts containerized Postgres and Keycloak services for testing as well as a container running the "ryuk" service which is responsible for properly cleaning up services when exiting out of dev mode. Run podman ps
to see running containers:
podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fbf4f903bac2 docker.io/testcontainers/ryuk:0.11.0 /bin/ryuk 24 minutes ago Up 24 minutes 0.0.0.0:44455->8080/tcp testcontainers-ryuk-496632b7-bbff-421c-9081-f6c4b204eaa9
752675033d8f docker.io/library/postgres:17 --max_prepared_tr... 24 minutes ago Up 24 minutes 0.0.0.0:41137->5432/tcp wizardly_borg
dee29616ed5a quay.io/keycloak/keycloak:26.0.7 start --http-enab... 24 minutes ago Up 24 minutes 0.0.0.0:34753->8080/tcp friendly_cori
NOTE: Quarkus also comes with a Dev UI, that helps with interacting with application and supporting services. Go to [SERVER_ADDRESS]/q/dev-ui
(for example: https://localhost:8443/q/dev-ui) to view.
They Keycloak dev service is prepopulated using the configuration from the Keycloak Dev Services example, defined in src/main/resources/quarkus-realm.json
. This configuration defines users alice
, and admin
, whose passwords are the same as their usernames. admin
is given both admin
and user
roles, and alice
is given user
role only.
The Postgres dev service has the database automatically setup and is prepopulated using src/main/resources/import.sql
. This only applied in dev mode.
- Go to the
nsclient
directory - Run
./gen-code.sh
to generate the Go code for gRPC - Run
go build
. This will create the executablensclient
- Lookup the keycloak server port by running
podman ps
and looking at the port of the image running keycloak. For example, if the port shows0.0.0.0:34753->8080/tcp
, the keycloak server port is 34753. - Run the following to see the existing devices in the server
./nsclient list device --keycloak-url=http://localhost:KEYCLOAK_PORT
2025/05/08 11:24:23 Connecting to localhost:8443
2025/05/08 11:24:23 List devices: [name:"Device A" description:"Device A description" node_hostname:"Node A"]
The client app by default is using the config file nsclient.yaml
in the same directory as the executable, which is configured specifically for the dev-mode and test setup. Run ./nsclient
to see all commands and options available.
Let's now try adding a new device. Input for the device can be passed in via JSON data. To see the JSON format run
./nsclient create device --keycloak-url=http://localhost:KEYCLOAK_PORT --json-template
{"name":"Device","description":"(optional)Description","nodeHostname":"NodeHostname"}
To create the device:
./nsclient create device --keycloak-url=http://localhost:KEYCLOAK_PORT --json='{"name":"Device B", "description": "my new device", "nodeHostname":"Node A"}'
This example links the new device to the same node as the already existing device.
Similar commands can be run on other resources such as channels, nodes, locations, etc. To see list of commands, run ./nsclient -h
.
In dev mode, the quarkus automatically creates the database at startup using Hibernate ORM database generation based on the Hibernate ORM Java classes defined in src/main/java/nameserver/model
. It will also re-create the database tables if any of the Hibernate ORM classes change. This is great when you're in development! Once you're at the point you want to deploy the server, you can use Flyway to create the initial schema and manage any schema changes.
Open pom.xml
and uncomment the following lines:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-flyway</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
Run the application in dev mode and go to the Dev UI > Extensions > Datasources link in the Flyway panel (See previous section about how to get to the Dev UI). Hit the Migration button (see documentation). This will create the initial data schema file V1.0.0__acorn-nameserver.sql
under src/main/resources/db/migration
.
To have the schema file automatically applied startup, add quarkus.flyway.migrate-at-start=true
to the application.properties
file in src/main/resources
.
To make further schema changes, update the Hibernate ORM Java classes and add a V<VERSION>__<NAME>.sql
where <VERSION>
is the version number to the db/migration
directory.
Flyway keeps track which migration versions have been applied already in a separate table, to prevent migration files from being applied more than once.
Open the application.properties
file in src/main/resources/
, make sure the following settings are set correctly to work with your external services
Postgres settings
%prod.quarkus.datasource.db-kind = postgresql
%prod.quarkus.datasource.username = quarkus
%prod.quarkus.datasource.password = quarkus
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/nameserver
Keycloak settings
%prod.quarkus.oidc.auth-server-url=http://localhost:8180/realms/quarkus
quarkus.oidc.client-id=backend-service
quarkus.oidc.credentials.secret=secret
SSL settings
quarkus.http.ssl.certificate.files=ssl/server.crt
quarkus.http.ssl.certificate.key-files=ssl/server.key
Note that if you need to have different configuration between development (i.e dev mode), test, and in production, prefix settings with %dev
, %test
, or %prod
to only use the settings in either develeopment, test, or production.
Quarkus may different options for packaging and deploying applicaitons. The ones covered are:
- As a JAR file
- As a container
- As a Kubernetes application (brief)
The instructions for packaging as a JAR file is included here as a quick way to run and test the nameserver as a packaged application.
The application can be packaged using:
./mvnw package
It produces the quarkus-run.jar
file in the target/quarkus-app/
directory.
The application is now runnable using java -jar target/quarkus-app/quarkus-run.jar
.
Add the quarkus extension for the type of container image you are using. See list of extensions here.
Once the extension is added, run the following to create a container:
./mvnw package -Dnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true
This uses Dockerfile.native
in src/main/docker
to build the image.
You should find an container image like this:
#show image path
$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/echandler/acorn-nameserver 1.0.0-SNAPSHOT f8c924274a41 2 minutes ago 203 MB
Here's an example of running the container. It works with unmodified settings from applications.properties
- Start up the supporting services using helper scripts.
$ cd test_scripts
$ ./start-services.sh
Starting postgres
7c1fee4a608b7d4fe1fc6ba49a17a04cc8f0e72af84e65a9474bbbe0805a886c
Creating keycloak test image
STEP 1/3: FROM quay.io/keycloak/keycloak:26.0.7
STEP 2/3: COPY quarkus-realm.json /opt/keycloak/data/import/realm-export.json
--> 5fb5918bc404
STEP 3/3: CMD ["start-dev", "--import-realm"]
COMMIT ns-test-keycloak:0
--> 33fd8e5dc77d
Successfully tagged localhost/ns-test-keycloak:0
33fd8e5dc77d7330169bc3cb7b81a4404cc081090e54ba26e1d7ee7e9f5e3514
Starting keycloak
29fc91bf9c8e72dbcb74bca1dc43dd7c0ee4566644cec12f9928fbf2eb4394ce
All services started
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7c1fee4a608b docker.io/library/postgres:17 postgres 3 minutes ago Up 3 minutes 0.0.0.0:5432->5432/tcp ns-test-postgres
29fc91bf9c8e localhost/ns-test-keycloak:0 start-dev --impor... 3 minutes ago Up 3 minutes 0.0.0.0:8180->8080/tcp ns-test-keycloak
- Run nameserver container. Use host network to access exposed ports from postgres and keycloak containers
$ podman run --network=host -p 8443:8443 localhost/echandler/acorn-nameserver:1.0.0-SNAPSHOT
Quarkus has support for automatically generating Kubernetes resources and deploying your application to a Kubernetes cluster. This was not tested, however, below are useful links to learn more information:
Note that SmallRye Health extension is already added to the application and quarkus.grpc.server.use-separate-server
is set to false
, so you don't need to change quarkus.kubernetes.ingress.target-port
.
Below list several other services provided:
Server has gRPC reflection enabled to allow clients lookup what methods are available.
#List gRPC services available
$ grpcurl -key server.key -cert server.crt -insecure localhost:8443 list
NameService
grpc.health.v1.Health
#Describe the NameService
$ grpcurl -key server.key -cert server.crt -insecure localhost:8443 describe NameService
NameService is a service:
service NameService {
rpc AddChannelAccessControl ( .AddChannelAccessControlRequest ) returns ( .ChannelAccessControlResponse );
rpc AddChannelAlarm ( .AddChannelAlarmRequest ) returns ( .ChannelAlarmResponse );
rpc AddChannelTransform ( .AddChannelTransformRequest ) returns ( .ChannelTransformResponse );
rpc CreateAlarmType ( .CreateAlarmTypeRequest ) returns ( .AlarmTypeResponse );
rpc CreateChannel ( .CreateChannelRequest ) returns ( .ChannelResponse );
rpc CreateDevice ( .CreateDeviceRequest ) returns ( .DeviceResponse );
rpc CreateLocation ( .CreateLocationRequest ) returns ( .LocationResponse );
rpc CreateLocationType ( .CreateLocationTypeRequest ) returns ( .LocationTypeResponse );
rpc CreateNode ( .CreateNodeRequest ) returns ( .NodeResponse );
rpc CreateRole ( .CreateRoleRequest ) returns ( .RoleResponse );
...
}
#Describe a message type
$ grpcurl -key server.key -cert server.crt -insecure localhost:8443 describe .CreateChannelRequest
CreateChannelRequest is a message:
message CreateChannelRequest {
.Channel channel = 1;
}
Quarkus gRPC provides a health check
$ grpcurl -key server.key -cert server.crt -insecure localhost:8443 grpc.health.v1.Health/Check
{
"status": "SERVING"
}
There is also a Health REST API you can access by going to [SERVER_URL]/q/health
. This is part of the smallrye-health
Quarkus extension. See documentation for more details.
Source code is at src/main/java/nameserver
. The main entry point is in NameServiceImpl.java
, which defines the main gRPC service. Code uses Hiberate ORM to define the SQL data schema and handles interactions with the database. The Hibernate ORM entities are defined in the nameserver/model
.
Proto files are defined in src/main/proto