Nous avons créé un environnement pour exécuter des tests unitaires à l'aide de la base de données Oracle (oracle12c) sur l'image Docker in Docker (dind) de GitLab-CI

C'est un mémorandum parce que j'ai construit un environnement comme un titre dans mon travail et j'ai trébuché à divers endroits.

supposition

--Je voulais écrire un test unitaire pour un programme batch qui se connecterait à la base de données Oracle (oracle12c) avec sqlalchemy et viderait les informations requises. ―― Puisque SQL a été écrit directement dans ce code produit, j'ai essayé de construire et de vérifier l'environnement SQLite sur le test unitaire, mais j'ai vu une opinion raisonnable qu'il est préférable d'avoir le client DB dans l'environnement de test unitaire et l'environnement de production. Par conséquent, j'ai décidé de créer un environnement oracle12c même dans un environnement de test unitaire. ――Il est raisonnable de dire qu'il n'est pas bon d'écrire directement SQL, mais pour refactoriser cela, vous devez d'abord écrire un test unitaire.

Créer un environnement de test unitaire localement

Puisqu'il n'y a pas d'environnement de test unitaire en premier lieu, nous avons d'abord construit un environnement de test unitaire localement. Tout comme la création d'un environnement de test unitaire à l'aide d'une base de données commune, vous pouvez exécuter le test unitaire tout en démarrant db avec docker-compose.

Cliquez ici pour créer une image de menu fixe pour oracle12c.

docker-compose.yml


version: '2'

services:
  oracle-database:
    image: oracle/database:12.1.0.2-ee
    container_name: oracle-database
    ports:
      - 1521:1521
    volumes:
      - ./startup:/opt/oracle/scripts/startup
    environment:
      - ORACLE_SID=SID
      - ORACLE_PWD=passw0rd
      - ORACLE_PDB=pdb

Une certaine préparation est nécessaire pour se connecter à cette base de données.

Dans le cas d'une base de données Oracle, la base de données sera créée dans l'utilisateur (schéma), il est donc nécessaire de créer d'abord l'utilisateur. Il est fastidieux d'écrire du code créé par l'utilisateur dans le code de test à chaque fois, alors laissez l'utilisateur être créé au démarrage de la base de données.

startup/startup.sql


ALTER SESSION SET container = pdb;
GRANT DBA TO PDBADMIN;
GRANT UNLIMITED TABLESPACE TO PDBADMIN;

CREATE USER testuser
    IDENTIFIED BY passw0rd
    DEFAULT TABLESPACE users
    TEMPORARY TABLESPACE temp;
GRANT DBA TO testuser;
GRANT UNLIMITED TABLESPACE TO testuser;

L'image docker de la base de données Oracle exécute le fichier sql dans le dossier de démarrage en tant qu'utilisateur sysdb, modifiez donc la session pour ajouter l'utilisateur sur la base de données enfichable (PDB). Aussi, dans l'état initial, PDBADMIN n'a pas l'autorité équivalente à admin (pourquoi?), Donc l'autorité est accordée.

Ici, l'utilisateur est créé en tant que testuser / passw0rd. Comme il n'est utilisé que pour les tests unitaires, cet utilisateur dispose également de privilèges d'administrateur équivalents.

tests/test.py


import unittest

import cx_Oracle
import sqlalchemy

class Test(unittest.TestCase):
    def setUp(self):
        self.sut = ... #Instance testée
        dsn = cx_Oracle.makedsn("oracle-database", 1521, service_name = "pdb")
        self.testuser = sqlalchemy.create_engine(f"oracle+cx_oracle://testuser:passw0rd@{dsn}")
        self.create_testtable()

    def tearDown(self):
        self.drop_testtable()

    def test__testmethod__describe(self):
        # SetUp
        expected = ...
        # Exercise
        self.sut.testmethod(...)
        # Verify
        actual = ...
        self.assertEqual(expected, actual)

    def create_testtable(self):
        self.testuser.execute(f"""
            CREATE TABLE testtable (
                ... define columns ...
            )
        """)

    def drop_testtable(self):
        self.testuser.execute("DROP TABLE testtable")

if __name__ == "__main__":
    unittest.main()

Je pense qu'il existe différentes manières d'écrire un test, donc à titre d'exemple. La partie importante ici est la partie connectée à oracle12c avec la méthode setUp. Créez un DSN et utilisez ces informations pour créer une URL de connexion et créer un moteur de connexion. Il existe différentes méthodes de connexion décrites dans sqlalchemy, mais pour vous connecter à PDB, vous devez vous connecter par ServiceName (et non par SID), et vous devez créer un DSN pour cela. Faites correspondre le nom d'hôte dans le DSN avec le nom du conteneur défini dans docker-compose.yml.

Si vous préparez «__init __. Py» dans les tests, le test unitaire sera exécuté en exécutant «pytest» à la racine.

docker-compose up -d
pytest

problème

Pour le moment, cela mettra en place l'environnement minimum. Cependant, il y a des problèmes avec cet environnement.

Comme vous pouvez le voir, après docker-compose up, la construction de la base de données initiale prend un temps considérable et il faut environ 5 à 10 minutes pour que la base de données s'ouvre enfin (= se connecter). De plus, la construction initiale de la base de données échoue rarement (même si elle est construite avec Docker). Considérant l'environnement comme un test unitaire, la manipulation est assez mauvaise.

Sauvegardez la base de données initiale pour résoudre ce problème. Il y a une raison de dire «sauvegarde» ici. En effet, cette base de données initiale sera mise à jour régulièrement sous la forme d'une sauvegarde par écrasement après la connexion à partir du SGBD. Il en va de même lorsque la base de données devient sale en exécutant un test unitaire, mais même si rien n'est fait, la taille du fichier augmente et la base de données devient de plus en plus "sale". Puisqu'il est indéniable que le test peut échouer en raison de tels facteurs externes, il est nécessaire de restaurer la base de données initiale qui a été sauvegardée immédiatement avant docker-compose up afin de construire un environnement de test stable. y a-t-il.

Une certaine technique est nécessaire pour créer la base de données initiale et préparer une sauvegarde. Un exemple est montré ici.

Commencez par créer une image de base de données Oracle pour créer la base de données initiale. L'exemple utilise docker-compose, mais vous pouvez utiliser une commande docker similaire.

docker-compose-oradata.yml


version: '2'

services:
  oracle-database:
    image: oracle/database:12.1.0.2-ee
    container_name: oracle-database
    ports:
      - 1521:1521
    volumes:
      - ./oradata:/opt/oracle/oradata
    environment:
      - ORACLE_SID=SID
      - ORACLE_PWD=passw0rd
      - ORACLE_PDB=pdb
docker-compose -f docker-compose-oradata.yml up

Après exécution, la base de données initiale sera construite dans le dossier oradata. Cette construction prend environ 5 à 10 minutes. Dans l'exemple ci-dessus, il n'est pas en mode détaché afin de vérifier le moment où il est terminé ou non.

À partir de là, il est important d'arrêter la base de données avec Ctrl + C immédiatement après la fin de la construction. Si vous vous penchez sur cela, la base de données initiale deviendra en surpoids (environ 1,5 Go). Vous pouvez toujours le monter, mais cela n'a pas de sens si les fichiers que vous sauvegardez sont inutilement volumineux, alors fermez-les immédiatement après la construction.

Starting oracle-database ... done
Attaching to oracle-database
oracle-database    | ORACLE PASSWORD FOR SYS, SYSTEM AND PDBADMIN: passw0rd
oracle-database    | 
oracle-database    | LSNRCTL for Linux: Version 12.1.0.2.0 - Production on 15-OCT-2020 12:03:40
oracle-database    | 
oracle-database    | Copyright (c) 1991, 2014, Oracle.  All rights reserved.
oracle-database    | 
oracle-database    | Starting /opt/oracle/product/12.1.0.2/dbhome_1/bin/tnslsnr: please wait...
oracle-database    | 
oracle-database    | TNSLSNR for Linux: Version 12.1.0.2.0 - Production
oracle-database    | System parameter file is /opt/oracle/product/12.1.0.2/dbhome_1/network/admin/listener.ora
oracle-database    | Log messages written to /opt/oracle/diag/tnslsnr/bf429c874900/listener/alert/log.xml
oracle-database    | Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1)))
oracle-database    | Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))
oracle-database    | 
oracle-database    | Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1)))
oracle-database    | STATUS of the LISTENER
oracle-database    | ------------------------
oracle-database    | Alias                     LISTENER
oracle-database    | Version                   TNSLSNR for Linux: Version 12.1.0.2.0 - Production
oracle-database    | Start Date                15-OCT-2020 12:03:40
oracle-database    | Uptime                    0 days 0 hr. 0 min. 0 sec
oracle-database    | Trace Level               off
oracle-database    | Security                  ON: Local OS Authentication
oracle-database    | SNMP                      OFF
oracle-database    | Listener Parameter File   /opt/oracle/product/12.1.0.2/dbhome_1/network/admin/listener.ora
oracle-database    | Listener Log File         /opt/oracle/diag/tnslsnr/bf429c874900/listener/alert/log.xml
oracle-database    | Listening Endpoints Summary...
oracle-database    |   (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1)))
oracle-database    |   (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))
oracle-database    | The listener supports no services
oracle-database    | The command completed successfully
oracle-database    | Cleaning up failed steps
oracle-database    | 4% complete
oracle-database    | Copying database files
oracle-database    | 5% complete
oracle-database    | 6% complete
oracle-database    | 30% complete
oracle-database    | Creating and starting Oracle instance
oracle-database    | 32% complete
oracle-database    | 35% complete
oracle-database    | 36% complete
oracle-database    | 37% complete
oracle-database    | 41% complete
oracle-database    | 44% complete
oracle-database    | 45% complete
oracle-database    | 48% complete
oracle-database    | Completing Database Creation
oracle-database    | 50% complete
oracle-database    | 53% complete
oracle-database    | 55% complete
oracle-database    | 63% complete
oracle-database    | 66% complete
oracle-database    | 74% complete
oracle-database    | Creating Pluggable Databases
oracle-database    | 79% complete
oracle-database    | 100% complete
oracle-database    | Look at the log file "/opt/oracle/cfgtoollogs/dbca/SID/SID0.log" for further details.
oracle-database    | 
oracle-database    | SQL*Plus: Release 12.1.0.2.0 Production on Thu Oct 15 12:11:34 2020
oracle-database    | 
oracle-database    | Copyright (c) 1982, 2014, Oracle.  All rights reserved.
oracle-database    | 
oracle-database    | 
oracle-database    | Connected to:
oracle-database    | Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
oracle-database    | With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
oracle-database    | 
oracle-database    | SQL> 
oracle-database    | System altered.
oracle-database    | 
oracle-database    | SQL> 
oracle-database    | Pluggable database altered.
oracle-database    | 
oracle-database    | SQL> Disconnected from Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
oracle-database    | With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options
oracle-database    | The Oracle base remains unchanged with value /opt/oracle
oracle-database    | #########################
oracle-database    | DATABASE IS READY TO USE!
oracle-database    | #########################
oracle-database    | The following output is now a tail of the alert.log:
oracle-database    | Completed: alter pluggable database PDB open
oracle-database    | Thu Oct 15 12:11:33 2020
oracle-database    | CREATE SMALLFILE TABLESPACE "USERS" LOGGING  DATAFILE  '/opt/oracle/oradata/SID/PDB/PDB_users01.dbf' SIZE 5M REUSE AUTOEXTEND ON NEXT  1280K MAXSIZE UNLIMITED  EXTENT MANAGEMENT LOCAL  SEGMENT SPACE MANAGEMENT  AUTO
oracle-database    | Completed: CREATE SMALLFILE TABLESPACE "USERS" LOGGING  DATAFILE  '/opt/oracle/oradata/SID/PDB/PDB_users01.dbf' SIZE 5M REUSE AUTOEXTEND ON NEXT  1280K MAXSIZE UNLIMITED  EXTENT MANAGEMENT LOCAL  SEGMENT SPACE MANAGEMENT  AUTO
oracle-database    | ALTER DATABASE DEFAULT TABLESPACE "USERS"
oracle-database    | Completed: ALTER DATABASE DEFAULT TABLESPACE "USERS"
oracle-database    | Thu Oct 15 12:11:34 2020
oracle-database    | ALTER SYSTEM SET control_files='/opt/oracle/oradata/SID/control01.ctl' SCOPE=SPFILE;
oracle-database    |    ALTER PLUGGABLE DATABASE PDB SAVE STATE
oracle-database    | Completed:    ALTER PLUGGABLE DATABASE PDB SAVE STATE
^CERROR: Aborting.

Sauvegardez le dossier oradata quelque part afin que le contenu de ce dossier oradata soit restauré avant d'exécuter docker-compose up avant d'exécuter le test (dans les affaires réelles, le contenu de oradata doit être restauré. zip compressé et sauvegardé sur S3). Le fichier docker-compose.yml d'origine doit monter le dossier oradata.

docker-compose.yml


version: '2'

services:
  oracle-database:
    image: oracle/database:12.1.0.2-ee
    container_name: oracle-database
    ports:
      - 1521:1521
    volumes:
      - ./oradata:/opt/oracle/oradata
      - ./startup:/opt/oracle/scripts/startup
    environment:
      - ORACLE_SID=SID
      - ORACLE_PWD=passw0rd
      - ORACLE_PDB=pdb

En faisant cela, le temps nécessaire pour ouvrir la base de données pourrait être réduit à environ 1 à 2 minutes.

Exécutez le test unitaire ci-dessus avec GitLab CI

Le sujet principal.

Lorsque vous utilisez une base de données pour des tests unitaires sur GitLab CI, c'est une voie royale d'utiliser les services. Cependant, l'image de la base de données Oracle doit être gérée par une sorte de référentiel privé, je n'ai jamais mis en place de référentiel privé dans les services et j'ai fait d'autres images de docker dans la pratique, donc ici j'utiliserai Docker dans Docker (dind) J'ai construit un environnement.

Il y a un problème lors de la création d'un environnement dind avec GitLab CI. Autrement dit, le répertoire dans lequel le volume est monté devient le répertoire hôte. Normalement (non limité à GitLab CI) l'exécution du travail elle-même se fait dans le conteneur docker, donc si vous exécutez docker-compose comme celui-ci, les dossiers oradata et startup seront dans le conteneur docker où le travail a été exécuté. Au lieu de cela, vous finissez par spécifier le dossier sur l'hôte qui exécute ce docker.

Il existe différentes solutions à cela, mais cette fois je l'ai évité en créant une image docker qui inclut des oradata. Étant donné que l'image de la base de données oracle doit être placée dans un référentiel privé en premier lieu, l'image contenant oradata n'est préparée que là-bas, de sorte que l'environnement local ne change pas beaucoup.

Le Dockerfile qui crée l'image de la base de données oracle, y compris l'oradata créé précédemment, ressemble à ceci.

Dockerfile


FROM oracle/database:12.1.0.2-ee

ENV ORACLE_SID SID
ENV ORACLE_PWD passw0rd
ENV ORACLE_PDB pdb

COPY --chown=oracle:dba oradata/ /opt/oracle/oradata/
COPY startup/ /opt/oracle/scripts/startup/
docker build -t oracle/database:12.1.0.2-ee-with-oradata

Il y a deux points à noter.

Tout d'abord, oradata dépend des variables d'environnement ORACLE_SID, ORACLE_PWD et ORACLE_PDB, alors ajoutez également ces variables d'environnement à votre Dockerfile. Si ceux-ci changent, recréez également les oradata.

L'autre consiste à changer le propriétaire en oracle: dba lors de la copie d'oradata. Si vous le copiez tel quel, il deviendra l'utilisateur root et l'utilisateur oracle ne pourra pas monter la base de données initiale.

Ensuite, utilisez cette image et exécutez-la dans .gitlab-ci.yml.

docker-compose.yml


version: '2'

services:
  oracle-database:
    image: oracle/database:12.1.0.2-ee-with-oradata
    container_name: oracle-database
    ports:
      - 1521:1521

yml:.gitlab-ci.yml


pytest:
  stage: test
  image: docker:dind
  script:
    - apk update && apk add bash python3 python3-dev py3-pip docker-compose
    - python3 -m pip install pytest
    - docker-compose up -d
    - sleep 120s
    - pytest

(Je ne suis pas sûr de créer un environnement d'exécution pour pytest, je le vérifierai plus tard)

Puisqu'il faut du temps pour ouvrir la base de données initiale, nous utilisons «sleep 120s» comme exemple ici.

Pour le moment, en faisant quelque chose comme ça, il est devenu possible d'exécuter des tests unitaires en utilisant la base de données Oracle à la fin de GitLab CI.

Sommaire

Recommended Posts

Nous avons créé un environnement pour exécuter des tests unitaires à l'aide de la base de données Oracle (oracle12c) sur l'image Docker in Docker (dind) de GitLab-CI
J'ai construit un environnement Ubuntu sur Windows 10 en utilisant WSL2.
Comment installer Docker dans l'environnement local d'une application Rails existante [Rails 6 / MySQL 8]
J'ai essayé de construire l'environnement petit à petit en utilisant docker
J'ai essayé d'utiliser Docker parce que je ne veux pas polluer l'environnement local dans le développement de l'onglet Microsoft Teams de MS Learn
J'ai essayé de créer un environnement de WSL2 + Docker + VSCode
Je veux recréer le contenu des actifs à partir de zéro dans l'environnement construit avec capistrano
Après avoir appris Progate, j'ai essayé de créer une application SNS en utilisant Rails dans l'environnement local
J'ai essayé de créer un environnement de serveur UML Plant avec Docker
Comment définir l'image pour dessiner à gauche / à droite du bouton à l'aide de la police d'icône (Iconics)
J'ai essayé de créer un exemple de programme en utilisant le problème du spécialiste des bases de données dans la conception pilotée par domaine
Comment exécuter avec des commandes de langage de développement normales dans l'environnement de développement Docker
Je souhaite afficher un message d'erreur lors de l'inscription dans la base de données
J'ai essayé de migrer le portfolio créé sur Vagrant vers l'environnement de développement de Docker
Essayez d'ajouter du texte à une image avec Scala en utilisant la bibliothèque standard de Java
Jusqu'au lancement d'un conteneur Docker construit avec RedHat Quarkus
Une note rapide sur l'utilisation de jshell avec l'image Docker officielle du JDK
Poussez l'image vers le hub docker à l'aide de Jib
Pour ceux qui souhaitent utiliser MySQL pour la base de données dans la construction de l'environnement de Rails6 ~.
Comment utiliser git avec la puissance de jgit dans un environnement sans commandes git
Manipulez les pixels pour créer une image d'un ensemble de Mandelbrot
SSL dans l'environnement local de Docker / Rails / puma
[Rails] Comment afficher les images dans la vue
Exemple de code pour obtenir les valeurs des principaux types SQL dans Java + Oracle Database 12c
J'ai essayé de développer la fonction de cache d'Application Container Cloud Service dans l'environnement local