[Perl] DAQ system for the FAPG.
feat schedule weather ingestion with systemd
Run synchronous weather fetching in an hourly one-shot service instead of Hypnotoad workers. Install and enable the service/timer through deploy while preserving the existing dashboard unit. Move the application secret to StateDirectory and migrate the legacy file when present.
Changed files
roles/dashboard/deploy
@@ -4,15 +4,23 @@
4
4
5
5
readonly REPO_DIR="/opt/fapg/fapg-daq"
6
6
readonly DASHBOARD_SCRIPT="${REPO_DIR}/roles/dashboard/script/dashboard"
7
Added:
readonly WEATHER_SCRIPT="${REPO_DIR}/roles/dashboard/script/fetch-weather"
7
8
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
9
readonly SCRIPT_DIR
9
10
10
11
readonly SERVICE_NAME="fapg-daq-dashboard.service"
11
Removed:
readonly SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}"
12
Added:
readonly WEATHER_SERVICE_NAME="fapg-daq-dashboard-weather.service"
13
Added:
readonly WEATHER_TIMER_NAME="fapg-daq-dashboard-weather.timer"
14
Added:
readonly SERVICE_SOURCE="${SCRIPT_DIR}/etc/systemd/system/${SERVICE_NAME}"
15
Added:
readonly WEATHER_SERVICE_SOURCE="${SCRIPT_DIR}/etc/systemd/system/${WEATHER_SERVICE_NAME}"
16
Added:
readonly WEATHER_TIMER_SOURCE="${SCRIPT_DIR}/etc/systemd/system/${WEATHER_TIMER_NAME}"
17
Added:
readonly SYSTEMD_DIR="/etc/systemd/system"
12
18
readonly NGINX_GZIP_SOURCE="${SCRIPT_DIR}/etc/nginx/conf.d/fapg-daq-dashboard-gzip.conf"
13
19
readonly NGINX_GZIP_FILE="/etc/nginx/conf.d/fapg-daq-dashboard-gzip.conf"
14
20
15
21
readonly STATE_DIR="/var/lib/fapg-daq"
22
Added:
readonly SECRET_FILE="${STATE_DIR}/dashboard.secret"
23
Added:
readonly LEGACY_SECRET_FILE="${REPO_DIR}/roles/dashboard/.mojo-secrets"
16
24
readonly RUN_USER="fapg-daq"
17
25
readonly RUN_GROUP="fapg-daq"
18
26
@@ -57,16 +65,30 @@
57
65
chown "${RUN_USER}:${RUN_GROUP}" "${STATE_DIR}"
58
66
}
59
67
68
Added:
function migrate_application_secret() {
69
Added:
if [[ ! -e "${SECRET_FILE}" && -f "${LEGACY_SECRET_FILE}" ]]; then
70
Added:
install -m 0600 -o "${RUN_USER}" -g "${RUN_GROUP}" \
71
Added:
"${LEGACY_SECRET_FILE}" \
72
Added:
"${SECRET_FILE}"
73
Added:
fi
74
Added:
}
75
Added:
60
76
function check_repo_layout() {
61
77
[[ -d "${REPO_DIR}" ]] || die "Repo not found at ${REPO_DIR}."
62
78
[[ -f "${DASHBOARD_SCRIPT}" ]] || die "${DASHBOARD_SCRIPT} does not exist."
79
Added:
[[ -x "${WEATHER_SCRIPT}" ]] || die "${WEATHER_SCRIPT} is not executable."
80
Added:
[[ -f "${SERVICE_SOURCE}" ]] || die "${SERVICE_SOURCE} does not exist."
81
Added:
[[ -f "${WEATHER_SERVICE_SOURCE}" ]] || die "${WEATHER_SERVICE_SOURCE} does not exist."
82
Added:
[[ -f "${WEATHER_TIMER_SOURCE}" ]] || die "${WEATHER_TIMER_SOURCE} does not exist."
63
83
[[ -f "${NGINX_GZIP_SOURCE}" ]] || die "${NGINX_GZIP_SOURCE} does not exist."
64
84
}
65
85
66
Removed:
function write_systemd_service() {
86
Added:
function write_systemd_services() {
67
87
install -m 0644 -o root -g root \
68
Removed:
"${SCRIPT_DIR}/etc/systemd/system/fapg-daq-dashboard.service" \
69
Removed:
"${SERVICE_FILE}"
88
Added:
"${SERVICE_SOURCE}" \
89
Added:
"${WEATHER_SERVICE_SOURCE}" \
90
Added:
"${WEATHER_TIMER_SOURCE}" \
91
Added:
"${SYSTEMD_DIR}/"
70
92
}
71
93
72
94
function configure_nginx_compression() {
@@ -79,9 +101,10 @@
79
101
systemctl reload nginx
80
102
}
81
103
82
Removed:
function enable_service() {
104
Added:
function enable_services() {
83
105
systemctl daemon-reload
84
106
systemctl enable "${SERVICE_NAME}"
107
Added:
systemctl enable --now "${WEATHER_TIMER_NAME}"
85
108
systemctl restart "${SERVICE_NAME}"
86
109
}
87
110
@@ -90,7 +113,9 @@
90
113
Deployment complete.
91
114
Current service state:
92
115
EOF
93
Removed:
systemctl --no-pager --full status "${SERVICE_NAME}"
116
Added:
systemctl --no-pager --full status \
117
Added:
"${SERVICE_NAME}" \
118
Added:
"${WEATHER_TIMER_NAME}"
94
119
}
95
120
96
121
function main() {
@@ -98,9 +123,10 @@
98
123
install_os_dependencies
99
124
create_runtime_user
100
125
check_repo_layout
101
Removed:
write_systemd_service
126
Added:
migrate_application_secret
127
Added:
write_systemd_services
102
128
configure_nginx_compression
103
Removed:
enable_service
129
Added:
enable_services
104
130
show_status
105
131
}
106
132
roles/dashboard/etc/systemd/system/fapg-daq-dashboard-weather.service
@@ -0,0 +1,22 @@
1
Added:
[Unit]
2
Added:
Description=FAPG DAQ Dashboard Weather Ingestion
3
Added:
Wants=network-online.target
4
Added:
After=network-online.target
5
Added:
6
Added:
[Service]
7
Added:
Type=oneshot
8
Added:
9
Added:
User=fapg-daq
10
Added:
Group=fapg-daq
11
Added:
12
Added:
WorkingDirectory=/opt/fapg/fapg-daq/roles/dashboard
13
Added:
14
Added:
StateDirectory=fapg-daq
15
Added:
StateDirectoryMode=0750
16
Added:
17
Added:
Environment=HOME=/var/lib/fapg-daq
18
Added:
Environment=MOJO_MODE=production
19
Added:
Environment=FAPG_DAQ_DB=/var/lib/fapg-daq/readings.sqlite3
20
Added:
Environment=FAPG_DAQ_SECRET_FILE=/var/lib/fapg-daq/dashboard.secret
21
Added:
22
Added:
ExecStart=/opt/fapg/fapg-daq/roles/dashboard/script/fetch-weather
roles/dashboard/etc/systemd/system/fapg-daq-dashboard-weather.timer
@@ -0,0 +1,12 @@
1
Added:
[Unit]
2
Added:
Description=Fetch FAPG DAQ Dashboard Weather Data Hourly
3
Added:
4
Added:
[Timer]
5
Added:
OnBootSec=30s
6
Added:
OnUnitActiveSec=1h
7
Added:
AccuracySec=1m
8
Added:
RandomizedDelaySec=2m
9
Added:
Unit=fapg-daq-dashboard-weather.service
10
Added:
11
Added:
[Install]
12
Added:
WantedBy=timers.target
roles/dashboard/etc/systemd/system/fapg-daq-dashboard.service
@@ -20,6 +20,7 @@
20
20
Environment=HOME=/var/lib/fapg-daq
21
21
Environment=MOJO_MODE=production
22
22
Environment=FAPG_DAQ_DB=/var/lib/fapg-daq/readings.sqlite3
23
Added:
Environment=FAPG_DAQ_SECRET_FILE=/var/lib/fapg-daq/dashboard.secret
23
24
24
25
ExecStart=/usr/bin/hypnotoad -f /opt/fapg/fapg-daq/roles/dashboard/script/dashboard
25
26
roles/dashboard/script/fetch-weather
@@ -0,0 +1,35 @@
1
Added:
#!/usr/bin/env perl
2
Added:
3
Added:
use strict;
4
Added:
use warnings;
5
Added:
6
Added:
use Mojo::File qw(curfile);
7
Added:
use lib curfile->dirname->sibling('lib')->to_string;
8
Added:
9
Added:
use FAPG::DAQ::Dashboard;
10
Added:
11
Added:
my $app = FAPG::DAQ::Dashboard->new;
12
Added:
my $weather_config = $app->config->{weather} // {};
13
Added:
14
Added:
if ( !$weather_config->{enabled} ) {
15
Added:
print "Weather ingestion is disabled in dashboard.yml\n";
16
Added:
exit 0;
17
Added:
}
18
Added:
19
Added:
my ( $fetched, $stored );
20
Added:
my $success = eval {
21
Added:
my $rows = $app->weather_fetcher->fetch;
22
Added:
$fetched = scalar @$rows;
23
Added:
$stored = $app->weather->store_batch($rows) // 0;
24
Added:
1;
25
Added:
};
26
Added:
27
Added:
if ( !$success ) {
28
Added:
my $error = $@ || 'Unknown weather ingestion failure';
29
Added:
chomp $error;
30
Added:
print STDERR "$error\n";
31
Added:
exit 1;
32
Added:
}
33
Added:
34
Added:
print "Weather ingestion fetched $fetched points and stored $stored points\n";
35
Added:
exit 0;