Skip to content
Merged

Dev #466

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
20 changes: 17 additions & 3 deletions .github/workflows/ci-base-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ name: CI - Build base image

on:
workflow_dispatch:
inputs:
version:
description: 'Version tag for base image (e.g., v1.0.0, latest)'
required: true
default: 'latest'

jobs:
build-base:
Expand All @@ -20,8 +25,17 @@ jobs:
docker build . \
--file docker/Dockerfile \
--target emfos-base \
-t ${{ vars.DOCKER_USERNAME }}/emfos-base:latest
docker push ${{ vars.DOCKER_USERNAME}}/emfos-base:latest
-t ${{ vars.DOCKER_USERNAME }}/emfos-base:${{ github.event.inputs.version }}
docker push ${{ vars.DOCKER_USERNAME }}/emfos-base:${{ github.event.inputs.version }}

- name: Conditionally push as latest
shell: bash
run: |
if [[ "${{ github.event.inputs.version }}" == v* ]]; then
docker tag ${{ vars.DOCKER_USERNAME }}/emfos-base:${{ github.event.inputs.version }} \
${{ vars.DOCKER_USERNAME }}/emfos-base:latest
docker push ${{ vars.DOCKER_USERNAME }}/emfos-base:latest
fi

- name: Display Success Message
run: echo "emfos-base image has been built and pushed manually."
run: echo "emfos-base:${{ github.event.inputs.version }} image has been built and pushed manually."
28 changes: 13 additions & 15 deletions .github/workflows/ci-workers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ on:
version:
description: 'Version tag for the release (e.g., v1.0.0)'
required: true
base_image_tag:
description: 'Base image tag to use (e.g., latest, v1.0.0)'
required: true
default: 'latest'

jobs:
build_workers:
Expand All @@ -41,6 +45,7 @@ jobs:
# echo "IMAGE_TAG=$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV
echo "IMAGE_NAME=${{ vars.DOCKER_USERNAME }}/${{ matrix.worker }}" >> $GITHUB_ENV
echo "LATEST_TAG=latest" >> $GITHUB_ENV
echo "BASE_IMAGE_TAG=${{ github.event.inputs.base_image_tag || 'latest' }}" >> $GITHUB_ENV

- name: Compute IMAGE_TAG
id: meta
Expand All @@ -58,33 +63,26 @@ jobs:
fi

echo "IMAGE_TAG=$TAG" >> "$GITHUB_ENV"
echo "Computed IMAGE_TAG=$TAG"
echo "Computed IMAGE_TAG=$TAG with base image tag=${{ env.BASE_IMAGE_TAG }}"

- name: Pull base image or fallback to build
run: |
docker pull ${{ vars.DOCKER_USERNAME }}/emfos-base:latest || \
docker build . \
--file docker/Dockerfile \
--target emfos-base \
-t ${{ vars.DOCKER_USERNAME }}/emfos-base:main
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ vars.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build worker image
- name: Build worker image with specified base image
run: |
docker build . \
--file docker/Dockerfile \
--target ${{ env.WORKER_NAME }} \
--build-arg BASE_IMAGE=${{ vars.DOCKER_USERNAME }}/emfos-base:${{ env.BASE_IMAGE_TAG }} \
-t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}

- name: Tag image as latest
run: |
docker tag ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} ${{ env.IMAGE_NAME }}:${{ env.LATEST_TAG }}

- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ vars.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Push versioned image
run: |
docker push ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
Expand Down
3 changes: 2 additions & 1 deletion config/integrations/minio.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ MINIO_USERNAME = None
MINIO_PASSWORD = None
TOKEN_EXPIRATION = 86400
TOKEN_RENEW_MARGIN = 120
MAXSIZE = 50
MAXSIZE = 50
SLEEP_DURATION = 2
19 changes: 12 additions & 7 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Global argument for base image for workers, can be overridden when building with --build-arg
ARG BASE_IMAGE=balticrcc/emfos-base:latest

# Define base container image (platform has to be defined if using with ARM64 architecture)
FROM python:3.11-slim as emfos-base
#FROM --platform=linux/amd64 python:3.11-slim as emfos-base
Expand All @@ -23,7 +26,7 @@
# Set to use interpreter from venv (if venv is used)
#ENV PATH="/.venv/bin:$PATH"

FROM balticrcc/emfos-base:latest as emfos-model-retriever
FROM ${BASE_IMAGE} as emfos-model-retriever

Check warning on line 29 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a specific version tag for the image.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpJDmikfhuLL68K&open=AZ5PSrpJDmikfhuLL68K&pullRequest=466

Check warning on line 29 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "as" with upper case format "AS".

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68Q&open=AZ5PSrpKDmikfhuLL68Q&pullRequest=466

# Set working directory
WORKDIR /src
Expand All @@ -38,7 +41,7 @@
ENTRYPOINT ["python", "emf/model_retriever/opdm_worker.py"]
#ENTRYPOINT ["/bin/bash"]

FROM balticrcc/emfos-base:latest as emfos-local-model-retriever
FROM ${BASE_IMAGE} as emfos-local-model-retriever

Check warning on line 44 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "as" with upper case format "AS".

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68R&open=AZ5PSrpKDmikfhuLL68R&pullRequest=466

Check warning on line 44 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a specific version tag for the image.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68L&open=AZ5PSrpKDmikfhuLL68L&pullRequest=466

# Set working directory
WORKDIR /src
Expand All @@ -52,7 +55,7 @@
ENV PYTHONPATH="${PYTHONPATH}:/src"
ENTRYPOINT ["python", "emf/model_retriever/local_worker.py"]

FROM balticrcc/emfos-base:latest as emfos-model-validator
FROM ${BASE_IMAGE} as emfos-model-validator

Check warning on line 58 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a specific version tag for the image.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68M&open=AZ5PSrpKDmikfhuLL68M&pullRequest=466

Check warning on line 58 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "as" with upper case format "AS".

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68S&open=AZ5PSrpKDmikfhuLL68S&pullRequest=466

# Set working directory
WORKDIR /src
Expand All @@ -67,7 +70,7 @@
ENTRYPOINT ["python", "emf/model_validator/worker.py"]
#ENTRYPOINT ["/bin/bash"]

FROM balticrcc/emfos-base:latest as emfos-model-merger
FROM ${BASE_IMAGE} as emfos-model-merger

Check warning on line 73 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a specific version tag for the image.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68N&open=AZ5PSrpKDmikfhuLL68N&pullRequest=466

Check warning on line 73 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "as" with upper case format "AS".

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68T&open=AZ5PSrpKDmikfhuLL68T&pullRequest=466

# Set working directory
WORKDIR /src
Expand All @@ -82,7 +85,7 @@
ENTRYPOINT ["python", "emf/model_merger/worker.py"]
#ENTRYPOINT ["/bin/bash"]

FROM balticrcc/emfos-base:latest as emfos-task-generator
FROM ${BASE_IMAGE} as emfos-task-generator

Check warning on line 88 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "as" with upper case format "AS".

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68U&open=AZ5PSrpKDmikfhuLL68U&pullRequest=466

Check warning on line 88 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a specific version tag for the image.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68O&open=AZ5PSrpKDmikfhuLL68O&pullRequest=466

# Set working directory
WORKDIR /src
Expand All @@ -97,14 +100,16 @@
ENTRYPOINT ["python", "emf/task_generator/worker.py"]
#ENTRYPOINT ["/bin/bash"]

# Model Quality image
FROM balticrcc/emfos-base:latest as emfos-model-quality
FROM ${BASE_IMAGE} as emfos-model-quality

Check warning on line 103 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use a specific version tag for the image.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68P&open=AZ5PSrpKDmikfhuLL68P&pullRequest=466

Check warning on line 103 in docker/Dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "as" with upper case format "AS".

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrpKDmikfhuLL68W&open=AZ5PSrpKDmikfhuLL68W&pullRequest=466

# Set working directory
WORKDIR /src

# Copy files to container
COPY emf/model_quality/ ./emf/model_quality/
COPY emf/common/ ./emf/common/
COPY config/ ./config/

# Start process
ENV PYTHONPATH="${PYTHONPATH}:/src"
ENTRYPOINT ["python", "emf/model_quality/worker.py"]
86 changes: 76 additions & 10 deletions emf/common/integrations/minio_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import mimetypes
import re
import time
import logging
import config
import functools
Expand Down Expand Up @@ -64,7 +65,7 @@
http_client=self.http_client,
)

def _get_credentials(self, action: str = "AssumeRoleWithLDAPIdentity", version: str = "2011-06-15"):

Check failure on line 68 in emf/common/integrations/minio_api.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 23 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrlGDmikfhuLL68E&open=AZ5PSrlGDmikfhuLL68E&pullRequest=466
"""
Method to get temporary credentials for LDAP user
:param action: string of action
Expand All @@ -77,19 +78,84 @@
"LDAPUsername": self.username,
"LDAPPassword": self.password,
"Version": version,
"DurationSeconds": TOKEN_EXPIRATION,
"DurationSeconds": '3600',
}

# Sending request for temporary credentials and parsing it out from returned xml
response = requests.post(f"https://{self.server}", params=params, verify=False).content
credentials = {}
root = etree.fromstring(response)
et = root.find("{*}AssumeRoleWithLDAPIdentityResult/{*}Credentials")
for element in et:
_, _, tag = element.tag.rpartition("}")
credentials[tag] = element.text
last_error = None

return credentials
for attempt in range(1, 4):
response = None
try:
response = requests.post(f"https://{self.server}", params=params, verify=False, timeout=30)

Check failure on line 89 in emf/common/integrations/minio_api.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Enable server certificate validation on this SSL/TLS connection.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrlGDmikfhuLL68F&open=AZ5PSrlGDmikfhuLL68F&pullRequest=466

Check failure

Code scanning / SonarCloud

Server certificates should be verified during SSL/TLS connections High

Enable server certificate validation on this SSL/TLS connection. See more on SonarQube Cloud
logger.debug(f"MinIO auth response received, attempt {attempt}, HTTP status={response.status_code}")

# Parse received response content
root = etree.fromstring(response.content)

# Handle MinIO error response
err = root.find(".//{*}Error")
if err is not None:
code = err.findtext("{*}Code")
message = err.findtext("{*}Message")
request_id = root.findtext(".//{*}RequestId")

last_error = (
f"STS error: Code={code}, "
f"Message={message}, "
f"RequestId={request_id}"
)

logger.warning(f"MinIO auth failed on attempt {attempt}: {last_error}")

if attempt < 3:
logger.info(f"Retrying credential request in 1 second...")

Check warning on line 111 in emf/common/integrations/minio_api.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add replacement fields or use a normal string instead of an f-string.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrlGDmikfhuLL68G&open=AZ5PSrlGDmikfhuLL68G&pullRequest=466
time.sleep(int(SLEEP_DURATION))
continue

raise RuntimeError(last_error)

# Handle non-200 HTTP response without STS Error XML
if response.status_code != 200:
last_error = (
f"HTTP error: status={response.status_code}, "
f"body={response.content!r}"
)

logger.warning(f"MinIO HTTP error on attempt {attempt}: {last_error}")

if attempt < 3:
logger.info(f"Retrying credential request in 1 second...")

Check warning on line 127 in emf/common/integrations/minio_api.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add replacement fields or use a normal string instead of an f-string.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrlGDmikfhuLL68H&open=AZ5PSrlGDmikfhuLL68H&pullRequest=466
time.sleep(int(SLEEP_DURATION))
continue

raise RuntimeError(last_error)

# Extract credentials
credentials = {}
et = root.find("{*}AssumeRoleWithLDAPIdentityResult/{*}Credentials")
if et is None:
raise RuntimeError(f"Credentials not found in response: {response.content!r}")

for element in et:
tag = etree.QName(element).localname
credentials[tag] = element.text

return credentials

except requests.RequestException as e:
last_error = f"Request failed: {e}"
raise RuntimeError(last_error) from e

except etree.XMLSyntaxError as e:
body = response.content if response is not None else b""
last_error = (
f"Failed to parse XML response: {e}. "
f"Response body: {body!r}"
)

raise RuntimeError(last_error) from e

raise RuntimeError(last_error or "Failed to get credentials")

@staticmethod
def dict_to_tags(tags: dict):
Expand Down Expand Up @@ -151,7 +217,7 @@
return file_data.read()

except minio.error.S3Error as err:
logger.error(err)

Check failure on line 220 in emf/common/integrations/minio_api.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use "logging.exception()" instead.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrlGDmikfhuLL68I&open=AZ5PSrlGDmikfhuLL68I&pullRequest=466

@renew_authentication_token
def object_exists(self, object_name: str, bucket_name: str) -> bool:
Expand All @@ -178,7 +244,7 @@
objects = self.client.list_objects(bucket_name, prefix, recursive, start_after, include_user_meta, include_version)
return objects
except minio.error.S3Error as err:
logger.error(err, exc_info=True)

Check failure on line 247 in emf/common/integrations/minio_api.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use "logging.exception()" instead.

See more on https://sonarcloud.io/project/issues?id=Baltic-RCC_EMF&issues=AZ5PSrlGDmikfhuLL68J&open=AZ5PSrlGDmikfhuLL68J&pullRequest=466

@renew_authentication_token
def query_objects(self, bucket_name: str, metadata: dict = None, prefix: str = None, use_regex: bool = False):
Expand Down
Loading