Skip to content

Commit 0891400

Browse files
committed
feat(CRON): log file archival job
1 parent ee36417 commit 0891400

File tree

6 files changed

+140
-3
lines changed

6 files changed

+140
-3
lines changed

pi_portal/config.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
)
99

1010
CRON_INTERVAL_DEAD_MAN_SWITCH = 60 * 5
11+
CRON_INTERVAL_LOGS_UPLOAD = 60 * 30
1112
CRON_INTERVAL_VIDEO_UPLOAD = 10
1213

1314
FILE_BEAT_BINARY = os.getenv("PI_PORTAL_FILEBEAT_LOCATION", "/usr/bin/filebeat")

pi_portal/modules/cron/jobs/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
from typing import List, Type
44

55
from ..bases.job import CronJobBase
6-
from . import dead_man_switch, video_upload
6+
from . import dead_man_switch, log_upload, video_upload
77

88
cron_jobs: List[Type[CronJobBase]] = [
99
dead_man_switch.DeadManSwitchCronJob,
10+
log_upload.LogFileUploadCronJob,
1011
video_upload.VideoUploadCronJob,
1112
]
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""LogFileUploadCronJob class."""
2+
3+
import logging
4+
import os
5+
from datetime import datetime
6+
7+
from pi_portal import config
8+
from pi_portal.modules.configuration import state
9+
from ..bases import s3_upload_job
10+
11+
12+
class LogFileUploadCronJob(s3_upload_job.S3UploadCronJobBase):
13+
"""S3 upload cron for log file archival.
14+
15+
:param log: The logging instance for this cron job.
16+
"""
17+
18+
interval = config.CRON_INTERVAL_LOGS_UPLOAD
19+
name = "Log File Upload"
20+
path = config.PATH_QUEUE_LOG_UPLOAD
21+
22+
def __init__(self, log: logging.Logger) -> None:
23+
running_state = state.State()
24+
aws_config = running_state.user_config["ARCHIVAL"]["AWS"]
25+
self.bucket_name = aws_config["AWS_S3_BUCKETS"]["LOGS"]
26+
super().__init__(log)
27+
28+
def object_name(self, file_name: str) -> str:
29+
"""Extract a timestamp and logfile name.
30+
31+
:param file_name: The name of the file being processed.
32+
:returns: The S3 object name that will be used.
33+
"""
34+
base_name = os.path.basename(file_name)
35+
parsed_file_name = base_name.split("_")
36+
timestamp = datetime.fromisoformat(parsed_file_name[0])
37+
log_name = "".join(parsed_file_name[1:])
38+
39+
return os.path.join(
40+
timestamp.date().isoformat(),
41+
f"{timestamp.timetz().isoformat()}_{log_name}"
42+
)

pi_portal/modules/cron/jobs/tests/conftest.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66
from pi_portal.modules.configuration.tests.fixtures import mock_state
7-
from .. import dead_man_switch, video_upload
7+
from .. import dead_man_switch, log_upload, video_upload
88

99

1010
@pytest.fixture
@@ -14,6 +14,15 @@ def dead_man_switch_cron_job_instance(
1414
return dead_man_switch.DeadManSwitchCronJob(mocked_cron_logger)
1515

1616

17+
@pytest.fixture
18+
def log_upload_cron_job_instance(
19+
mocked_cron_logger: logging.Logger
20+
) -> log_upload.LogFileUploadCronJob:
21+
with mock_state.mock_state_creator():
22+
instance = log_upload.LogFileUploadCronJob(mocked_cron_logger)
23+
return instance
24+
25+
1726
@pytest.fixture
1827
def video_upload_cron_job_instance(
1928
mocked_cron_logger: logging.Logger

pi_portal/modules/cron/jobs/tests/dead_man_switch_test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class TestDeadManSwitchCronJob:
1313
def test__initialization__attrs(
1414
self,
1515
dead_man_switch_cron_job_instance: dead_man_switch.DeadManSwitchCronJob,
16-
mocked_cron_logger: logging.Logger
16+
mocked_cron_logger: logging.Logger,
1717
) -> None:
1818
assert dead_man_switch_cron_job_instance.interval == \
1919
config.CRON_INTERVAL_DEAD_MAN_SWITCH
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""Test the LogFileUploadCronJob class."""
2+
import logging
3+
import os
4+
from datetime import datetime, timezone
5+
6+
from pi_portal import config
7+
from pi_portal.modules.configuration import state
8+
from pi_portal.modules.integrations.folder import queue
9+
from pi_portal.modules.integrations.s3 import client as s3_client
10+
from ...bases import s3_upload_job
11+
from .. import log_upload
12+
13+
14+
class TestLogFileUploadCronJob:
15+
"""Test the LogFileUploadCronJob class."""
16+
17+
def test__initialization__attrs(
18+
self,
19+
log_upload_cron_job_instance: log_upload.LogFileUploadCronJob,
20+
mocked_cron_logger: logging.Logger,
21+
mocked_state: state.State,
22+
) -> None:
23+
aws_config = mocked_state.user_config["ARCHIVAL"]["AWS"]
24+
assert log_upload_cron_job_instance.bucket_name == \
25+
aws_config["AWS_S3_BUCKETS"]["LOGS"]
26+
assert log_upload_cron_job_instance.interval == \
27+
config.CRON_INTERVAL_LOGS_UPLOAD
28+
assert log_upload_cron_job_instance.log == \
29+
mocked_cron_logger
30+
assert log_upload_cron_job_instance.name == \
31+
"Log File Upload"
32+
assert log_upload_cron_job_instance.path == \
33+
config.PATH_QUEUE_LOG_UPLOAD
34+
35+
def test__initialization__inheritance(
36+
self,
37+
log_upload_cron_job_instance: log_upload.LogFileUploadCronJob,
38+
) -> None:
39+
assert isinstance(
40+
log_upload_cron_job_instance,
41+
s3_upload_job.S3UploadCronJobBase,
42+
)
43+
44+
def test__initialization__disk_queue(
45+
self,
46+
log_upload_cron_job_instance: log_upload.LogFileUploadCronJob,
47+
) -> None:
48+
assert isinstance(
49+
log_upload_cron_job_instance.disk_queue,
50+
queue.DiskQueueIterator,
51+
)
52+
assert log_upload_cron_job_instance.disk_queue.path == \
53+
log_upload_cron_job_instance.path
54+
55+
def test__initialization__s3_client(
56+
self,
57+
log_upload_cron_job_instance: log_upload.LogFileUploadCronJob,
58+
mocked_state: state.State,
59+
) -> None:
60+
aws_config = mocked_state.user_config["ARCHIVAL"]["AWS"]
61+
assert isinstance(
62+
log_upload_cron_job_instance.s3_client,
63+
s3_client.S3BucketClient,
64+
)
65+
assert log_upload_cron_job_instance.s3_client.bucket_name == \
66+
aws_config["AWS_S3_BUCKETS"]["LOGS"]
67+
68+
def test__object_name__correct_object_name(
69+
self,
70+
log_upload_cron_job_instance: log_upload.LogFileUploadCronJob,
71+
) -> None:
72+
timestamp = datetime.now().replace(tzinfo=timezone.utc)
73+
lof_file_name = "mock.log"
74+
archival_name = os.path.join(
75+
config.PATH_QUEUE_LOG_UPLOAD,
76+
f"{timestamp.isoformat()}_{lof_file_name}",
77+
)
78+
79+
object_name = log_upload_cron_job_instance.object_name(archival_name)
80+
81+
assert object_name == os.path.join(
82+
timestamp.date().isoformat(),
83+
f"{timestamp.timetz().isoformat()}_{lof_file_name}",
84+
)

0 commit comments

Comments
 (0)