Amundsen est un outil de catalogage / aggrégation de sources de données. Il a été identifié comme "data catalog" dans le benchmark de l'investigation. Certaines administrations l'emploient déjà pour leurs propres besoins de catalogage. L'idée serait de leur permettre de continuer à gérer le catalogage avec l'outil qu'ils connaissent, tout en synchronisant cela sur le Catalogue partagé.
Objet
Explorer la partie "connecteurs" du projet Catalogage en se servant d'Amundsen comme d'un exemple.
Déterminer comment structurer une telle intégration techniquement
Préciser fonctionnellement comment une telle "intégration" pourrait s'intégrer avec la logique de contribution : synchronisation lecture / écriture ? Importation "one shot" ? Etc.
Explorer l'API Amundsen pour voir comment on pourrait s'y intégrer : la doc de l'API est dispo sur une instance, à l'adresse http://localhost:5002/apidocs/
Rapporter les résultats et enseignements ici
Résultats
Démarche
Suivre le "quickstart" : https://www.amundsen.io/amundsen/installation/ pour démarrer Amundsen avec Docker, et ajouter des données fictives avec le sample_data_loader. Le script suivant devrait exécuter l'ensemble des étapes en une fois :
export AMUNDSEN_DIR=debug/amundsen # A modifier au besoin
git clone --recursive https://github.com/amundsen-io/amundsen.git $AMUNDSEN_DIR
cd $AMUNDSEN_DIR
cd databuilder
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt
venv/bin/pip install psycopg2-binary
venv/bin/python setup.py install
venv/bin/python example/scripts/sample_data_loader.py
Vérifier la bonne installation en visitant http://localhost:5000 et en cherchant test
Ajouter la table dataset actuellement sur le repo :
Modifier la fonction connection_string() du script amundsen/example/scripts/sample_postgres_loader.py pour pointer vers la DB PostgreSQL de dev (catalogage)
"Table" : un ensemble de données tabulaires, pouvant prendre des formes diverses (ex : une table SQL ou une vue SQL si la source de données sous-jacente est MySQL, Hive, etc). Dans l'UI frontend (mais pas dans les URL), les "tables" sont aussi appelées "datasets".
"Resource" : une "table" ou un "dashboard"
"Feature" : a priori un concept technique lié à l'architecture d'Amundsen, sans lien avec la donnée
Suggestion d'une liste de jeux de données populaires
Il n'y a pas d'endpoint d'API "obtenir la liste de toutes les tables".
En revanche, il y a un endpoint d'API "tables populaires", calculées selon un "algorithme type Page Rank" :
Exemple de réponse JSON
```json
{
"Table": [
{
"schema_description": null,
"schema": "test_schema",
"cluster": "gold",
"name": "test_table1",
"description": "1st test table",
"database": "hive"
}
],
"Dashboard": {}
}
```
Une intégration Amundsen pourrait donc, à partir de cet endpoint, proposer un ensemble initial jeux de données à ajouter automatiquement au catalogue, càd les plus populaires (moyennant quelques autres paramètres techniques, comme la netloc de type http://host:port).
Ingestion d'un jeu de donnée bien défini
Il serait possible d'ajouter un jeu de données en rentrant les paramètres Amundsen de la table, par exemple son URI qui ressemble à ceci :
# --(1)--- ---(2)---- -(3)-- --(4)--
postgres://catalogage.public/dataset
# En langage simple : table 'dataset' dans le schéma 'public' de la BDD PostgreSQL 'catalogage'
# Dans la terminologie Amundsen : (1) = database, (2) = cluster, (3) = schema, (3) = name
L'URI est une donnée technique qui pourrait être obtenue automatiquement à partir de l'URL frontend d'une table, une autre donnée technique plus facile à trouver par les utilisateurs :
L'ingestion initiale de tables pourra se faire sans trop de problèmes a priori, en utilisant les tables populaires ou une table particulière.
Quid en revanche des modifications (description, nom, gérants, etc) apportées côté catalogue ? L'API Amundsen ne permet pas de les modifier en retour. Si on ajoute une "source Amundsen", doit-on avoir un état "pristine/dirty" sur les métadonnées : lorsqu'une métadonnée est "pristine", alors elle est synchronisée depuis Amundsen, lorsqu'elle est "dirty" (a déjà été modifiée dans le Catalogue), elle n'est pas synchronisée (mais on pourrait montrer quelle est la "valeur originale" dans Amundsen ?)
Annexes
Métadonnées d'une "table"
Table dataset de la PostgreSQL de dev (appelée catalogage) telle qu'importée en brut :
Je vais clore ceci pour l'instant, pas grand chose d'autre à rajouter à cette "exploration" que ce que j'ai rapporté ici. On pourra s'y référer pour de futures intégrations.
Contexte
Amundsen est un outil de catalogage / aggrégation de sources de données. Il a été identifié comme "data catalog" dans le benchmark de l'investigation. Certaines administrations l'emploient déjà pour leurs propres besoins de catalogage. L'idée serait de leur permettre de continuer à gérer le catalogage avec l'outil qu'ils connaissent, tout en synchronisant cela sur le Catalogue partagé.
Objet
Plan
Résultats
Démarche
sample_data_loader
. Le script suivant devrait exécuter l'ensemble des étapes en une fois :test
Ajouter la table
dataset
actuellement sur le repo :connection_string()
du scriptamundsen/example/scripts/sample_postgres_loader.py
pour pointer vers la DB PostgreSQL de dev (catalogage
)databuilder/
, lancer:venv/bin/python example/scripts/sample_postgres_loader.py
alembic_version
).Enseignements
Définitions Amundsen :
Suggestion d'une liste de jeux de données populaires
Il n'y a pas d'endpoint d'API "obtenir la liste de toutes les tables".
En revanche, il y a un endpoint d'API "tables populaires", calculées selon un "algorithme type Page Rank" :
Exemple de réponse JSON
```json { "Table": [ { "schema_description": null, "schema": "test_schema", "cluster": "gold", "name": "test_table1", "description": "1st test table", "database": "hive" } ], "Dashboard": {} } ```Une intégration Amundsen pourrait donc, à partir de cet endpoint, proposer un ensemble initial jeux de données à ajouter automatiquement au catalogue, càd les plus populaires (moyennant quelques autres paramètres techniques, comme la netloc de type http://host:port).
Ingestion d'un jeu de donnée bien défini
Il serait possible d'ajouter un jeu de données en rentrant les paramètres Amundsen de la table, par exemple son URI qui ressemble à ceci :
L'URI est une donnée technique qui pourrait être obtenue automatiquement à partir de l'URL frontend d'une table, une autre donnée technique plus facile à trouver par les utilisateurs :
Voir ci-dessous pour les métadonnées d'une table que l'on pourrait "ingérer" dans le catalogue.
Détail d'une table
Il est possible de récupérer les métadonnées d'une table avec un endpoint d'API prenant en paramètre son URI (en percent-encoded), par exemple :
(Cf annexe ci-dessous pour la réponse complète)
Les champs a priori d'intérêt sont :
Des indications sur la source technique peuvent être reconstruites grâce aux champs suivantss :
database
,cluster
,schema
etname
. Par ex :Questions ouvertes
Par-delà l'ingestion initiale
L'ingestion initiale de tables pourra se faire sans trop de problèmes a priori, en utilisant les tables populaires ou une table particulière.
Quid en revanche des modifications (description, nom, gérants, etc) apportées côté catalogue ? L'API Amundsen ne permet pas de les modifier en retour. Si on ajoute une "source Amundsen", doit-on avoir un état "pristine/dirty" sur les métadonnées : lorsqu'une métadonnée est "pristine", alors elle est synchronisée depuis Amundsen, lorsqu'elle est "dirty" (a déjà été modifiée dans le Catalogue), elle n'est pas synchronisée (mais on pourrait montrer quelle est la "valeur originale" dans Amundsen ?)
Annexes
Métadonnées d'une "table"
Table
dataset
de la PostgreSQL de dev (appeléecatalogage
) telle qu'importée en brut :Table d'exemple Hive :
Cliquer pour voir la réponse JSON complète
```json { "is_view": false, "key": null, "database": "hive", "tags": [ { "tag_type": "default", "tag_name": "tag1" }, { "tag_type": "default", "tag_name": "tag2" }, { "tag_type": "default", "tag_name": "low_quality" }, { "tag_type": "default", "tag_name": "expensive" } ], "last_updated_timestamp": 1570230473, "programmatic_descriptions": [ { "source": "quality_service", "text": "### Quality Report:\n--- \nIpsus enom. Ipsus enom ipsus lorenum.\n---\n[![Build Status](https://api.travis-ci.com/amundsen-io/amundsendatabuilder.svg?branch=master)](https://travis-ci.com/amundsen-io/amundsendatabuilder)" }, { "source": "s3_crawler", "text": "**Size**: 50T\n\n**Monthly Cost**: $5000" } ], "common_filters": [], "cluster": "gold", "name": "test_table1", "table_readers": [ { "user": { "display_name": null, "github_username": null, "role_name": null, "first_name": null, "manager_id": null, "email": "roald.amundsen@example.org", "manager_fullname": null, "is_active": true, "employee_type": null, "team_name": null, "user_id": "roald.amundsen@example.org", "manager_email": null, "full_name": null, "slack_id": null, "profile_url": null, "other_key_values": {}, "last_name": null }, "read_count": 500 }, { "user": { "display_name": null, "github_username": null, "role_name": null, "first_name": null, "manager_id": null, "email": "coald2@example.org", "manager_fullname": null, "is_active": true, "employee_type": null, "team_name": null, "user_id": "coald2@example.org", "manager_email": null, "full_name": null, "slack_id": null, "profile_url": null, "other_key_values": {}, "last_name": null }, "read_count": 100 }, { "user": { "display_name": null, "github_username": null, "role_name": null, "first_name": null, "manager_id": null, "email": "foald5@example.org", "manager_fullname": null, "is_active": true, "employee_type": null, "team_name": null, "user_id": "foald5@example.org", "manager_email": null, "full_name": null, "slack_id": null, "profile_url": null, "other_key_values": {}, "last_name": null }, "read_count": 100 }, { "user": { "display_name": null, "github_username": null, "role_name": null, "first_name": null, "manager_id": null, "email": "aoald0@example.org", "manager_fullname": null, "is_active": true, "employee_type": null, "team_name": null, "user_id": "aoald0@example.org", "manager_email": null, "full_name": null, "slack_id": null, "profile_url": null, "other_key_values": {}, "last_name": null }, "read_count": 100 }, { "user": { "display_name": null, "github_username": null, "role_name": null, "first_name": null, "manager_id": null, "email": "goald6@example.org", "manager_fullname": null, "is_active": true, "employee_type": null, "team_name": null, "user_id": "goald6@example.org", "manager_email": null, "full_name": null, "slack_id": null, "profile_url": null, "other_key_values": {}, "last_name": null }, "read_count": 100 } ], "table_writer": { "id": "event_test/hive.test_schema.test_table1", "application_url": "https://airflow_host.net/admin/airflow/tree?dag_id=event_test", "name": "Airflow", "kind": null, "description": "Airflow with id event_test/hive.test_schema.test_table1" }, "badges": [ { "badge_name": "beta", "category": "table_status" } ], "columns": [ { "sort_order": 1, "badges": [ { "badge_name": "pk", "category": "column" } ], "stats": [ { "stat_type": "num nulls", "end_epoch": 1562300762, "stat_val": "\"500320\"", "start_epoch": 1432300762 }, { "stat_type": "verified", "end_epoch": 1562300762, "stat_val": "\"230430\"", "start_epoch": 1432300762 }, { "stat_type": "min", "end_epoch": 1562300762, "stat_val": "\"aardvark\"", "start_epoch": 1432300762 }, { "stat_type": "distinct values", "end_epoch": 1562300762, "stat_val": "8", "start_epoch": 1432300762 }, { "stat_type": "max", "end_epoch": 1562300762, "stat_val": "\"zebra\"", "start_epoch": 1432300762 } ], "key": null, "name": "col1", "description": "col1 description", "col_type": "string" }, { "sort_order": 2, "badges": [ { "badge_name": "pii", "category": "column" } ], "stats": [], "key": null, "name": "col2", "description": "col2 description", "col_type": "string" }, { "sort_order": 3, "badges": [ { "badge_name": "fk", "category": "column" }, { "badge_name": "pii", "category": "column" } ], "stats": [], "key": null, "name": "col3", "description": "col3 description", "col_type": "string" }, { "sort_order": 4, "badges": [], "stats": [], "key": null, "name": "col4", "description": "col4 description", "col_type": "string" }, { "sort_order": 5, "badges": [], "stats": [ { "stat_type": "average", "end_epoch": 1572300762, "stat_val": "\"5.0\"", "start_epoch": 1532300762 }, { "stat_type": "min", "end_epoch": 1572300762, "stat_val": "\"-500.0\"", "start_epoch": 1534300762 }, { "stat_type": "max", "end_epoch": 1572300762, "stat_val": "\"500.0\"", "start_epoch": 1534300762 } ], "key": null, "name": "col5", "description": "col5 description", "col_type": "float" } ], "schema": "test_schema", "watermarks": [ { "watermark_type": "low_watermark", "partition_key": "col3", "create_time": "2019-10-01T12:13:14", "partition_value": "2017-04-22/col4=0" }, { "watermark_type": "high_watermark", "partition_key": "col3", "create_time": "2019-10-01T12:13:14", "partition_value": "2019-09-30/col4=11" } ], "description": "1st test table", "common_joins": [], "resource_reports": null, "owners": [ { "display_name": null, "github_username": null, "role_name": null, "first_name": null, "manager_id": null, "email": "roald.amundsen@example.org", "manager_fullname": null, "is_active": true, "employee_type": null, "team_name": null, "user_id": "roald.amundsen@example.org", "manager_email": null, "full_name": null, "slack_id": null, "profile_url": null, "other_key_values": {}, "last_name": null }, { "display_name": null, "github_username": null, "role_name": null, "first_name": null, "manager_id": null, "email": "chrisc@example.org", "manager_fullname": null, "is_active": true, "employee_type": null, "team_name": null, "user_id": "chrisc@example.org", "manager_email": null, "full_name": null, "slack_id": null, "profile_url": null, "other_key_values": {}, "last_name": null } ], "source": { "source_type": "github", "source": "https://github.com/amundsen-io/amundsen/" } } ```