diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index c41233f..0000000 --- a/.drone.yml +++ /dev/null @@ -1,29 +0,0 @@ -kind: pipeline -type: docker -name: Deployment - -trigger: - branch: - - production - - dev - -steps: - - name: 'Unit Tests' - image: ubuntu:latest - commands: - - apt update && apt install openjdk-11-jdk -y - - ./gradlew :test - - - name: deploy - image: appleboy/drone-scp - settings: - port: 22 - host: sancy.iut.uca.fr - username: lafourcade - target: public_html/TBasket - source: build/libs/server-all.jar - key: - from_secret: DEPLOY_KEY_SANCY - secrets: - - source: DEPLOY_KEY_SANCY - rm: false \ No newline at end of file diff --git a/API/src/main/scala/org/tbasket/api/Endpoint.scala b/API/src/main/scala/org/tbasket/api/Endpoint.scala index be27204..ef99857 100644 --- a/API/src/main/scala/org/tbasket/api/Endpoint.scala +++ b/API/src/main/scala/org/tbasket/api/Endpoint.scala @@ -11,7 +11,7 @@ import zio.http.model.Status import scala.collection.mutable -class Endpoint(hostname: String, port: Int) extends ZIOAppDefault { +class Endpoint(port: Int) extends ZIOAppDefault { private val handlers = mutable.HashMap.empty[String, APIRequestHandler] @@ -46,7 +46,7 @@ class Endpoint(hostname: String, port: Int) extends ZIOAppDefault { val configLayer = ServerConfig.live(config) Server.install(app).flatMap { port => - LOG.info(s"Listening API entries on $hostname:$port") + LOG.info(s"Listening API entries on $port") ZIO.never }.provide(configLayer, Server.live) } diff --git a/build.gradle b/build.gradle index d9bbb9b..0e2c3bb 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'scala' id 'application' //for 'run' task id 'com.adarshr.test-logger' version '3.2.0' //fancy prints during tests - id 'com.github.johnrengelman.shadow' version '7.1.2' //for optimised jar + id 'com.github.johnrengelman.shadow' version '7.1.2' //for building optimised jar } final var scalaVersion = "2.13" diff --git a/drone/.drone.yml b/drone/.drone.yml new file mode 100644 index 0000000..3a5a6bc --- /dev/null +++ b/drone/.drone.yml @@ -0,0 +1,30 @@ +kind: pipeline +type: docker +name: Deployment + + +steps: + - name: Unit tests + image: amazoncorretto:11 + volumes: + - name: build + path: ./build + commands: + - echo + - ./gradlew :test + + - name: Deploy + image: amazoncorretto:11 + depends_on: + - 'Unit tests' + environment: + SSH_PRIVATE_KEY: + from_secret: SSH_PRIVATE + SSH_PUBLIC_KEY: + from_secret: SSH_PUBLIC + volumes: + - name: build + path: ./build + commands: + - chmod 777 drone/deliver.sh + - drone/deliver.sh diff --git a/drone/deliver.sh b/drone/deliver.sh old mode 100644 new mode 100755 index 77c9b82..2b8f212 --- a/drone/deliver.sh +++ b/drone/deliver.sh @@ -1,12 +1,10 @@ #!/usr/bin/env bash -apt update && apt install openjdk-11-jdk sftp -y +chmod 700 ./drone/prepare-deliver.sh +source ./drone/prepare-deliver.sh -PATH="$PATH:." +./gradlew :shadowJar -gradlew :shadowJar - -mkdir -p ~/.ssh -echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa -echo "$SSH_PUBLIC_KEY" > ~/.ssh/id_rsa.pub -sftp "$USER@$IP:tbasket/" <<< $'put build/libs/server-all.jar drone/deploy.sh' \ No newline at end of file +echo "making delivery onto '$USER@$IP:$DIR/backend'" +scp -o "StrictHostKeyChecking no" "build/libs/server-all.jar" "drone/deploy.sh" "drone/start.sh" "$USER@$IP:$DIR/backend" +echo "chmod 700 $DIR/backend/*; $DIR/backend/deploy.sh" | ssh -o "StrictHostKeyChecking no" $USER@$IP diff --git a/drone/deploy.sh b/drone/deploy.sh index f1e2b5f..116fb44 100644 --- a/drone/deploy.sh +++ b/drone/deploy.sh @@ -1,19 +1,38 @@ +#!/usr/bin/env bash +DIR=$(readlink -e "$(dirname "$0")") +PROD_SERVER_JAR_NAME="server-prod.jar" +NEW_SERVER_JAR_NAME="server-all.jar" -SERVER_JAR_NAME="server-all.jar" +prod_pid() { + ps -aux | tr -s " " | grep -E "\bjava -jar .*$PROD_SERVER_JAR_NAME\b" | cut -d " " -f2 +} -OLD_PID=$(ps -aux | grep "-jar $SERVER_JAR_NAME" | tr -s " " | cut -d " " -f2) +PROD_PID=$(prod_pid) -#if $OLD_PID is not empty but isn't a number, something went wrong -if [ "$OLD_PID" ] && ! grep -E -q "^[0-9]+$"; then - echo "error, unable to retrieve old server pid: $OLD_PID" >&2 +#if $PROD_PID is not empty but isn't a number, something went wrong +if [ "$PROD_PID" ] && ! echo "$PROD_PID" | grep -E -q "^[0-9]+$"; then + echo "error, unable to retrieve old server pid: $PROD_PID" >&2 exit 2 fi - -if [ "$OLD_PID" ]; then +if [ "$PROD_PID" ]; then #will cause the old server to gracefully shutdown echo "shutting down old server version ..." - kill SIGQUIT "$OLD_PID" - wait "$OLD_PID" + kill "$PROD_PID" + while [ ! "$(prod_pid)" ]; do sleep 1; done #sleep until process ends + echo "server shut down" +fi + +rm "$DIR/$PROD_SERVER_JAR_NAME" +mv "$DIR/$NEW_SERVER_JAR_NAME" "$DIR/$PROD_SERVER_JAR_NAME" || ls + +SCREEN="basket" + +# create screen if not exists +if ! screen -ls | grep -q -E "\b[0-9]+\.$SCREEN\b"; then + screen -S "$SCREEN" -d -m fi + +screen -d -r "$SCREEN" -X stuff $"$DIR/start.sh\n" +echo "server is started into $SCREEN screen." diff --git a/drone/prepare-deliver.sh b/drone/prepare-deliver.sh new file mode 100755 index 0000000..f5edc98 --- /dev/null +++ b/drone/prepare-deliver.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +echo "installing packages..." +#use the right command to install packages +if [ "$(command -v apt)" ]; then + apt update > /dev/null + apt -y install openssh-client openssh-clients > /dev/null +elif [ "$(command -v yum)" ]; then + yum update > /dev/null + yum -y install openssh-client openssh-clients > /dev/null +elif [ "$(command -v apk)" ]; then + apk update > /dev/null + apk add openssh > /dev/null +else + echo "could not find a package manager, aborting" >&2 + exit 1 +fi +echo "done." + +mkdir /root/.ssh +echo "$SSH_PRIVATE_KEY" > /root/.ssh/id_rsa +echo "$SSH_PUBLIC_KEY" > /root/.ssh/id_rsa.pub +chmod 0600 /root/.ssh/* +chmod 700 /root/.ssh + +case "$DRONE_BRANCH" in + "dev") + USER=maxime + IP=92.132.18.192 + DIR=server/TBasket/ + ;; + "production") + USER=palafour + IP=193.49.118.205 + DIR=public_html/TBasket/ + ;; + "") + echo "DRONE_BRANCH is missing" >&2 + exit 1 + ;; + *) + echo "branch $1 is unable to perform delivery, authorized branches are 'dev and production' for delivery." >&2 + echo "delivery step skipped" + exit 0 +esac \ No newline at end of file diff --git a/drone/start.sh b/drone/start.sh new file mode 100644 index 0000000..31297b0 --- /dev/null +++ b/drone/start.sh @@ -0,0 +1,3 @@ +DIR=$(readlink -e "$(dirname "$0")") +echo "starting server" +java -jar "$DIR/server-prod.jar" diff --git a/src/main/resources/server.properties b/src/main/resources/server.properties index 824af47..aea62c7 100644 --- a/src/main/resources/server.properties +++ b/src/main/resources/server.properties @@ -1 +1 @@ -endpoint.url=localhost:48485 \ No newline at end of file +endpoint.port=48485 \ No newline at end of file diff --git a/src/main/scala/org/tbasket/EndpointSetup.scala b/src/main/scala/org/tbasket/EndpointSetup.scala index 578cd5d..622b00f 100644 --- a/src/main/scala/org/tbasket/EndpointSetup.scala +++ b/src/main/scala/org/tbasket/EndpointSetup.scala @@ -1,32 +1,30 @@ package org.tbasket import org.tbasket.api.Endpoint -import org.tbasket.compute.IncrementHandler +import org.tbasket.handler.IncrementHandler import java.util.Properties object EndpointSetup { - final val EndpointUrl = "endpoint.url" - final val EndpointUrlDefault = s"localhost:48485" + final val EndpointPort = "endpoint.port" + final val EndpointPortDefault = "48485" - def setupEndpoint(): Endpoint = { + def setupEndpoint(config: Properties): Endpoint = { Main.LOG.debug("Initializing API endpoint...") - val endpoint = createEndpoint() + val endpoint = createEndpoint(config) endpoint.bind("counter")(IncrementHandler) endpoint } - private def createEndpoint(): Endpoint = { - val properties = new Properties() - val in = getClass.getClassLoader.getResourceAsStream("server.properties") - properties.load(in) - val (hostname, port) = properties - .getProperty(EndpointUrl, EndpointUrlDefault) match { - case s"$ip:$port" => (ip, port.toInt) - case v => throw new InternalBasketServerException(s"$EndpointUrl property value is wrong: $v must be :/[endpointPath]") + private def createEndpoint(config: Properties): Endpoint = { + val port = config + .getProperty(EndpointPort, EndpointPortDefault) match { + case s"$port" if port.toIntOption.isDefined => port.toInt + case v => + throw new InternalBasketServerException(s"$EndpointPort property value is wrong: $v must be integer") } - new Endpoint(hostname, port) + new Endpoint(port) } } diff --git a/src/main/scala/org/tbasket/Main.scala b/src/main/scala/org/tbasket/Main.scala index 6977068..2486558 100644 --- a/src/main/scala/org/tbasket/Main.scala +++ b/src/main/scala/org/tbasket/Main.scala @@ -5,6 +5,8 @@ import org.apache.logging.log4j.LogManager import zio._ import java.lang +import java.nio.file.{Files, Path} +import java.util.Properties import scala.io.StdIn import scala.util.control.NonFatal @@ -13,8 +15,19 @@ object Main { def main(args: Array[String]): Unit = { LOG.info("Starting server") - val endpoint = EndpointSetup.setupEndpoint() - val runtime = Runtime.default + val config = retrieveConfig + db(config) + api(config) + LOG.info("Server successfully started") + } + + private def db(config: Properties): Unit = new Thread({ () => + }, "Database").start() + //TODO + + private def api(config: Properties): Unit = new Thread({ () => + val endpoint = EndpointSetup.setupEndpoint(config) + val runtime = Runtime.default Unsafe.unsafe { implicit u => runtime.unsafe.run(endpoint.run).catchSome { case NonFatal(e) => @@ -22,9 +35,18 @@ object Main { throw e } } - LOG.info("Server successfully started") - println("enter to exit") - StdIn.readLine() + }: Runnable, "API").start() + + private def retrieveConfig: Properties = { + val configFile = Path.of("server.properties") + if (Files.notExists(configFile)) { + val in = getClass.getResourceAsStream("/server.properties") + Files.writeString(configFile, new String(in.readAllBytes())) + } + val in = Files.newInputStream(configFile) + val properties = new Properties() + properties.load(in) + properties } //add a shutdown hook to log when the server is about to get killed diff --git a/src/main/scala/org/tbasket/compute/IncrementHandler.scala b/src/main/scala/org/tbasket/compute/IncrementHandler.scala deleted file mode 100644 index e8aed70..0000000 --- a/src/main/scala/org/tbasket/compute/IncrementHandler.scala +++ /dev/null @@ -1,21 +0,0 @@ -package org.tbasket.compute - -import org.tbasket.api.compute.APIRequestHandler -import zio.http.{Request, Response} - -object IncrementHandler extends APIRequestHandler { - - @volatile private var i = 0 - - def getCounter: Int = i - - override def get(request: Request): Response = { - Response.json(s"{\"value\": $i}") - } - - override def post(request: Request): Response = { - i += 1 - println(s"Counter is now $i") - Response.ok - } -} diff --git a/src/main/scala/org/tbasket/handler/IncrementHandler.scala b/src/main/scala/org/tbasket/handler/IncrementHandler.scala new file mode 100644 index 0000000..3b37467 --- /dev/null +++ b/src/main/scala/org/tbasket/handler/IncrementHandler.scala @@ -0,0 +1,23 @@ +package org.tbasket.handler + +import org.tbasket.api.compute.APIRequestHandler +import zio.http.{Request, Response} + +import java.util.concurrent.atomic.AtomicInteger + +object IncrementHandler extends APIRequestHandler { + + private val counter = new AtomicInteger(0) + + def getCounter: Int = counter.get() + + override def get(request: Request): Response = { + Response.json(s"{\"value\": ${counter.get()}}") + } + + override def post(request: Request): Response = { + val i = counter.incrementAndGet() + println(s"(thread ${Thread.currentThread()}) Counter incremented : $i") + Response.ok + } +} diff --git a/src/test/scala/org/tbasket/test/IncrementRequestHandler.scala b/src/test/scala/org/tbasket/test/IncrementRequestHandler.scala index 17f0aac..c1bd5ba 100644 --- a/src/test/scala/org/tbasket/test/IncrementRequestHandler.scala +++ b/src/test/scala/org/tbasket/test/IncrementRequestHandler.scala @@ -2,7 +2,7 @@ package org.tbasket.test import io.circe.parser._ import org.junit.jupiter.api.{Assertions, Test} -import org.tbasket.compute.IncrementHandler +import org.tbasket.handler.IncrementHandler import zio.http.model.Status import zio.http.{Body, Path, Request, URL} @@ -18,6 +18,8 @@ class IncrementRequestHandler { Assertions.assertEquals(last + 1, IncrementHandler.getCounter) } + + @Test def testGetIncrement(): Unit = { val counter = IncrementHandler.getCounter