Skip to content

Commit

Permalink
Merge pull request #141 from IFRCGo/feature/helm
Browse files Browse the repository at this point in the history
Feature/helm
  • Loading branch information
sudan45 authored Feb 10, 2025
2 parents 391d638 + 7c9b1a8 commit b57cff1
Show file tree
Hide file tree
Showing 38 changed files with 1,948 additions and 31 deletions.
157 changes: 157 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: Python check

on:
workflow_call:
inputs:
push_docker_image:
type: string # true or false
default: "false"
outputs:
docker_image_name:
description: "Only docker image name"
value: ${{ jobs.test.outputs.docker_image_name }}
docker_image_tag:
description: "Only docker image tag"
value: ${{ jobs.test.outputs.docker_image_tag }}
docker_image:
description: "docker image with tag"
value: ${{ jobs.test.outputs.docker_image }}
pull_request:
# NOTE: For other, they should be run through helm github action ./helm-publish.yml

env:
COMPOSE_FILE: docker-compose.yml:gh-docker-compose.yml
DJANGO_SECRET_KEY: "ci-test-insecure-django-secret-key"
EMDAT_AUTHORIZATION_KEY: dummy-value
IDMC_CLIENT_ID: dummy-value
EOAPI_DOMAIN: https://montandon-eoapi.dummy.com

jobs:
pre_commit_checks:
name: Pre-Commit checks
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@main
with:
submodules: true

- uses: actions/setup-python@v5
- uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Sync
run: uv sync --all-extras
- uses: pre-commit/action@main

test:
name: Test
runs-on: ubuntu-latest
needs: pre_commit_checks

outputs:
docker_image_name: ${{ steps.prep.outputs.tagged_image_name }}
docker_image_tag: ${{ steps.prep.outputs.tag }}
docker_image: ${{ steps.prep.outputs.tagged_image }}

steps:
- uses: actions/checkout@main
with:
submodules: true

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
if: ${{ inputs.push_docker_image }}
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: 🐳 Prepare Docker
id: prep
env:
IMAGE_NAME: ghcr.io/${{ github.repository }}
run: |
BRANCH_NAME=$(echo $GITHUB_REF_NAME | sed 's|:|-|' | tr '[:upper:]' '[:lower:]' | sed 's/_/-/g' | cut -c1-100 | sed 's/-*$//')
# XXX: Check if there is a slash in the BRANCH_NAME eg: project/add-docker
if [[ "$BRANCH_NAME" == *"/"* ]]; then
# XXX: Change the docker image package to -alpha
IMAGE_NAME="$IMAGE_NAME-alpha"
TAG="$(echo "$BRANCH_NAME" | sed 's|/|-|g').$(echo $GITHUB_SHA | head -c7)"
else
TAG="$BRANCH_NAME.$(echo $GITHUB_SHA | head -c7)"
fi
IMAGE_NAME=$(echo $IMAGE_NAME | tr '[:upper:]' '[:lower:]')
echo "tagged_image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "tagged_image=${IMAGE_NAME}:${TAG}" >> $GITHUB_OUTPUT
echo "::notice::Tagged docker image: ${IMAGE_NAME}:${TAG}"
- name: 🐳 Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3

- name: 🐳 Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.ref }}
restore-keys: |
${{ runner.os }}-buildx-refs/develop
${{ runner.os }}-buildx-
- name: 🐳 Build image
uses: docker/build-push-action@v6
with:
context: .
builder: ${{ steps.buildx.outputs.name }}
file: Dockerfile
push: false
load: true
provenance: false # XXX: Without this we have untagged images in ghcr.io
tags: ${{ steps.prep.outputs.tagged_image }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: 🕮 Validate if there are no pending django migrations.
env:
DOCKER_IMAGE_BACKEND: ${{ steps.prep.outputs.tagged_image }}
run: |
docker compose run --rm web bash -c 'wait-for-it db:5432 && ./manage.py makemigrations --check --dry-run' || {
echo 'There are some changes to be reflected in the migration. Make sure to run makemigrations';
exit 1;
}
# - name: 🤞 Run Test 🧪 & Publish coverage to code climate
# env:
# DOCKER_IMAGE_BACKEND: ${{ steps.prep.outputs.tagged_image }}
# run: docker compose run --rm web /code/scripts/run_tests.sh

- name: 🐳 Docker push
if: ${{ inputs.push_docker_image }}
env:
DOCKER_IMAGE_BACKEND: ${{ steps.prep.outputs.tagged_image }}
run: docker push $DOCKER_IMAGE_BACKEND

validate_helm:
name: Validate Helm
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@main

- name: Install Helm
uses: azure/setup-helm@v4

- name: 🐳 Helm dependency
run: |
yq --indent 0 '.dependencies | map(["helm", "repo", "add", .name, .repository] | join(" ")) | .[]' ./helm/Chart.lock | sh --
helm dependency build ./helm
- name: 🐳 Helm lint
run: helm lint ./helm --values ./helm/values-test.yaml

- name: 🐳 Helm template
run: helm template ./helm --values ./helm/values-test.yaml
84 changes: 84 additions & 0 deletions .github/workflows/helm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Helm

on:
workflow_dispatch:
# Build and push Docker image and Helm charts on every push to develop branch
# and on every tag push
push:
branches:
- develop
- project/*
- chore/*
tags:
- "**"

permissions:
packages: write

jobs:
ci:
name: CI
uses: ./.github/workflows/ci.yml
with:
push_docker_image: true

build:
name: Publish Helm
needs: ci
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: 🐳 Helm dependency
run: |
yq --indent 0 '.dependencies | map(["helm", "repo", "add", .name, .repository] | join(" ")) | .[]' ./helm/Chart.lock | sh --
helm dependency build ./helm/
- name: Tag docker image in Helm Chart values.yaml
env:
IMAGE_NAME: ${{ needs.ci.outputs.docker_image_name }}
IMAGE_TAG: ${{ needs.ci.outputs.docker_image_tag }}
run: |
# Update values.yaml with latest docker image
sed -i "s|SET-BY-CICD-IMAGE|$IMAGE_NAME|" ./helm/values.yaml
sed -i "s/SET-BY-CICD-TAG/$IMAGE_TAG/" ./helm/values.yaml
- name: Package Helm Chart
id: set-variables
env:
IMAGE_TAG: ${{ needs.ci.outputs.docker_image_tag }}
run: |
# XXX: Check if there is a slash in the BRANCH_NAME eg: project/add-docker
if [[ "$GITHUB_REF_NAME" == *"/"* ]]; then
# XXX: Change the helm chart to <chart-name>-alpha
sed -i 's/^name: \(.*\)/name: \1-alpha/' ./helm/Chart.yaml
fi
sed -i "s/SET-BY-CICD/$IMAGE_TAG/g" ./helm/Chart.yaml
helm package ./helm/ -d ./helm/.helm-charts
- name: Push Helm Chart
env:
IMAGE: ${{ needs.ci.outputs.docker_image }}
OCI_REPO: oci://ghcr.io/${{ github.repository }}
run: |
OCI_REPO=$(echo $OCI_REPO | tr '[:upper:]' '[:lower:]')
PACKAGE_FILE=$(ls ./helm/.helm-charts/*.tgz | head -n 1)
echo "# Helm Chart" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Tagged Image: **$IMAGE**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Helm push output" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
helm push "$PACKAGE_FILE" $OCI_REPO 2>> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
FROM python:3.12-slim-bullseye AS base
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
COPY --from=ghcr.io/astral-sh/uv:0.5.29 /uv /uvx /bin/

LABEL maintainer="Togglecorp Dev"
LABEL maintainer="Montandon Dev"
LABEL org.opencontainers.image.source="https://github.com/IFRCGo/montandon-etl/"

ENV PYTHONUNBUFFERED=1

Expand Down
1 change: 0 additions & 1 deletion apps/etl/extraction/sources/usgs/extract.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import json
import logging

import requests
Expand Down
9 changes: 7 additions & 2 deletions apps/etl/load/sources/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import urllib.parse

import requests
from celery.utils.log import get_task_logger
from django.conf import settings
from django.core.management.base import BaseCommand

from apps.etl.models import PyStacLoadData
Expand All @@ -10,8 +13,10 @@

def send_post_request_to_stac_api(result, collection_id):
try:
# url = f"http://montandon-eoapi-stage.ifrc.org/stac/collections/{collection_id}/items"
url = f"https://montandon-eoapi-1.ifrc-go.dev.togglecorp.com/stac/collections/{collection_id}/items"
url = urllib.parse.urljoin(
settings.EOAPI_DOMAIN,
f"/stac/collections/{collection_id}/items",
)

response = requests.post(url, json=result, headers={"Content-Type": "application/json"})
response.raise_for_status()
Expand Down
15 changes: 15 additions & 0 deletions apps/etl/management/commands/local_cache_pull.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import logging

from django.conf import settings
from django.core.management.base import BaseCommand

logger = logging.getLogger(__name__)


class Command(BaseCommand):
help = "Load data to stac api"

def handle(self, *args, **options):
# NOTE: Make sure to adjust helm values `localCacheVolume.size` according to fetched data size
# TODO: Add logic to pull data as required
print(f"Data will be loaded to directory: {settings.LOCAL_CACHE_DATA_DIR}")
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='extractiondata',
name='hazard_type',
field=models.CharField(blank=True, choices=[('EQ', 'Earthquake'), ('FL', 'Flood'), ('TC', 'Cyclone'), ('EP', 'Epidemic'), ('FI', 'Food Insecurity'), ('SS', 'Storm Surge'), ('DR', 'Drought'), ('TS', 'Tsunami'), ('CD', 'Cyclonic Wind'), ('WF', 'WildFire'), ('VO', 'Volcano'), ('CW', 'Cold Wave'), ('CE', 'Complex Emergency'), ('EC', 'Extratropical Cyclone'), ('ET', 'Extreme temperature'), ('FA', 'Famine'), ('FR', 'Fire'), ('FF', 'Flash Flood'), ('HT', 'Heat Wave'), ('IN', 'Insect Infestation'), ('LS', 'Land Slide'), ('MS', 'Mud Slide'), ('ST', 'Severe Local Strom'), ('SL', 'Slide'), ('AV', 'Snow Avalanche'), ('AC', 'Tech. Disaster'), ('TO', 'Tornado'), ('VW', 'Violent Wind'), ('WV', 'Wave/Surge')], max_length=100, null=True, verbose_name='hazard type'),
field=models.CharField(blank=True, choices=[('EQ', 'Earthquake'), ('FL', 'Flood'), ('TC', 'Cyclone'), ('EP', 'Epidemic'), ('FI', 'Food Insecurity'), ('SS', 'Storm Surge'), ('DR', 'Drought'), ('TS', 'Tsunami'), ('CD', 'Cyclonic Wind'), ('WF', 'WildFire'), ('VO', 'Volcano'), ('CW', 'Cold Wave'), ('CE', 'Complex Emergency'), ('EC', 'Extratropical Cyclone'), ('ET', 'Extreme temperature'), ('FA', 'Famine'), ('FR', 'Fire'), ('FF', 'Flash Flood'), ('HT', 'Heat Wave'), ('IN', 'Insect Infestation'), ('LS', 'Land Slide'), ('MS', 'Mud Slide'), ('ST', 'Severe Local Strom'), ('SL', 'Slide'), ('AV', 'Snow Avalanche'), ('AC', 'Tech. Disaster'), ('TO', 'Tornado'), ('VW', 'Violent Wind'), ('WV', 'Wave/Surge'), ('OT', 'Other')], max_length=100, null=True, verbose_name='hazard type'),
),
migrations.AlterField(
model_name='extractiondata',
Expand Down
5 changes: 3 additions & 2 deletions apps/etl/transform/sources/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
"emdat-impacts": PyStacLoadData.ItemType.IMPACT,
}

geocoder = GAULGeocoder(gpkg_path="/code/gaul2014_2015.gpkg")


@shared_task
def transform_data(source, transformer, data_source, extraction_id, data):
Expand All @@ -32,6 +30,9 @@ def transform_data(source, transformer, data_source, extraction_id, data):
# initialize bulk manager to create the PyStacLoadData in bulk.
bulk_mgr = BulkCreateManager(chunk_size=1000)

# XXX: Local file is used
geocoder = GAULGeocoder(gpkg_path="/code/gaul2014_2015.gpkg")

try:
# Get transformer for each source and transform it to stac item..
transformer = transformer(data=data_source(source_url=ext_instance.url, data=data), geocoder=geocoder)
Expand Down
24 changes: 20 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

x-server: &base_server_setup
restart: unless-stopped
image: etl-montandon/server
Expand All @@ -8,7 +9,7 @@ x-server: &base_server_setup
tty: true
env_file:
- .env
environment:
environment: &base_server_environments
DJANGO_DEBUG: ${DJANGO_DEBUG:-true}
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:?err}
DB_NAME: ${DB_NAME:-postgres}
Expand All @@ -17,10 +18,15 @@ x-server: &base_server_setup
DB_HOST: ${DB_HOST:-db}
DB_PORT: ${DB_PORT:-5432}
CELERY_REDIS_URL: ${CELERY_REDIS_URL:-redis://redis:6379/0}
DJANGO_CACHE_REDIS_URL: ${DJANGO_CACHE_REDIS_URL:-redis://redis:6379/1}

# ETL Sources
EMDAT_AUTHORIZATION_KEY: ${EMDAT_AUTHORIZATION_KEY?error}
IDMC_CLIENT_ID: ${IDMC_CLIENT_ID?error}
# ETL Load
EOAPI_DOMAIN: ${EOAPI_DOMAIN?error}
volumes:
- .:/code
- ipython_data_local:/root/.ipython/profile_default # persist ipython data, including ipython history
- local_cache_pull:/local-cache-data
depends_on:
- db
- redis
Expand All @@ -44,22 +50,32 @@ services:
web:
<<: *base_server_setup
command: bash -c "/code/scripts/run_develop.sh"
environment:
<<: *base_server_environments
DJANGO_APP_TYPE: "web"
ports:
- 8000:8000

depends_on:
- db
- redis

celery-beat:
<<: *base_server_setup
environment:
<<: *base_server_environments
DJANGO_APP_TYPE: "worker"
command: bash -c "/code/scripts/run_worker_beat.sh"

worker:
<<: *base_server_setup
environment:
<<: *base_server_environments
DJANGO_APP_TYPE: "worker"
# command: celery -A main worker --loglevel=info
command: bash -c "/code/scripts/run_worker.sh"

volumes:
postgres-data:
redis-data:
ipython_data_local:
local_cache_pull:
Loading

0 comments on commit b57cff1

Please sign in to comment.