Installation d’un service drone.io simple machine sur architecture amd64

Installation de docker

En root,

apt install apt-transport-https ca-certificates curl gnupg2 software-properties-common

curl -fsSL https://download.docker.com/linux/debian/gpg > docker.key
apt-key add docker.key

add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"

apt update

apt install docker-ce docker-ce-cli containerd.io

Installation de drone.io

docker pull drone/drone:1

https://docs.drone.io/installation/gitea/single-machine/

documentation drone.io
#!/bin/bash

DRONE_GITEA_SERVER=https://nom.de.domaine.de.gitea
DRONE_SERVER_HOST=nom.de.domaine.de.drone
DRONE_SERVER_PROTO=http
DRONE_GIT_ALWAYS_AUTH=false
DRONE_DATA_VOLUME=/var/lib/drone
DRONE_ADMIN_USER=user.gitea.qui.sera.admin.drone
docker run \
--volume=/var/run/docker.sock:/var/run/docker.sock \
--env=DRONE_USER_CREATE=username:${DRONE_ADMIN_USER},admin:true \
--volume=${DRONE_DATA_VOLUME}:/data \
--env=DRONE_GITEA_SERVER=${DRONE_GITEA_SERVER} \
--env=DRONE_GIT_ALWAYS_AUTH=${DRONE_GIT_ALWAYS_AUTH} \
--env=DRONE_RUNNER_CAPACITY=2 \
--env=DRONE_SERVER_HOST=${DRONE_SERVER_HOST} \
--env=DRONE_SERVER_PROTO=${DRONE_SERVER_PROTO} \
--env=DRONE_TLS_AUTOCERT=false \
--publish=80:80 \
--publish=443:443 \
--restart=always \
--detach=true \
--name=drone \
drone/drone:1

On va se logger sur https://nom.de.domaine.de.drone avec le login et mot de passe de https://nom.de.domaine.de.gitea
Ce qui va nous donner un affichage avec tout les dépôts du lab…

La base de donnée sera disponible dans /var/lib/drone

Installation de drone-cli

apt install wget
wget https://github.com/drone/drone-cli/releases/download/v1.0.7/drone_linux_amd64.tar.gz
tar -zxvf drone_linux_amd64.tar.gz
sudo install -t /usr/local/bin drone

on insert dans ~/.bashrc les informations issues de la page de paramètre du compte dans drone.

export DRONE_SERVER=https://nom.de.domaine.de.drone
export DRONE_TOKEN=msodihEVsohdFs231fSdfT13s51f03s

et on lance

drone info

Tail -f avec Python

Le but est réaliser une version minimaliste sans prétention de la commande unix tail avec l’option f. Donc le but est de suivre le contenu d’un fichier comme une log.

J’ai commencé par créer une bibliothèque (libtail) qui porte la fonction tail.

#!/usr/bin/env python3

from time import sleep

def tail (filename, n=0, polling=0.01):
  num_lines = sum(1 for line in open(filename))
  f = open(filename,'r')
  cpt = 0
  while cpt < num_lines - n :
    cpt += 1
    next(f)

  try:
    while True:
      try:
        l = next(f)
        print(filename,":",l[:-1])
      except:
        sleep(polling)
  except:
    f.close()

Et un main.py qui l’appel…

#!/usr/bin/env python3

from libtail import tail
from sys import argv

if len(argv) != 2:
  print("""utilisation:
%s FICHIER"""%argv[0])
  exit(1)
tail(argv[1])

Moteur d’inférence

Pendant mes études, j’ai du codé un moteur d’inférence en java… j’en ai fait une version Python beaucoup plus épuré que ce que j’avais pu faire à l’époque. C’est un chaînage avant. Avec cet outil, on peut construire un système expert capable de déduction depuis une base de fait en se basant sur des règles définis par l’utilisateur.

#!/usr/bin/env python3
# coding: utf8

import logging

# construction du logger
LOGGER = logging.getLogger()
#LOGGER.setLevel(logging.WARNING)
LOGGER.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s :: %(module)s :: %(levelname)s :: %(message)s')

stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
LOGGER.addHandler(stream_handler)


class regle () :
  """ 
    Les règles sont constitué d'un ensemble de faits appelé prémisse dont la conséquence est appelé la conclusion.
  """
  def __init__ (self,Premisse,Conclusion):
    self.Premisse=Premisse
    self.Conclusion=Conclusion
    
  def aff (self) :
    return str(self.Premisse) + ' donne ' + self.Conclusion
  
  def __str__(self):
    return str(self.Premisse) + ' donne ' + self.Conclusion
  

class Brain () :
  """ 
  Le moteur d'inférence.
  """
  
  def __init__ (self):
    self.BaseDeFait=[]
    self.BaseDeRegle=[]
    
  def present(self, Premisse):
    """
    Dit si une prémisse est présente dans l'ensemble.
    """
    # utilise les fonctions ensembliste pour faire le test
    # A dans B
    S1 = set(self.BaseDeFait)
    S2 = set(Premisse)
    if S1.intersection(S2) == S2 :
        return True
    else :
        return False
   
  def AjoutDeFait (self,Fait):
    """
    Ajout un fait à la base de fait.
    """
    self.BaseDeFait.append(Fait)
  
  def AjoutDeRegle (self,Regle):
    """
    Ajout une règle à la base de règle
    """
    self.BaseDeRegle.append(Regle)
  
  def Analyse (self) :
    """
    Analyse la base de fait et déduit de nouveau fait en fonction de la base de règle.
    """
    ConclusionAjouter = 1
    LOGGER.debug("Début de la boucle d'analyse.")
    while (len(self.BaseDeRegle) > 0 and ConclusionAjouter > 0 ):
        LOGGER.debug("La base de règle contient %d règles.", len(self.BaseDeRegle)) 
        RegleMarquee = []
        ConclusionAjouter = 0
        
        LOGGER.debug("Recherche des règles valide.")
        for Regle in self.BaseDeRegle :
            if (self.present(Regle.Premisse)):
                self.AjoutDeFait(Regle.Conclusion)
                RegleMarquee.append(Regle)
                ConclusionAjouter += 1
                LOGGER.debug("Règle valide : %s.",Regle)
 
        if ConclusionAjouter > 0:
            LOGGER.debug("Suppression des règles utilisées de la base de règle.")
            for Regle in RegleMarquee :
                self.BaseDeRegle.remove(Regle)
                LOGGER.debug("Règle supprimée : %s.",Regle)
        
        if ConclusionAjouter == 0:
            LOGGER.debug("Aucune règle n'a été ajouté. L'analyse des règles est terminées.")
        else:
            LOGGER.debug("%d nouveaux fait ont été ajouté à la base de fait. L'analyse doit reprendre.", ConclusionAjouter)
    LOGGER.debug("Fin de la boucle d'analyse.")

  def aff(self) :
    return (self.BaseDeFait)

if __name__ == '__main__' :
    db=Brain()

    db.AjoutDeFait('a')
    db.AjoutDeFait('c')
    db.AjoutDeFait('f')

    db.AjoutDeRegle(regle([],'Z')) # une regle sans premisse est toujours vrai
    db.AjoutDeRegle(regle(['a','c'],'e'))
    db.AjoutDeRegle(regle(['e','g','f'],'h'))
    db.AjoutDeRegle(regle(['c','f'],'d'))
    db.AjoutDeRegle(regle(['d','e'],'g'))
    db.AjoutDeRegle(regle(['j','k'],'m')) # regle non réalisable dans le cas présent
    db.AjoutDeRegle(regle(['a','h','e'],'l'))

    print("Base de fait", db.aff())
    db.Analyse()
    print("Base de fait après Analyse : ", db.aff())

Ce moteur pourrait être intégré à une carte électronique (NodeMCU) prenant en charge le python (micropython) afin d’utiliser les capteurs relié à la dite carte comme base de fait.

Si fumé + élévation de la température (calcul de seconde de la température) alors il y a probablement un incendie.

Installation de la forge logiciel Gitea sur linux

Installation

Nous allons voir comment mettre en œuvre Gitea, un fork communautaire de Gogs.

On commence par la création de l’utilisateur git

useradd git

Téléchargement de la version ad hoc en fonction de l’architecture du processeur de la machine hôte.

su - git
# sur linux 64bit
wget https://dl.gitea.io/gitea/1.7.0/gitea-1.7.0-linux-amd64
# sur Raspbian (Debian modifié pour Raspberry pi)
wget https://dl.gitea.io/gitea/1.7.0/gitea-1.7.0-linux-arm-7
# on change les droits sur le fichier
chmod +x gitea-1.7.0-linux-arm-7
# On crée un lien symbolique vers gitea-1.7.0-linux-arm-7
ln -s gitea-1.7.0-linux-arm-7 gitea

On exécute la commande suivante une fois.

./gitea

Ce qui va créer l’arborescence des fichiers de configuration de gitea.

Configuration

Le fichier de configuration est dans : /home/git/gitea/custom/conf/app.ini

APP_NAME = Gitea: Git with a cup of tea
RUN_USER = git
RUN_MODE = prod


[database]
DB_TYPE  = sqlite3
HOST     = 127.0.0.1:3306
NAME     = gitea
USER     = gitea
PASSWD   =
SSL_MODE = disable
PATH     = data/gitea.db


[repository]
ROOT = /home/git/gitea-repositories


[server]
SSH_DOMAIN       = ton.domaine.fr
HTTP_PORT        = 3000
ROOT_URL         = https://ton.domaine.fr/
DISABLE_SSH      = false
SSH_PORT         = 22
LFS_START_SERVER = false
OFFLINE_MODE     = false
LOCAL_ROOT_URL   = http://localhost:3000/

[mailer]
ENABLED = false

[service]
REGISTER_EMAIL_CONFIRM     = false
ENABLE_NOTIFY_MAIL         = false
DISABLE_REGISTRATION       = true
ENABLE_CAPTCHA             = false
REQUIRE_SIGNIN_VIEW        = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
NO_REPLY_ADDRESS           = noreply.example.org

[picture]
DISABLE_GRAVATAR        = true
ENABLE_FEDERATED_AVATAR = false

[session]
PROVIDER = file

[log]
MODE      = file
LEVEL     = Info
ROOT_PATH = /home/git/gitea/log

[security]
INSTALL_LOCK   = true
SECRET_KEY     = XXXXXXXX
INTERNAL_TOKEN = XXXXXXXXXXXXXXXXXX

Déclaration du service dans systemD

Voici la déclaration du service à déployer dans systemD. Avec l’utilisateur root, on place le fichier dans /etc/systemd/system/gitea.service.

[Unit]
Description=Gitea service
After=network.target
After=syslog.target

[Service]
Type=simple
WorkingDirectory=/home/git/gitea
ExecStart=/home/git/gitea/gitea web -p 3000
Restart=always
User=git
Groups=git
Environment=USER=git HOME=/home/git

[Install]
WantedBy=multi-user.target

On ajoute le service avec la commande:

systemctl -q enable gitea.service

On supprime le service avec la commande

systemctl disable gitea.service

Script de base avec Python3

Je présente ici un point de départ pour le développement de programme Python3 avec gestion des paramètres avec argparse ainsi que la gestion de log.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import logging
from logging.handlers import RotatingFileHandler
import argparse

# construction du logger
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.WARNING)
#LOGGER.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s :: %(module)s :: %(levelname)s :: %(message)s')

# écriture d'un fichier de log
file_handler = RotatingFileHandler('activity.log', 'a', 1000000, 1)
file_handler.setFormatter(formatter)
LOGGER.addHandler(file_handler)

# écriture de log sur la sortie standard
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
LOGGER.addHandler(stream_handler)

# gestion des arguments avec argparse, ici on reprend un version modifié mise en place pour agent_dpluzz
# https://docs.python.org/fr/3.6/howto/argparse.html

PARSER = argparse.ArgumentParser(
    description="présentation générale \
de l'outil.")

# FLAG
PARSER.add_argument("-d",
                    "--download",
                    action="store_true",
                    help="description de la fonction.")

# Passage de parametre
PARSER.add_argument("-lpf",
                    "--pattern_load",
                    type=str,
                    metavar=('FICHIER'),
                    help="Charge un fichier de motif (les lignes vide et commençant pas un # sont filtré).")

# Compteur
PARSER.add_argument("-v",
                    "--verbosity",
                    action='count',
                    help="défini le niveau de verbosité à afficher.")

# Parsing des arguments
ARGS = PARSER.parse_args()

# controle du compteur
if ARGS.verbosity == 5:
    LOGGER.setLevel(logging.DEBUG)
if ARGS.verbosity == 4:
    LOGGER.setLevel(logging.INFO)
if ARGS.verbosity == 3:
    LOGGER.setLevel(logging.WARNING)
if ARGS.verbosity == 2:
    LOGGER.setLevel(logging.ERROR)
if ARGS.verbosity == 1:
    LOGGER.setLevel(logging.CRITICAL)

# controle du FLAG
if ARGS.download:
    pritnt("fonction download activé")
    exit(0)

# controle du passage de parametre
if ARGS.pattern_load:
    print("motif : %s"%ARGS.pattern_load)
    exit(0)

Script de base avec Bash

Je présente ici le point de départ pour un script Bash à même de gérer un fichier de configuration, des paramètres par défaut ainsi que des paramètres fourni au lancement

    #!/usr/bin/bash
     
    ##
    ## Bloc des fonctions
    ##
     
    function utilisation
    {
    	echo "Aide du programme"
    	echo "mon_prog [OPTION] ..."
    	echo "  -a, --append, append... [obligatoire]"
    	echo "  -e, --exec, execute..."
    	echo ""
    }
     
    function controle_parm
    {
    	if [[ -z "$a" ]] then
    		echo "append non défini"
    		utilisation
    		return 1
    	fi
    }
     
    ##
    ## Chargement des valeurs par défaut
    ##
     
    config_fic=~/mon_scriptrc.cfg
    tmp_cfg=/tmp/mon_scriptrc.cfg.tmp
    b="blabla"
    c="machine"
     
    ##
    ## gestion d'un fichier de configuration
    ##
     
    # si le fichier de config existe
    if [[ -f $config_fic ]] then 
    	# suppression des lignes commençant par un #
    	grep "^#" $config_fic > $tmp_cfg
    	# traitement du fichier
    	while read ligne
    	do
    		parm=$(echo $ligne | awk -F "=" '{print $1;}')
    		val=$(echo $ligne | awk -F "=" '{print $2;}')
		# ici, on va placer les valeurs attendu du fichier de configuration
		# Le fichier de conf étant de la forme parm=val
		# parm)
		#	parm=$val;

    		case $parm in
    			append)
    				a=$val;
    			;;
     
    			*)
    				echo "parametre $parm inconnue..."
    				exit 1
    			;;
    		esac
    	done < $tmp_cfg
    	# suppression du fichier temporaire
    	rm $tmp_cfg
    fi
     
    ##
    ## gestion de paramètre en entrée saisie par l'opérateur
    ##
     
    while [[ $# -gt 0 ]]
    do
    	case $1 in
    		-a|--append)
    			shift;
    			a=$1;
    			shift;
    		;;
     
    		-e|--exec)
    			exec ;
    			shift;
    		;;
     
    		*)
    			echo "parametre $1 inconnue";
    			shift;
    		;;
    	esac
    done
     
    ##
    ## contrôle des paramètres obligatoires
    ##
     
    controle_parm
    if [[ $? -eq 1 ]] then
    	exit 1
    fi
     
    ##
    ## début du script
    ##
     
    ##
    ## fin du script 
    ##
     
    return $RC; # pensez au return code !!!