Skip to content

Commit a79b285

Browse files
committed
feat: adaptation de l'etl pour une utilisation avec Agents en intervention et Scaleway
1 parent e7f090c commit a79b285

9 files changed

Lines changed: 269 additions & 36 deletions

File tree

.env.example

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
ETL_DB_URL=postgresql://rdv_sp_etl:rdv_sp_etl@localhost:5432/rdv-sp-etl
22
METABASE_USERNAME=rdv_metabase
33

4-
RDV_SOLIDARITES_DB_URL=postgresql://user:pwd@localhost:10000/rdv_solidarites
5-
RDV_SERVICE_PUBLIC_DB_URL=postgresql://user:pwd@localhost:10000/rdv_service_public
6-
RDV_INSERTION_DB_URL=postgresql://user:pwd@localhost:10000/rdv_insertion
4+
#ORIGIN_DB_URL=postgresql://user:pwd@localhost:10000/rdv_solidarites
5+
#ORIGIN_DB_URL=postgresql://user:pwd@localhost:10000/rdv_service_public
6+
#ORIGIN_DB_URL=postgresql://user:pwd@localhost:10000/rdv_insertion
7+
#CONFIG_PATH=https://gitlab.com/incubateur-territoires/startups/agents-intervention/agents-en-intervention/-/raw/main/config.etl.yml
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Build and Push Docker Image
2+
3+
on:
4+
push:
5+
branches: [ main, master, feat/adaptation-aei-scaleway ]
6+
tags: [ 'v*' ]
7+
workflow_dispatch:
8+
9+
jobs:
10+
build-and-push:
11+
runs-on: ubuntu-latest
12+
13+
permissions:
14+
contents: read
15+
packages: write
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v3
20+
21+
- name: Set up Docker Buildx
22+
uses: docker/setup-buildx-action@v2
23+
24+
- name: Login to GitHub Container Registry
25+
uses: docker/login-action@v2
26+
with:
27+
registry: ghcr.io
28+
username: ${{ github.repository_owner }}
29+
password: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Extract metadata for Docker
32+
id: meta
33+
uses: docker/metadata-action@v4
34+
with:
35+
images: ghcr.io/${{ github.repository }}
36+
tags: |
37+
type=ref,event=branch
38+
type=ref,event=tag
39+
type=sha,format=short
40+
type=raw,value=latest,enable={{is_default_branch}}
41+
42+
- name: Build and push Docker image
43+
uses: docker/build-push-action@v4
44+
with:
45+
context: .
46+
push: true
47+
tags: ${{ steps.meta.outputs.tags }}
48+
labels: ${{ steps.meta.outputs.labels }}
49+
cache-from: type=gha
50+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
FROM ruby:3.3.3-slim
2+
3+
# Add PostgreSQL repository for version 16
4+
RUN apt-get update && apt-get install -y gnupg2 lsb-release wget \
5+
&& wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
6+
&& echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
7+
# Install PostgreSQL 16 client and other dependencies
8+
&& apt-get update && apt-get install -y \
9+
build-essential \
10+
curl \
11+
git \
12+
libpq-dev \
13+
postgresql-client-16 \
14+
&& rm -rf /var/lib/apt/lists/*
15+
16+
WORKDIR /app
17+
18+
# Copy the Gemfile and Gemfile.lock
19+
COPY Gemfile Gemfile.lock ./
20+
21+
# Copy the rest of the application
22+
COPY . .
23+
24+
# Install gems
25+
RUN bundle install --jobs 4
26+
27+
# Set environment variables
28+
ENV LANG=C.UTF-8
29+
30+
# Command to run
31+
#CMD ["echo", "use -it to run the container"]
32+
CMD ["bundle", "exec", "ruby", "main.rb", "--app", "${APP}"]

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,3 @@ gem "pg", "~> 1.5"
99
gem "dotenv", "~> 3.1"
1010

1111
gem "anonymizer", git: "https://github.com/betagouv/rdv-service-public.git", branch: "production", glob: "lib/anonymizer/anonymizer.gemspec"
12-
# gem "anonymizer", path: "../rdv-service-public/lib/anonymizer"

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,26 @@ Le user Postgres de la base remplie par l’ETL utilisé par Metabase n’a pas
3737

3838
## Usage en staging et production
3939

40+
### Hébergeur Scalingo
41+
4042
```shell
4143
scalingo --region osc-secnum-fr1 --app rdv-service-public-etl-staging run --detached "bundle exec ruby main.rb --app rdvi"
4244
```
4345

4446
Des CRON jobs réguliers seront bientôt configurés pour lancer ça.
4547

48+
### Hébergeur Scaleway
49+
50+
TODO
51+
4652
## Usage en local
4753

4854
> [!WARNING]
4955
> Assurez vous de supprimer les fichiers de dumps, les bases de données restaurées, et les mots de passe des users Postgres utilisés en local après vos tests
5056
5157
### Variables d’environnement
5258

53-
Copiez les variables d’environnement dans un fichier `.env` :
59+
Copiez le fichier `.env.example` puis renseignez les variables d’environnement dans un fichier `.env` :
5460

5561
`cp .env.example .env`
5662

@@ -99,3 +105,21 @@ Dans un autre terminal, lancer l’ETL :
99105
```shell
100106
bundle exec ruby main.rb --app rdvs
101107
```
108+
109+
## Usage en local avec Docker
110+
111+
Commencer par renseigner les variables d'environnement nécessaires dans le fichier `.env`, par exemple :
112+
113+
```dotenv
114+
ETL_DB_URL=postgresql://esd:[password]@192.168.120.44:5432/aei-etl
115+
METABASE_USERNAME=esd
116+
ORIGIN_DB_URL=postgresql://esd:[password]@192.168.120.44:5432/api-aei
117+
CONFIG_PATH=https://gitlab.com/incubateur-territoires/startups/agents-intervention/agents-en-intervention/-/raw/feat/metabase/config.etl.yml
118+
```
119+
120+
Puis :
121+
122+
```shell
123+
docker build -t etl .
124+
docker run -it docker run --env-file .env -it etl bundle exec ruby main.rb --app [app]
125+
```

docs/DEPLOYMENT.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Déploiement de RDV Service Public ETL
2+
3+
Ce document décrit le processus de déploiement de l'application RDV Service Public ETL via une image Docker publiée dans GitHub Container Registry (GHCR).
4+
5+
L'exemple pris pour cette documentation est un déploiement avec Terraform sur Scaleway
6+
7+
## Architecture de déploiement
8+
9+
L'architecture de déploiement est composée de deux projets distincts :
10+
11+
1. **Projet d'application ETL** (ce projet)
12+
- Contient le code Ruby de l'ETL
13+
- Inclut le Dockerfile pour la construction de l'image
14+
- Configure le pipeline CI/CD pour construire et publier l'image Docker dans GHCR
15+
16+
2. **Projet de déploiement** (dépôt séparé)
17+
- Contient la configuration de déploiement, par exemple Terraform
18+
- Référence l'image Docker publiée dans GHCR
19+
- Gère le déploiement sur l'hébergeur de votre choix
20+
21+
## Configuration CI/CD pour la publication de l'image
22+
23+
Ce projet inclut un workflow GitHub Actions (`.github/workflows/build-and-push.yml`) qui construit et publie automatiquement l'image Docker dans GitHub Container Registry à chaque push sur la branche principale ou à chaque tag.
24+
25+
### Avantages de GitHub Container Registry
26+
27+
- Intégration native avec GitHub
28+
- Authentification simplifiée avec les jetons GitHub
29+
- Bande passante et stockage inclus avec votre compte GitHub
30+
- Gestion des permissions basée sur les rôles GitHub
31+
32+
### Secrets nécessaires
33+
34+
Aucun secret supplémentaire n'est nécessaire pour GHCR, car le workflow utilise déjà `GITHUB_TOKEN` qui est automatiquement fourni par GitHub Actions.
35+
36+
### Tags des images
37+
38+
Les images sont taguées automatiquement selon ces règles :
39+
- Pour chaque branche : `{nom-de-branche}`
40+
- Pour chaque tag : `{tag}`
41+
- Pour chaque commit : `{sha-court}`
42+
- Pour la branche par défaut : `latest`
43+
44+
## Publication manuelle de l'image
45+
46+
Si vous souhaitez publier manuellement l'image, vous pouvez utiliser les commandes suivantes :
47+
48+
```bash
49+
# Authentification à GitHub Container Registry
50+
echo $GITHUB_TOKEN | docker login ghcr.io -u $GITHUB_USERNAME --password-stdin
51+
52+
# Construction de l'image
53+
docker build -t ghcr.io/votre-organisation/rdv-service-public-etl:latest .
54+
55+
# Publication de l'image
56+
docker push ghcr.io/votre-organisation/rdv-service-public-etl:latest
57+
```
58+
59+
## Utilisation dans le projet de déploiement
60+
61+
Pour utiliser cette image dans votre projet de déploiement, référencez-la dans votre configuration Terraform :
62+
63+
```hcl
64+
resource "scaleway_container" "etl_container" {
65+
name = var.app_name
66+
namespace_id = scaleway_container_namespace.main.id
67+
registry_image = "ghcr.io/votre-organisation/rdv-service-public-etl:latest"
68+
# Autres configurations...
69+
}
70+
```
71+
72+
Vous pouvez également paramétrer le tag de l'image via une variable Terraform :
73+
74+
```hcl
75+
variable "image_tag" {
76+
type = string
77+
default = "latest"
78+
}
79+
80+
resource "scaleway_container" "etl_container" {
81+
name = var.app_name
82+
namespace_id = scaleway_container_namespace.main.id
83+
registry_image = "ghcr.io/votre-organisation/rdv-service-public-etl:${var.image_tag}"
84+
# Autres configurations...
85+
}
86+
```
87+
88+
### Configuration d'authentification pour GHCR dans Scaleway
89+
90+
Pour permettre à Scaleway d'extraire des images depuis GHCR, vous devrez configurer les informations d'authentification. Dans Terraform, vous pouvez le faire comme suit :
91+
92+
```hcl
93+
resource "scaleway_container" "etl_container" {
94+
name = var.app_name
95+
namespace_id = scaleway_container_namespace.main.id
96+
registry_image = "ghcr.io/votre-organisation/rdv-service-public-etl:${var.image_tag}"
97+
98+
# Configuration d'authentification pour GHCR
99+
registry_credentials {
100+
username = var.ghcr_username # Généralement votre nom d'utilisateur GitHub
101+
password = var.ghcr_token # Un Personal Access Token avec les droits read:packages
102+
}
103+
104+
# Autres configurations...
105+
}
106+
```
107+
108+
Dans ce cas, vous devrez ajouter ces variables à votre fichier `variables.tf` :
109+
110+
```hcl
111+
variable "ghcr_username" {
112+
description = "Nom d'utilisateur GitHub pour l'authentification à GHCR"
113+
type = string
114+
sensitive = false
115+
}
116+
117+
variable "ghcr_token" {
118+
description = "Personal Access Token GitHub pour l'authentification à GHCR"
119+
type = string
120+
sensitive = true
121+
}

lib/etl.rb

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@
88
class Etl
99
include Utils
1010

11-
VALID_APPS = %w[rdvi rdvs rdvsp].freeze
11+
attr_reader :app, :etl_db_url, :origin_db_url, :config_path, :metabase_username
1212

13-
attr_reader :app, :etl_db_url, :rdv_db_url, :config_path, :metabase_username
14-
15-
def initialize(app:, etl_db_url:, rdv_db_url:, config_path:, metabase_username:)
13+
def initialize(app:, etl_db_url:, origin_db_url:, config_path:, metabase_username:)
1614
@app = app
17-
raise 'invalid app' if VALID_APPS.exclude?(app)
1815

1916
@etl_db_url = etl_db_url
20-
@rdv_db_url = rdv_db_url
17+
@origin_db_url = origin_db_url
2118
@config_path = config_path
2219
@metabase_username = metabase_username
2320
end
@@ -30,8 +27,8 @@ def run
3027
@config = Anonymizer::Config.new(YAML.safe_load(File.read(config_path)))
3128

3229
# make sure RDV db connection works
33-
log_around "connect to RDV database #{rdv_db_url}" do
34-
ActiveRecord::Base.establish_connection rdv_db_url
30+
log_around "connect to origin database #{origin_db_url}" do
31+
ActiveRecord::Base.establish_connection origin_db_url
3532
ActiveRecord::Base.connection # triggers connection
3633
end
3734

@@ -54,7 +51,7 @@ def run
5451
pg_dump --clean --no-privileges --format tar \
5552
#{@config.truncated_table_names.map { "--exclude-table #{_1}" }.join(' ')} \
5653
-f #{dump_filename} \
57-
#{rdv_db_url}
54+
#{origin_db_url}
5855
SH
5956
)
6057
end
@@ -84,8 +81,13 @@ def run
8481
# workaround for a problematic column that we could also exclude
8582
# ERROR: cannot insert a non-DEFAULT value into column "text_search_terms" (PG::GeneratedAlways)
8683
# DÉTAIL : Column "text_search_terms" and "text_search_terms_with_notification_email" are generated columns.
87-
run_sql_command %(ALTER TABLE users DROP COLUMN IF EXISTS text_search_terms CASCADE)
88-
run_sql_command %(ALTER TABLE users DROP COLUMN IF EXISTS text_search_terms_with_notification_email CASCADE)
84+
# Vérifier que la table users existe avant de faire l'alter table
85+
if ActiveRecord::Base.connection.table_exists?("users")
86+
run_sql_command %(ALTER TABLE users DROP COLUMN IF EXISTS text_search_terms CASCADE)
87+
run_sql_command %(ALTER TABLE users DROP COLUMN IF EXISTS text_search_terms_with_notification_email CASCADE)
88+
else
89+
logger.info "La table users n'existe pas, aucune modification n'est appliquée."
90+
end
8991

9092
# STEP : move from public to target schema
9193
target_schema = app

main.rb

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,44 @@
1111

1212
app = nil
1313
OptionParser.new do |opts|
14-
opts.on('-a', '--app APP', Etl::VALID_APPS) { app = _1 }
14+
opts.on('-a', '--app APP') { app = _1 }
1515
end.parse!
1616

17-
config_path = if ENV["CONFIG_PATH"] && File.exist?(ENV["CONFIG_PATH"])
18-
ENV["CONFIG_PATH"]
19-
else
20-
config_url = {
21-
"rdvi" => "https://raw.githubusercontent.com/betagouv/rdv-insertion/main/config/anonymizer.yml",
22-
"rdvs" => "https://raw.githubusercontent.com/betagouv/rdv-service-public/production/config/anonymizer.yml",
23-
"rdvsp" => "https://raw.githubusercontent.com/betagouv/rdv-service-public/production/config/anonymizer.yml"
24-
}[app]
25-
run_command "curl -o config.yml \"#{config_url}\""
26-
"config.yml"
17+
unless ENV["CONFIG_PATH"]
18+
raise "La variable d'environnement CONFIG_PATH n'est pas définie"
2719
end
2820

29-
rdv_db_url_env_var = {
30-
"rdvi" => "RDV_INSERTION_DB_URL",
31-
"rdvs" => "RDV_SOLIDARITES_DB_URL",
32-
"rdvsp" => "RDV_SERVICE_PUBLIC_DB_URL"
33-
}[app]
21+
# unless File.exist?(ENV["CONFIG_PATH"])
22+
# raise "La variable d'environnement CONFIG_PATH pointe vers un fichier inexistant"
23+
# end
3424

25+
config_path = ENV["CONFIG_PATH"]
26+
27+
# Si le nom du fichier commence par https://, alors il s'agit d'une URL
28+
if config_path.starts_with?("https://")
29+
# Télécharger le fichier
30+
run_command "curl -o config.yml \"#{config_path}\""
31+
config_path = "config.yml"
32+
end
33+
34+
unless File.exist?(config_path)
35+
raise "La variable d'environnement CONFIG_PATH pointe vers un fichier inexistant"
36+
end
37+
38+
origin_db_url_env_var = "ORIGIN_DB_URL"
3539
etl_db_url_env_var = "ETL_DB_URL"
3640
metabase_username_env_var = "METABASE_USERNAME"
3741

3842
[
39-
rdv_db_url_env_var,
43+
origin_db_url_env_var,
4044
etl_db_url_env_var,
4145
metabase_username_env_var
4246
].each do |env_var|
4347
raise "Missing environment variable #{env_var}" if ENV[env_var].blank?
4448
end
4549

46-
rdv_db_url = ENV[rdv_db_url_env_var]
50+
origin_db_url = ENV[origin_db_url_env_var]
4751
etl_db_url = ENV[etl_db_url_env_var]
4852
metabase_username = ENV[metabase_username_env_var]
4953

50-
Etl.new(app:, etl_db_url:, rdv_db_url:, config_path:, metabase_username:).run
54+
Etl.new(app:, etl_db_url:, origin_db_url:, config_path:, metabase_username:).run

0 commit comments

Comments
 (0)