Fév 24 2022

Gérez les Entrées DNS avec Terraform

Publié par sous DevOps,Terraform

Terraform aide à construction des infrastructures as code. De plus en plus d’hébergeurs Internet offrent désormais des plugins Terraform permettant de gérer les zones DNS. Gandi est l’un d’entre eux, avec OVH et bien d’autres. Testons la version 2 qui vient juste de sortir, et voyons comment créer les enregistrements DNS sur Gandi depuis un dépôt Git.

DNS avec Terraform


Le Provider DNS Terraform Gandi

Le provider Terraform de Gandi se déclare dans un fichier, habituellement provider.tf. Vous pouvez y ajouter l’API key Gandi nécessaire pour s’authentifier et enregistrer des ressources. Elle peut être générée depuis leur site web dans « Paramètres utilisateur », « Gestion du compte utilisateur et paramètres de sécurité » sous l’onglet Sécurité.
Pour des raisons évidentes de sécurité, il est recommandé de créer la clé d’API key dans la variable d’environnement GANDI_KEY. Je laisse comme ceci dans provider.tf dans le cadre du test.

terraform {
  required_providers {
    gandi = {
      version = "~> 2.0"
      source   = "go-gandi/gandi"
    }
  }
}

provider "gandi" {
  key = "XXXXXXXXXXXXXXXXXXX"
}


Fichier des Enregistrements DNS

Gandi fait des sauvegardes automatiques des entrées DNS dans un format de fichier simple, dans lequel j’ai puisé mon inspiration pour le fichier d’enregistrements DNS. Appelons-le domaine.txt où « domaine » votre nom de domaine. Voici un exemple avec 3 entrées DNS.

www1 300 IN A 127.0.0.1
www2 300 IN A 127.0.0.1
www  300 IN CNAME www1


Nous devons déclarer le domaine en tant que variable que nous utiliserons par la suite, dans variable.tf.

variable domain {
  default = "mydomain.com"
}


Code Terraform des Entrées DNS

Voici un fichier Terraform minimaliste main.tf qui lit le fichier contenant les entrées DNS, et crée un nouvel enregistrement à partir de chaque ligne.

data "local_file" "dns_file" {
  filename = "./${var.domain}.txt"
}

locals {
  # Convertit le fichier en tableau
  dns_records = toset([ for e in split("\n", data.local_file.dns_file.content) :
                        replace(e, "/\\s+/", " ") if e != "" ])
}
  
resource "gandi_livedns_record" "record" {
  for_each =  local.dns_records
  zone     =  var.domain
  name     =  split(" ",each.value)[0]
  type     =  split(" ",each.value)[3]
  ttl      =  split(" ",each.value)[1]
  values   = [split(" ",each.value)[4]]
}


Vous pouvez apprécier la puissance de Terraform qui peut faire beaucoup de choses en quelques lignes de code.

Notez que je supprime les espaces multiples – replace(e, « /\\s+/ »,  » « ) – en séparant les lignes car la fonction split crée un élément vide si elle traite deux espaces. J’ignore aussi les lignes vides. Ensuite nous bouclons sur chaque ligne pour créer des enregistrements DNS avec une ressource de type gandi_livedns_record.
Ca fonctionne avec les entrées A et les CNAME.

Nous pourrions appliquer des traitements différents pour chaque type d’enregistrement en créant des tableaux distincts, ou pour ignorer les autres type de DNS:

dns_records   = [ for e in split("\n", data.local_file.dns_file.content) :
                  replace(e, "/\\s+/", " ") if e != "" ]
A_records     = toset([for e in local.dns_records : e if split(" ",e)[3] == "A"])
CNAME_records = toset([for e in local.dns_records : e if split(" ",e)[3] == "CNAME"])


Nous pourrions ensuite boucler sur A_records pour créer les entrées A, et traiter les CNAME dans un deuxième block de ressources.
D’autres possibilités: gérer les CNAME à valeurs multiples, les enregistrements TXT, etc …


Plan et Application de la Création des Entrées DNS

Exécutez terraform plan pour vérifier ce que terraform compte changer. Il devrait afficher que 3 resources seront créées. lancez terraform apply pour créer les entrées DNS.

$ terraform plan

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # gandi_livedns_record.record["www 300 IN CNAME www1"] will be created
  + resource "gandi_livedns_record" "record" {
      + href   = (known after apply)
      + id     = (known after apply)
      + name   = "www"
      + ttl    = 300
      + type   = "CNAME"
      + values = [
          + "www1",
        ]
      + zone   = "domain.com"
    }

  [...]

Plan: 3 to add, 0 to change, 0 to destroy.


Côté performances, j’ai testé la création de 200 entrées, et cela a pris 2’57 » depuis une assez mauvaise connexion. Oui, c’est un peu lent comme ca repose sur l’appel d’une API REST pour chaque entrée. La création d’une entrée supplémentaire sera très rapide par contre.
Un terraform plan a pris 1’31 » pour rafraichir ces 200 entrées.

Bien sûr, vous pourrez toujours éviter l’étape de refresh avec terraform plan -refresh=false qui prend moins d’une seconde.
Le plan peut être sauvegardé dans un fichier temporaire, qui peut être chargé lorsqu’on applique les changements.

$ terraform plan -refresh=false -out=plan.file
$ terraform apply plan.file
 

Aucune Réponse

Jan 17 2022

Ressource Partagée Terraform par l’Exemple: les Clés SSH

Publié par sous DevOps,Terraform

Terraform permet d’automatiser la création d’infrastructures dans le cloud, ce qu’on appelle communément l’infrastructure as code. Il nous faut créer une machine virtuelle, laquelle doit contenir les clés SSH de 3 administrateurs. Le but sera de faire en sorte que cette ressource partagée Terraform soit réutilisable par d’autres modules. Cet exemple sur le Cloud IBM s’appuie sur le plugin IBM pour Terraform mais la méthode reste valide pour les autres cloud providers évidemment.
Je n’ai pas mis la création du VPC, des subnets et security groups pour gagner en lisibilité.

Ressource Partagée Terraform


Ressources dans un Module Unique

Commençons par créer les fichiers ssh.tf contenant le code créant les clés des administrateurs, et vm.tf le code de création du serveur dans un même répertoire. Les clés sont données en paramètre à la VM.

resource "ibm_is_ssh_key" "user1_sshkey" {
  name       = "user1"
  public_key = "ssh-rsa AAAAB3[...]k+XR=="
}

resource "ibm_is_ssh_key" "user2_sshkey" {
  name       = "user2"
  public_key = "ssh-rsa AAAAB3[...]Zo9R=="
}

resource "ibm_is_ssh_key" "user3_sshkey" {
  name       = "user3"
  public_key = "ssh-rsa AAAAB3[...]67GqV="
}
resource "ibm_is_instance" "server1" {
  name    = "server1"
  image   = var.image
  profile = var.profile
  vpc  = ibm_is_vpc.vpc.id
  zone = var.zone1

  primary_network_interface {
    subnet          = ibm_is_subnet.subnet1.id
    security_groups = [ibm_is_vpc.vpc.default_security_group]
  }

  keys = [
    ibm_is_ssh_key.user1_sshkey.id,
    ibm_is_ssh_key.user2_sshkey.id,
    ibm_is_ssh_key.user3_sshkey.id
  ]
}


Le code est simple mais pose un problème majeur:
Les clés SSH ne sont pas réutilisables dans un autre module Terraform. Si on copie/colle ce code pour créer une 2me VM, une erreur indiquera que les clés existent déjà. L’ajout d’une clé nécessite de modifier les 2 fichiers Terraform.


Ressources Communes Terraform

Il faut donc créer les clés SSH dans un module Terraform indépendant et les rendre accessibles depuis les autres modules. On peut y parvenir en exportant les id des clés grâce aux valeurs outputs. Les outputs permettent de rendre des variables accessibles en ligne de commande ou à d’autres modules Terraform pour les réutiliser.
Déplaçons la déclaration des clés dans un nouveau répertoire Terraform auquel nous ajoutons une sortie output ssh_keys qui renvoie un tableau de leurs id, puisque c’est ce qu’attendent les VM en paramètre.

resource "ibm_is_ssh_key" "user1_sshkey" {
  name       = "user1"
  public_key = "ssh-rsa AAAAB3[...]k+XR=="
}

resource "ibm_is_ssh_key" "user2_sshkey" {
  name       = "user2"
  public_key = "ssh-rsa AAAAB3[...]Zo9R=="
}

resource "ibm_is_ssh_key" "user3_sshkey" {
  name       = "user3"
  public_key = "ssh-rsa AAAAB3[...]67GqV="
}

output "ssh_keys" {
  value = [
    ibm_is_ssh_key.user1_sshkey.id,
    ibm_is_ssh_key.user2_sshkey.id,
    ibm_is_ssh_key.user3_sshkey.id
  ]
}


Après avoir lancé terraform apply, on peut afficher les valeurs output avec terraform output:

$ terraform output
ssh_keys = [
  "r010-3e98b94b-9518-4e11-9ac4-a014120344dc",
  "r010-b271dce5-4744-48c3-9001-a620e99563d9",
  "r010-9358c6ab-0eed-4de7-a4a0-4ba20b2c04c9",
]


C’est exactement ce que nous voulions. Ne reste qu’à récupérer le contenu de l’output sous forme de data lookup pour l’exploiter dans le module VM.

data "terraform_remote_state" "ssh_keys" {
  backend = "local"
  config = {
    path = "../ssh_keys/terraform.tfstate"
  }
}

resource "ibm_is_instance" "server1" {
  name    = "server1"
  image   = var.image
  profile = var.profile

  primary_network_interface {
    subnet          = ibm_is_subnet.subnet1.id
    security_groups = [ibm_is_vpc.vpc.default_security_group]
  }

  vpc  = ibm_is_vpc.vpc.id
  zone = var.zone1
  keys = data.terraform_remote_state.ssh_keys.outputs.ssh_keys
}


C’est beaucoup mieux, on est capable de gérer les clés SSH indépendamment des autres modules Terraform et de les réutiliser comme bon nous semble. Le path du data lookup est le chemin relatif vers le répertoire contenant le fichier ssh.tf.


Variables sous Forme de Liste

C’est pas mal mais on pourrait rendre la création des ressources partagées (ici les clés SSH) plus élégante.
En effet, l’ajout d’une nouvelle clé se fait à 2 endroits: créer une ressource Terraform, et l’ajouter aux valeurs retournées dans l’output. Ce qui est contraignant et générateur d’erreurs.
De plus, ca reste assez difficile à lire et il serait plus clair de séparer valeurs et code.

Pour cela, nous allons stocker les clés dans une table de type map dans un fichier terraform.tfvars, qui sera chargé automatiquement.

ssh_keys = {
  "user1" = "ssh-rsa AAAAB3[...]k+XR=="
  "user2" = "ssh-rsa AAAAB3[...]Zo9R=="
  "user3" = "ssh-rsa AAAAB3[...]67GqV="
}


Dans ssh.tf, nous allons ensuite exécuter une boucle sur ce tableau de clé/valeur pour créer les ressources, et les exporter dans l’output.

# Définition du tableau
variable "ssh_keys" {
  type = map(string)
}

resource "ibm_is_ssh_key" "keys" {
  for_each = var.ssh_keys
  name = each.key
  public_key = each.value
}

output "ssh_keys" {
  value = values(ibm_is_ssh_key.keys)[*].id
}


La récupération des valeurs est un peu complexe. J’ai commencé par sortir en output values(ibm_is_ssh_key.keys) pour analyser la structure et ainsi récupérer les ids.

Au final, une nouvelle ressource partagée (une clé SSH dans notre cas) se fait en l’ajoutant simplement dans un tableau, dans un fichier ne contenant que des variables. A un seul endroit. N’importe qui peut s’en charger sans même lire ou comprendre le code.

 

Aucune Réponse

Oct 30 2021

Support des Versions SSL sur ma JVM

Publié par sous Java

Les versions SSL ou TLS supportées par une JVM dépendent de beaucoup de choses. Nous allons voir quelques facteurs dont ils dépendent, ainsi que différentes façons d’afficher les versions SSL qui sont disponibles et activées sur votre JVM.


Facteurs Affectant le Support SSL de la JVM

Les versions SSL supportées dépend en premier lieu de la version de votre JDK. TLS 1.0 et 1.1 sont désactivés sur de plus en plus de distributions JDK par défaut, alors que le TLS 1.2 est devenu plus standard. TLS 1.3 est supporté sur la JDK 11 et les suivantes, et aussi sur la JDK8 à partir du build 8u261.

Mais vous pouvez vous affranchir des paramètres par défaut et désactiver un algorithme TLS dans le fichier de sécurité Java, qui se nomme java.security. Les versions SSL / TLS peuvent être désactivés avec le paramètre jdk.tls.disabledAlgorithms.
Il n’y a en fait pas moyen d’activer explicitement une version TLS en Java: elle doit être supportée par distribution JDK et ne pas être dans la liste des algorithmes désactivés.

Vous pouvez aussi forcer l’utilisation d’une version TLS, et d’un cipher, dans les paramètres de la commande java. Faites un saut au dernier paragraphe.


Vérifier les Versions SSL / TLS par Programme

Les versions TLS supportées et activées peuvent être affichées grâce à un simple bout de code Java. La méthode getProtocols() de la classe SSLContext permet d’afficher les versions SSL disponibles sur votre JVM.

import java.util.*;
import javax.net.ssl.SSLContext;

public class tls
{

  public static void main (String[] args) throws Exception
  {
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, null, null);
    String[] supportedProtocols = context.getDefaultSSLParameters().getProtocols();
    System.out.println(Arrays.toString(supportedProtocols));
  }

}


Exécutez les commandes suivantes pour afficher la version de la JVM ainsi que les protocoles SSL supportés

java -version
echo "Supported TLS:"
javac tls.java
java tls


Les versions TLS sur certaines JVM de mes serveurs:

Default OpenJDK
openjdk version "1.8.0_302"
OpenJDK Runtime Environment (build 1.8.0_302-b08)
OpenJDK 64-Bit Server VM (build 25.302-b08, mixed mode)
Supported TLS:
[TLSv1.2]

IBM OpenJ9
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
Eclipse OpenJ9 VM (build openj9-0.18.1, JRE 1.8.0 Linux amd64-64-Bit
Compressed References 20200122_511 (JIT enabled, AOT enabled)
OpenJ9   - 51a5857d2
OMR      - 7a1b0239a
JCL      - 8cf8a30581 based on jdk8u242-b08)
Supported TLS:
[TLSv1, TLSv1.1, TLSv1.2]


Et les versions TLS sur un mac:

Support Versions SSL JVM

Vous avez maintenant une liste sure des versions TLS activées sur votre JVM.


Vérifier Versions TLS et Ciphers avec le Mode Debug

Si vous souhaitez aller plus loin et voir vraiment ce qui se passe, utilisez la fonctionnalité debug de Java. Vous obtiendrez des détails sur les protocoles SSL désactivés, les ciphers disponibles, et les négociations SSL et bien plus. C’est très verbeux, vous êtes prévenu! Mais tellement utile.
Lancez simplement votre java avec java -Djavax.net.debug=all. Un simple programme à une base Mariadb avec JDBC et SSL activé pourrait se lancer comme ceci:

java -Djavax.net.debug=all \
     -Djavax.net.ssl.trustStore=keystore.jks \
     -Djavax.net.ssl.trustStorePassword=changeit \
     -cp "mariadb-java-client-2.7.3.jar:." monProgramme


Ceci affichera beaucoup de d’informations sur le protocole SSL et les ciphers. Voici quelques lignes extraites:

System property jdk.tls.server.cipherSuites is set to 'null'
Ignoring disabled cipher suite: TLS_DH_anon_WITH_AES_256_CBC_SHA
[...]
update handshake state: client_hello[1]
upcoming handshake states: server_hello[2]
*** ClientHello, TLSv1.2
Cipher Suites: [TLS_DHE_RSA_WITH_AES_128_GCM_SHA256]
[...]
%% Initialized:  [Session-1, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256]
[...]
0000: 01 00 00 01 0A 49 00 00   02 03 64 65 66 12 69 6E  .....I....def.in
0010: 66 6F 72 6D 61 74 69 6F   6E 5F 73 63 68 65 6D 61  formation_schema
0020: 06 54 41 42 4C 45 53 06   54 41 42 4C 45 53 09 54  .TABLES.TABLES.T
0030: 41 42 4C 45 5F 43 41 54   0C 54 41 42 4C 45 5F 53  ABLE_CAT.TABLE_S
0040: 43 48 45 4D 41 0C 2D 00   00 01 00 00 FD 01 00 00  CHEMA.-.........
0050: 00 00 21 00 00 03 03 64   65 66 00 00 00 0B 54 41  ..!....def....TA
0060: 42 4C 45 5F 53 43 48 45   4D 00 0C 3F 00 00 00 00  BLE_SCHEM..?....
[...]

jdk.tls.server.cipherSuites est à ‘null’ car il n’a pas été surchargé.
Il y a en général une longue liste de ciphers désactivés puisqu’ils sont liés aux protocoles TLS désactivés pour la plupart d’entre eux
On voit ensuite le hello client qui montre la version TLS choisie pour le handshake.
Cipher Suites affiche normalement une longue liste de ciphers disponibles sur la JVM. Il y en a juste un ici car je l’ai forcé.
Un peu plus bas, on peut même voir les requêtes SQL envoyées au serveur Mariadb durant l’initialisation de la connexion.
Oracle fournit une bonne documentation sur comment debugguer SSL/TLS et interpréter ces messages.


Forcer Versions TLS et Ciphers

Il est possible de forcer la JVM à utiliser certaines versions TLS et ciphers sur la ligne de command. C’est très utile si vous n’avez pas accès à la configuration de la JVM, ou si vous désirez des paramètres spécifiques pour un programme Java en particulier.
Cela peut-être fait avec les paramètres jdk.tls.client.protocols et jdk.tls.client.cipherSuites.

java -Djavax.net.debug=all \
     -Djavax.net.ssl.trustStore=keystore.jks \
     -Djavax.net.ssl.trustStorePassword=changeit \
     -Djdk.tls.client.protocols=TLSv1.2 \
     -Djdk.tls.server.cipherSuites="TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" \
     -cp "mariadb-java-client-2.7.3.jar:." monProgramme


Les protocoles TLS et les ciphers peuvent aussi être définis dans la chaîne de connexion JDBC si vous utiliser l’encryption pour la connexion à la base de données.

String url = "jdbc:mariadb://my_database_server:3306/ma_database?"+
             "useSSL=true"+
             "&serverTimezone=UTC"+
             "&enabledSslProtocolSuites=TLSv1.2"+
             "&enabledSSLCipherSuites=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA";
 

Aucune Réponse

Oct 10 2021

Renouveler un Certificat LetsEncrypt Automatiquement sur Kubernetes

Publié par sous Kubernetes

Installer cert-manager

Cert-manager est un chart Helm apportant ses propres ressources que vous pouvez installer sur un cluster Kubernetes. Il aide à l’automatisation des certificats, leur renouvellement et leur gestion. C’est une véritable valeur ajoutée lorsque vous faîtes appel à des fournisseurs de certificat qui offrent des APIs permettant d’automatiser ces traitements. De plus, il vaut mieux renouveler les certificats LetsEncrypt automatiquement puisqu’ils ne sont valides que pour une période de 3 mois.

cert-manager est disponible sur le dépôt Helm Jetstack, ajoutez-le à votre liste de repo comme ceci:

helm repo add jetstack https://charts.jetstack.io
helm repo update


Cert-manager tourne dans son propre namespace, donc créez-le tout d’abord, et installez le chart Helm cert-manager

kubectl create namespace cert-manager
helm install cert-manager \
     --namespace cert-manager jetstack/cert-manager \
     --set installCRDs=true

–set installCRDs=true indique à cert-manager d’installer les custom ressources tels que les certificaterequests, les certificates ou encore les clusterissuers.


Cluster Issuer LetsEncrypt

Un cluster issuer va contenir les informations d’un fournisseur de certificat. Si vous souhaitez obtenir vos certificats SSL signés par LetsEncrypt, vous devrez appliquer ce fichier yaml au cluster Kubernetes:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: it@company.xxx
    privateKeySecretRef:
      name: letsencrypt-prod
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - http01:
        ingress:
          class: public-iks-k8s-nginx


LetsEncrypt appartient à la catégorie des issuers ACME, ce qui signifie qu’ils ont la confiance des navigateurs web. Il fournit un certificat après avoir vérifié que vous êtes le propriétaire du domaine. La vérification peut se faire de 2 façons: soit une entrée DNS TXT ou un challenge HTTP. Kubernetes dessert du HTTP donc la plupart choisiront le challenge HTTP01. Cela se définit dans la section solvers.

La seconde information importante est la classe. cert-manager va regarder les ingresses dont la classe correspond et leur fournira un certificat SSL. L’annotation classe des ingresses publics IBM Cloud s’appelle public-iks-k8s-nginx, vous devez donc la paramétrer dans la configuration du cluster issuer. Vérifiez votre ingress pour adapter à vos propres besoins.


Définition de l’Ingress

Maintenant que vous avez un cluster issuer et que cert-manager est installé, vous devrez leur spécifier à quel ingress ils doivent fournir des certificates. Cela se fait avec les annotations de l’ingress.
Spécifiez le cluster issuer dans l’annotation cert-manager.io/cluster-issuer tout simplement.
Comme vu précédemment, l’annotation kubernetes.io/ingress.class est fixée à public-iks-k8s-nginx sur IKS. Entrez ce qui convient dans votre setup.
Ajoutez acme.cert-manager.io/http01-edit-in-place selon que vous vouliez un ingress séparé pour le challenge HTTP ou qu’il fasse partie de l’ingress existant.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  labels:
    name: app-ingress
  annotations:
    acme.cert-manager.io/http01-edit-in-place: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/ingress.class: public-iks-k8s-nginx
spec:
  tls:
  - hosts:
    - www.netexpertise.eu
    secretName: letsencrypt-netexpertise

  rules:
  - host: www.netexpertise.eu
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-backend-app
            port:
              number: 80


Renouveler les Certificats LetsEncrypt

Cert-manager va créer ingress, service et pod dans votre namespace qui fournira une page web pour le challenge HTTP. Ils disparaîtront dès que le certificat LetsEncrypt a été renouvelé et délivré dans le secret défini dans secretName.

Si vous rencontrez un problème, vous pouvez jeter un oeil aux logs des différents pods dans le namespace cert-manager, ainsi que dans le statut de la ressource certificate. Un kubectl describe cert devrait donner toutes les informations nécessaires.

 

Aucune Réponse

Oct 09 2021

Toutes les Icônes Disponibles du Diagram as Code

Publié par sous Python

Le Diagram as Code de Mingrammer permet de réaliser de très beaux diagrammes tout en offrant de nombreuses icônes. C’est juste frustrant de devoir aller dans chaque sous-répertoire pour chercher toutes les icônes disponibles. J’ai ainsi décidé d’écrire un bout de code qui importe les classes des sous-modules du package Diagram as Code.
J’ai ensuite rassemblé les sous-modules de chaque groupe dans un cluster – qui s’affichent dans la partie bleue – et les classes en vert. Une classe correspond à une icône spécifique.

Vous trouverez sur cette image toutes les icônes disponibles pour vos diagrammes as code. Cliquez pour agrandir l’image dans une meilleure résolution. Vous pouvez l’enregistrer d’un simple clic droit.

Diagram as code toutes les icones


Voici le code source qui génère l’image ci-dessus que vous pouvez adapter à vos besoins pour obtenir toutes les dernières icônes. Peut-être voulez-vous afficher les icônes d’un groupe particulier ou générer des images séparées pour chacun d’entre eux.

J’ai dû lier les icônes entre elles pour qu’elles s’affichent horizontalement plutôt que sur une colonne simple infinie. C’est ce que fait la fonction récursive display_icons. Pour une raison qui m’échappe, la récursivité fonctionne en supprimant le dernier élément, mais pas le premier.

#!/usr/bin/env python3
 
import importlib
import pkgutil
import sys, inspect
 
import diagrams
from diagrams import Cluster, Diagram, Edge
 
def get_modules(module):
   path_list = []
   spec_list = []
   for importer, modname, ispkg in pkgutil.walk_packages(module.__path__):
      import_path = f"{module.__name__}.{modname}"
      if ispkg:
         spec = pkgutil._get_spec(importer, modname)
         importlib._bootstrap._load(spec)
         spec_list.append(spec)
      else:
         path_list.append(import_path)
   return path_list
 
def get_classes(module):
   class_list = []
   for name, obj in inspect.getmembers(module,inspect.isclass):
      if not name.startswith('_'):
         class_list.append([name,obj]) 
   return class_list
 
def add_module_to_provider_list(providers, module):
   # eg diagrams.azure.database
   # add "database" to array linked to key "azure"
   (diagram,provider,pclass) = module.split('.')
   if provider not in providers:
      providers[provider] = [pclass]
   else:
      providers[provider].append(pclass)
 
def get_provider_list(providers):
   return providers.keys()
 
def get_provider_classes(providers, provider):
   return providers[provider]
 
def display_icons(class_list):
   if len(class_list) < 1: return
   if len(class_list) == 1: return class_list[0][1](class_list[0][0])
   length = len(class_list)
   return display_icons(class_list[:-1]) - class_list[length-1][1](class_list[length-1][0])
 
if __name__ == "__main__":
   providers = {}
   modules = get_modules(diagrams)
 
   for module in modules:
      # Module exception
      # /usr/local/lib/python3.8/site-packages/diagrams/oci/database.py
      # NameError: name 'AutonomousDatabase' is not defined
      if module not in ['diagrams.oci.database']:
         add_module_to_provider_list(providers, module)
 
   with Diagram("all_Icons", show="False", outformat="png"):
      for provider in get_provider_list(providers):
         with Cluster(provider):
            for pclassname in get_provider_classes(providers, provider):
               with Cluster(pclassname):
                  classes = get_classes(importlib.import_module('diagrams.'+provider+'.'+pclassname))
                  display_classes(classes)
 

Aucune Réponse

Suivant »