Génération d’un PDF depuis une JSP avec iText5 et iText7

NOTA : la version utilisée dans cette première version est la iText5 une prochaine version utilisera la nouvelle version iText7

En premier lieu il faut récupérer les bibliothèques jar suivantes:

  • itextpdf-5.5.13.3.jar
  • log4j-1.2.16.jar
  • slf4j-api-1.7.9.jar
  • slf4j-log4j12-1.7.13.jar

Placer ces fichiers dans le dossier WebContent/WEB-INF/lib de votre application Dynamic Web Application , les sélectionner et faire click droit > Build Path > Add to Build Path

Vous pouvez maintenant coder une simple JSP pour générer un ficheir PDF. Attention, si vous cette erreur « getOutputStream() has already been called for this response » il faut retirer tout espace ou retour chariot qui pourrait être interprétés par la JSP comme du HTML à envoyer donc tout se trouve sur une seule ligne pour les tag JSP de type <%@page import %>:

<%@ page language="java" contentType="application/pdf; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%><%@page import="com.itextpdf.text.Document" %><%@page import="com.itextpdf.text.PageSize" %><%@page import="com.itextpdf.text.Paragraph" %><%@page import="com.itextpdf.text.pdf.PdfWriter" %><%
Document document = new Document(PageSize.A4, 36, 36, 36, 72);

PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "inline; filename=\"todo.pdf\"");

document.open();
document.add( new Paragraph ("Hello world"));
document.add( new Paragraph ("Bonjour le monde"));
document.close();
writer.close();
%>

Version iText7

Pour la version iText7, après avoir ajouté les bilbiothèques suivantes en remplacement de celles de iText5:

  • barcodes-7.2.1.jar
  • commons-7.2.1.jar
  • font-asian-7.2.1.jar
  • forms-7.2.1.jar
  • hyph-7.2.1.jar
  • io-7.2.1.jar
  • kernel-7.2.1.jar
  • layout-7.2.1.jar
  • log4j-1.2.16.jar
  • pdfa-7.2.1.jar
  • pdftest-7.2.1.jar
  • sign-7.2.1.jar
  • slf4j-api-1.7.9.jar
  • slf4j-log4j12-1.7.13.jar
  • styled-xml-parser-7.2.1.jar
  • svg-7.2.1.jar

Du coup le code pour la JSP est similaire avec l’utilisation d’un ByteArrayOutputStream pour gérer le PDF en mémoire.

<%@ page language="java" contentType="application/pdf; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%><%@ page import="com.itextpdf.io.source.ByteArrayOutputStream" %><%@ page import="com.itextpdf.kernel.pdf.PdfDocument" %><%@ page import="com.itextpdf.kernel.pdf.PdfWriter" %><%@ page import="com.itextpdf.layout.Document" %><%@ page import="com.itextpdf.layout.element.Paragraph" %><%
ByteArrayOutputStream memory = new ByteArrayOutputStream();
Document document = new Document(new PdfDocument(new PdfWriter(memory)));
document.add( new Paragraph ("Hello world"));
document.close();
ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(memory.toByteArray());
servletOutputStream.flush();
servletOutputStream.close();
%>
Publié dans Développement | Laisser un commentaire

Alternative tcpdump windows 10 avec pktmon

L’outil pktmon.exe disponible par défaut dans Windows 10 Pro permet de faire un peu de dump de paquet TCP.

Bien sûr, la solution de npcap/winpcap/wireshark est beaucoup plus performant, mais avec quelques commandes on peut extraire l’information utile. Je me concentre surtout sur la partie HTTP qui me permet de vérifier les échanges de données même si la limitation de pktmon à ne pas pouvoir fonctionner sur la boucle locale 127.0.0.1 est clairement une limitation pour moi.

Démarrons par ajouter un filtre sur le protocole TCP sur le port 80

 C:\Windows\system32\pktmon.exe filter add -t TCP  -p 80

On peut lister ces filtres avec la commande suivante

 C:\Windows\system32\pktmon.exe filter list

Filtres de paquet :
     # Nom    Protocole Port
     - ---    --------- ----
     1 <vide> TCP         80

Et pour démarrer la capture, on voit le rappel de la liste des paquets

 C:\Windows\system32\pktmon.exe  start --etw  --pkt-size 0

Filtres de paquet :
     # Nom    Protocole Port
     - ---    --------- ----
     1 <vide> TCP         80

Un fichier PktMon.etl va donc être fabriqué contenant les opérations que l’on fait avec son navigateur vers un port 80 d’un serveur web par exemple.

Mais je vous propose cette version du filtrage pour se concentrer sur la partie Web des requêtes :

C:\Windows\system32\pktmon.exe filter add Port80 -t TCP PSH -p 80 -m 00-AA-BB-CC-DD-EE

On ajoute un nom Port80 et on va récupérer que les paquets marqués TCP de type PUSH (codé PSH) sur le port 80 en provenance de la Mac Address de ma carte réseau 00-AA-BB-CC-DD-EE à remplacer par votre valeur.

Il suffit maintenant d’arrêter la capture est de convertir le fichier PktMon.etl en format lisible à moins d’utiliser l’outil d’analyse de Microsoft Network Monitor.

Le fichier ftp.txt est assez clair pour être lu et vérifier les packets TCP

C:\Windows\system32\pktmon.exe  stop

C:\Windows\system32\pktmon.exe  format PktMon.etl -o file.txt

Exemple d’accès sur un site web en méthode GET en regardant le contenu du fichier file.txt :

00-AA-BB-CC-DD-EE > 00-11-12-13-14-15, ethertype IPv4 (0x0800), length 457: 192.168.1.1.49676 > 213.186.33.2.80: Flags [P.], seq 2764848503:2764848906, ack 3028623659, win 513, length 403: HTTP: GET /accueil/?s=test HTTP/1.1

On peut retirer le filtre au besoin pour revenir dans l’état initial

C:\Windows\system32\pktmon.exe  filter remove

On peut aussi exporter dans un format plus verbeux avec l’option de formatage etl2txt:

C:\Windows\system32\pktmon.exe  etl2txt .\PktMon.etl v 3 -m -o file.txt

Cela va produire les entête HTTP des contenus des requêtes HTTP par exemple avec le fichier JavaScript suivant :

	HTTP/1.1 200 OK
	date: Wed, 23 Feb 2022 15:11:35 GMT
	content-type: application/javascript
	content-length: 229
	server: Apache
	last-modified: Tue, 10 Dec 2019 18:53:39 GMT
	accept-ranges: bytes
	cache-control: max-age=900
	expires: Wed, 23 Feb 2022 15:26:35 GMT
	vary: Accept-Encoding
	x-iplb-request-id: C1339D28:28B3_D5BA2102:0050_62164EA7_6F6E:1CD33
	x-iplb-instance: 29005
	
	
	
	var $j = jQuery.noConflict();
	
	$j(document).ready(function() {
	    // Toggle Single Bibtex entry
	    $j('a.papercite_toggle').click(function() {
		$j( "#" + $j(this).attr("id") + "_block" ).toggle();
		return false;
	    });
	});

Ensuite on pourra utiliser les outils de développements des navigateurs pour une lecture plus simple, mais la liaison TPC/IP sur le contenu est clairement lisible avec cet outil fourni par défaut.

L’option suivante est soit de faire plus de réseau avec tcpdump ou wireshark qui sont dédiés aux couches réseaux soit de partir en développement web avec les outils embarqués dans les navigateurs Web.


La partie que je chercherai à ajouter prochainement, est la capture de la boucle locale via des outils tierce comme indiqué sur cette page : https://wiki.wireshark.org/CaptureSetup/Loopback

Je pense que cette option semble prometteuse même si je n’arrive pas encore à la faire fonctionner : https://web.archive.org/web/20150726062624/http://ig2600.blogspot.com/2011/03/powershell-script-to-enable-windows-to.html

Publié dans Développement | Laisser un commentaire

Ajout de l’authentification One-Time-Password à taiga.io

La nouvelle version de taiga.io avec docker inclus un installateur bien simplifié.

Il me parait utile d’y ajouter simplement l’authentification OTP qui permet d’accepter le mot de passe ajouté avec 6 chiffres calculé par l’horloge à partir d’un code. Vous trouverez des explications sur ce processus dans cet article: https://en.wikipedia.org/wiki/One-time_password

Le seul fichier à éditer pour inclure l’OTP dans taiga est le fichier /taiga-back/taiga/users/services.py dans le containeur taiga-docker_taiga-back_1.

En premier lieu, il faut ajouter au containeur taiga-docker_taiga-back_1 le module pyotp:

docker exec -it taiga-docker_taiga-back_1 pip install pyotp

Ensuite on peut copier sur la machine hôte le fichier services.py pour y ajouter l’OTP:

docker cp taiga-docker_taiga-back_1:/taiga-back/taiga/users/services.py  services.py 

Nous pouvons donc éditer le fichier services.py pour y adjoindre et modifier le programme, en premier lieu en important le module pyotp avant toutes les fonctions:

import pyotp

Puis on va chercher la fonction get_and_validate_user qui a cette forme:

def get_and_validate_user(*, username: str, password: str) -> bool:
    """
    Check if user with username/email exists and specified
    password matchs well with existing user password.

    if user is valid,  user is returned else, corresponding
    exception is raised.
    """

    user = get_user_by_username_or_email(username)


    if not user.check_password(password) or not user.is_active or user.is_system:
        raise exc.WrongArguments(_("Username or password does not matches user."))

    return user

Nous allons ajouter l’extraction des 6 derniers symboles du mot de passe pour trouver le code PIN à valider par OTP avec le code fourni en paramètre. Donc la nouvelle version de la fonction get_and_validate_user

def get_and_validate_user(*, username: str, password: str) -> bool:
    """
    Check if user with username/email exists and specified
    password matchs well with existing user password.

    if user is valid,  user is returned else, corresponding
    exception is raised.
    """

    user = get_user_by_username_or_email(username)

    """
    PJE   verification OTP
    """
    totp = pyotp.TOTP("base32secret3232")
    passwordPIN = password
    password = passwordPIN[:-6]
    pin = totp.verify( passwordPIN[-6:] , valid_window=2 )
    if pin is False:
        raise exc.WrongArguments(_("Username or password does not matches user PIN."))

    """
    / PJE   verification OTP
    """


    if not user.check_password(password) or not user.is_active or user.is_system:
        raise exc.WrongArguments(_("Username or password does not matches user."))

    return user

En incluant dans la fonction d’authentification, l’extraction des 6 derniers symboles fournis par l’utilisateur, on peut ajouter une authentification avec code OTP. On note une nouvelle exception envoyé par le serveur en cas d’erreur sur le code PIN.

NOTA: je pensais avoir un problème de Timezone dans le containeur mais au final, je pense que c’est valide comme installation. J’ai testé quelques solutions de changement dans le containeur de timezone mais finalement je pense que je peux aussi m’appuyer sur l’argument for_time de la fonction verify : https://pyauth.github.io/pyotp/_modules/pyotp/totp.html#TOTP.verify

Publié dans Développement | Laisser un commentaire

Génération des classes Java Entity depuis un fichier JDL

Je trouve très pratique pour définir les entités l’outil en ligne de Jhipster JDL-Studio.

Prenons une application avec plusieurs liaisons OneToMany d’une liste de recette dont chaque recette a une liste de commentaires et une liste d’ingrédients.

À partir du fichier jhipster-jdl.jdl téléchargé depuis le JDL Studio, je souhaite fabriquer les entités en Java pour les intégrer dans un développement.

Je n’ai pas trouvé toutes les options d’automatisation, mais en ligne de commande après avoir installé Jhipster, on peut lancer les 2 lignes suivantes  dans un dossier contenant le fichier jhipster-jdl.jdl.

La première permet de fabriquer le projet en répondant à 8 questions par validation automatique.

 jhipster --skip-cache --skip-git --skip-install --skip-prompts --skip-client --skip-user-management  --db sql --skip-checks  --skip-fake-data --base-name App --application-type microservices   --skip-checks

Les questions seront validées par la touche « entrée » automatiquement :

? What is your default Java package name? com.mycompany.myapp
? Which *type* of database would you like to use? SQL (H2, PostgreSQL, MySQL, MariaDB, Oracle, MSSQL)
? Which *production* database would you like to use? PostgreSQL
? Which *development* database would you like to use? H2 with disk-based persistence
? Which cache do you want to use? (Spring cache abstraction) Ehcache (local cache, for a single node)
? Do you want to use Hibernate 2nd level cache? Yes
? Would you like to use Maven or Gradle for building the backend? Maven
? Which other technologies would you like to use?

La seconde commande va fabriquer les entités en Java depuis le fichier jhipster-jdl.jdl :

jhipster jdl jhipster-jdl.jdl --skip-client

Repondez-yes aux 2 questions d’overwrite sans inquiétudes, nous n’allons pas exploiter d’autres fonctions que la génération des entités.

Vous trouverez dans le dossier src/main/java/com/mycompany/myapp/domain/ les fichiers java des entités avec les liaisons réalisées et les propriétés créées.

Il s’agit d’une utilisation d’une toute petite fonctionnalité de Jhipster mais je trouve cela très pratique.

Publié dans Développement | Laisser un commentaire

Gestion de fichiers images dans Springboot

La gestion de l’upload de fichiers images dans Springboot s’appuie sur plusieurs classes et fichiers.

FileUploadController agit comme un contrôleur qui va recevoir la demande d’upload de fichier (via l’url /upload) et la demande d’afficher l’image ( via l’url /files/{filename:.+} ). nCette classe contient une référence sur une classe FileSystemStorageService implementant l’interface StorageService.

StorageService est une simple interface qui permet de définir les actions classiques d’un service Spring de stockage.

FileSystemStorageService implémente de façon réelle l’interface StorageService pour la gestion du lieu de stockage des fichiers. Par ailleurs, pour une raison de sécurité, on peut stocker les fichiers uploadés dans un dossier dont l’accès est protégé pour éviter l’upload de fichiers critiques sur le serveur en devinant l’URL.

upload.html est un fichier gérant le formulaire HTML permettant d’envoyer un fichier simplement sans javascript encore une fois pour simplifier cet exemple.

Après les liaisons entre les fichiers, voici le contenu très simplifié de ces derniers:

<!DOCTYPE html><html lang="en"><head>
  <title>upload</title>
<body>
<form method="post" enctype="multipart/form-data" action="/upload">
 <div>
   <label for="file">Sélectionner le fichier à envoyer</label>
   <input type="file" id="file" name="file" >
 </div> <div>
   <button>Envoyer</button>
 </div>
</form>
</body></html>

L’upload d’un fichier image se fait vers le contrôleur FileUploadController via l’action /upload, cette classe permet aussi l’affichage d’une ressource image dans le navigateur (il manque la gestion des exceptions à coder en plus) :

@Controller
public class FileUploadController {
  // Service spring de stockage des fichiers
  private final StorageService storageService;
  // Constructeur de la classe avec l'activation du service
  @Autowired
  public FileUploadController(StorageService storageService) {
    this.storageService = storageService;
  }

  @GetMapping("/files/{filename:.+}")
  @ResponseBody
   public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
  Resource file = storageService.loadAsResource(filename);
  String mimeType = URLConnection.guessContentTypeFromName(file.getFilename());
   long contentLength = 0;
   try {
	contentLength = file.contentLength();
   } catch (IOException e) { }
		
   InputStream fileInputStream = null;
   try {   fileInputStream = file.getInputStream();
    } catch (IOException e) {}
		
   return ResponseEntity.ok().contentLength( contentLength )
	.contentType(MediaType.parseMediaType( mimeType ))
	.body(new InputStreamResource( fileInputStream ));
  }

//Upload d'un fichier 
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
   storageService.store(file);
   return "redirect:/";
}

}

L’interface StorageService permet de définir les méthodes à implémenter par FileSystemStorageService.

public interface StorageService {
    void init();
    String store(MultipartFile file);
    Stream<Path> loadAll();
    Path load(String filename);
    Resource loadAsResource(String filename);
    void deleteAll();
}

Enfin la classe FileSystemStorageService permet de créer un service qui va stocker les fichiers dans un dossier ./Stockage dans le projet Spring (la gestion des exceptions est à coder en plus):

@Service
public class FileSystemStorageService implements StorageService {
  private final Path rootLocation;
  @Autowired
  public FileSystemStorageService() {
    this.rootLocation = Paths.get("./Stockage/");
   }
  @Override
  @PostConstruct
  public void init() {
     try {
	Files.createDirectories(rootLocation);
    } catch (IOException e) {}
   }

  @Override
   public String store(MultipartFile file) {
String filename = StringUtils.cleanPath(file.getOriginalFilename());
  try {
  if (file.isEmpty()) {}  
  if (filename.contains("..")) { }
   try (InputStream inputStream = file.getInputStream()) {
    Files.copy(inputStream, this.rootLocation.resolve(filename), StandardCopyOption.REPLACE_EXISTING);
    }
   } catch (IOException e) {	}
   return filename;
}

@Override
public Stream<Path> loadAll() {
 try {
    return Files.walk(this.rootLocation, 1).filter(path -> !path.equals(this.rootLocation)).map(this.rootLocation::relativize);
 } catch (IOException e) {	return null; }

}

@Override
public Path load(String filename) {
  return rootLocation.resolve(filename);
}

@Override
public Resource loadAsResource(String filename) {
  Path file = load(filename);
  Resource resource = null;
  try {
     resource = new UrlResource(file.toUri());
   if (resource.exists() || resource.isReadable()) {
      return resource;
   } else {
     System.out.println("File not found " + filename); 
   }
  } catch (MalformedURLException e) { }
		return resource;
}

@Override
public void deleteAll() {
  FileSystemUtils.deleteRecursively(rootLocation.toFile());
}

}

Le code du contrôleur FileUploadController permet d’afficher le fichier image dans le navigateur, si on souhaite proposer en téléchargement un fichier, la méthode serveFile doit être modifiée:

    Resource file = storageService.loadAsResource(filename);
   return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"").body(file);

Merci à cet article (https://spring.io/guides/gs/uploading-files/) qui a permit la rédaction de ce contenu.

Publié dans Développement, IMT Mines Alès | Laisser un commentaire

Programmation par aspect avec Java

L’idée de base dans la programmation par Aspect est d’extraire du flot normal d’exécution d’un code les parties très techniques.

Dans Spring on a classiquement la partie du code qui gère le Controleur de l’entité Todo via cette méthode :

@RequestMapping(value = "/todos", method = RequestMethod.GET)
public String listeTodos() {
   
  System.out.println("listeTodos");

  List<Todo> listTodo = todoDao.listTodo();
  String json = listTodo.stream()
		.map( todo -> todo.toJson() )
		.collect( Collectors.joining( "," , "[" , "]") );
		
   return json;
}

Pourtant, on pourrait vouloir faire un log de cet appel en ajoutant classiquement un logger au sein de cette méthode listeTodos() sauf que le principe de la programmation par Aspect est de sortir cette partie technique en proposant l’interception de l’appel de cette méthode listeTodos avec un code qui s’exécute avant et/ou après l’appel à listeTodos.

De manière concrète on a cet enchainement :

  • Appel à une méthode technique avant listeTodos
  • Appel à listeTodos
  • Appel à une méthode technique après listeTodos (optionnel)

En premier lieu, il faut ajouter sur la méthode principal l’ajout des fonctionnaltiés de programmation par Aspect:

@SpringBootApplication
@Configuration
@EnableAspectJAutoProxy
public class DemoApplication {
}

Ensuite, il faut coder une classe qui va assurer l’interception avant et après dans cet exemple avec les critères d’interceptions :

@Component
@Aspect
public class RestLogAspect {
	    
    @Around("execution(* fr.ema.ceris.todo.demo.TodoControleur..*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public Object restLog(ProceedingJoinPoint joinPoint) throws Throwable {
    	
        // Partie @Before 
        Object returnValue = null;
        Throwable throwable = null;
        try {
            System.out.println("Appel à TodoControleur avant");
            
            returnValue = joinPoint.proceed();       

            System.out.println("Appel à TodoControleur après");
 
        } catch (Throwable throwable1) {
            throwable = throwable1;
            System.out.println(String.format("[RestLogAspect] : %s", throwable1.getMessage()));
        }


        //Partie  @After 
        
        return returnValue;
    }

}

L’annotation @Around indique que l’appel à cette méthode sera fait avant et après les méthodes ayant les 2 critères suivants (notation && entre les 2 critères) :

  • execution(* fr.ema.ceris.todo.demo.TodoControleur..*(..)) : une classe se trouvant dans ce package avec ce nom de classe
  • @annotation(org.springframework.web.bind.annotation.RequestMapping), une annotion de type RequestMapping

Messages de la console :

Appel à TodoControleur avant
listeTodos
Appel à TodoControleur après

Nous avons donc un affichage des messages dans la console avant et après l’appel à la méthode listeTodos ce qui permet d’instrumentaliser le code fonctionnel par un code technique.

La valeur de la variable returnValue est aussi le résultat de l’appel à la fonction listeTodos donc le contenu json de la variable que l’on pourrait donc formater ou compresser avant renvoi vers la partie front du site web.

On aurait aussi pu utiliser l’annotation @Before à la place de l’annotation @Around pour avoir une exécution avant la méthode listeTodos et/ou l’annoation @After pour avoir une exécution après la méthode listeTodos. L’annotation @Around est un excellent exemple car elle permet de disposer des effets avant et après l’appel.

Publié dans Développement | Laisser un commentaire

Visual Code xdebug et le rechargement automatique de Chrome

Dans la suite de l’article sur « Ligne de commande pour recharger une page Chrome« , j’ai eu l’objectif d’utiliser Visual Code pour lancer xdebug avec le langage de programmation PHP et avoir l’auto refresh de Chrome lié avec la sauvegarde d’un fichier dans Visual Code.

Etape 1 : xdebug

Xdebug doit être configuré et installer dans Wamp et l’édition du fichier php.ini d’Apache de l’outil Wamp devrait contenir ce genre d’informations

[xdebug]
zend_extension="c:/Developpement/wamp64-3.2.3/bin/php/php7.4.9/zend_ext/php_xdebug-2.9.6-7.4-vc15-x86_64.dll"
;PJE xdebug.remote_enable = off
xdebug.remote_enable=1

Correction pour la nouvelle version de xdebug le réglage s’appelle xdebug.mode a mettre donc en valeur debug au lieu de develop et il faut ajouter le réglage xdebug.start_with_request

[xdebug]
zend_extension="c:/Developpement/wamp64/bin/php/php7.4.26/zend_ext/php_xdebug-3.1.1-7.4-vc15-x86_64.dll"
;xdebug.mode allowed are : off develop coverage debug gcstats profile trace
;PJE xdebug.mode =develop
xdebug.mode=debug
xdebug.start_with_request

Toujours en xdebug 3 le port n’est plus 9000 mais 9003

Notez que je vous propose d’utiliser le « remote debug » de xdebug. Bien sûr, dans l’affichage de la page phpinfo, il faut vérifier que l’extension xdebug est bien activé :

Plus bas dans cette page on pourra vérifier que xdebug a les bons réglages dans la partie qui est dédié à xdebug.

Information que xdebug est actif dans la page phpinfo

Etape 2: Visual Code

Dans Visual Code, il faut activer l’extension PHP Debug pour disposer des outils de liaison entre Visual Code et xdebug. L’extension que j’ai utilisé est de Felix Becker : https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug.

Nous partons du principe que dans le dossier www de Wamp se trouve un sous-dossier dans lequel notre développement sera réalisé.

Pour éviter d’utiliser un fichier tasks.json, j’ai préparé une double configuration dans le fichier launch.json a créé dans le dossier de travail de notre projet. Ce fichier doit permettre d’indiquer à la fois à Visual Code d’écouter les appels de xdebug pour un débogage et de lancer un navigateur chrome avec les options de débogage à distance pour relancer l’affichage de la page en cas de modificaiton du programme php, d’une page html ou d’un fichier CSS ou Javascript.

Le fichier launch.json dont on modifiera les chemins de Chromium et l’URL d’appel en fonction des besoins du projet Web:

{
    // Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
    // Pointez pour afficher la description des attributs existants.
    // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [        
        {
            "type": "chrome",
            "request": "launch",
            "name": "Chromium",         
            "program": "C:/Users/pierre.jean/AppData/Local/Chromium/Application/chrome.exe",
            "runtimeArgs": [
               "--disable-web-security",
               "--auto-open-devtools-for-tabs",
               "--remote-debugging-port=9222",
               "http://127.0.0.1/${workspaceFolderBasename}/${relativeFile}?XDEBUG_SESSION_START"
             ],
            "webRoot": "${workspaceRoot}"
            },        
        {
            "name": "Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9000,
        },
    ]
    ,"compounds": [
    {
      "name": "Xdebug & Chromium",
      "configurations": ["Chromium","Xdebug"],      
    }
  ]
}

L’appel au débogage doit être la première fois sur le choix « Xdebug & Chromium » pour lancer l’écoute sur l’URL http://127.0.0.1/dossier/index.php?XDEBUG_SESSION_START et ouvrir un navigateur Chromium avec les outils de développement Web ouvert.

Etape 3: Sauver un fichier relancer l’auto refresh de Chrome

Pour exploiter l’auto-refresh de Chrome/Chromium, j’ai pensé chercher l’équivalent des « builders » d’Eclipse, au final, la philosophie de Visual Code est d’utiliser une extension (https://marketplace.visualstudio.com/items?itemName=wk-j.save-and-run) dont l’objectif est de lancer automatiquement un programme debug.cmd qui est une variation de l’ancien programme.

L’extension Save And Run doit être programmé en modifiant le fichier settings.json se trouvant dans le dossier « C:\Users\pierre.jean\AppData\Roaming\Code\User\ » voici ma proposition de réglages pour déclencher le programme debug.cmd.


    "saveAndRun": {
      "commands": [
       {
          "match": "\\.(php|html|css|js)$",
          "cmd": "${workspaceRoot}/debug.cmd",
          "useShortcut": false,
          "silent": false
       }
      ]
    }    

Le fichier debug.cmd se trouvera dans le dossier de notre projet Web mais il pourrait être ailleurs. Ce fichier s’appuie encore une fois sur le programme websocat_win64.exe ( pour la communication par WebSocket), disponible sur cette page https://github.com/vi/websocat/releases/.

Bien sûr, devant la complexité de la programmation en batch/cmd je me suis posé la question de développer un programme complet capable de faire les appels WebSocket, capable de trier les objets json des réponses et pour simplifier l’installation selon les systèmes d’exploitations.

Etape 4: debug.cmd pour faire l’auto-refresh de la page

Le programme debug.cmd cherche dans la réponse à l’URL de débogage de Chrome http://127.0.0.1:9222/json un objet json contenant le mot « page » puis en cherchant l’URI pour le WebSocket avec le numéro unique qui permet de demander le rechargement de la page.

@echo off
 2 SETLOCAL ENABLEDELAYEDEXPANSION
 3 
 4 set count=1
 5 set pattern1="""type"": ""page"","
 6 set pattern2="webSocketDebuggerUrl"
 7 
 8 set findPage=0
 9 FOR /F "tokens=* USEBACKQ" %%F IN (`curl -s  http://127.0.0.1:9222/json`) DO (
10   REM find only page not web developper page with 'type' : 'page'
11   REM echo %%F | find %pattern1% 
12   for /f "tokens=2" %%I in ('echo %%F ^| find %pattern1%') DO (
13      if "%ERRORLEVEL%" == "0" (
14         set findPage=1
15      )
16   )
17 
18   REM find now webSocketDebuggerUrl
19   REM echo "!findPage!"
20   if !findPage!  == 1 (
21     REM find with 'webSocketDebuggerUrl' : 'ws://...'
22     for /f "tokens=2" %%L in ('echo %%F ^| find %pattern2%') DO (
23       if "%ERRORLEVEL%" == "0" (
24         set var=%%L
25       )
26     )
27   )
28 )
29 set str=%var:"devtoolsFrontendUrl":=%
30 set str=%str:,= %
31 set str=%str:"/devtools/inspector.html?=%
32 set str=%str: =%
33 set str=%str:"=%
34 set str=%str:ws=%
35 set str=!str:~1!
36 set str=ws://%str%
37 
38 echo {"id":0,"method":"Page.reload","params":{"ignoreCache":true}} | websocat_win64.exe -n1 %str% > NUL
39 
40 ENDLOCAL

J’ai essayé de rajouter une méthode pour trouver uniquement la page dont j’ai le nom passé en paramètre et gérer le cas de plusieurs onglets de Chrome. Malheureusement la performance est trop faible en batch/cmd. Il faudrait donc un vrai programme pour gérer cette situation et conserver en fait l’identifiant de l’onglet chrome à débogger une fois pour toute la session.

Conclusion

Donc xdebug est activé dans Wamp, Visual Code dispose d’une extension PHP Debug capable d’écouter les appels de xdebug. Visual Code a une configuration launch.json capable de lancer l’écoute et un navigateur qui va ouvrir une URL avec le paramètre XDEBUG_SESSION_START pour lancer xdebug.

Il manque que d’ajouter une extension Save and Run à Visual Code pour déclencher à la sauvegarde d’un fichier php du projet un auto-refresh de Chrome qui a été démarré avec les options de développement Web.

Le « Proof-of-concept » est réalisé grâce à cet enchainement. Mais pour commencer facilement, faite juste l’installation des 2 extensions, créer un sous-dossier dans le dossier www de Wamp, ajouter les fichiers dans ce dossier debug.cmd, launch.json et websocat_win64.exe et cela devrait commencer à fonctionner si votre fichier php.ini de Wamp a xdebug activé aussi.

Publié dans Développement | Laisser un commentaire

X-Sens Dot Sensor

Dans le cadre d’un projet de recherche, j’ai testé un senseur de chez X-Sens: X-Sens Dot :

Ce capteur doit permettre de remonter des informations accélération pour l’expérience. Devant la documentation en ligne sur ce dispositif et avec l’accord du support très réactif de l’entreprise X-sens, je pense qu’un tutoriel plus complet sur ce dispositif peut être utile au plus grand nombre.

Des informations actuellement que je dispose, je n’ai pas trouvé de solution pour connecter mon Windows 10 sur le X-Sens, le développement se fait via mon smartphone android (un samsung S10+) et avec Android Studio.

Je ne peux que vous conseiller de télécharger l’application officielle (xsensdot-apk-v2020.0.apk en Avril 2020) de X-Sens pour les X-Sens Dot et le SDK X-Sens Dot dans la rubrique Software/Wearable Sensor: https://www.xsens.com/software-downloads

Le SDK contient une documentation PDF et .chm et un fichier XsensDotSdk.aar qui a servi de base à ce tutoriel.

 

1/ Démarrage d’Android Studio

En premier lieu créons dans Android Studio un nouveau projet se basant sur une Activity vide:

Configuration du projet avec un nom, un nom de package, un dossier de stockage et surtout une version de SDK minimale à API 22 selon les recommandations de la documentation:

En premier ajoutons les droits spécifiques et nécessaire sur cette application, il est à noter que je déploie sur un smartphone réel et non vers l’émulateur. Je trouve que c’est plus rapide et plus efficace de développer sur un smartphone réel en suivant ces recommandations sur developer.android.com.

Ajoutons donc ces permissions, qu’il faudra activer aussi sur le smartphone individuellement pour Storage et Position (voir plus bas dans le tutoriel):

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 

2/ Importation de XsensDotSdk.aar

L’importation d’un module aar se fait en plusieurs étapes que j’ai découvert avec ce projet.

En premier lieu, il faut indiquer l’importation de ce module via le Menu File > New > New Module … puis choisir « Import .JAR/.AAR Package » , faire Next:

 

Indiquer le chemin vers le module XsensDotSdk.aar (pour le nom du subproject le laisser par défaut):

Ce n’est pas parce que le module est importé en tant que subproject que notre projet a une dépendance envers lui.

Donc, Menu File > Project Structure … > puis Dependencies > app (notre projet par défaut) puis le petit bouton + (Add Depency) puis choisir « Module Depency » :

 

 

 

Indiquons que l’on veut une dépendance vers XsensDotSDK, Ok pour valider cette fenêtre et ensuite de nouveau OK pour valider la fenêtre précédente:

Une phase de build se déclenche plus ou moins longue selon la performance de son ordinateur…

 

3/ Utilisation des classes du  XsensDotSdk.aar

Nous allons travailler dans ce tutoriel sur la MainActivity, ce n’est pas l’idéal mais pour un développement de test nous pouvons réaliser un premier essai.

Ajoutons l’implémentation des classes XsensDotDeviceCv et XSensDotScannerCb:

Nous avons une erreur d’importation que l’on règle en laissant la souris sur les 2 classes problématiques, IntelliJ nous propose d’importer les packages manquants:

Nous pouvons maintenant aussi fabriquer les implémentations manquantes pour les méthodes nécessaires aux classes XsensDotDeviceCv et XSensDotScannerCb en survolant avec notre curseur ces dernières:

 

L’ajout de toutes les méthodes se fait rapidement:

 

 

4/ Développement des fonctions de connexion au X-sens DOT

Nous allons donc coder dans la méthode onCreate encore une fois par simplicité puis nous détecterons que le smartphone a bien trouvé le X-Sens Dot en mettant un point d’arrêt dans la méthode onXsensDotScanner:

public void onXsensDotScanned(BluetoothDevice bluetoothDevice) {

Donc on code une propriété mxScanner

private XsensDotScanner mXsScanner;

Puis dans la méthode onCreate :

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    String version = XsensDotSdk.getSdkVersion();
    XsensDotSdk.setDebugEnabled(true);
    XsensDotSdk.setReconnectEnabled(true);
    mXsScanner = new XsensDotScanner(this.getApplicationContext(), this);
    mXsScanner.setScanMode(ScanSettings.SCAN_MODE_BALANCED);
    mXsScanner.startScan();
}

 

Enfin un bout de code pour récupérer les informations sur le device:

@Override
public void onXsensDotScanned(BluetoothDevice bluetoothDevice) {
    String name = bluetoothDevice.getName();
    String address = bluetoothDevice.getAddress();
}

Choisissez ensuite votre smartphone (il faut que le débuggage soit possible sur ce smartphone d’ailleurs cf developer.android.com) puis le bouton « débug app » :

Voici les indications de débugage avec les adresses (factice sur cet écran) de réponse:

Ajoutons maintenant la récupération des données par le capteur en ajoutant une nouvelle propriété et  en modification la méthode onXsensDotScanned:

Nouvelle propriété: xsDevice pour gérer la connexion à notre Xsens Dot

private XsensDotDevice xsDevice = null;

 

Nouvelle version de onXsensDotScanned

@Override
public void onXsensDotScanned(BluetoothDevice bluetoothDevice) {
    String name = bluetoothDevice.getName();
    String address = bluetoothDevice.getAddress();

    xsDevice = new XsensDotDevice(this.getApplicationContext(), bluetoothDevice,MainActivity.this);
    xsDevice.connect();
    xsDevice.startMeasuring();
}

Notre X-Sens Dot devrait capture les données dans la méthode onXsensDotDataChanged et l’envoyer dans le loger LogCat d’Android:

@Override
public void onXsensDotDataChanged(String address, XsensDotData xsensDotData) {
    Log.i( "XsensDot", "onXsensDotDataChanged " + address + " " + xsensDotData );
}

 

Nous lançons le débugage avec un point d’arrêt sur l’évènement onXsensDotDataChanged et un regard sur le LogCat dans IntelliJ:

Aucune donnée n’arrive sur la méthode onXsensDotDataChanged….

 

5/ Ajout des droits dans le smartphone

Les données n’arrivent pas car il faut ajouter les droits sur l’application directement sur ce dernier. j’avais eu déjà le problème avec les IMU embarquées dans le smartphone.

Il faut donc aller sur l’application, faire un appuie long, puis choisir « Infos sur l’appli. » puis dans l’écran « informations sur l’application » choisir le menu « Autorisations » puis accepter les 2 autorisations « Localisation » et « Stockage » pour enfin pour relancer l’application avec les droits complets:

Et donc récupération des données sur un point d’arrêt du débug quand les données arrivent:

 

6/ Liaison Xsens DOT – Smartphone Android – Code JAVA

Pour la récupération des données de l’accélération  angulaire sur X du capteur Xsens DOT, j’ai utilisé une liaison bluetooth additionnel de type Port COM à 115200 bauds avec un programme en Java sur mon PC:

 

Vous pouvez constater sur la vidéo ci-dessous que le résultat est quand même assez impressionnant, la rotation de ma main déclenche bien le déplacement du point rouge sur le smartphone et l’envoie au PC de l’information Gauche/Droite/Centre pour faire réaliser le même déplacement au point vert.

Il y a un délais bien sur entre la modification d’orientation et le déplacement du point. L’algorithme doit être améliorer pour diminuer ce délais.

7/ Nouvelle version des SDK de Xsens

Suite à la conférence d’Avril 2020 de Xsens, j’ai la possibilité de récupérer les données directement depuis le PC. Donc la nouvelle version doit être directement développée sur l’ordinateur pour récupérer les données via le protocole Bluetooth BLE.

Pour simplifier le développement sur Windows, j’ai développé le code en C#. En effet, mes anciens développement avec le bluetooth m’ont montré que ce langage est le plus proche du système d’exploitation sur windows; aucune surprise le langage C# appartient à Microsoft. Du coup, je pense que les ouvertures/fermetures des canaux de communication bluetooth BLE seront simplifiées par ce langage de programmation.

Mon conseil est d’en premier lieu de travailler à partir du document « Xsens DOT BLE Services Specifications » proposé en téléchargement par Xsens sur son site internet. Ce document indique bien une évolution dans l’accès à certaines données envoyés par la nouvelle version (1.3) des Xsens. Personnellement, je vais continuer à utiliser la version 1.0.0 car moins de données = plus simple à interpréter.

Nota: attention au format big-endiant/little-endian des données, j’ai passé un peu de temps en lisant les octets dans le mauvais ordre.
Ce n’est pas encore standardisé, heureusement la documentation indique « All the big-endian members will be changed to little-endian in the next firmware release ».

Le développement a du être mis en stand-by pour cause de projet en parallèle, mais les premiers résultats sont encourageant. Le standard Bluetooth BLE est bien accepté par le xSens DOT comme montre la vidéo suivante qui affiche les accélérations du composant sur un seul axe.

Démonstration de la liaison bleutooth BLE du Xsens avec un développement C# sur un PC Windows 10 Pro version 18362 (Indiquer ma version n’est pas anodin pour le développement bluetooth BLE).

Publié dans Développement | Laisser un commentaire

git sourcesup

https://sourcesup.renater.fr/account/
Mettre un mot de passe du compte unix
Vérifier le mot de passe: https://sourcesup.renater.fr/account/change_pw.php

Trouver l’URL
URL : https://pierre.jean@git.renater.fr/authscm/pierre.jean/git/rearm/rearm.git cf copie d’écran en dessous pour le trouver
login UNIX pierre.jean
le BON MDP de SOurcesup

Ouvrir GitHub Desktop, faire clone a repository du smart https
https://pierre.jean@git.renater.fr/authscm/pierre.jean/git/rearm/rearm.git

—–
Ne fonctionne plus ?

Commande
git init

git clone ssh://git@git.renater.fr:2222/nom_projet.git

git clone https://pierre.jean@git.renater.fr/authscm/pierre.jean/git/rearm/rearm.git

Ouvre fenêtre authentification pour cloner sauf…

git config –global user.email « pierre.jean@mines-ales.fr »
git config –global user.name « pierre.jean »
git add rearm
git commit -m « Message de validation »

Publié dans Développement | Laisser un commentaire

Taiga Docker surprises

Pour une première étape avec Docker, j’ai essayé d’installer le logiciel taiga via cet outil de container. La solution la plus classique serait de prendre une image ubuntu et de suivre l’installation de docker (Setup development installation taiga).

Sauf qu’après avoir suivi une formation de Docker, j’ai compris que l’intérêt de Docker ou du moins un intéret est dans les micro service soit d’avoir 5 containers au lieu d’un seul.


NOTE: Vous avez un problème pour impoter votre projet taiga dans taiga (par exemple après une mise à jours) ?

Taiga contient un utilitaire qui se trouve dans le cotnaineur taiga-back et qui permet de faire l’importation suivant cette commande:

/usr/bin/python3 manage.py load_dump dump.json p.jean@mines-ales.fr

Taiga et Docker

Taiga est un logiciel de gestion de projet agile de type assez classique avec kanban et sprint intégré. Ce que j’aime dans Taiga en plus qu’il soit open source est que l’on peut activer ou non les outils agile, par exemple commencé avec un simple kanban et ensuite rajouter des sprint et/ou un wiki et/ou un gestionnaire de bug.

Taiga n’a pas de version officielle de Docker. Plusieurs images sont disponibles mais j’avoue que parfois les personnes qui ont fait les images avaient des idées très précises de leur utilisation qui ne m’allait par.

Dans ma solution je souhaite que Docker puisse gérer les cinq containers avec l’avantage de pouvoir renforcer un container ou le remplacer au besoin :

  • taiga-db database postgreSQL
  • taiga-front pour la partie front web de Taiga
  • taiga-back pour la partie backoffice de Taiga
  • taiga-rabbit pour le gestionnaire de message dans Taiga
  • taga-events pour l’envoie des notifications dans Taiga via le navigateur
  • taiga-proxy pour le serveur front nginx dispatchant les requêtes.

L’image de docker de base

Je pars donc d’une image existante de docker. En fait, j’en essaye plusieurs avant de trouver une image qui correspond à ce que je souhaite soit avec une base de données et avec la partie notifications/messages.

La version suivante : https://github.com/docker-taiga/taiga semble être un bon compromis. Je télécharge donc le dépot github et je fais une installation sans problème sur un serveur linux de test.

Hors, cette installation ne fonctionne qu’une seule fois. Je n’arrive pas en relançant à récupérer le fonctionnement de mon serveur taiga. Après plusieurs essaies et avec l’obligation de devoir intégrer mes propres développements dans Taiga, je suis donc contraint de prépare une installation à partir du fichier docker-compose.yml.

Docker-taiga-pje

Pourquoi faire une version de taiga docker personnelles:

  • Je souhaite intégrer 1 fichier et une bibliothèque python en plus.
  • Je souhaite débugger avec un outil curl dans taiga-back et taiga-proxy
  • Je souhaite que le déploiement se fasse tout seul avec le minimum d’intervention de ma part
  • Le container taiga-back contient un serveur web nginx qui fait double emploie pour moi avec le serveur taiga-proxy en frontal
  • Monter en compétence avec Docker avec un exemple et des contraintes réelles
  • Docker sur Windows 10 avec tout les désavantages que l’on y trouve (et il y en a Docker est fait pour Linux et cela se voit)

La première étape que je vais faire est de créer un dossier pour le taiga-proxy et modifier le code de nginx.conf et nginx_ssl.conf pour que le nginx de ce container aille chercher le http://back:8000/. Cela suppose aussi la modificaiton du docker-compose.yml pour qu’il prenne en charge cette nouvelle image appelée taiga-proxy-pje.

Puis je faire la même modification pour le taiga-back pour qu’il prenne en charge de nouveau modules python et modifier le script de démarrage pour ne pas charger le serveur nginx. J’en profiterai pour que le port 8000 de ce container soit accessible pour faire le débogage avec curl installé dans ce container.

Etape 1 création de l’image taiga-proxy-pje

Téléchargement des fichiers du dépot git-hub depuis https://github.com/docker-taiga/proxy

Version initiale du fichier nginx.conf

server {
	server_name $TAIGA_HOST;
	listen 80;
	location ^~ /events {
		proxy_pass http://$EVENTS_HOST:8888/;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_connect_timeout 7d;
		proxy_send_timeout 7d;
		proxy_read_timeout 7d;
	}
	location ^~ /api {
		include proxy_params;
		proxy_pass http://$TAIGA_BACK_HOST;
	}
	location ^~ /admin {
		include proxy_params;
		proxy_pass http://$TAIGA_BACK_HOST;
	}
	location ^~ /static {
		include proxy_params;
		proxy_pass http://$TAIGA_BACK_HOST;
	}
	location ^~ /media {
		include proxy_params;
		proxy_pass http://$TAIGA_BACK_HOST;
	}
	location / {
		include proxy_params;
		proxy_pass http://$TAIGA_FRONT_HOST;
	}
}

Version modifiée du fichier nginx.conf

server {
server_name $TAIGA_HOST;
listen 80;
location ^~ /events {
proxy_pass http://$EVENTS_HOST:8888/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection « upgrade »;
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
}
location ^~ /api {
include proxy_params;
proxy_pass http://$TAIGA_BACK_HOST:8000;
}
location ^~ /admin {
include proxy_params;
proxy_pass http://$TAIGA_BACK_HOST;
}
location ^~ /static {
include proxy_params;
proxy_pass http://$TAIGA_BACK_HOST;
}
location ^~ /media {
include proxy_params;
proxy_pass http://$TAIGA_BACK_HOST;
}
location / {
include proxy_params;
proxy_pass http://$TAIGA_FRONT_HOST;
}
}

Maintenant dans le dossier taiga-pje/proxy en ligne de commande pour construire l’image taiga-proxy-pje :

docker build -t taiga-proxy-pje .

Il faut ensuite modifier le fichier docker-compose.yml pour ne plus appeler l’image taiga-proxy mais l’image taiga-proxy-pje, donc après avoir téléchargé tous les fichiers du dossier taiga via git

git clone –depth=1 -b latest https://github.com/docker-taiga/taiga.git

Nous obtenons le dossier suivant:

Nous pouvons donc ensuite modifier le fichier docker-compose.yml:

version: ‘3’

services:
back:
image: taiga-back
container_name: taiga-back
restart: unless-stopped
depends_on:
– db
– events
networks:
– default
volumes:
– ./data/media:/taiga-media
– ./conf/back:/taiga-conf
env_file:
– variables.env

front:
image: dockertaiga/front
container_name: taiga-front
restart: unless-stopped
networks:
– default
volumes:
– ./conf/front:/taiga-conf
env_file:
– variables.env

db:
image: postgres:11-alpine
container_name: taiga-db
restart: unless-stopped
networks:
– default
env_file:
– variables.env
volumes:
– ./data/db:/var/lib/postgresql/data

rabbit:
image: dockertaiga/rabbit
container_name: taiga-rabbit
restart: unless-stopped
networks:
– default
env_file:
– variables.env

events:
image: dockertaiga/events
container_name: taiga-events
restart: unless-stopped
depends_on:
– rabbit
networks:
– default
env_file:
– variables.env

proxy:
image: taiga-proxy-pje
container_name: taiga-proxy
restart: unless-stopped
depends_on:
– back
– front
– events
networks:
– default
ports:
– 80:80
– 443:443
volumes:
#- ./cert:/taiga-cert
– ./conf/proxy:/taiga-conf
env_file:
– variables.env

networks:
default:

Pour fabriquer l’ensemble des containers en allant chercher les images, la magie de docker est appeler avec

cd taiga
docker-compose.exe up -d

Pour mémoire, le dossier taiga/ contient le docker-compose.yml qui va faire l’assemblage des images et le dossier proxy contient l’image spéciale docker-proxy-pje.

Rapidement, vous allez faire un fichier go.bat avec:

cd proxy
docker build -t taiga-proxy-pje .
cd ..
cd taiga
docker-compose.exe up -d
cd ..

et un purge.bat qui fait plusieurs actions: retire les fichiers indiquant que ce démarrage n’est pas la version initiale, arrête l’option redémarrage des container pour pouvoir les arrêter :

  • retire les fichiers .initial_setup.lock qui indiquent que la configuration a déjà été faite (cf processus de création des fichiers conf ci-dessous)
  • mettre à jours le critères restart à no pour me permettre d’arrêter les containers car sinon docker les redémarre automatiquement, vous pouvez aussi faire docker update –restart=np $(docker ps -q) pour empecher tous les containers en fonctionnement de redémarrer
  • effacer les containeurs
  • effacer les images
  • effacer les volumes non utilisé par aucun container, attention cela pourrait retirer aussi d’autres volumes qui ne serait pas lié à des containers
rm taiga/conf/back/.initial_setup.lock
rm taiga/conf/front/.initial_setup.lock
rm taiga/conf/proxy/.initial_setup.lock
docker update –restart=no $(docker ps -a -q –filter « name=taiga-proxy » –filter « name=taiga-events » –filter « name=taiga-front » –filter « name=taiga-rabbit » –filter « name=taiga-db »)
docker rm taiga-proxy taiga-back taiga-events taiga-front taiga-rabbit taiga-db
docker rmi taiga-proxy-pje dockertaiga/front dockertaiga/events dockertaiga/rabbit dockertaiga/back
docker volume prune –force

Attention c’est assez violent de retirer tous les volumes non utilisé. Dans mon cas je n’avais que les containers et images liés à taiga.

Processus de créations des fichiers

J’ai eu beaucoup de problème pour comprendre qui fabrique les fichiers dans le dossier taiga/conf. En effet, au premier démarrage des containers taiga-back, taiga-proxy, taiga-front, le script appelé par le RUN (habituellement start.sh) de chaque Dockerfile va créer les dossiers et fichiers de la configuraton pour qu’au redémarrage du container, ces fichiers soient utilisés au lieu de les recréer à chaque fois. Le problème est de créer un processus complet de fabrication et non pas de bricoler dans les fichiers de taiga/conf/. On peut modifier ces fichiers pour tester quelqus réglages de nginx par exemple mais il faut bien qu’à la fin on ai la totalité du processus from scratch

Enfin, j’ai un taiga proxy qui appelle sur le port 8000, le serveur django. Maintenant, il faut configurer le taiga-back pour ne pas utiliser le nginx inclus mais directement le django

taiga-back-pje

Création du dossier back et modification du fichier go.sh

cd proxy
docker build -t taiga-proxy-pje .
cd ..
cd back
docker build -t taiga-back-pje .
cd ..

cd taiga
docker-compose.exe up -d
cd ..

et purge.bat:

rm taiga/conf/back/.initial_setup.lock
rm taiga/conf/front/.initial_setup.lock
rm taiga/conf/proxy/.initial_setup.lock
docker update –restart=no $(docker ps -a -q –filter « name=taiga-proxy » –filter « name=taiga-events » –filter « name=taiga-front » –filter « name=taiga-rabbit » –filter « name=taiga-db »)
docker rm taiga-proxy taiga-back taiga-events taiga-front taiga-rabbit taiga-db
docker rmi taiga-proxy-pje dockertaiga/front dockertaiga/events dockertaiga/rabbit taiga-back-pje
docker volume prune –force

Dans notre dossier back, nous allons trouver:

d—– 11/06/2019 11:04 users <— Le dossier contenant le nouveau fichier python pour l’OTP
-a—- 13/06/2019 18:43 1225 config.py <— Le fichier de configuration de taiga back
-a—- 08/06/2019 14:52 1956 Dockerfile <— Le fichier Dockerfile où le travail est le plus important
-a—- 02/06/2019 17:26 1466 Dockerfile.v0 <— Fichier Dockerfile initial, pour voir entre Linux et Windows
-a—- 08/06/2019 14:17 157 ntpd.conf <— Le container n’a pas accès à l’heure, cf plus bas
-a—- 13/02/2019 10:26 74 README.md <— Le fichier README de github
-a—- 10/06/2019 20:05 2015 start.sh <— Le nouveau fichier start.sh qui va bien changer

Il faut avec le terminal créer une nouvelle image docker-back que l’on va appeler docker-back-pje.

docker build -t taiga-back-pje .

Cette commande fabrique une nouvelle image de taiga-back. Sauf que sur windows, j’ai une erreur :

WARNING: Ignoring APKINDEX.b89edf6e.tar.gz: No such file or directory
WARNING: Ignoring APKINDEX.737f7e01.tar.gz: No such file or directory

Cette erreur vient d’un problème de dossier cache des packages d’alpine.

La solution est de modifier le dockerfile pour détruire ce cache et d’ajouter le package ca-certificates.
Je rajoute aussi le package « jq » pour avoir un outil d’affichage des données json lisible.

FROM alpine:latestENV TAIGA_HOST=taiga.lan \
TAIGA_SECRET=secret \
TAIGA_SCHEME=http \
POSTGRES_HOST=db \
POSTGRES_DB=taiga \
POSTGRES_USER=postgres \
POSTGRES_PASSWORD=password \
RABBIT_HOST=rabbit \
RABBIT_PORT=5672 \
RABBIT_USER=taiga \
RABBIT_PASSWORD=password \
RABBIT_VHOST=taiga \
STARTUP_TIMEOUT=15sWORKDIR /srv/taigaRUN rm -rf /var/cache/apk/* && rm -rf /tmp/* \
&& apk –no-cache add python3 gettext postgresql-dev libxslt-dev libxml2-dev libjpeg-turbo-dev zeromq-dev libffi-dev nginx \
&& apk add –no-cache –virtual .build-dependencies musl-dev python3-dev linux-headers git zlib-dev libjpeg-turbo-dev gcc ca-certificates jq \
&& mkdir logs \
&& git clone –depth=1 -b stable https://github.com/taigaio/taiga-back.git back && cd back \
&& sed -e ‘s/cryptography==.*/cryptography==2.3.1/’ -i requirements.txt \
&& pip3 install –upgrade pip \
&& pip3 install -r requirements.txt \
&& rm -rf /root/.cache \
&& apk del .build-dependencies \
&& rm /srv/taiga/back/settings/local.py.example \
&& rm /etc/nginx/conf.d/default.confEXPOSE 80

WORKDIR /srv/taiga/back

COPY config.py /tmp/taiga-conf/
COPY nginx.conf /etc/nginx/conf.d/
COPY start.sh /

VOLUME [« /taiga-conf », « /taiga-media »]

CMD [« /start.sh »]

Ok cela fabrique l’image, je peux modifier le fichier docker-compose.yml et recréer l’ensemble des images manquantes et des containers associés avec le script go.sh

version: ‘3’

services:
back:
image: taiga-back-pje
container_name: taiga-back
restart: unless-stopped
depends_on:
– db
– events
networks:
– default
volumes:
– ./data/media:/taiga-media
– ./conf/back:/taiga-conf
env_file:
– variables.env

front:
image: dockertaiga/front
container_name: taiga-front
restart: unless-stopped
networks:
– default
volumes:
– ./conf/front:/taiga-conf
env_file:
– variables.env

db:
image: postgres:11-alpine
container_name: taiga-db
restart: unless-stopped
networks:
– default
env_file:
– variables.env
volumes:
– ./data/db:/var/lib/postgresql/data

rabbit:
image: dockertaiga/rabbit
container_name: taiga-rabbit
restart: unless-stopped
networks:
– default
env_file:
– variables.env

events:
image: dockertaiga/events
container_name: taiga-events
restart: unless-stopped
depends_on:
– rabbit
networks:
– default
env_file:
– variables.env

proxy:
image: taiga-proxy-pje
container_name: taiga-proxy
restart: unless-stopped
depends_on:
– back
– front
– events
networks:
– default
ports:
– 80:80
– 443:443
volumes:
#- ./cert:/taiga-cert
– ./conf/proxy:/taiga-conf
env_file:
– variables.env

networks:
default:

Sauf que après un petit tour dans le container taiga-back fabriqué à partir de mon image taiga-back-pje

docker exec -it taiga-back sh

La date est complètement fausse sur Windows

En fait, docker windows ne peut accéder (avec la version que j’avais en mars 2019) à l’horloge système.
Il n’y a pas de problème sous Linux.

Comme j’ai besoins de cette date à jour pour la suite du projet. Je dois injecter un serveur de temps openntpd et le configurer dans l’image docker-back-pje

Nouveaux modifications du fichier Dockerfile

FROM alpine:latestENV TAIGA_HOST=taiga.lan \
TAIGA_SECRET=secret \
TAIGA_SCHEME=http \
POSTGRES_HOST=db \
POSTGRES_DB=taiga \
POSTGRES_USER=postgres \
POSTGRES_PASSWORD=password \
RABBIT_HOST=rabbit \
RABBIT_PORT=5672 \
RABBIT_USER=taiga \
RABBIT_PASSWORD=password \
RABBIT_VHOST=taiga \
STARTUP_TIMEOUT=15sWORKDIR /srv/taigaRUN rm -rf /var/cache/apk/* && rm -rf /tmp/* \
&& apk –no-cache add python3 gettext postgresql-dev libxslt-dev libxml2-dev libjpeg-turbo-dev zeromq-dev libffi-dev nginx \
&& apk add –no-cache –virtual .build-dependencies musl-dev python3-dev linux-headers git zlib-dev libjpeg-turbo-dev gcc ca-certificates jq openntpd \
&& mkdir logs \
&& cp /usr/share/zoneinfo/Europe/Paris /etc/localtime && echo « Europe/Paris » > /etc/timezone && renice -n-8 $(pidof ntpd -s) \
&& git clone –depth=1 -b stable https://github.com/taigaio/taiga-back.git back && cd back \
&& sed -e ‘s/cryptography==.*/cryptography==2.3.1/’ -i requirements.txt \
&& pip3 install –upgrade pip \
&& pip3 install -r requirements.txt \
&& rm -rf /root/.cache \
&& apk del .build-dependencies tzdata \
&& rm /srv/taiga/back/settings/local.py.example \
&& rm /etc/nginx/conf.d/default.confEXPOSE 80

WORKDIR /srv/taiga/back

COPY ntpd.conf /etc/ntpd.conf
COPY config.py /tmp/taiga-conf/
COPY nginx.conf /etc/nginx/conf.d/
COPY start.sh /

VOLUME [« /taiga-conf », « /taiga-media »]

CMD [« /start.sh »]

On note qu’il y a une injection de la timezone dans le fichier /etc/timezone pour indiquer le fuseau horaire

Bien sur le fichier ntpd.conf sur le disque dur a ces informations

# See ntpd.conf(5) and /etc/examples/ntpd.conf
listen on *server time1.google.com
server time2.google.com
server time3.google.com
server time4.google.com

On peut indiquer d’autres serveurs de temps dans le cas d’une structure qui aurait une politique locale des serveurs de temps

Je suis toujours dessus pour améliorer le processus…

Publié dans Développement | Laisser un commentaire