From c2a212f122d1292ab6c85a374ec82f8d483b8e1d Mon Sep 17 00:00:00 2001
From: daschwanden <daschwanden@google.com>
Date: Tue, 20 Aug 2024 10:33:56 +0200
Subject: [PATCH] Continuous Development with Docker Compose Watch (#1115)

* Adds Docker Compose Watch feature
* Updates components to be restarted with source code changes
* Adds comment to explain startup script switch
---
 Dockerfile                                    | 10 +++---
 compose.yaml                                  | 32 +++++++++++++++++++
 .../client/grr_fleetspeak_client.sh           | 12 +++++++
 .../client/textservices/grr_client.service    |  2 +-
 .../healthchecks/grr-client.sh                |  5 +--
 5 files changed, 53 insertions(+), 8 deletions(-)
 create mode 100755 docker_config_files/client/grr_fleetspeak_client.sh

diff --git a/Dockerfile b/Dockerfile
index 30ab1edbf..67c96d4b4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -40,11 +40,11 @@ FROM ubuntu:22.04
 
 LABEL maintainer="grr-dev@googlegroups.com"
 
-ENV DEBIAN_FRONTEND noninteractive
+ENV DEBIAN_FRONTEND=noninteractive
 # Buffering output (sometimes indefinitely if a thread is stuck in
 # a loop) makes for a non-optimal user experience when containers
 # are run in the foreground, so we disable that.
-ENV PYTHONUNBUFFERED 0
+ENV PYTHONUNBUFFERED=0
 
 RUN apt-get update && \
   apt-get install -y \
@@ -63,8 +63,8 @@ RUN apt-get update && \
 # building as part of Github Actions.
 COPY ./_installers* /client_templates
 
-ENV VIRTUAL_ENV /usr/share/grr-server
-ENV GRR_SOURCE /usr/src/grr
+ENV VIRTUAL_ENV=/usr/share/grr-server
+ENV GRR_SOURCE=/usr/src/grr
 
 RUN python -m venv --system-site-packages $VIRTUAL_ENV
 ENV PATH=${VIRTUAL_ENV}/bin:${PATH}
@@ -73,7 +73,7 @@ RUN ${VIRTUAL_ENV}/bin/python -m pip install wheel nodeenv grpcio-tools==1.60
 
 RUN ${VIRTUAL_ENV}/bin/nodeenv -p --prebuilt --node=16.13.0
 
-RUN mkdir ${GRR_SOURCE}
+RUN mkdir -p ${GRR_SOURCE}
 ADD . ${GRR_SOURCE}
 
 WORKDIR ${GRR_SOURCE}
diff --git a/compose.yaml b/compose.yaml
index 78e1af46b..2cf8da2bd 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -27,6 +27,7 @@ services:
       retries: 10
 
   grr-admin-ui:
+    build: .
     image: ghcr.io/google/grr:latest
     container_name: grr-admin-ui
     hostname: admin-ui
@@ -57,8 +58,16 @@ services:
       test: "/configs/healthchecks/grr-admin-ui.sh"
       timeout: 10s
       retries: 10
+    develop:
+      watch:
+        - action: sync+restart
+          path: ./grr
+          target: /usr/src/grr/grr
+          ignore:
+            - client/
 
   grr-fleetspeak-frontend:
+    build: .
     image: ghcr.io/google/grr:latest
     container_name: grr-fleetspeak-frontend
     hostname: grr-fleetspeak-frontend
@@ -78,6 +87,13 @@ services:
       - -config
       - /configs/server/grr.server.yaml
       - --verbose
+    develop:
+      watch:
+        - action: sync+restart
+          path: ./grr
+          target: /usr/src/grr/grr
+          ignore:
+            - client/
 
   fleetspeak-admin:
     image: ghcr.io/google/fleetspeak:latest
@@ -129,6 +145,7 @@ services:
     ]
 
   grr-worker:
+    build: .
     image: ghcr.io/google/grr:latest
     container_name: grr-worker
     volumes:
@@ -146,8 +163,16 @@ services:
       - -config
       - /configs/server/grr.server.yaml
       - --verbose
+    develop:
+      watch:
+        - action: sync+restart
+          path: ./grr
+          target: /usr/src/grr/grr
+          ignore:
+            - client/
 
   grr-client:
+    build: .
     image: ubuntu:22.04
     container_name: grr-client
     depends_on:
@@ -177,6 +202,13 @@ services:
       test: "/configs/healthchecks/grr-client.sh"
       timeout: 10s
       retries: 10
+    develop:
+      watch:
+        - action: sync+restart
+          path: ./grr
+          target: /usr/src/grr/grr
+          ignore:
+            - server/
 
 volumes:
   db_data:
diff --git a/docker_config_files/client/grr_fleetspeak_client.sh b/docker_config_files/client/grr_fleetspeak_client.sh
new file mode 100755
index 000000000..283bb692c
--- /dev/null
+++ b/docker_config_files/client/grr_fleetspeak_client.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# This script is run when Fleetspeak runs the GRR client.
+# It first checks whether we run Docker Compose Watch (i.e. in dev mode).
+#   If so we run the grr_fleetspeak_client python script so Docker Compose can
+#   swap in the source code updates on a continuous basis.
+#   If not we run grrd as we would do so in production.
+if [ -f /usr/share/grr-server/bin/grr_fleetspeak_client ]; then
+  /usr/share/grr-server/bin/python /usr/share/grr-server/bin/grr_fleetspeak_client "$@"
+else
+  /usr/sbin/grrd "$@"
+fi
diff --git a/docker_config_files/client/textservices/grr_client.service b/docker_config_files/client/textservices/grr_client.service
index 05d3a5e11..09ea0be27 100644
--- a/docker_config_files/client/textservices/grr_client.service
+++ b/docker_config_files/client/textservices/grr_client.service
@@ -2,7 +2,7 @@ name: "GRR"
 factory: "Daemon"
 config: {
   [type.googleapis.com/fleetspeak.daemonservice.Config]: {
-    argv: "grrd"
+    argv: "/configs/client/grr_fleetspeak_client.sh"
     argv: "--config"
     argv: "/configs/client/grr.client.yaml"
   }
diff --git a/docker_config_files/healthchecks/grr-client.sh b/docker_config_files/healthchecks/grr-client.sh
index 387a96a71..d28109503 100755
--- a/docker_config_files/healthchecks/grr-client.sh
+++ b/docker_config_files/healthchecks/grr-client.sh
@@ -6,8 +6,9 @@
 
 set -ex
 
-if [[ "$(ps aux | grep grrd | grep -v grep | wc -l)" == "0" ]]
+if [[ "$(ps aux | grep grrd | grep -v grep | wc -l)" == "0" ]] && 
+   [[ "$(ps aux | grep '/usr/share/grr-server/bin/grr_fleetspeak_client' | grep -v grep | wc -l)" == "0" ]]
 then
     echo "Healthckeck: GRR client process not running"
     exit 1
-fi
\ No newline at end of file
+fi