diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..4832d787d92fad663ef201dad1a64082abbd6289 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.github/ +.gitlab/ +.idea/ +.gitlab-ci.yml +LICENSE.txt +readme.md diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000000000000000000000000000000000000..aaf407311910c0ba016005098199400718d7ff9b --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,28 @@ +--- +name: Build to Docker Hub + +on: + push: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Build & publish the project + run: mvn compile jib:build -X -DjibSerialize=true diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 37d0199ee01c08b586ed61fecf9487dee12aa5cb..40914893a4787d6e60f2bf3788d11c4f4de6c4a1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,25 +5,17 @@ variables: # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work. MAVEN_OPTS: > -Dhttps.protocols=TLSv1.2 - -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository + -Dmaven.repo.local=${CI_PROJECT_DIR}/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true - # As of Maven 3.3.0 instead of this, you may define these options in `.mvn/maven.config` so the same config is used - # when running from the command line. - # `installAtEnd` and `deployAtEnd` are only effective with the recent version of the corresponding plugins. - MAVEN_CLI_OPTS: > - --batch-mode - --errors - --fail-at-end - --show-version - -DinstallAtEnd=true - -DdeployAtEnd=true # Check the list of available templates at https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates include: - template: Jobs/Secret-Detection.gitlab-ci.yml + - template: Jobs/Dependency-Scanning.latest.gitlab-ci.yml - template: Workflows/MergeRequest-Pipelines.gitlab-ci.yml + - local: .gitlab/ci/deploy-template.gitlab-ci.yml - local: .gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml stages: @@ -48,7 +40,7 @@ build: - pwd script: - mvn $MAVEN_CLI_OPTS -DskipTests - -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository package + -Dmaven.repo.local=${CI_PROJECT_DIR}/.m2/repository package cache: key: files: @@ -88,27 +80,32 @@ test: - target/surefire-reports/TEST-*.xml deploy: - stage: deploy - image: docker.io/dokku/ci-docker-image:0.9.3 + extends: .base-dokku-deploy rules: !reference [.upstream-deploy-production-rules, rules] variables: - APP_NAME: spring-petclinic-rest - GIT_DEPTH: "0" - SSH_REMOTE: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22" - GIT_REMOTE_URL: "$SSH_REMOTE/$APP_NAME" - SSH_PRIVATE_KEY: $PRODUCTION_SSH_PRIVATE_KEY - BRANCH: $CI_DEFAULT_BRANCH + DOKKU_APP_NAME: spring-petclinic-rest + DOKKU_SSH_REMOTE: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22" + DOKKU_SSH_PRIVATE_KEY: $PRODUCTION_SSH_PRIVATE_KEY script: + - dokku network:exists "${APP_NAME}-network" + || dokku network:create "${APP_NAME}-network" + - dokku postgres:exists "${APP_NAME}-database" + || dokku postgres:create "${APP_NAME}-database" + --image docker.io/library/postgres + --image-version 16.0-alpine + --post-create-network "${APP_NAME}-network" + - dokku apps:exists "$APP_NAME" + || dokku apps:create "$APP_NAME" + - dokku network:set "$APP_NAME" attach-post-create "${APP_NAME}-network" + - dokku postgres:link "${APP_NAME}-database" "$APP_NAME" + - dokku config:set "$APP_NAME" SPRING_PROFILES_ACTIVE="default,csui" - dokku-deploy - - sh .gitlab/dokku-ci-post-deploy.sh - after_script: - - dokku-unlock - environment: - name: production - url: "https://$APP_NAME.dokku-ppl.cs.ui.ac.id" + - sh .gitlab/bin/dokku-ci-post-deploy.sh needs: - test - dependencies: [] + environment: + name: production + url: "https://${APP_NAME}.dokku-ppl.cs.ui.ac.id" visualize-coverage: stage: report @@ -136,6 +133,6 @@ sonarqube-check: script: - mvn -DskipTests verify sonar:sonar cache: - key: "${CI_JOB_NAME}" + key: "$CI_JOB_NAME" paths: - .sonar/cache diff --git a/.gitlab/bin/dokku-ci-post-deploy.sh b/.gitlab/bin/dokku-ci-post-deploy.sh new file mode 100644 index 0000000000000000000000000000000000000000..91e90a8a148ee22501f55cb4281db4ebf96b0cd8 --- /dev/null +++ b/.gitlab/bin/dokku-ci-post-deploy.sh @@ -0,0 +1,31 @@ +#!/bin/sh -l +set -e + +CERT_PATH="${CERT_PATH:-/home/dokku/.local/share/certs/cert.pem}" +PRIVKEY_PATH="${PRIVKEY_PATH:-/home/dokku/.local/share/certs/privkey.pem}" + +if [ -z "$APP_NAME" ]; then + echo "Error: APP_NAME is not set. Ensure the script is executed in the dokku/ci-docker-image environment." >&2 + exit 1 +fi + +if [ -z "$SSH_REMOTE" ]; then + echo "Error: SSH_REMOTE is not set. Ensure the script is executed in the dokku/ci-docker-image environment." >&2 + exit 1 +fi + +dokku_cert_add() { + echo "⚙ Assigning certs at $CERT_PATH and $PRIVKEY_PATH to $APP_NAME ..." + ssh "$SSH_REMOTE" -- certs:add "$APP_NAME" "$CERT_PATH" "$PRIVKEY_PATH" || echo "Failed to assign certs" >&2 +} + +dokku_map_port_to_https() { + port="$1" + echo "⚙ Mapping $APP_NAME container port $port to HTTPS ..." + ssh "$SSH_REMOTE" -- ports:add "$APP_NAME" "https:443:$port" || echo "Failed to map container port $port to HTTPS" >&2 +} + +dokku_cert_add +dokku_map_port_to_https 9966 + +echo "✅ Post-deployment configuration completed" diff --git a/.gitlab/bin/dokku.sh b/.gitlab/bin/dokku.sh new file mode 100755 index 0000000000000000000000000000000000000000..722ce87258c549219caa8823f4a78d90798d1cf9 --- /dev/null +++ b/.gitlab/bin/dokku.sh @@ -0,0 +1,31 @@ +#!/bin/sh -l +# Dokku wrapper script, based from https://github.com/dokku/ci-docker-image/issues/24#issuecomment-1211264189 +# by @mcgaw on GitHub +set -e + +check_command() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Error: '$1' not found. Ensure the script is executed in the dokku/ci-docker-image environment." >&2 + exit 1 + fi +} + +check_command setup-ssh +check_command parse-ci-commit +check_command parse-app-name +check_command parse-ssh-host +check_command parse-ssh-port +check_command ssh + +setup-ssh + +commit_sha="$(parse-ci-commit)" +app_name="$(parse-app-name)" + +if [ -z "$SSH_REMOTE" ]; then + ssh_remote="ssh://dokku@$(parse-ssh-host):$(parse-ssh-port)" +else + ssh_remote="$SSH_REMOTE" +fi + +ssh "$ssh_remote" -- "#@" diff --git a/.gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml b/.gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml index ab3b799d4ab29cf7edef3ae489e1c2dd12a56ec2..8577e0970e65382b49b3d07d89ff2c0fc84776d3 100644 --- a/.gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml +++ b/.gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml @@ -1,20 +1,5 @@ --- -deploy-workshop: - stage: deploy - image: docker.io/dokku/ci-docker-image:0.9.3 - rules: !reference [.upstream-deploy-production-rules, rules] - variables: - APP_NAME: "spring-petclinic-rest-$PARTICIPANT_NAME" - GIT_DEPTH: "0" - SSH_REMOTE: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22" - GIT_REMOTE_URL: "$SSH_REMOTE/$APP_NAME" - SSH_PRIVATE_KEY: $WORKSHOP_SSH_PRIVATE_KEY - BRANCH: $CI_DEFAULT_BRANCH - script: - - dokku-deploy - - sh .gitlab/dokku-ci-post-deploy.sh - after_script: - - dokku-unlock +.participants: ¶llel-by-participants parallel: matrix: - PARTICIPANT_NAME: @@ -35,9 +20,21 @@ deploy-workshop: - sonya - kamrozi - agra + +deploy-workshop: + extends: .base-dokku-deploy + rules: !reference [ .upstream-deploy-production-rules, rules ] + variables: + DOKKU_APP_NAME: "spring-petclinic-rest-$PARTICIPANT_NAME" + DOKKU_SSH_REMOTE: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22" + DOKKU_SSH_PRIVATE_KEY: $WORKSHOP_SSH_PRIVATE_KEY + <<: *parallel-by-participants + script: + - dokku-deploy + - sh .gitlab/dokku-ci-post-deploy.sh + needs: + - test environment: name: workshop/$PARTICIPANT_NAME url: "https://$APP_NAME.dokku-ppl.cs.ui.ac.id" - needs: - - test - dependencies: [] + resource_group: workshop diff --git a/.gitlab/ci/deploy-template.gitlab-ci.yml b/.gitlab/ci/deploy-template.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..f59611eec745efe2f50b903c1199b9b4de848c3e --- /dev/null +++ b/.gitlab/ci/deploy-template.gitlab-ci.yml @@ -0,0 +1,15 @@ +--- +.base-dokku-deploy: + stage: deploy + image: docker.io/dokku/ci-docker-image:0.9.3 + variables: + APP_NAME: "${DOKKU_APP_NAME:-$CI_PROJECT_PATH_SLUG}" + SSH_REMOTE: "${DOKKU_SSH_REMOTE:-ssh://dokku@dokku-ppl.cs.ui.ac.id:22}" + SSH_PRIVATE_KEY: "${DOKKU_SSH_PRIVATE_KEY:-}" + BRANCH: $CI_DEFAULT_BRANCH + GIT_DEPTH: 0 + before_script: > + [ -f .gitlab/bin/dokku.sh ] \ + && chmod +x .gitlab/bin/dokku.sh \ + && cp .gitlab/bin/dokku.sh /bin/dokku + dependencies: [] diff --git a/.gitlab/dokku-ci-post-deploy.sh b/.gitlab/dokku-ci-post-deploy.sh deleted file mode 100755 index 2b776b01996b06a08f573b586d46428decab8973..0000000000000000000000000000000000000000 --- a/.gitlab/dokku-ci-post-deploy.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -l -export CERT_PATH="/home/dokku/.local/share/certs/cert.pem" -export PRIVKEY_PATH="/home/dokku/.local/share/certs/privkey.pem" - -echo "⚙ Assigning certs at $CERT_PATH and $PRIVKEY_PATH to $APP_NAME ..." -ssh "$SSH_REMOTE" -- certs:add "$APP_NAME" "$CERT_PATH" "$PRIVKEY_PATH" || echo "Failed to assign certs" - -echo "⚙ Mapping $APP_NAME container port 9966 to HTTPS ..." -ssh "$SSH_REMOTE" -- ports:add "$APP_NAME" https:443:9966 || echo "Failed to map container port to HTTPS" - -echo "✅ Post-deploy configuration completed" diff --git a/Dockerfile b/Dockerfile index 148295997e900da32b6d96051b059c345787636d..024acc0177c738e0b659ecf4632845224f854f6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Use JDK & Maven image to build the application -FROM docker.io/library/maven:3.9.4-eclipse-temurin-17-alpine AS builder +FROM docker.io/library/maven:3.9.5-eclipse-temurin-17-alpine AS builder # Set the working directory inside the container WORKDIR /src @@ -11,7 +11,7 @@ COPY . . RUN mvn -DskipTests package # Use JRE image for running the application -FROM docker.io/library/eclipse-temurin:17.0.8.1_1-jre-alpine +FROM docker.io/library/eclipse-temurin:17.0.9_9-jre-alpine # Create a non-root user named "app" to own and run the application RUN addgroup app \ @@ -29,5 +29,12 @@ COPY --chown=app:app --from=builder /src/target/spring-petclinic-rest-*.jar . # Expose port 9966 EXPOSE 9966 +# Configure Spring Boot application profile via environment variable +ENV SPRING_PROFILES_ACTIVE="default" + # Run the application JAR file CMD ["/bin/sh", "-c", "java -jar spring-petclinic-rest-*.jar"] + +# Add container healthcheck +HEALTHCHECK CMD curl -f http://localhost:9966/petclinic/actuator/health \ + | grep --quiet '"status":"UP"' || exit 1 diff --git a/readme.md b/readme.md index 790ed840676ef7480dd64bb65bff03e8fa7913c4..0499c6eb08e17721309e5432c322013d997cda98 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,7 @@ # REST version of Spring PetClinic Sample Application (spring-framework-petclinic extend ) -[](https://github.com/spring-petclinic/spring-petclinic-rest/actions/workflows/maven-build.yml) +[](https://gitlab.cs.ui.ac.id/pmpl/examples/spring-petclinic-rest/-/commits/csui) +[](https://gitlab.cs.ui.ac.id/pmpl/examples/spring-petclinic-rest/-/commits/csui) This backend version of the Spring Petclinic application only provides a REST API. **There is no UI**. The [spring-petclinic-angular project](https://github.com/spring-petclinic/spring-petclinic-angular) is a Angular front-end application which consumes the REST API. @@ -16,6 +17,7 @@ The [spring-petclinic-angular project](https://github.com/spring-petclinic/sprin ## Running petclinic locally ### With maven command line + ``` git clone https://github.com/spring-petclinic/spring-petclinic-rest.git cd spring-petclinic-rest @@ -23,6 +25,7 @@ cd spring-petclinic-rest ``` ### With Docker + ``` docker run -p 9966:9966 springcommunity/spring-petclinic-rest ``` @@ -136,6 +139,7 @@ mvn clean install ``` ## Security configuration + In its default configuration, Petclinic doesn't have authentication and authorization enabled. ### Basic Authentication @@ -187,7 +191,6 @@ git clone https://github.com/spring-petclinic/spring-petclinic-rest.git File -> Import -> Maven -> Existing Maven project ``` - ## Looking for something in particular? | Layer | Source | @@ -222,13 +225,8 @@ hosted in a special GitHub org: [spring-petclinic](https://github.com/spring-pet If you have a special interest in a different technology stack that could be used to implement the Pet Clinic then please join the community there. - # Contributing The [issue tracker](https://github.com/spring-petclinic/spring-petclinic-rest/issues) is the preferred channel for bug reports, features requests and submitting pull requests. For pull requests, editor preferences are available in the [editor config](https://github.com/spring-petclinic/spring-petclinic-rest/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <http://editorconfig.org>. - - - - diff --git a/src/main/resources/application-csui.properties b/src/main/resources/application-csui.properties new file mode 100644 index 0000000000000000000000000000000000000000..353f5756f8294c9010c6122ad4c9bea891846a01 --- /dev/null +++ b/src/main/resources/application-csui.properties @@ -0,0 +1,17 @@ +# Application configuration +petclinic.security.enable=true + +# Database configuration +spring.sql.init.mode=always +spring.sql.init.schema-locations=classpath*:db/postgresql/initDB.sql +spring.sql.init.data-locations=classpath*:db/postgresql/populateDB.sql + +# PostgreSQL config start +#---------------------------------------------------------------- +spring.datasource.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/petclinic} +spring.datasource.driver-class-name=org.postgresql.Driver +spring.jpa.database=POSTGRESQL +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.hibernate.ddl-auto=none +#---------------------------------------------------------------- +# PostgreSQL config end