OSX : connaitre la version en ligne de commande

Vous pouvez avoir besoin de connaitre la version courante pour les besoins d’un script ou tout simplement vouloir la connaitre rapidement en ligne de commande.

Un utilitaire sw_vers est livré en standard et vous affiche la version et le build courant :

$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.10.5
BuildVersion: 14F1605

Des paramètres existent pour obtenir directement l’information dont vous avez besoin :

$ sw_vers -productName
Mac OS X
$ sw_vers -productVersion
10.10.5
$ sw_vers -buildVersion
14F1605

 

Phantomjs : faire un screenshot d’un site en https

J’utilise phantomjs pour faire des captures d’écran régulièrement de pages d’un site pour voir l’évolution de la page ou tout simplement créer des archives sans avoir à y penser.

screenshots avec phantomjs en javascript

Phantomjs permet de manipuler en javascript le moteur de rendu webkit afin de simuler un navigateur en ligne de commande.

Problème de rendu avec phantomjs et le https

En prenant des screenshots de notre site de coaching,  je me suis aperçu que les fichiers png générés étaient vides.

Après investigation, il s’avère que par défaut, phantomjs peut avoir des problèmes avec les sites en https.

Pour résoudre la situation, il faut utiliser le switch –ssl-protocol=any ou –ssl-protocol=TLSv1. Par défaut phantomjs utilise SSLv3 qui ne fonctionne pas dans mon cas.

phantomjs --ssl-protocol=any fitnext.js

Script pour faire le rendu phantomjs

Le javascript final qui permet de faire le screenshot ressemble à ceci :

var page = new WebPage();
page.settings.userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.65 Safari/537.36';
page.viewportSize = { width: 1280, height: 800 };
page.open('https://www.fitnext.com', function (status) {
    var now = new Date();
    var month = now.getMonth()+1; if (month < 10) month = '0'+month;
    var day = now.getDate(); if (day < 10) day = '0'+day;
    var filename = 'homepage/' + now.getFullYear() +'-'+month+'-'+day+ '_homepage_fitnext.png';
    page.render(filename);
    phantom.exit();
});

Dans ce script, j’utilise les fonctions javascript de date pour créer un nom de fichier qui correspond à la date du jour.

Ici, le nom de fichier de la capture effectuée par phantomjs ressemblera à : 2016-02-20_homepage_fitnext.png

Automatisation des captures d’écran phantomjs

Pour prendre des screenshots quotidiens de la homepage, je configure une tâche cron pour cela :

40 12 * * *     cd /chemin/de/votre/script && phantomjs --ssl-protocol=tlsv1 fitnext.js > /dev/null

Je redirige la sortie standard vers /dev/null pour ne pas recevoir d’email contenant les erreurs javascript de la console.

 

Postfix: envoyer des emails à travers un tunnel ssh

postfix-ssh

 

Dans le cadre de la création d’emails de reporting, je dois envoyer des emails en local pour mes tests.

Ma machine de développement est un mac. J’ai toujours eu des problèmes pour envoyer des mails en local, donc une des solutions est d’envoyer les emails via un serveur qui est déjà correctement configuré.

Pour cela, on peut tout simplement utiliser un tunnel ssh et configurer postfix pour passer par ce tunnel pour envoyer les emails.

Configuration du tunnel

Pour créer le tunnel, utiliser la commande suivante avec monserveur.com correspondant à votre machine de relais :

$ ssh -f -o ServerAliveInterval=10 -L 54321:localhost:25 monserveur.com sleep 31622400

 

Configuration de postfix

Pour router tous les emails arrivant dans postfix vers votre relais, configurer le fichier transport, généralement /etc/postfix/transport en ajoutant la ligne suivante :

*             smtp:[127.0.0.1]:54321

Pour les détails sur la configuration de ce fichier, se reporter à la page man correspondante.

Ici, avec l’astérisque, on indique que tous les emails passeront via la passerelle 127.0.0.1 sur le port 54321 correspondant au point d’entrée de notre tunnel vers notre relais.

Enfin dans la configuration principale de postfix, /etc/postfix/main.cf il faut indiquer que l’on a un fichier de configuration de mapping pour la distribution des emails :

transport_maps = hash:/etc/postfix/transport

 

Pour prendre en compte la nouvelle configuration, sous osx :

$ sudo postmap /etc/postfix/transport
$ sudo launchctl stop org.postfix.master
$ sudo launchctl start org.postfix.master

Lors de l’émission d’un email en local, on peut observer la livraison via notre tunnel dans les logs (/var/log/mail.log) :

Feb 11 12:10:42 MacBook-Pro-de-Charles-ADG-2.local postfix/pickup[60932]: A8A7025D17CE: uid=502 from=<charles>
Feb 11 12:10:42 MacBook-Pro-de-Charles-ADG-2.local postfix/cleanup[60934]: A8A7025D17CE: message-id=<20160211111042.A8A7025D17CE@fitnext.com>
Feb 11 12:10:42 MacBook-Pro-de-Charles-ADG-2.local postfix/qmgr[60933]: A8A7025D17CE: from=<charles@fitnext.com>, size=304, nrcpt=1 (queue active)
Feb 11 12:10:42 MacBook-Pro-de-Charles-ADG-2.local postfix/smtp[60936]: A8A7025D17CE: to=<charles@fitnext.com>, relay=127.0.0.1[127.0.0.1]:54321, delay=0.78, delays=0.7/0.02/0.02/0.04, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as B75763701063)
Feb 11 12:10:42 MacBook-Pro-de-Charles-ADG-2.local postfix/qmgr[60933]: A8A7025D17CE: removed

 

Et dans les logs de notre relais :

Feb 11 12:10:42 localhost postfix/smtpd[43921]: connect from localhost[127.0.0.1]
Feb 11 12:10:42 localhost postfix/smtpd[43921]: B75763701063: client=localhost[127.0.0.1]
Feb 11 12:10:42 localhost postfix/cleanup[43924]: B75763701063: message-id=<20160211111042.A8A7025D17CE@fitnext.com>
Feb 11 12:10:42 localhost postfix/smtpd[43921]: disconnect from localhost[127.0.0.1]
Feb 11 12:10:42 localhost postfix/qmgr[1673]: B75763701063: from=<charles@fitnext.com>, size=484, nrcpt=1 (queue active)
Feb 11 12:10:44 localhost postfix/smtp[43925]: B75763701063: to=<charles@fitnext.com>, relay=aspmx.l.google.com[64.233.184.27]:25, delay=1.4, delays=0.03/0/1.2/0.2, dsn=2.0.0, status=sent (250 2.0.0 OK 1455189044 wg3si11321364wjb.162 - gsmtp)
Feb 11 12:10:44 localhost postfix/qmgr[1673]: B75763701063: removed

 

 

Ubuntu : mettre à jour son système

Pour voir la version de votre Ubuntu :

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.3 LTS
Release: 14.04
Codename: trusty

Vous pouvez vérifier les périodes de support des différentes versions d’Ubuntu sur le site officiel.

Récupérer la liste des mises à jour disponibles 

$ apt-get update
Get:1 http://security.ubuntu.com trusty-security InRelease [64.4 kB]
Ign http://archive.ubuntu.com trusty InRelease
Get:2 http://archive.ubuntu.com trusty-updates InRelease [64.4 kB]
Get:3 http://security.ubuntu.com trusty-security/main amd64 Packages [410 kB]
Hit http://archive.ubuntu.com trusty Release.gpg
[...]

 

Mettre à jour les paquets installés

$ apt-get upgrade

Mettre à jour les paquets ainsi que les dépendances

$ apt-get dist-upgrade

Commandes de base pour PostgreSQL

C’est toujours pratique d’avoir sous la main une liste de commandes de base de postgresql pour les tâches quotidiennes.

Lister les bases de données

psql=> \l
                              List of databases
     Name     |  Owner   | Encoding | Collation  |   Ctype    |   Access privileges
--------------+----------+----------+------------+------------+-----------------------
 test         | test     | UTF8     | fr_FR.UTF8 | fr_FR.UTF8 |
(...)

Lister les tables

psql> \dt *

... 
public | metrics | table | charles
public | news    | table | charles
...

 

Créer une base de données postgres

psql> CREATE DATABASE sport WITH ENCODING 'UTF8' OWNER=charles ;

Syntaxe complète

CREATE DATABASE db_name
    OWNER =  role_name
    TEMPLATE = template
    ENCODING = encoding
    LC_COLLATE = collate
    LC_CTYPE = ctype
    TABLESPACE = tablespace_name
    CONNECTION LIMIT = max_concurrent_connection
  • db_name: le nom de la base de données. Ce nom est unique sur le serveur.
  • role_name: le rôle propriétaire de la base. Le rôle par défaut est celui de l’utilisateur qui exécute la commande.
  • template: le modèle de base pour la création de votre base. Par défaut, PostgreSQL utilise le template « template1 »
  • encoding: l’encodage de la base. Par défaut, c’est celui du template.
  • collate: définit les règles de collation. Ces règles agissent sur le tri (ORDER BY) des chaînes de caractères. Par défaut c’est celui du template qui est utilisé.
  • ctype: définit les classes de caractères (chiffre, minuscule, majuscule, …).  Par défaut, celui du template est utilisé.
  • tablespace_name: définit le tablespace , c’est à dire l’espace de stockage, où la base va être stockée. Par défaut, celui du template est utilisé.
  • max_concurrent_connection: définit le nombre de connexion simultanées sur la base. Par défaut, -1 est utilisé, qui signifie pas de limite.

 

Lister les contraintes d’une table

 

SELECT
    tc.constraint_name, tc.table_name, kcu.column_name, 
    ccu.table_name AS foreign_table_name,
    ccu.column_name AS foreign_column_name
FROM
    information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
WHERE tc.table_name='meals';

 

 constraint_name | table_name | column_name | foreign_table_name | foreign_column_name
-----------------+------------+-------------+--------------------+---------------------
 meals_pkey      | meals      | id          | meals              | id

Sauvegarder une base de données

 

$ pg_dump -U charles sport > sport.dump.sql

 

Importer un dump

 

$ psql -U charles sport < sport.dump.sql

 

Sauvegarder toutes les bases de données postgresql

 

$ pg_dumpall > all.dump.sql

 

Sauvegarder une table en particulier

 

$ pg_dump --table matches -U charles sport -f sport.matches.dump.sql

 

Supprimer toutes les tables d’une base

 

psql -U <user> -t -d <database> -c "SELECT 'DROP TABLE ' || n.nspname || '.' || c.relname || ' CASCADE;' FROM pg_catalog.pg_class AS c LEFT JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace WHERE relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'pg_toast') AND
pg_catalog.pg_table_is_visible(c.oid)" >/tmp/droptables
psql -U <user> -d <database> -f /tmp/droptables

 

Exporter une requête en .csv

 

psql> COPY (SELECT * FROM ingredients) TO '/tmp/export.csv' WITH CSV HEADER;

 

Pour plus d’informations, la documentation officielle en français.

Apache: compresser les logs générés par cronolog

Dans un article précédent, j’expliquais comment utiliser cronolog avec Apache pour créer des fichiers en fonction de la date.

On obtient des fichiers classés de la sorte :

/var/log/apache2# ls -l 2016/01/
-rw-r--r-- 1 root root 4136552 Jan 1 23:59 01_blog.preprod_access.log
-rw-r--r-- 1 root root 86773599 Jan 1 23:59 01_blog.prod_access.log
-rw-r--r-- 1 root root 5314165 Jan 2 23:59 02_blog.preprod_access.log
-rw-r--r-- 1 root root 177562123 Jan 2 23:59 02_blog.prod_access.log
-rw-r--r-- 1 root root 5804359 Jan 3 23:59 03_blog.preprod_access.log
-rw-r--r-- 1 root root 159230127 Jan 3 23:59 03_blog.prod_access.log
[...]

Cronolog  ne compresse pas les fichiers qu’il écrit.

Pour faire de la place et archiver progressivement les logs, un script à base de la commande find fait largement l’affaire :

find /var/log/apache2/`date +%Y`/ -name « *.log » -mtime +1 -exec gzip « {} » « ; »

Ici, j’ai ajouté `date +%Y` dans le chemin de recherche des logs pour fonctionner avec les logs de l’année courante.

  • -mtime +1 : recherche les fichiers dont la date de modification est supérieure à 24h (1*24h)
/var/log/apache2# ls -l 2016/01/
total 579552
-rw-r--r-- 1 root root 180159 Jan 1 23:59 01_blog.preprod_access.log.gz
-rw-r--r-- 1 root root 5723517 Jan 1 23:59 01_blog.prod_access.log.gz
-rw-r--r-- 1 root root 225293 Jan 2 23:59 02_blog.preprod_access.log.gz
-rw-r--r-- 1 root root 9896313 Jan 2 23:59 02_blog.prod_access.log.gz
-rw-r--r-- 1 root root 241911 Jan 3 23:59 03_blog.preprod_access.log.gz
-rw-r--r-- 1 root root 9817631 Jan 3 23:59 03_blog.prod_access.log.gz
[...]

Utilisation de tar en ligne de commande

Tar est un utilitaire historique de gestion d’archives de fichiers. Un fichier .tar peut contenir un ou plusieurs fichiers.

Créer une archive d’un répertoire

$ tar cvf books.tar books/
a books
a books/.git
a books/.git/branches
a books/.git/config
a books/.git/description
a books/.git/HEAD
[...]
$ ls -l books.tar
-rw-r--r-- 1 charles staff 32256 16 jan 18:36 books.tar

 

Créer une archive compressée avec gzip

$ tar czvf books.tar.gz books/
a books
a books/.git
a books/.git/branches
a books/.git/config
a books/.git/description
a books/.git/HEAD
[...]
$ ls -l books.tar.gz
-rw-r--r-- 1 charles staff 6215 16 jan 18:37 books.tar.gz

 

Créer une archive compressée avec bzip2

$ tar cjvf books.tar.bz2 books/
a books
a books/.git
a books/.git/branches
a books/.git/config
a books/.git/description
a books/.git/HEAD
[...]
$ ls -l books.tar.bz2
-rw-r--r-- 1 charles staff 5996 16 jan 18:38 books.tar.bz2

 

Lister les fichiers d’une archive, compressée ou non

Tar détecte automatique via l’extension de l’archive si elle est compressée ou non.

$ tar tf books.tar
books/
books/.git/
books/.git/branches/
books/.git/config
books/.git/description
books/.git/HEAD
[...]

 

$ tar tf books.tar.gz
books/
books/.git/
books/.git/branches/
books/.git/config
books/.git/description
books/.git/HEAD
[...]

Extraire une archive dans un répertoire particulier

$ tar xzvf books.tar.gz -C /tmp/

Extraire un fichier particulier d’une archive

$ tar xzvf books.tar.gz books/.git/description

Options de la ligne commande

t: Liste le contenu d'une archive
v: affiche les fichiers traités sur la sortie standard
z: compresse/décompresse le contenu de l'archive avec gzip
j: compresse/décompresse le contenu de l'archive avec bz2
f: nom de l'archive à traiter
x: extraire les fichiers de l'archive

 

Ssh: utiliser une passerelle pour atteindre une machine

 

L’accès à certaines infrastructures peut-être protégé avec un whitelisting par rapport à l’adresse IP source.

Disons que vous vous trouver sur une machine A, voulez accéder à un serveur C sur lequel seule la machine B est autorisée à se connecter.

Vous pouvez indiquer automatiquement à ssh que lorsque vous vous connectez à la machine C, il faut passer par la machine B.

Dans le .ssh/config de l’utilisateur de la machine A :

 

# Passerelle
Host machineB
    Hostname 192.168.42.101
    User charlybr
# machineC via machineB
Host machineC
    User ops
    Hostname 192.168.100.73
    Port 4225
    ProxyCommand ssh machineB nc %h %p

 

Ensuite en ligne de commande :

$ ssh machineC
 Last login: Sat Jan 16 11:17:56 2016 from xxx
 ops@machineC ~ 2016-01-16 11:19:30 0 0
 $ hostname
 machineC

Cela fonctionne pareil si vous devez faire des synchronisations de fichiers via rsync :

$ rsync -av images machineC:~/public_html/

 

Mysql: dumper une base à travers ssh

J’ai un serveur de QA dont le disque n’a pas assez d’espace libre pour faire un dump d’une base mysql. Pour récupérer ce dump et transférer le service sur un autre serveur, j’ai redirigé le dump à travers ssh directement sur la nouvelle machine.

mysqldump --skip-extended-insert -u user -p dbname | ssh user@serveur 'cat >~/database.dump.sql'

Avec –skip-extended-insert pour ne faire qu’un INSERT sql par ligne.

 

Mysql: retrouver toutes les tables qui contiennent un champ donné

J’ai eu besoin pour une migration de retrouver toutes les tables qui contenaient un champ spécifique : tag_id

Mysql fournit une base spéciale « information_schema » qui contient toutes les informations sur les noms de bases, de tables et colonnes (entre autres). Le détail des tables de cette base est disponible dans la documentation MySQL.

Pour retrouver un champ en particulier, il faut interroger la table columns :

SELECT DISTINCT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS
    WHERE COLUMN_NAME IN ('tag_id')
    AND TABLE_SCHEMA='750g';

Exemple de résultats :

bloc
link
multicontent
tags_recipes

J’ai filtré ici sur TABLE_SCHEMA pour n’afficher que les résultats de la base sur laquelle je travaille.