Web App Development: Development Versions of Spring Boot and ExpressJS Rockcraft Extensions, Block on Required Config

Since the last update we have added a new feature where configuration options can be marked with optional: false which will mean that the application does not get started until that configuration option is supplied. This avoids the application crashing because of, for example, a missing API token required to connect to a service. This implementation mirrors the optional: false implementation for integrations (such as a database) where if an integration is marked as such, the app won’t get started until the database is available. Below is an example for how to have such a configuration for a FastAPI app. This feature is available across all supported frameworks.

We’ve also made progress with adding the ExpressJS and Spring Boot frameworks to Rockcraft. We are keen for any input on the design. You will find instructions below for trying out the Rockcraft extensions which are both still at the PR/ development stage.

FastAPI Application with Required Configuration

This was delivered by @alithethird

To demonstrate the new block on required configuration missing feature, we will create a FastAPI application. This feature is also available on the other supported frameworks (Flask, Django and Go). We’ll start with creating a FastAPI application using multipass:

multipass launch --cpus 4 --disk 50G --memory 4G --name charm-dev 24.04
multipass shell charm-dev

sudo snap install rockcraft --channel latest/edge --classic
sudo snap install lxd
lxd init --auto
sudo snap install charmcraft --channel latest/edge --classic

sudo snap install microk8s --channel 1.31-strict/stable
sudo adduser $USER snap_microk8s
newgrp snap_microk8s
sudo microk8s enable hostpath-storage
sudo microk8s enable registry
sudo microk8s enable ingress

sudo snap install juju --channel 3.6/stable
mkdir -p ~/.local/share
juju bootstrap microk8s dev-controller

mkdir fastapi-hello-world
cd fastapi-hello-world

Now we can create the FastAPI app. Start with a requirements.txt file:

fastapi[standard]

And then create an app.py file with the following contents:

import os

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    print(os.getenv("APP_TOKEN"))
    return {"message": "Hello World"}

Any requests to the root endpoint will print the APP_TOKEN environment variable which we will later use as the required configuration. This token might be needed to connect to an external service such as for taking payments. It is generally not recommended to print sensitive values as they will end up in the logs. This is done here for illustrative purposes only and should not be done in production.

Let’s create the rock and the charm:

rockcraft init --profile fastapi-framework
ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack
rockcraft.skopeo --insecure-policy copy --dest-tls-verify=false \
   oci-archive:fastapi-hello-world_0.1_amd64.rock \
   docker://localhost:32000/fastapi-hello-world:0.1

mkdir charm
cd charm
charmcraft init --profile fastapi-framework --name fastapi-hello-world

Now we need to edit the charmcraft.yaml file to add the required configuration. Add the following snippet at the end of the file:

config:
  options:
    token:
      description: The token for the service.
      type: string
      optional: false

We can now continue with creating the charm and proceed with the deployment:

CHARMCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true charmcraft pack

juju add-model fastapi-hello-world
juju deploy ./fastapi-hello-world_amd64.charm fastapi-hello-world \
   --resource app-image=localhost:32000/fastapi-hello-world:0.1

Use juju status --watch 2s to monitor the deployment of the FastAPI app. It should eventually end up in the blocked state indicating that the token configuration option is missing: missing options: token. Let’s add the token configuration option:

juju config fastapi-hello-world token=service-token

Go back to monitoring the deployment using juju status --watch 2s. The FastAPI application should now be started. To verify it is working, let’s add an ingress and send a request to the root endpoint:

juju deploy nginx-ingress-integrator
juju integrate nginx-ingress-integrator fastapi-hello-world
juju config nginx-ingress-integrator \
   service-hostname=fastapi-hello-world path-routes=/
juju status --watch 2s  # wait for everything to be active
curl http://fastapi-hello-world  --resolve fastapi-hello-world:80:127.0.0.1

The last command should have the {"message":"Hello World"} response from the FastAPI application. We can check the logs to verify the token was passed to the application:

juju ssh --container app fastapi-hello-world/0 pebble exec --context=fastapi -- pebble logs

The service-token value should appear in the logs. Again, usually sensitive values should not be written to the logs, this is being done here for illustrative purposes only.

That’s it for showing how the new required configuration feature works. We deployed a FastAPI application with a required token. We initially didn’t set that configuration and the deployment entered the blocked state and the app wasn’t started. Once we supplied the token, the app was started and the token was available to the app.

To clean up, exit the multipass VM and run multipass delete --purge charm-dev.

Trying Out the ExpressJS Extension for Rockcraft

This is being worked on by @charlie4284

The next topic is showing you how you can try out the ExpressJS extension in Rockcraft. Note that it is at the PR stage so we will need to build Rockcraft based on the code in the PR. Also note that we may make changes to the extension during the PR stage. Let’s start with a new multipass VM:

multipass launch --cpus 4 --disk 50G --memory 4G --name rock-dev 24.04
multipass shell rock-dev

sudo snap install lxd
lxd init --auto

sudo snap install snapcraft --classic
git clone https://github.com/yanksyoon/rockcraft.git
cd rockcraft
git checkout feat/expressjs-framework
snapcraft --use-lxd
sudo snap install rockcraft*.snap --dangerous --classic
cd ..

sudo snap install docker
sudo addgroup --system docker
sudo adduser $USER docker
newgrp docker
sudo snap disable docker
sudo snap enable docker

mkdir expressjs-hello-world
cd expressjs-hello-world

Next, we need to create the ExpressJS app, we’ll use express-generator for that:

sudo apt-get update -y && sudo apt-get install npm -y
sudo npm install -g express-generator@4
express app

The next step is to create a Rock:

rockcraft init --profile expressjs-framework
ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack

We should have a .rock file in the directory after the command finishes with a size of roughly 90MB:

ls *.rock -l --block-size=MB

Let’s run the Rock using Docker:

sudo rockcraft.skopeo --insecure-policy \
  copy oci-archive:expressjs-hello-world_0.1_amd64.rock \
  docker-daemon:expressjs-hello-world:0.1
sudo docker run --rm -d -p 3000:3000 \
  --name expressjs-hello-world expressjs-hello-world:0.1

We can test the application using curl localhost:3000 --fail which should return the Welcome to Express message. We can check the application logs using sudo docker exec expressjs-hello-world pebble logs expressjs.

That’s the end of this demo! We showed how to create a new ExpressJS app and pack it into a Rock using the expressjs-framework extension. To clean up, exit the Multipass vm and run multipass delete --purge rock-dev.

Trying Out the Spring Boot Extension for Rockcraft

This is being worked on by @charlie4284

The last topic is showing you how you can try out the Spring Boot extension in Rockcraft. Note that it is at the development stage so we will need to build Rockcraft based on the code in the branch and we may make changes before this is finished. Let’s start with a new multipass VM:

multipass launch --cpus 4 --disk 50G --memory 4G --name rock-dev 24.04
multipass shell rock-dev

sudo snap install lxd
lxd init --auto

sudo snap install snapcraft --classic
git clone https://github.com/yanksyoon/rockcraft.git
cd rockcraft
git checkout feat/springboot-dev
snapcraft --use-lxd
sudo snap install rockcraft*.snap --dangerous --classic
cd ..

sudo snap install docker
sudo addgroup --system docker
sudo adduser $USER docker
newgrp docker
sudo snap disable docker
sudo snap enable docker

mkdir spring-boot-hello-world
cd spring-boot-hello-world

We will use the Spring CLI to create a Spring Boot started app:

sudo apt update -y && sudo apt install -y unzip openjdk-17-jdk
wget "https://github.com/spring-projects/spring-cli/releases/download/v0.9.0/spring-cli-standalone-0.9.0-linux.x86_64.zip"
unzip spring-cli-standalone-0.9.0-linux.x86_64.zip
spring-cli-standalone-0.9.0-linux-x86_64/bin/spring boot new .
chmod +x ./mvnw

Note that, we are planning a change where the Spring Boot application will be in the app directory instead of the project root directory. The next step is to create and pack the Rock:

rockcraft init --profile spring-boot-framework
ROCKCRAFT_ENABLE_EXPERIMENTAL_EXTENSIONS=true rockcraft pack

We should now have a .rock file in the directory after the command finishes with a size of roughly 70MB:

ls *.rock -l --block-size=MB

Let’s run the Rock using Docker:

sudo rockcraft.skopeo --insecure-policy \
  copy oci-archive:spring-boot-hello-world_0.1_amd64.rock \
  docker-daemon:spring-boot-hello-world:0.1
sudo docker run --rm -d -p 8080:8080 \
  --name spring-boot-hello-world spring-boot-hello-world:0.1

We can test the application using curl localhost:8080 which should return the current time and a 404 because the root endpoint has not been defined on the spring boot application. We can check the application logs using sudo docker exec spring-boot-hello-world pebble logs spring-boot.

That’s the end of this demo! We showed how to create a new Spring Boot app and pack it into a Rock using the spring-boot-framework extension. To clean up, exit the Multipass vm and run multipass delete --purge rock-dev.

2 Likes