Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
pip install vcrpy
pip install prometheus_client
pip install redis
pip install minio
- name: Analysing the code with pylint
run: |
# Set PYTHONPATH so pylint can find the app module
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/env
/__pycache__
/out.txt
/tests/__pycache__
/tests/__pycache__
/.pytest_cache
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ RUN pip install --no-cache-dir -r /app/requirements.txt --require-hashes && \

ENV FLASK_APP=app.main.py:app \
PYTHONUNBUFFERED=1 \
REDIS_HOST=localhost \
REDIS_PORT=6379 \
CACHE_TTL=300
REDIS_DB=0 \
CACHE_TTL=300 \
MINIO_PORT=9000 \
MINIO_ACCESS_KEY=minioadmin \
MINIO_SECRET_KEY=minioadmin

USER appuser

Expand Down
6 changes: 6 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from flask import Flask, Response
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST
from app import opensense
from app import storage

app = Flask(__name__)

Expand All @@ -26,5 +27,10 @@ def metrics():
'''Function to return Prometheus metrics.'''
return Response(generate_latest(), mimetype=CONTENT_TYPE_LATEST)

@app.route('/store')
def store():
'''Function to store results in MinIO.'''
return storage.store_temperature_data()

if __name__ == "__main__":
app.run()
1 change: 0 additions & 1 deletion app/opensense.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
socket_connect_timeout=240,
socket_timeout=240
)

redis_client.ping()
REDIS_AVAILABLE = True
print("Connected to Redis successfully!")
Expand Down
67 changes: 67 additions & 0 deletions app/storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'''This script uploads the output to a MinIO bucket.'''
import os
import io
import datetime
from minio import Minio
from minio.error import S3Error, InvalidResponseError
from app import opensense

MINIO_HOST = os.getenv('MINIO_HOST', 'localhost')
MINIO_PORT = int(os.environ.get('MINIO_PORT', 9000))
MINIO_ACCESS_KEY = os.environ.get('MINIO_ACCESS_KEY', 'minioadmin')
MINIO_SECRET_KEY = os.environ.get('MINIO_SECRET_KEY', 'minioadmin')

def store_temperature_data():
'''Function to upload temperature data to MinIO.'''
try:
client = Minio(f"{MINIO_HOST}:{MINIO_PORT}",
access_key=MINIO_ACCESS_KEY,
secret_key=MINIO_SECRET_KEY,
secure=False
)

# Check if the MinIO server is reachable
try:
client.list_buckets()
except ConnectionError as conn_exc:
error_msg = f"Cannot connect to MinIO server: {conn_exc}"
print(error_msg)
return error_msg

bucket_name = "temperature-data"
destination_file = f"temperature_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S%f')}.txt"

# Get the original temperature data format
temperature_data = opensense.get_temperature()

text_bytes = temperature_data.encode('utf-8')
text_stream = io.BytesIO(text_bytes)

# Make the bucket if it doesn't exist.
found = client.bucket_exists(bucket_name)
if not found:
client.make_bucket(bucket_name)
print("Created bucket", bucket_name)
else:
print("Bucket", bucket_name, "already exists")

# Upload the data
client.put_object(
bucket_name,
destination_file,
text_stream,
length=len(text_bytes),
content_type='text/plain'
)

return (f"Temperature data successfully uploaded as "
f"{destination_file} to bucket {bucket_name}")

except (S3Error, InvalidResponseError) as exc:
error_msg = f"MinIO S3 error occurred: {exc}"
print(error_msg)
return error_msg

if __name__ == "__main__":
result = store_temperature_data()
print(result)
44 changes: 44 additions & 0 deletions k8s/cronjob.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: temperature-storage-cronjob
labels:
app: hivebox-cronjob
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
securityContext:
runAsNonRoot: true
runAsUser: 65534 # nobody user
seccompProfile:
type: RuntimeDefault
containers:
- name: temperature-storage
image: curlimages/curl:8.15.0@sha256:4026b29997dc7c823b51c164b71e2b51e0fd95cce4601f78202c513d97da2922
command: ["curl"]
args:
- "-f" # Fail on HTTP errors
- "-s" # Silent mode
- "-S" # Show errors
- "--max-time"
- "60"
- "http://hivebox-service/store"
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
memory: "32Mi"
cpu: "50m"
requests:
memory: "16Mi"
cpu: "10m"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
63 changes: 54 additions & 9 deletions k8s/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,8 @@ spec:
env:
- name: REDIS_HOST
value: "redis-service"
- name: REDIS_PORT
value: "6379"
- name: REDIS_DB
value: "0"
- name: CACHE_TTL
value: "300"
- name: MINIO_HOST
value: "minio-service"
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
Expand Down Expand Up @@ -60,9 +56,9 @@ spec:
httpGet:
path: /version
port: 5000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
initialDelaySeconds: 60
periodSeconds: 120
timeoutSeconds: 10
failureThreshold: 3
volumeMounts:
- name: tmp-volume
Expand Down Expand Up @@ -119,3 +115,52 @@ spec:
volumes:
- name: valkey-data
emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
labels:
app: minio
spec:
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
spec:
securityContext:
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: minio
image: minio/minio:RELEASE.2025-07-23T15-54-02Z@sha256:d249d1fb6966de4d8ad26c04754b545205ff15a62e4fd19ebd0f26fa5baacbc0
ports:
- containerPort: 9000
command: ["minio"]
args: ["server", "/data"]
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsGroup: 1000
runAsUser: 1000
capabilities:
drop:
- ALL
resources:
limits:
memory: "256Mi"
cpu: "250m"
requests:
memory: "128Mi"
cpu: "100m"
volumeMounts:
- name: minio-data
mountPath: /data
volumes:
- name: minio-data
emptyDir: {}
11 changes: 11 additions & 0 deletions k8s/ingress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,14 @@ spec:
ports:
- port: 6379
targetPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: minio-service
spec:
selector:
app: minio
ports:
- port: 9000
targetPort: 9000
3 changes: 2 additions & 1 deletion requirements.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Flask==3.1.1
requests==2.32.4
prometheus-client==0.22.1
redis==6.2.0
redis==6.2.0
minio==7.2.16
Loading