refactor: separate out dashboard docker image
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Sun, 8 Sep 2024 22:23:17 +0000 (00:23 +0200)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Sun, 8 Sep 2024 22:23:17 +0000 (00:23 +0200)
closes #1040

Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
14 files changed:
.github/workflows/ci.yml
docker/Dockerfile
docker/autoconfig.sh
docker/config.json
docker/docker-compose.yml
docker/start.sh
ui/web/README.md
ui/web/docker/Dockerfile [new file with mode: 0644]
ui/web/docker/Makefile [new file with mode: 0644]
ui/web/docker/autoconfig.sh [new file with mode: 0755]
ui/web/docker/config.json [new file with mode: 0644]
ui/web/docker/docker-compose.yml [new file with mode: 0644]
ui/web/docker/start.sh [new file with mode: 0755]
ui/web/src/composables/UIClient.ts

index 3a1df8bd50c98f4eecad327b2b5af9fe8be65488..3522cbbd855f03604f717e38349c1981312861bc 100644 (file)
@@ -145,9 +145,24 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
-  build-docker-image:
+  build-simulator-docker-image:
     runs-on: ubuntu-latest
-    name: Build docker image
+    name: Build simulator docker image
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Docker Buildx
+        id: buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Build docker image
+        run: |
+          cd docker
+          make SUBMODULES_INIT=false
+  build-dashboard-docker-image:
+    runs-on: ubuntu-latest
+    defaults:
+      run:
+        working-directory: ui/web
+    name: Build dashboard docker image
     steps:
       - uses: actions/checkout@v4
       - name: Setup Docker Buildx
index baef3220648d1643421afee14539079a8b2aa7e2..b794b93642a3baf04f1b9af2b21e9628a572c9f7 100644 (file)
@@ -1,6 +1,6 @@
 FROM node:lts-alpine AS builder
 
-# Build simulator and dashboard
+# Build simulator
 WORKDIR /usr/builder
 COPY . ./
 RUN set -ex \
@@ -13,10 +13,7 @@ RUN set -ex \
   && cp docker/config.json src/assets/config.json \
   && cp docker/idtags.json src/assets/idtags.json \
   && pnpm build \
-  && apk del .gyp \
-  && cd ui/web \
-  && cp src/assets/config-template.json public/config.json \
-  && pnpm build
+  && apk del .gyp
 
 FROM node:lts-alpine
 
@@ -26,7 +23,6 @@ ARG MAX_OLD_SPACE_SIZE=768
 ENV NODE_OPTIONS="--stack-trace-limit=${STACK_TRACE_LIMIT} --max-old-space-size=${MAX_OLD_SPACE_SIZE}"
 
 WORKDIR /usr/app
-COPY --from=builder /usr/builder/ui/web ./ui/web
 COPY --from=builder /usr/builder/node_modules ./node_modules
 COPY --from=builder /usr/builder/dist ./dist
 COPY package.json README.md LICENSE ./
index 24973efcee5b31cfec90f402cd5e9dc3a2089756..b8067bffa4b33d14727d9d068cb7ca4cc3975dfa 100755 (executable)
@@ -9,7 +9,6 @@ then
 
   cp $emobility_install_dir/dist/assets/configs-docker/$emobility_server_type-$emobility_service_type-$emobility_landscape.json $emobility_install_dir/dist/assets/config.json
   cp $emobility_install_dir/dist/assets/configs-docker/$emobility_server_type-$emobility_service_type-$emobility_landscape-idtags.json $emobility_install_dir/dist/assets/idtags.json
-  cp $emobility_install_dir/dist/assets/configs-docker/$emobility_server_type-$emobility_service_type-$emobility_landscape-webui.json $emobility_install_dir/ui/web/public/config.json
 else
   echo "no emobility env defined, start with default configuration"
 fi
index c207106a03271e955b4566ee3fa8279f8e4cf612..5bd7ccee417749c3a558520b39cdb30419db2efa 100644 (file)
@@ -15,6 +15,9 @@
   },
   "uiServer": {
     "enabled": true,
+    "options": {
+      "host": "::"
+    },
     "type": "ws",
     "authentication": {
       "enabled": true,
index 999f8a7e14a98a7622056cb9697156709a2a75a8..e5044cf1bb62bafd6d8dbdccef8332fa49df67a9 100644 (file)
@@ -1,4 +1,3 @@
-version: '3.7'
 networks:
   ev_network:
     driver: bridge
@@ -12,7 +11,5 @@ services:
         MAX_OLD_SPACE_SIZE: 768
     networks:
       - ev_network
-    expose:
-      - 8080
     ports:
-      - '3030:3030'
+      - 8080:8080
index 65a466c9482247edb99c1bc595dc1ce4b4819770..189983a293e7cff99ddd3962ad60b6dc07f36362 100755 (executable)
@@ -1,4 +1,3 @@
 #!/usr/bin/env sh
 
-node dist/start.js &
-node ui/web/start.js
+node dist/start.js
index 43bb9569139480ec1b1d3fd25f130666e4ec01c5..039d90156c4c7c6ef14cdf124bcd5bb3558e1cc4 100644 (file)
@@ -17,6 +17,7 @@ The Web UI code and configuration is in the repository directory [ui/web](./../.
   - [Run](#run)
     - [Compiles and run for production](#compiles-and-run-for-production)
     - [Preview locally](#preview-locally)
+    - [Docker](#docker)
   - [Development](#development)
     - [Compiles and run for development](#compiles-and-run-for-development)
     - [Formats files](#formats-files)
@@ -119,6 +120,14 @@ You can now follow the link displayed in the terminal. The Web UI looks like the
 1. With the buttons on the top you can change UI server, start/stop the simulator, add new charging stations and refresh the content.
 2. Each charging station is a row in the table with specific 'Actions' to execute on. Try 'Stop Charging Station' and refresh with the large blue button and see the status 'Started' turns from 'Yes' into 'No'.
 
+#### Docker
+
+In the [docker](./docker) folder:
+
+```shell
+make
+```
+
 ### Development
 
 #### Compiles and run for development
diff --git a/ui/web/docker/Dockerfile b/ui/web/docker/Dockerfile
new file mode 100644 (file)
index 0000000..5737d4b
--- /dev/null
@@ -0,0 +1,25 @@
+FROM node:lts-alpine AS builder
+
+# Build dashboard
+WORKDIR /usr/builder
+COPY . ./
+RUN set -ex \
+  && corepack enable \
+  && corepack prepare pnpm@latest --activate \
+  && pnpm set progress=false \
+  && pnpm config set depth 0 \
+  && pnpm install --ignore-scripts \
+  && cp docker/config.json public/config.json \
+  && pnpm build
+
+FROM node:lts-alpine
+
+WORKDIR /usr/app
+COPY --from=builder /usr/builder ./
+COPY docker/start.sh /start.sh
+COPY docker/autoconfig.sh /autoconfig.sh
+RUN set -ex \
+  && chmod +x /start.sh \
+  && chmod +x /autoconfig.sh
+
+CMD /autoconfig.sh && /start.sh
diff --git a/ui/web/docker/Makefile b/ui/web/docker/Makefile
new file mode 100644 (file)
index 0000000..fc0e9cd
--- /dev/null
@@ -0,0 +1,50 @@
+PROJECT_NAME?=evse
+NAME:=e-mobility-charging-stations-dashboard
+SUBMODULES_INIT?=false
+DOCKER_ECR_ACCOUNT_ID?=166296450311
+DOCKER_ECR_REGION?=eu-west-3
+DOCKER_ECR_REGISTRY_NAME?=e-mobility-charging-stations-dashboard
+DOCKER_ECR_TAG?=latest
+
+.PHONY: all
+
+default: all
+
+submodule-update:
+       git submodule update --init --recursive
+
+submodules-init=
+ifeq '$(SUBMODULES_INIT)' 'true'
+       submodules-init += submodule-update
+endif
+
+$(NAME): $(submodules-init)
+       docker compose -p $(PROJECT_NAME) up -d
+
+$(NAME)-force: $(submodules-init)
+       docker compose -p $(PROJECT_NAME) up -d --build --force-recreate
+
+all: $(NAME)
+
+clean-images:
+       -docker rmi $(PROJECT_NAME)-$(NAME)
+
+clean-containers:
+       -docker compose -p $(PROJECT_NAME) down
+
+clean: clean-containers clean-images
+
+docker-tag-ecr:
+       docker tag $(PROJECT_NAME)-$(NAME) $(DOCKER_ECR_ACCOUNT_ID).dkr.ecr.$(DOCKER_ECR_REGION).amazonaws.com/$(DOCKER_ECR_REGISTRY_NAME):$(DOCKER_ECR_TAG)
+
+docker-push-ecr: $(NAME)-force docker-tag-ecr
+       aws ecr get-login-password --region $(DOCKER_ECR_REGION) | docker login --username AWS --password-stdin $(DOCKER_ECR_ACCOUNT_ID).dkr.ecr.$(DOCKER_ECR_REGION).amazonaws.com/$(DOCKER_ECR_REGISTRY_NAME)
+       docker push $(DOCKER_ECR_ACCOUNT_ID).dkr.ecr.$(DOCKER_ECR_REGION).amazonaws.com/$(DOCKER_ECR_REGISTRY_NAME):$(DOCKER_ECR_TAG)
+
+dist-clean-images:
+       docker image prune -a -f
+
+dist-clean-volumes:
+       docker volume prune -f
+
+dist-clean: clean-containers dist-clean-volumes dist-clean-images
diff --git a/ui/web/docker/autoconfig.sh b/ui/web/docker/autoconfig.sh
new file mode 100755 (executable)
index 0000000..f9c57ee
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env sh
+
+if ! [ -z $emobility_server_type ] && ! [ -z $emobility_service_type ]
+then
+  [ -z $emobility_install_dir ] && { echo "emobility installation directory env variable not found, exiting"; exit 1; }
+  [ -z $emobility_landscape ] && { echo "emobility landscape env variable not found, exiting"; exit 1; }
+  [ -z $emobility_server_type ] && { echo "emobility env server type variable not found, exiting"; exit 1; }
+  [ -z $emobility_service_type ] && { echo "emobility env service type variable not found, exiting"; exit 1; }
+
+  cp $emobility_install_dir/dist/assets/configs-docker/$emobility_server_type-$emobility_service_type-$emobility_landscape.json $emobility_install_dir/public/config.json
+else
+  echo "no emobility env defined, start with default configuration"
+fi
diff --git a/ui/web/docker/config.json b/ui/web/docker/config.json
new file mode 100644 (file)
index 0000000..1542583
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "uiServer": {
+    "host": "localhost",
+    "port": 8080,
+    "protocol": "ui",
+    "version": "0.0.1",
+    "authentication": {
+      "enabled": true,
+      "type": "protocol-basic-auth",
+      "username": "admin",
+      "password": "admin"
+    }
+  }
+}
diff --git a/ui/web/docker/docker-compose.yml b/ui/web/docker/docker-compose.yml
new file mode 100644 (file)
index 0000000..0220dfe
--- /dev/null
@@ -0,0 +1,12 @@
+networks:
+  ev_network:
+    driver: bridge
+services:
+  e-mobility-charging-stations-dashboard:
+    build:
+      context: ..
+      dockerfile: docker/Dockerfile
+    networks:
+      - ev_network
+    ports:
+      - 3030:3030
diff --git a/ui/web/docker/start.sh b/ui/web/docker/start.sh
new file mode 100755 (executable)
index 0000000..865f7dd
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+node start.js
index ae626dec0c7baffc6cd1bf2bdeebfb2399b8174a..d105b605c26055327d7de55a493b76e16e321ac3 100644 (file)
@@ -69,14 +69,16 @@ export class UIClient {
     )
     this.ws.onopen = () => {
       useToast().success(
-        `WebSocket to UI server '${this.uiServerConfiguration.host}' successfully opened`
+        `WebSocket to UI server '${this.uiServerConfiguration.host}:${this.uiServerConfiguration.port.toString()}' successfully opened`
       )
     }
     this.ws.onmessage = this.responseHandler.bind(this)
     this.ws.onerror = errorEvent => {
-      useToast().error(`Error in WebSocket to UI server '${this.uiServerConfiguration.host}'`)
+      useToast().error(
+        `Error in WebSocket to UI server '${this.uiServerConfiguration.host}:${this.uiServerConfiguration.port.toString()}'`
+      )
       console.error(
-        `Error in WebSocket to UI server '${this.uiServerConfiguration.host}'`,
+        `Error in WebSocket to UI server '${this.uiServerConfiguration.host}:${this.uiServerConfiguration.port.toString()}'`,
         errorEvent
       )
     }