etalab / catalogage-donnees

Outil de catalogage de données développé par Etalab (service en production sur catalogue.data.gouv.fr)
https://catalogue.data.gouv.fr
GNU Affero General Public License v3.0
14 stars 3 forks source link

Exploration connecteurs : Catalogue <-> Amundsen #6

Closed florimondmanca closed 2 years ago

florimondmanca commented 2 years ago

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

  1. Démarrer un Amundsen en local avec Docker : https://www.amundsen.io/amundsen/installation/
  2. 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/
  3. Rapporter les résultats et enseignements ici

Résultats

Démarche

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

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" :

curl "http://localhost:5002/popular_resources/?limit=10&types=table"
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 :

http://localhost:5000/table_detail/catalogage/postgres/public/dataset
#                                  ---(2)---- --(1)--- -(3)-- --(4)--

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 :

curl http://localhost:5002/table/postgres%3A%2F%2Fcatalogage.public%2Fdataset

(Cf annexe ci-dessous pour la réponse complète)

Les champs a priori d'intérêt sont :

name         # string (Nom de la table)
description  # string | null (Description libre dans Amundsen)
tags         # [ { tag_type, tag_name }, ... ] (Tags techniques : #high-quality, #todo-clean, #pii, etc)
columns      # [ { name, col_type, sort_order, ... }, ... ] (Liste de colonnes dans la table)
owners       # [ { email, ... }, ... ]  (Contact de "responsables", entrés librement dans Amundsen)

Des indications sur la source technique peuvent être reconstruites grâce aux champs suivantss : database, cluster, schema et name. Par ex :

database  # "postgres"
cluster   # "catalogage"
schema    # "public"
name      # "dataset"

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ée catalogage) telle qu'importée en brut :

curl http://localhost:5002/table/postgres%3A%2F%2Fcatalogage.public%2Fdataset
{
    "is_view": false,
    "key": null,
    "database": "postgres",
    "tags": [],
    "last_updated_timestamp": null,
    "programmatic_descriptions": [],
    "common_filters": [],
    "cluster": "catalogage",
    "name": "dataset",
    "table_readers": [],
    "table_writer": null,
    "badges": [],
    "columns": [
        {
            "sort_order": 1,
            "badges": [],
            "stats": [],
            "key": null,
            "name": "id",
            "description": null,
            "col_type": "int4"
        },
        {
            "sort_order": 2,
            "badges": [],
            "stats": [],
            "key": null,
            "name": "name",
            "description": null,
            "col_type": "varchar"
        }
    ],
    "schema": "public",
    "watermarks": [],
    "description": null,
    "common_joins": [],
    "resource_reports": null,
    "owners": [],
    "source": null
}

Table d'exemple Hive :

curl http://localhost:5002/table/hive%3A%2F%2Fgold.test_schema%2Ftest_table1
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/" } } ```
florimondmanca commented 2 years ago

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.