Add deploy script for scouting webserver
This patch lets folks do this to deploy the webserver:
$ bazel run //scouting/deploy
It will copy the webserver to the scouting server, install it, and
start it. You can find a summary in the new README.md file.
Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: I997738db483dc0e7d01a336ef4dde150ccfed070
diff --git a/WORKSPACE b/WORKSPACE
index 398202c..58fb14f 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1057,3 +1057,20 @@
"https://github.com/bazelbuild/buildtools/archive/refs/tags/4.2.4.tar.gz",
],
)
+
+http_archive(
+ name = "rules_pkg",
+ patch_args = ["-p1"],
+ patches = [
+ "//third_party:rules_pkg/0001-Fix-tree-artifacts.patch",
+ ],
+ sha256 = "62eeb544ff1ef41d786e329e1536c1d541bb9bcad27ae984d57f18f314018e66",
+ urls = [
+ "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
+ "https://github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
+ ],
+)
+
+load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
+
+rules_pkg_dependencies()
diff --git a/scouting/BUILD b/scouting/BUILD
index 0ed540b..a769426 100644
--- a/scouting/BUILD
+++ b/scouting/BUILD
@@ -33,6 +33,7 @@
"//scouting/www:index.html",
"//scouting/www:zonejs_copy",
],
+ visibility = ["//scouting/deploy:__pkg__"],
)
protractor_ts_test(
diff --git a/scouting/deploy/BUILD b/scouting/deploy/BUILD
new file mode 100644
index 0000000..ed4b9cd
--- /dev/null
+++ b/scouting/deploy/BUILD
@@ -0,0 +1,60 @@
+load("@rules_pkg//pkg:pkg.bzl", "pkg_deb", "pkg_tar")
+load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
+
+pkg_files(
+ name = "systemd_files",
+ srcs = [
+ "scouting.service",
+ ],
+ prefix = "etc/systemd/system",
+)
+
+pkg_tar(
+ name = "server_files",
+ srcs = [
+ "//scouting",
+ ],
+ include_runfiles = True,
+ package_dir = "opt/frc971/scouting_server",
+ strip_prefix = ".",
+)
+
+pkg_tar(
+ name = "deploy_tar",
+ srcs = [
+ ":systemd_files",
+ ],
+ deps = [
+ ":server_files",
+ ],
+)
+
+pkg_deb(
+ name = "frc971-scouting-server",
+ architecture = "amd64",
+ data = ":deploy_tar",
+ description = "The FRC971 scouting web server.",
+ # TODO(phil): What's a good email address for this?
+ maintainer = "frc971@frc971.org",
+ package = "frc971-scouting-server",
+ postinst = "postinst",
+ predepends = [
+ "systemd",
+ ],
+ prerm = "prerm",
+ version = "1",
+)
+
+py_binary(
+ name = "deploy",
+ srcs = [
+ "deploy.py",
+ ],
+ args = [
+ "--deb",
+ "$(location :frc971-scouting-server)",
+ ],
+ data = [
+ ":frc971-scouting-server",
+ ],
+)
diff --git a/scouting/deploy/README.md b/scouting/deploy/README.md
new file mode 100644
index 0000000..6d223da
--- /dev/null
+++ b/scouting/deploy/README.md
@@ -0,0 +1,37 @@
+Deploying the scouting application
+================================================================================
+The scouting application is deployed to `scouting.frc971.org` via `bazel`:
+```console
+$ bazel run //scouting/deploy
+(Reading database ... 119978 files and directories currently installed.)
+Preparing to unpack .../frc971-scouting-server_1_amd64.deb ...
+Removed /etc/systemd/system/multi-user.target.wants/scouting.service.
+Unpacking frc971-scouting-server (1) over (1) ...
+Setting up frc971-scouting-server (1) ...
+Created symlink /etc/systemd/system/multi-user.target.wants/scouting.service → /etc/systemd/system/scouting.service.
+Connection to scouting.frc971.org closed.
+```
+
+You will need SSH access to the scouting server. You can customize the SSH host
+with the `--host` argument.
+
+The Blue Alliance API key
+--------------------------------------------------------------------------------
+You need to set up an API key on the scouting server so that the scraping logic
+can use it. It needs to live in `/var/frc971/scouting/tba_config.json` and look
+as follows:
+```json
+{
+ "api_key": "..."
+}
+```
+
+Starting and stopping the application
+--------------------------------------------------------------------------------
+When you SSH into the scouting server, use `systemctl` to manage
+`scouting.service` like any other service.
+```console
+$ sudo systemctl stop scouting.service
+$ sudo systemctl start scouting.service
+$ sudo systemctl restart scouting.service
+```
diff --git a/scouting/deploy/deploy.py b/scouting/deploy/deploy.py
new file mode 100644
index 0000000..c9886fb
--- /dev/null
+++ b/scouting/deploy/deploy.py
@@ -0,0 +1,34 @@
+import argparse
+from pathlib import Path
+import subprocess
+import sys
+
+def main(argv):
+ """Installs the scouting application on the scouting server."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--deb",
+ type=str,
+ required=True,
+ help="The .deb file to deploy.",
+ )
+ parser.add_argument(
+ "--host",
+ type=str,
+ default="scouting.frc971.org",
+ help="The SSH host to install the scouting web server to.",
+ )
+ args = parser.parse_args(argv[1:])
+ deb = Path(args.deb)
+
+ # Copy the .deb to the scouting server, install it, and delete it again.
+ subprocess.run(["rsync", "-L", args.deb, f"{args.host}:/tmp/{deb.name}"],
+ check=True, stdin=sys.stdin)
+ subprocess.run(f"ssh -tt {args.host} sudo dpkg -i /tmp/{deb.name}",
+ shell=True, check=True, stdin=sys.stdin)
+ subprocess.run(f"ssh {args.host} rm -f /tmp/{deb.name}",
+ shell=True, check=True, stdin=sys.stdin)
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
diff --git a/scouting/deploy/postinst b/scouting/deploy/postinst
new file mode 100644
index 0000000..a7a8b16
--- /dev/null
+++ b/scouting/deploy/postinst
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# This script runs after the frc971-scouting-server package is installed. This
+# script is responsible for making sure the webserver has everything it needs,
+# then starts the webserver.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+# Create a directory for the database to live in.
+mkdir -p /var/frc971/scouting/
+
+# Create an empty The Blue Alliance configuration file.
+if [[ ! -e /var/frc971/scouting/tba_config.json ]]; then
+ echo '{}' > /var/frc971/scouting/tba_config.json
+fi
+
+# Make sure it's all usable by the user.
+chown -R www-data:www-data /var/frc971/scouting/
+
+systemctl daemon-reload
+systemctl enable scouting.service
+systemctl start scouting.service || :
diff --git a/scouting/deploy/prerm b/scouting/deploy/prerm
new file mode 100644
index 0000000..31e3fbc
--- /dev/null
+++ b/scouting/deploy/prerm
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# This script gets run before the frc971-scouting-server package gets removed
+# or upgraded. This script is responsible for stopping the webserver before the
+# underlying files are removed by dpkg.
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+systemctl stop scouting.service
+systemctl disable scouting.service
+systemctl daemon-reload
diff --git a/scouting/deploy/scouting.service b/scouting/deploy/scouting.service
new file mode 100644
index 0000000..2990f42
--- /dev/null
+++ b/scouting/deploy/scouting.service
@@ -0,0 +1,17 @@
+[Unit]
+Description=FRC971 Scouting Server
+After=systemd-networkd-wait-online.service
+
+[Service]
+User=www-data
+Group=www-data
+Type=simple
+WorkingDirectory=/opt/frc971/scouting_server
+ExecStart=/opt/frc971/scouting_server/scouting/scouting \
+ -port 8080 \
+ -database /var/frc971/scouting/scouting.db \
+ -tba_config /var/frc971/scouting/tba_config.json
+Restart=always
+
+[Install]
+WantedBy=multi-user.target
diff --git a/third_party/rules_pkg/0001-Fix-tree-artifacts.patch b/third_party/rules_pkg/0001-Fix-tree-artifacts.patch
new file mode 100644
index 0000000..567aba7
--- /dev/null
+++ b/third_party/rules_pkg/0001-Fix-tree-artifacts.patch
@@ -0,0 +1,28 @@
+From d654cc64ae71366ea82ac492106e9b2c8fa532d5 Mon Sep 17 00:00:00 2001
+From: Philipp Schrader <philipp.schrader@gmail.com>
+Date: Thu, 10 Mar 2022 23:25:21 -0800
+Subject: [PATCH] Fix tree artifacts
+
+For some reason the upstream code strips the directory names from the
+`babel()` rule that we use. This patch makes it so the directory is
+not stripped. This makes runfiles layout in the tarball match the
+runfiles layout in `bazel-bin`.
+---
+ pkg/pkg.bzl | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/pkg/pkg.bzl b/pkg/pkg.bzl
+index d7adbbc..a241b26 100644
+--- a/pkg/pkg.bzl
++++ b/pkg/pkg.bzl
+@@ -157,8 +157,8 @@ def _pkg_tar_impl(ctx):
+ # Tree artifacts need a name, but the name is never really
+ # the important part. The likely behavior people want is
+ # just the content, so we strip the directory name.
+- dest = "/".join(d_path.split("/")[0:-1])
+- add_tree_artifact(content_map, dest, f, src.label)
++ #dest = "/".join(d_path.split("/")[0:-1])
++ add_tree_artifact(content_map, d_path, f, src.label)
+ else:
+ # Note: This extra remap is the bottleneck preventing this
+ # large block from being a utility method as shown below.