Skip to content

Commit 596e514

Browse files
authored
feat: add support for loading to MinIO (#12)
1 parent 171e004 commit 596e514

8 files changed

Lines changed: 386 additions & 181 deletions

File tree

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
# ACR Loader
22

33
Loads data from ACRCloud's broadcat monitoring service and stores it
4-
in our ownCloud instance. Runs as a cronjob and is scheduled to run
5-
once per day.
4+
in our ownCloud instance and/or MinIO service. Runs as a cronjob and
5+
is scheduled to run once per day.
66

77
## Usage
88

99
```
1010
helm install my-acrloader oci://ghcr.io/radiorabe/helm/acrloader \
1111
--version x.y.z \
1212
--set acr.bearerToken=<token>,acr.projectId=<pid>,streamId=<sid> \
13-
--set oc.url=<url>,oc.user=<user>,oc.pass=<pass>,oc.path=<path>
13+
--set oc.enabled=true \
14+
--set oc.url=<url>,oc.user=<user>,oc.pass=<pass>,oc.path=<path> \
15+
--set minio.enabled=true \
16+
--set minio.url=<url>,minio.access_key=<key>,minio.secret_key=<secret>
1417
```
1518

1619
## Development

charts/acrloader/templates/cm.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ metadata:
77
data:
88
ACR_PROJECT_ID: {{ .Values.acr.projectId | quote }}
99
ACR_STREAM_ID: {{ .Values.acr.streamId | quote }}
10+
{{- if .Values.oc.enabled }}
11+
OC: "true"
1012
OC_URL: {{ .Values.oc.url | quote }}
1113
OC_USER: {{ .Values.oc.user | quote }}
1214
OC_PATH: {{ .Values.oc.path | quote }}
15+
{{- end }}
16+
{{- if .Values.minio.enabled }}
17+
MINIO: "true"
18+
MINIO_URL: {{ .Values.minio.url | quote }}
19+
MINIO_BUCKET: {{ .Values.minio.bucket | quote }}
20+
MINIO_SECURE: {{ .Values.minio.secure | quote }}
21+
MINIO_CERT_REQS: {{ .Values.minio.cert_reqs | quote }}
22+
{{- with .Values.minio.ca_certs }}
23+
MINIO_CA_CERTS: {{ . | quote }}
24+
{{- end }}
25+
{{- end }}

charts/acrloader/templates/secret.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,11 @@ metadata:
77
{{- include "acrloader.labels" . | nindent 4 }}
88
data:
99
ACR_BEARER_TOKEN: {{ .Values.acr.bearerToken | b64enc | quote }}
10+
{{- if .Values.oc.enabled }}
1011
OC_PASS: {{ .Values.oc.pass | b64enc | quote }}
12+
{{- end }}
13+
{{- if .Values.minio.enabled }}
14+
MINIO_ACCESS_KEY: {{ .Values.minio.access_key | b64enc | quote }}
15+
MINIO_SECRET_KEY: {{ .Values.minio.secret_key | b64enc | quote }}
16+
{{- end }}
1117
{{- end }}

charts/acrloader/values.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,18 @@ acr:
6464
streamId: ""
6565

6666
oc:
67+
enabled: true
6768
url: ""
6869
user: ""
6970
pass: ""
7071
path: ""
72+
73+
minio:
74+
enabled: true
75+
url: ""
76+
bucket: ""
77+
access_key: ""
78+
secret_key: ""
79+
secure: "True"
80+
cert_reqs: "CERT_REQUIRED"
81+
ca_certs: ""

main.py

Lines changed: 185 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
"""
22
Stores daily data from ACRCloud's broadcast monitoring service in ownCloud.
33
"""
4+
import json
5+
import os
46
from datetime import datetime, timedelta
5-
from logging import getLogger
67
from functools import cache
7-
import os
8-
import json
8+
from io import BytesIO
9+
from logging import getLogger
910

11+
import urllib3
1012
from acrclient import Client as ACRClient
1113
from acrclient.models import GetBmCsProjectsResultsParams
1214
from configargparse import ArgParser # type: ignore
15+
from minio import Minio # type: ignore
16+
from minio.error import S3Error # type: ignore
1317
from owncloud import Client as OwnCloudClient # type: ignore
1418
from owncloud.owncloud import HTTPResponseError as OCResponseError # type: ignore
1519
from tqdm import tqdm
1620

17-
1821
logger = getLogger(__name__)
1922

2023

@@ -47,7 +50,7 @@ def oc_file_exists(oc: OwnCloudClient, path: str):
4750
return False
4851

4952

50-
def check(oc: OwnCloudClient, oc_path: str) -> list[datetime]:
53+
def oc_check(oc: OwnCloudClient, oc_path: str) -> list[datetime]:
5154
"""
5255
Checks ownCloud for missing files.
5356
"""
@@ -71,34 +74,95 @@ def check(oc: OwnCloudClient, oc_path: str) -> list[datetime]:
7174
return missing
7275

7376

74-
def fetch(
77+
def mc_check(mc: Minio, bucket: str) -> list[datetime]:
78+
"""
79+
Checks MinIO for missing files.
80+
"""
81+
missing = []
82+
start = datetime.now() - timedelta(7)
83+
for requested in tqdm(daterange(start, datetime.now()), desc="Checking MinIO"):
84+
try:
85+
mc.stat_object(bucket, requested.strftime("%Y-%m-%d.json"))
86+
except S3Error as ex:
87+
if ex.code == "NoSuchKey":
88+
missing.append(requested)
89+
return missing
90+
91+
92+
@cache
93+
def fetch_one(
94+
acr: ACRClient,
95+
acr_project_id: str,
96+
acr_stream_id: str,
97+
requested: str,
98+
):
99+
return acr.get_bm_cs_projects_results(
100+
project_id=int(acr_project_id),
101+
stream_id=acr_stream_id,
102+
params=GetBmCsProjectsResultsParams(
103+
type="day",
104+
date=requested,
105+
min_duration=0,
106+
max_duration=3600,
107+
isrc_country="",
108+
),
109+
)
110+
111+
112+
def oc_fetch(
75113
missing: list[datetime],
76114
acr: ACRClient,
77115
oc: OwnCloudClient,
78116
acr_project_id: str,
79117
acr_stream_id: str,
80118
oc_path: str,
81119
):
82-
"""Fetches missing data from ACRCloud and stores it."""
83-
for requested in tqdm(missing, desc="Loading from ACRCloud"):
120+
"""Fetches missing data from ACRCloud and stores it in ownCloud."""
121+
for requested in tqdm(missing, desc="Loading into ownCloud from ACRCloud"):
84122
target = os.path.join(
85123
oc_path,
86124
str(requested.year),
87125
str(requested.month),
88126
requested.strftime("%Y-%m-%d.json"),
89127
)
90-
data = acr.get_bm_cs_projects_results(
91-
project_id=int(acr_project_id),
92-
stream_id=acr_stream_id,
93-
params=GetBmCsProjectsResultsParams(
94-
type="day",
95-
date=requested.strftime("%Y%m%d"),
96-
min_duration=0,
97-
max_duration=3600,
98-
isrc_country="",
128+
oc.put_file_contents(
129+
target,
130+
json.dumps(
131+
fetch_one(
132+
acr=acr,
133+
acr_project_id=acr_project_id,
134+
acr_stream_id=acr_stream_id,
135+
requested=requested.strftime("%Y%m%d"),
136+
)
99137
),
100138
)
101-
oc.put_file_contents(target, json.dumps(data))
139+
140+
141+
def mc_fetch(
142+
missing: list[datetime],
143+
acr: ACRClient,
144+
mc: Minio,
145+
acr_project_id: str,
146+
acr_stream_id: str,
147+
bucket: str,
148+
):
149+
"""Fetches missing data from ACRCloud and stores it in MinIO."""
150+
for requested in tqdm(missing, desc="Loading into MinIO from ACRCloud"):
151+
_as_bytes = json.dumps(
152+
fetch_one(
153+
acr=acr,
154+
acr_project_id=acr_project_id,
155+
acr_stream_id=acr_stream_id,
156+
requested=requested.strftime("%Y%m%d"),
157+
)
158+
).encode("utf-8")
159+
mc.put_object(
160+
bucket,
161+
requested.strftime("%Y-%m-%d.json"),
162+
BytesIO(_as_bytes),
163+
length=len(_as_bytes),
164+
content_type="application/json",
165+
)
102166

103167

104168
def main(): # pragma: no cover
@@ -134,6 +198,13 @@ def main(): # pragma: no cover
134198
env_var="ACR_STREAM_ID",
135199
help="ACRCloud stream id",
136200
)
201+
p.add(
202+
"--oc",
203+
default=False,
204+
action="store_true",
205+
env_var="OC_ENABLE",
206+
help="Enable ownCloud",
207+
)
137208
p.add(
138209
"--oc-url",
139210
default="https://share.rabe.ch",
@@ -158,29 +229,106 @@ def main(): # pragma: no cover
158229
env_var="OC_PATH",
159230
help="ownCloud path",
160231
)
232+
p.add(
233+
"--minio",
234+
default=False,
235+
action="store_true",
236+
env_var="MINIO_URL",
237+
help="MinIO URL",
238+
)
239+
p.add(
240+
"--minio-url",
241+
default="minio.service.int.rabe.ch:9000",
242+
env_var="MINIO_HOST",
243+
help="MinIO Hostname",
244+
)
245+
p.add(
246+
"--minio-secure",
247+
default=True,
248+
env_var="MINIO_SECURE",
249+
help="MinIO Secure param",
250+
)
251+
p.add(
252+
"--minio-cert-reqs",
253+
default="CERT_REQUIRED",
254+
env_var="MINIO_CERT_REQS",
255+
help="cert_reqs for urlib3.PoolManager used by MinIO",
256+
)
257+
p.add(
258+
"--minio-ca-certs",
259+
default="/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt",
260+
env_var="MINIO_CA_CERTS",
261+
help="ca_certs for urlib3.PoolManager used by MinIO",
262+
)
263+
p.add(
264+
"--minio-bucket",
265+
default="acrcloud.raw",
266+
env_var="MINIO_BUCKET",
267+
help="MinIO Bucket Name",
268+
)
269+
p.add(
270+
"--minio-access-key",
271+
default=None,
272+
env_var="MINIO_ACCESS_KEY",
273+
help="MinIO Access Key",
274+
)
275+
p.add(
276+
"--minio-secret-key",
277+
default=None,
278+
env_var="MINIO_SECRET_KEY",
279+
help="MinIO Secret Key",
280+
)
161281
options = p.parse_args()
162-
163-
# figure out what we are missing on ownCloud
164-
oc = OwnCloudClient(options.oc_url)
165-
oc.login(options.oc_user, options.oc_pass)
166-
missing = check(oc=oc, oc_path=options.oc_path)
167-
168-
# return early if no files are missing
169-
if not missing:
170-
return
171-
172-
# fetch and store missing data
173282
acr_client = ACRClient(
174283
bearer_token=options.acr_bearer_token,
175284
)
176-
fetch(
177-
missing,
178-
acr=acr_client,
179-
oc=oc,
180-
acr_project_id=options.acr_project_id,
181-
acr_stream_id=options.acr_stream_id,
182-
oc_path=options.oc_path,
183-
)
285+
286+
if options.oc:
287+
# figure out what we are missing on ownCloud
288+
oc = OwnCloudClient(options.oc_url)
289+
oc.login(options.oc_user, options.oc_pass)
290+
missing = oc_check(oc=oc, oc_path=options.oc_path)
291+
292+
# return early if no files are missing
293+
if not missing:
294+
return
295+
296+
# fetch and store missing data
297+
oc_fetch(
298+
missing,
299+
acr=acr_client,
300+
oc=oc,
301+
acr_project_id=options.acr_project_id,
302+
acr_stream_id=options.acr_stream_id,
303+
oc_path=options.oc_path,
304+
)
305+
if options.minio:
306+
mc = Minio(
307+
options.minio_url,
308+
options.minio_access_key,
309+
options.minio_secret_key,
310+
secure=options.minio_secure,
311+
http_client=urllib3.PoolManager(
312+
cert_reqs=options.minio_cert_reqs, ca_certs=options.minio_ca_certs
313+
),
314+
)
315+
if not mc.bucket_exists(options.minio_bucket):
316+
mc.make_bucket(options.minio_bucket)
317+
missing = mc_check(mc=mc, bucket=options.minio_bucket)
318+
319+
# return early if no files are missing
320+
if not missing:
321+
return
322+
323+
# fetch and store missing data
324+
mc_fetch(
325+
missing,
326+
acr=acr_client,
327+
mc=mc,
328+
acr_project_id=options.acr_project_id,
329+
acr_stream_id=options.acr_stream_id,
330+
bucket=options.bucket,
331+
)
184332

185333

186334
if __name__ == "__main__": # pragma: no cover

0 commit comments

Comments
 (0)