= Tutorial: Quarkus App with Docker
Thomas W. Stütz
2.0.0, 2023-04-29: How to use quarkus & docker @ HTL Leonding college
ifndef::imagesdir[:imagesdir: images]
//:toc-placement!:  // prevents the generation of the doc at this position, so it can be printed afterwards
:sourcedir: ../src/main/java
:icons: font
:sectnums:    // Nummerierung der Überschriften / section numbering
:toc: left
:toclevels: 5
:experimental: true
:linkattrs:   // so window="_blank" will be executed

//Need this blank line after ifdef, don't know why...
ifdef::backend-html5[]

// https://fontawesome.com/v4.7.0/icons/
icon:file-text-o[link=https://raw.githubusercontent.com/htl-leonding-college/quarkus-docker-gh-actions-demo/master/asciidocs/index.adoc] ‏ ‏ ‎
icon:github-square[link=https://github.com/htl-leonding-college/quarkus-docker-gh-actions-demo] ‏ ‏ ‎
icon:home[link=https://htl-leonding-college.github.io/quarkus-docker-gh-actions-demo/]
endif::backend-html5[]

// print the toc here (not at the default position)
//toc::[]

== Create a Simple Quarkus Application

=== Create the Project

* Configure on https://code.quarkus.io/?g=at.htlleonding.vehicle&a=vehicle-demo&e=resteasy-reactive&e=resteasy-reactive-jackson&e=smallrye-openapi&extension-search=origin:platform%20panache[quarkus.io^] and download as zip or push to github

* Alternatively create the project per maven

IMPORTANT: You have to install maven before

** Check the newest quarkus version https://quarkus.io/

[source,bash]
----
mvn io.quarkus.platform:quarkus-maven-plugin:3.0.1.Final:create \
    -DprojectGroupId=at.htlleonding.vehicle \
    -DprojectArtifactId=vehicle-demo \
    -Dextensions="resteasy-reactive, resteasy-reactive-jackson, smallrye-openapi"
----

=== First Run of the Project

.open a terminal
[source,bash]
----
./mvnw clean quarkus:dev
----

=== Manual Check of the Running Project

==== Curl

.open another terminal
[source,bash]
----
curl http://localhost:8080/hello
----

.output
----
Hello from RESTEasy Reactive
----

==== Intellij Rest Client

* You can also use the intellij rest client:

image::intellij-rest-client.png[]

<1> Create folder `http-requests` and create (text-)file `requests.http`.
<2> Write request
+
----
GET http://localhost:8080/hello

###
----
<3> Run request
<4> In the Service-window the output is shown

=== Build a jar-File

.build a jar-file (package)
----
./mvnw clean package
ls -lh ./target
----

.result
----
-rw-r--r--   1 stuetz  staff   5.4K Mar 14 12:08 quarkus-docker-gh-actions-demo-0.1.0-SNAPSHOT.jar
----

* we want to rename the jar-file to get rid of `demo.`
* -> we will rename the jar-file

=== Rename the jar-File

.rename the project into vehicle w/ version#
[source,xml]
----
//...
<build>
  <finalName>vehicle-${version}</finalName>
  <plugins>
    //...
  </plugins>
</build>
----

* There is also a property https://quarkus.io/guides/all-config#quarkus-core_quarkus.package.output-name[quarkus.package.output-name^]


.build a jar-file (package)
----
./mvnw clean package -DskipTests
----

It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory.
Be aware that it’s not an _uber-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory.

----
ls -l target
----

.result
----
-rw-r--r--  1 stuetz  staff   173K Apr 29 13:30 vehicle-1.0.0-SNAPSHOT.jar
----

.run the app
----
java -jar target/vehicle-1.0.0-SNAPSHOT.jar
----

.result
----
no main manifest attribute, in target/vehicle-1.0.0-SNAPSHOT.jar
----

.but there is also
----
tree target/quarkus-app

target/quarkus-app
├── app
│     └── vehicle-1.0.0-SNAPSHOT.jar
├── lib
│     ├── boot
│     │     ├── ...
│     │     ├── io.quarkus.quarkus-bootstrap-runner-3.0.1.Final.jar
│     │     └── ...
│     └── main
│         ├── ...
│         ├── com.fasterxml.jackson.core.jackson-annotations-2.14.2.jar
│         └── ...
├── quarkus
│   ├── generated-bytecode.jar
│   ├── quarkus-application.dat
│   └── transformed-bytecode.jar
├── quarkus-app-dependencies.txt
└── quarkus-run.jar
----

.run the app
----
java -jar target/quarkus-app/quarkus-run.jar
----

* the libraries are in the lib-folder available


== Run a local Docker Container

* To dockerize the quarkus application into a docker image, there are several options available:
** Usage of a Dockerfile
** Using a library (i.e. jib) to build an image
** ...

=== Using a Dockerfile

* When creating a quarkus project, you get pre-configured Dockerfiles

* Please notice:
** It is possible to package the quarkus app with all dependent libraries. This is called an uber-jar.
** But it is more performant to create a layer for the libraries and a layer for the quarkus app. So it is very fast, when you only have changes in the quarkus app. Only a very small layer would be created after the code changes.
** so the pre-configured Dockerfiles in `src/main/docker` the layers

//--

==== Create the jar-file

----
./mvnw clean package
----

.result
----
-rw-r--r--  1 stuetz  staff   173K Apr 29 13:42 vehicle-1.0.0-SNAPSHOT.jar
----

==== Create the Docker Image

----
docker build -f src/main/Docker/Dockerfile.jvm -t htl-leonding/vehicle .

docker image ls
----

.result
----
REPOSITORY                                TAG              IMAGE ID       CREATED             SIZE
htl-leonding/vehicle                      latest           49b4b82d75fa   7 seconds ago       440MB
----


==== Start the Docker Container

----
docker run -i --rm -p 8080:8080 htl-leonding/vehicle
----

TIP: In the `Dockerfile.jvm` you find in the comments all commands

.result
[%collapsible]
====
----
Starting the Java application using /opt/jboss/container/java/run/run-java.sh ...
INFO exec  java -Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -XX:MaxRAMPercentage=50.0 -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:+ExitOnOutOfMemoryError -cp "." -jar /deployments/quarkus-run.jar
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-04-29 11:54:21,899 INFO  [io.quarkus] (main) vehicle-demo 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.0.1.Final) started in 0.425s. Listening on: http://0.0.0.0:8080
2023-04-29 11:54:21,917 INFO  [io.quarkus] (main) Profile prod activated.
2023-04-29 11:54:21,917 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, smallrye-openapi, vertx]
----
====

.Use the REST-client for checking if the app works.
[%collapsible]
====
image::intellij-rest-client.png[]
====

=== Using Jib

* There is a Library called https://github.com/GoogleContainerTools/jib[Jib^] (Java Image Builder) from Google which makes it possible to build a Docker image without installing Docker

* Fortunately Quarkus has already implemented Jib

==== Add Jib-Dependency to pom.xml

----
./mvnw quarkus:add-extension -Dextensions='container-image-jib'
----

==== Configure application.properties

[source,properties]
----
quarkus.container-image.build=true  # <.>
quarkus.container-image.group=htl-leonding
quarkus.container-image.name=vehicle
quarkus.container-image.tag=latest
quarkus.jib.ports=8080
----

<.> this property is mandatory for building the docker image


==== Compile App and Build Image

----
./mvnw clean package
----

.result
[%collapsible]
====
----
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for at.htlleonding.vehicle:vehicle-demo:jar:1.0.0-SNAPSHOT
[WARNING] The expression ${version} is deprecated. Please use ${project.version} instead.
[WARNING] The expression ${version} is deprecated. Please use ${project.version} instead.
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] ----------------< at.htlleonding.vehicle:vehicle-demo >-----------------
[INFO] Building vehicle-demo 1.0.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ vehicle-demo ---
[INFO] Deleting /Users/stuetz/SynologyDrive/htl/skripten/themen/github/quarkus-docker-gh-actions-demo/labs/vehicle-demo/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ vehicle-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- quarkus-maven-plugin:3.0.1.Final:generate-code (default) @ vehicle-demo ---
[INFO]
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ vehicle-demo ---
[INFO] Changes detected - recompiling the module! :source
[INFO] Compiling 1 source file with javac [debug release 17] to target/classes
[INFO]
[INFO] --- quarkus-maven-plugin:3.0.1.Final:generate-code-tests (default) @ vehicle-demo ---
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ vehicle-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/stuetz/SynologyDrive/htl/skripten/themen/github/quarkus-docker-gh-actions-demo/labs/vehicle-demo/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.11.0:testCompile (default-testCompile) @ vehicle-demo ---
[INFO] Changes detected - recompiling the module! :dependency
[INFO] Compiling 2 source files with javac [debug release 17] to target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:3.0.0:test (default-test) @ vehicle-demo ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running at.htlleonding.vehicle.GreetingResourceTest
2023-04-29 14:16:32,049 INFO  [io.quarkus] (main) vehicle-demo 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.0.1.Final) started in 1.041s. Listening on: http://localhost:8081
2023-04-29 14:16:32,050 INFO  [io.quarkus] (main) Profile test activated.
2023-04-29 14:16:32,051 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, smallrye-openapi, swagger-ui, vertx]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.703 s - in at.htlleonding.vehicle.GreetingResourceTest
2023-04-29 14:16:32,713 INFO  [io.quarkus] (main) vehicle-demo stopped in 0.098s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ vehicle-demo ---
[INFO] Building jar: /Users/stuetz/SynologyDrive/htl/skripten/themen/github/quarkus-docker-gh-actions-demo/labs/vehicle-demo/target/vehicle-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- quarkus-maven-plugin:3.0.1.Final:build (default) @ vehicle-demo ---
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Starting (local) container image build for jar using jib.
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Using docker to run the native image builder
[WARNING] [io.quarkus.container.image.jib.deployment.JibProcessor] Base image 'registry.access.redhat.com/ubi8/openjdk-17-runtime:1.15' does not use a specific image digest - build may not be reproducible
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Using base image with digest: sha256:f921cf1f9147e4b306908f3bcb61dd215b4a51970f8db560ede02ee6a492fa99
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Container entrypoint set to [java, -Djava.util.logging.manager=org.jboss.logmanager.LogManager, -jar, quarkus-run.jar]
[INFO] [io.quarkus.container.image.jib.deployment.JibProcessor] Created container image htl-leonding/vehicle (sha256:ea1f3d84df3221e0ce05527735b558bf410132712113a50f6a59064c7bcdaf1e)

[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 5496ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  10.570 s
[INFO] Finished at: 2023-04-29T14:16:38+02:00
[INFO] ------------------------------------------------------------------------
----
====

----
docker image ls
----

----
REPOSITORY                                          TAG              IMAGE ID       CREATED         SIZE
htl-leonding/vehicle                                latest           de4acf85c454   4 minutes ago   382MB

----

==== Start the Docker Container

----
docker run --rm -p 8080:8080 htl-leonding/vehicle
----

.Use the REST-client for checking if the app works.
[%collapsible]
====
image::intellij-rest-client.png[]
====

== Run local docker-compose

* Now we have the docker image locally.

=== Create docker-compose.yaml

* We need a `docker-compose.yaml` - file

[source,yaml]
----
services:
  backend:
    image: htl-leonding/vehicle:latest
    ports:
      - "8080:8080"
    networks:
      - vehicle

networks:
  vehicle:
    name: quarkus-vehicle-network
----


=== Start docker-compose

----
docker compose up -d
----

image::intellij-services-docker.png[]

.Use the REST-client for checking if the app works.
[%collapsible]
====
image::intellij-rest-client.png[]
====

=== Stop docker-compose

----
docker compose down
----


== Run a local docker-compose with Database

=== Add Dependencies for the database

----
./mvnw quarkus:add-extension -Dextensions='jdbc-postgresql, hibernate-orm-panache'
----

=== Add code for accessing database


.Vehicle.java
[%collapsible]
====
[source,java]
----
package at.htlleonding.vehicle;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Vehicle {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private
    Long id;

    private String brand;
    private String model;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }

    @Override
    public String toString() {
        return String.format("%s %s", brand, model);
    }
}
----
====


.VehicleDto.java
[%collapsible]
====
[source,java]
----
package at.htlleonding.vehicle;

public record VehicleDto (Long id, String brand, String model) {}
----
====


.VehicleRepository.java
[%collapsible]
====
[source,java]
----
package at.htlleonding.vehicle;

import io.quarkus.hibernate.orm.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class VehicleRepository implements PanacheRepository<Vehicle> {
}
----
====


.VehicleResource.java
[%collapsible]
====
[source,java]
----
package at.htlleonding.vehicle;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import java.util.List;

@Path("/vehicle")
public class VehicleResource {

    @Inject
    VehicleRepository vehicleRepository;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Vehicle> listAll() {
        return vehicleRepository.listAll();
    }
}
----
====


.http-requests/requests.http
[%collapsible]
====
----
GET http://localhost:8080/hello

###

GET http://localhost:8080/vehicle

####
----
====


.src/main/resources/insert.sql
[%collapsible]
====
[source,sql]
----
insert into vehicle (brand, model) values ('Opel', 'Kadett');
insert into vehicle (brand, model) values ('VW', 'Käfer');
----
====


.add assertj-core to dependencies in pom.xml
[%collapsible]
====
[source,xml]
----
  <dependencies>
    ...
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>3.24.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
----
====





.src/main/test/java/at/htlleonding/vehicle/VehicleResourceTest.java
[%collapsible]
====
[source,java]
----
package at.htlleonding.vehicle;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import java.util.LinkedList;
import java.util.List;

import static io.restassured.RestAssured.given;

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

@QuarkusTest
public class VehicleResourceTest {

    @Test
    public void testHelloEndpoint() {

        List<VehicleDto> expectedVehicles = List.of(
                new VehicleDto(2L, "VW", "Käfer"),
                new VehicleDto(1L, "Opel", "Kadett")
        );

        List<VehicleDto> retrievedVehicles = new LinkedList<>();
        retrievedVehicles = given()
                .when().get("/vehicle")
                .then()
                .statusCode(200)
                .extract().body().jsonPath().getList(".", VehicleDto.class);

        assertThat(retrievedVehicles).hasSize(2)
                .usingRecursiveComparison()
                .ignoringFields("id")
                .ignoringCollectionOrder()
                .isEqualTo(expectedVehicles);
    }

}
----
====

.src/main/resources/application.properties
[%collapsible]
====
[source,properties]
----
# datasource configuration
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = app
quarkus.datasource.password = app
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/db

# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation=drop-and-create
%prod.quarkus.hibernate-orm.sql-load-script=import.sql # <.>

#quarkus.package.type=uber-jar

quarkus.container-image.build=true
quarkus.container-image.group=htl-leonding
quarkus.container-image.name=vehicle
quarkus.container-image.tag=latest
quarkus.jib.ports=8080
----
====

<.> For importing data in prod-profile


=== Build the docker image


----
./mvnw clean package -DskipTests -Dquarkus.container-image.build=true
----

* Because the ide is running on linux/arm64/v8 - architecture, we have to tell jib to create an linux/amd64 image -> `-Dquarkus.container-image.build=true`


=== Create the docker-compose.yaml for development

* For starting only the database and pgadmin.
** You could also use dev-services for starting a database.
* The quarkus-app ist startet in dev-mode w/o docker.

.docker-compose-postgres.yaml
[%collapsible]
====
[source,yaml]
----
services:

  db:
    container_name: postgres
    image: postgres:15.2-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: db
    ports:
      - 5432:5432
    volumes:
      - ./db-postgres/db:/var/lib/postgresql/data
      - ./db-postgres/import:/import
    networks:
      - postgres

#  adminer:
#    image: adminer
#    restart: always
#    ports:
#      - 8090:8080

# https://github.com/khezen/compose-postgres/blob/master/docker-compose.yml
  pgadmin:
    container_name: pgadmin
    image: dpage/pgadmin4:7.0
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin}
      PGADMIN_CONFIG_SERVER_MODE: 'False'
    volumes:
      - ./db-postgres/pgadmin:/root/.pgadmin
    ports:
      - 8090:80
    networks:
      - postgres
    restart: unless-stopped

networks:
  postgres:
    driver: bridge
----
====

.start
----
docker compose -f docker-compose-postgres.yaml up -d
----

.stop
----
docker compose -f docker-compose-postgres.yaml down
----

=== Create the docker-compose.yaml for production

.docker-compose-all-services.yaml
[source,yaml]
----
services:

  db:
    container_name: postgres
    image: postgres:15.2-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: db
    ports:
      - 5432:5432
    volumes:
      - ./db-postgres/db:/var/lib/postgresql/data
      - ./db-postgres/import:/import
    networks:
      - vehicle

# https://github.com/khezen/compose-postgres/blob/master/docker-compose.yml
  pgadmin:
    container_name: pgadmin
    image: dpage/pgadmin4:7.0
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin}
      PGADMIN_CONFIG_SERVER_MODE: 'False'
    volumes:
      - ./db-postgres/pgadmin:/root/.pgadmin
    ports:
      - 8090:80
    networks:
      - vehicle
    restart: unless-stopped

  backend:
    image: htl-leonding/vehicle:latest
    restart: unless-stopped
    environment:
      QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://db:5432/db
      QUARKUS_DATASOURCE_USERNAME: app
      QUARKUS_DATASOURCE_PASSWORD: app
    ports:
      - "8080:8080"
    depends_on:
      - db
    networks:
      - vehicle


networks:
  vehicle:
    name: quarkus-vehicle-network
    driver: bridge

----

.start
----
docker compose -f docker-compose-all-services.yaml up -d
----

.stop
----
docker compose -f docker-compose-all-services.yaml down
----


== CI - Deploy to github-packages

* sources:
** https://github.com/features
** https://docs.github.com/en/actions/learn-github-actions
** https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions
** https://docs.github.com/en/actions/automating-builds-and-tests
** https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
** https://docs.github.com/en/actions/publishing-packages
** https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions
** https://docs.github.com/en/actions/using-workflows[Using workflows^]





=== Create a basic pipeline

* When you create a quarkus-project with a jib-dependency, you get a sample `ci.yml`.

[source,yaml]
----
## A basic GitHub Actions workflow for your Quarkus application.

name: CI build

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout gh-repo
        uses: actions/checkout@v3

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: 17
          distribution: temurin
          cache: maven

      - name: Build
        run: |
          ./mvnw package -B
          docker image ls
----

* After pushing this file, the first error occurs

image::gh-workflow-001-error.png[]

.database-service is missing
image::gh-workflow-002-error-missing-database.png[]

* we could skip tests, or
* we use testcontainers
** so we configure quarkus to use testcontainers in test-profile

.application.properties
[source,properties]
----
# datasource configuration
%dev.quarkus.datasource.db-kind = postgresql  # <.>
%dev.quarkus.datasource.username = app
%dev.quarkus.datasource.password = app
%dev.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/db

%prod.quarkus.datasource.db-kind = postgresql  # <.>
%prod.quarkus.datasource.username = app
%prod.quarkus.datasource.password = app
%prod.quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/db

# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation=drop-and-create
%prod.quarkus.hibernate-orm.sql-load-script=import.sql

#quarkus.package.type=uber-jar

quarkus.container-image.build=true
quarkus.container-image.group=htl-leonding
quarkus.container-image.registry=ghcr.io
quarkus.container-image.name=vehicle
quarkus.container-image.tag=latest
quarkus.jib.ports=8080

%test.quarkus.datasource.devservices.enabled=true # <.>
----

<.> for dev-profile use local docker-compose-database-service
<.> for prod-profile use local docker-compose-database-service
<.> activate devservices for databases - because we provide only db-credentials for test- and prod-profile, in test-profile we use a postgres-testcontainer


image::gh-workflow-003-success.png[]


image::gh-workflow-004-docker-images.png[]

* You see, the docker image is built, we can continue to publish this docker image into gh-packages

* Because using the testcontainer is consuming a lot of github-resources (you normally have to pay), so we will skip tests, while developing the pipeline.

== Publishing to gh-packages

* source: https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions[Publishing and installing a package with GitHub Actions^]

"You can use a GITHUB_TOKEN in a GitHub Actions workflow to delete or restore a package using the REST API, if the token has `admin` permission to the package. Repositories that publish packages using a workflow, and repositories that you have explicitly connected to packages, are automatically granted `admin` permission to packages in the repository."
-- github-docs


* github provides a token to authenticate (https://docs.github.com/en/actions/security-guides/automatic-token-authentication[Automatic token authentication^])

image::gh-token-001.png[]

..github/workflows/ci.yaml
[source,yaml]
----
name: CI build

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
  PROJECT_NAME: vehicle
  PROJECT_VERSION: latest

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout gh-repo
        uses: actions/checkout@v3

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: 17
          distribution: temurin
          cache: maven

      - name: Log in to the Container registry
        uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.MY_GITHUB_TOKEN }}

      - name: Build
        run: ./mvnw package -DskipTests -B

      - name: Push to packages
        run: docker push ${{ env.REGISTRY }}/${{ github.actor }}/${{ env.PROJECT_NAME }}:${{ env.PROJECT_VERSION }}
----

* in the first attempt when using the provided `${{ secrets.GITHUB_TOKEN }}` did not work:
+
image::gh-workflow-005-access-denied.png[]

** So in the repository a token with package-read-permission was created
** A repository-scoped secret was created with this token -> ${{ secrets.MY_GITHUB_TOKEN }}
** Now it worked
*** Maybe this happened, because the repository was located in a github-organization

* Instead of the Docker-login-action you could login with
** `docker login -u ${{ github.actor }} -p ${{ secrets.MY_GITHUB_TOKEN }} ${{ env.REGISTRY }}`

* In the *Build*-Step you can omit `-DskipTests`

* Instead of `docker push ...` it would be possible to use `mvn install` - but in this case you have to supply the registry to the `pom-file`.


==== Configure permissions of GITHUB_TOKEN

* As in https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions[GitHub Docs^] explained, it is possible to modify the default permissions granted to the GITHUB_TOKEN

TIP: This is maybe the better way than creating a new token (MY_GITHUB_TOKEN)

image::gh-token-permissions-1.png[]
image::gh-token-permissions-2.png[]



=== Make package public

* To access the registry -> `https://github.com/<your_user_name>?tab=packages`

image::gh-packages-001.png[]

image::gh-packages-002.png[]

image::gh-packages-003.png[]

image::gh-packages-004.png[]














































































////


=== Choose a Packaging Format

* there are several aspects to pay attention:
** fast-jar or legacy-jar [https://www.heise.de/news/Java-Framework-Quarkus-1-12-erhebt-Fast-jar-zum-Standard-5064039.html[heise, window="_blank"]]
** create an uber-jar or deliver the quarkus-app-folder


* What is a uber-jar?
** Über is the german word for above or over (it's actually cognate with the English over).
Hence, in this context, an uber-jar is an "over-jar", one level up from a simple JAR (a), defined as one that contains both your package and all its dependencies in one single JAR file. The name can be thought to come from the same stable as ultrageek, superman, hyperspace, and metadata, which all have similar meanings of "beyond the normal".
** uber-jar is also known as fat jar i.e. jar with dependencies.
There are three common methods for constructing an uber jar (https://stackoverflow.com/a/39030649[stackoverflow, window="_blank"]):
. *Unshaded*: Unpack all JAR files, then repack them into a single JAR. Works with Java's default class loader. Tools *maven-assembly-plugin*
. *Shaded*: Same as unshaded, but rename (i.e., "shade") all packages of all dependencies. Works with Java's default class loader. Avoids some (not all) dependency version clashes. Tools *maven-shade-plugin*
. *JAR of JARs*: The final JAR file contains the other JAR files embedded within. Avoids dependency version clashes. All resource files are preserved. Tools: *Eclipse JAR File Exporter*

==== How to change the package type?

* You have to change the https://quarkus.io/guides/all-config#quarkus-core_quarkus.package.type[quarkus.package.type]-property
* options:
** jar -> fast-jar
** legacy-jar
** uber-jar
** mutable-jar -> remote development mode
** native
** native-sources

* Which options do you have to change the property?
** application.properties
+
----
quarkus.package.type=uber-jar
----
** pom.xml
+
[source,xml]
----
<properties>
  <quarkus.package.type>uber-jar</quarkus.package.type>
</properties>
----
** as maven-parameter
+
----
./mvnw clean package -Dquarkus.package.type=uber-jar
----

.result
----
-rw-r--r--  1 stuetz  staff  38347130 Apr 29 12:13 vehicle-1.0.0-SNAPSHOT-runner.jar
-rw-r--r--  1 stuetz  staff    180338 Apr 29 12:13 vehicle-1.0.0-SNAPSHOT.jar.original
----

<.> the -runner - file includes all necessary libraries for running the quarkus app


=== Run the jar-File

----
java -jar ./target/vehicle-0.1.0-SNAPSHOT-runner.jar
----

=== Check the Running App manually

----
curl http://localhost:8080/hello
----

.result
----
Hello RESTEasy
----

== Run in Docker

[source,bash]
----
./mvnw clean package
----

[source,sh]
----
docker build -f src/main/docker/Dockerfile.legacy-jar -t htl-leonding/vehicle .
----

https://catalog.redhat.com/software/containers/search?p=1&q=ubi[Red Hat container images, window="_blank"]

[source,shell]
----
docker run -i --rm -p 8080:8080 htl-leonding/vehicle
----

== Run in docker-compose

* Maybe you will download all docker-images
** docker pull adminer:4.8.0
** docker pull postgres:13.2

* Sources:
** https://docs.docker.com/compose/compose-file/compose-versioning/[docker-compose versions]
** https://github.com/docker/awesome-compose[awesome-compose, a curated list of docker-compose examples, window="_blank"]
** https://github.com/lreimer/hands-on-quarkus/blob/master/docker-compose.yml
** http://heidloff.net/article/multistage-dockerfiles-for-quarkus/[Multistage Dockerfiles for Quarkus, window="_blank"]



== Create a github-actions Pipeline


////