Loading search data...

Ansible - Spotter un outil qui complète Ansible-lint

Publié le : 26 février 2023 | Mis à jour le : 27 juin 2023

logo devops

J’ai découvert récemment spotter, un outil qui permet de contrôler la qualité du code Ansible. Vous allez me dire, mais Ansible-Lint le fait déjà ! Oui, mais spotter possède quelques fonctions supplémentaires.

Introduction

Pour ceux qui ne connaissent pas ansible-lint, je rappelle juste que c’est un outil qui vérifie vos playbooks ansible à la recherche d’erreurs, mais aussi recommande des améliorations. Steampunk Spotter, fait la même chose, mais va plus loin.

Découverte de spotter

Dans les caractéristiques que j’ai retrouvées sur le site de Spotter, j’ai pu lire qu’il devrait permettre de :

  • structurer vos playbooks pour qu’ils soient lisibles
  • éviter les anti-patterns les plus courants dans les playbooks,
  • identifier des erreurs difficiles à corriger,
  • identifier les changements de nom de modules et de collections,
  • identifier les paramètres manquants,
  • identifier les modules obsolètes
  • identifier les paramètres obsolètes,
  • afficher les fqcn complet,
  • aide les mise à jour de vos codes Ansible vers de nouvelles versions d'Ansible.

Spotter est plus qu’un simple outil de lint. En effet, il possède un site qui recense tous les rapports d’exécution.

spotter report

Spotter propose plusieurs plans de souscriptions dont un gratuit permettant de réaliser 100 scans/mois.

Création du compte

Pour créer le compte, il faut se rendre à cette adresse.

Installation de spotter

Spotter est un programme écrit en python et donc pour l’installer rien de plus simple avec pip :

pip install steampunk-spotter

Pour l’authentification, vous avez le choix :

  • D’utiliser le combo user/mot de passe:
    • via les paramètres -u <utilisateur> -p <mot-de-passe>.
    • via les variables d’environnement SPOTTER_USERNAME et SPOTTER_PASSWORD.
  • D’utiliser un token que vous aurez généré sur le portail de spotter dans la section my profil.
    • via le paramètre -t <api-token>.
    • via la variable d’environnement SPOTTER_API_TOKEN.

spotter report

Notre jeu de test

Je vais utiliser le playbook suivant pour vous montrer en quoi spotter fournit d’autres recommandations que celles retournées par ansible-lint :

---
- hosts: all
  gather_facts: false
  become: true
  vars:
    # datasource=github-releases depName=rundeck/rundeck
    rundeck_version: v3.4.10
    # datasource=github-tags depName=ansible-community/ansible-build-data
    ansible_version: 5.5.0
    rundeck_os_group: rundeck
    rundeck_os_user: rundeck
    timezone: Europe/Paris
    rundeck_installation_dir: /opt
    rundeck_data_dir: /data/rundeck
    nfs_path: devbox1:/data
    force_install: false
    rundeck_version_running: ""
    ansible_python_interpreter: /usr/libexec/platform-python
    server_address: "rundeck.robert.local"
    rundeck_xmx: "1024m"
    rundeck_xms: "256m"
    rundeck_maxmetaspacesize: "256m"

    ## Nginx
    nginx_version: 1.18
    nginx_fqdn: rundeck.robert.local
    cert_file: "{{ nginx_fqdn }}+3.pem"
    cert_key: "{{ nginx_fqdn }}+3-key.pem"
  tasks:
    - name: Wait 600 seconds for target connection to become reachable/usable
      ansible.builtin.wait_for_connection:
    - name: Get ansible_facts
      ansible.builtin.setup:
    - name: "Check rundeck-latest link stat in {{ rundeck_installation_dir }}"
      ansible.builtin.stat:
        path: "{{ rundeck_installation_dir }}/rundeck"
      register: running_version
      tags: version
    - name: Register current running version if any
      ansible.builtin.set_fact:
        rundeck_version_running: >-
          {{
            running_version.stat.lnk_target
            | regex_replace('^.*rundeck-(\d*\.\d*\.\d*-\d*)', '\1')
          }}          
      when:
        - running_version.stat.exists | default(false)
        - running_version.stat.islnk | default(false)
      tags: version
    - name: Create group rundeck
      ansible.builtin.group:
        name: "{{ rundeck_os_group }}"
        state: present
    - name: Create user rundeck
      ansible.builtin.user:
        name: "{{ rundeck_os_user }}"
        groups: "{{ rundeck_os_group }}"
        append: true
    - name: Install packages
      ansible.builtin.package:
        state: present
        name:
          - glibc-common
          - glibc-langpack-en
          - glibc-langpack-fr
          - java
          - tar
          - unzip
          - epel-release
          - python3-libsemanage
          - python3-pip
          - policycoreutils-python-utils
          - python3-libselinux
    - name: Install tools for debug
      ansible.builtin.package:
        name:
          - htop
          - net-tools
          - python39
        state: present
    - name: Correct python version selected
      community.general.alternatives:
        name: python3
        path: /usr/bin/python3.9
    - name: Set as default locale
      ansible.builtin.command: localectl set-locale LANG=en_US.UTF-8
    - name: Set timezone
      community.general.timezone:
        name: "{{ timezone }}"
    - name: Mount nfs /data
      ansible.posix.mount:
        src: "{{ nfs_path }}"
        path: /data
        state: mounted
        fstype: nfs
    - name: Mount /dev/shm
      ansible.posix.mount:
        fstype: tmpfs
        name: "/dev/shm"
        opts: "defaults,nodev,nosuid,noexec"
        src: tmpfs
        state: mounted
    - name: Create rundeck directory
      ansible.builtin.file:
        path: "{{ item }}"
        state: "directory"
        owner: "{{ rundeck_os_user }}"
        group: "{{ rundeck_os_group }}"
        mode: "0755"
      with_items:
        - "{{ rundeck_installation_dir }}"
        - "{{ rundeck_data_dir }}"
    - name: Get list of services
      ansible.builtin.service_facts:
    - name: Stop rundeck service
      ansible.builtin.service:
        name: rundeck
        enabled: true
        state: stopped
      when: "'rundeck.service' in services and (rundeck_version != rundeck_version_running or force_install)"
    - name: Create directory
      ansible.builtin.file:
        path: "{{ rundeck_installation_dir }}/rundeck-{{ rundeck_version }}"
        state: directory
        owner: "{{ rundeck_os_user }}"
        group: "{{ rundeck_os_group }}"
        mode: "0755"
      when: (rundeck_version_running | length == 0 or rundeck_version != rundeck_version_running or force_install)
    - name: Install rundeck
      become_user: rundeck
      ansible.builtin.get_url:
        url: "https://packagecloud.io/pagerduty/rundeck/packages/java/org.rundeck/rundeck-{{ rundeck_version }}.war/artifacts/rundeck-{{ rundeck_version }}.war/download"
        dest: "{{ rundeck_installation_dir }}/rundeck-{{ rundeck_version }}/rundeck.war"
        owner: "{{ rundeck_os_user }}"
        mode: "0755"
      when: (rundeck_version_running | length == 0 or rundeck_version != rundeck_version_running or force_install)
    - name: Uncompress war
      become_user: "{{ rundeck_os_user }}"
      ansible.builtin.command:
        cmd: "java -jar rundeck.war --installonly"
        chdir: "{{ rundeck_installation_dir }}/rundeck-{{ rundeck_version }}"
      environment:
        RDECK_BASE: "{{ rundeck_installation_dir }}/rundeck-{{ rundeck_version }}"
      when: (rundeck_version_running | length == 0 or rundeck_version != rundeck_version_running or force_install)
    - name: Update symlink rundeck
      ansible.builtin.file:
        path: "{{ rundeck_installation_dir }}/rundeck"
        src: "{{ rundeck_installation_dir }}/rundeck-{{ rundeck_version }}"
        owner: "{{ rundeck_os_user }}"
        group: "{{ rundeck_os_group }}"
        state: link
      when: (rundeck_version_running | length == 0 or rundeck_version != rundeck_version_running or force_install)
    - name: Update configuration
      ansible.builtin.template:
        src: templates/rundeck-config.properties
        dest: "{{ rundeck_installation_dir }}/rundeck/server/config"
        owner: "{{ rundeck_os_user }}"
        group: "{{ rundeck_os_group }}"
        mode: "0644"
      when: (rundeck_version_running | length == 0 or rundeck_version != rundeck_version_running or force_install)
    - name: Upgrade pip
      ansible.builtin.pip:
        name:
          - pip
    - name: Install pip configuration
      ansible.builtin.copy:
        src: pip.conf
        dest: /etc/
        owner: root
        group: root
        mode: "0644"
    - name: Install ansible
      ansible.builtin.pip:
        name:
          - ansible
        version: "{{ ansible_version }}"
    - name: Get list of services
      ansible.builtin.service_facts:
    - name: Create systemd service configuration
      ansible.builtin.template:
        src: "rundeck.service"
        dest: "/etc/systemd/system"
        mode: "0755"
    - name: Reload systemd service configuration
      ansible.builtin.service:
        name: rundeck
        enabled: true
        state: restarted
        daemon_reload: true
    - name: Add /usr/local/bin to path
      ansible.builtin.copy:
        dest: /etc/profile.d/rundeck.sh
        content: "PATH=$PATH:/usr/local/bin"
        owner: root
        group: root
        mode: "0644"
# Deploy Nginx
    - name: Install nginx
      ansible.builtin.dnf:
        name: '@nginx:{{ nginx_version }}'
        state: present
    - name: Copy nginx config
      ansible.builtin.copy:
        src: nginx.conf
        dest: /etc/nginx
        mode: "0644"
    - name: Template nginx configuration
      ansible.builtin.template:
        src: rundeck.conf
        dest: /etc/nginx/conf.d/rundeck.conf
        mode: "0640"
      notify: reload_nginx
    - name: Copy certificate
      ansible.builtin.copy:
        src: "certificats/{{ item }}"
        dest: "/etc/ssl/{{ item }}"
        mode: "0640"
      with_items:
        - "{{ cert_file }}"
        - "{{ cert_key }}"
      notify: reload_nginx
    - name: Set sebool httpd can network connect to on
      ansible.posix.seboolean:
        name: httpd_can_network_connect
        state: true
        persistent: true
    - name: Enable & start nginx
      ansible.builtin.service:
        name: nginx
        enabled: true
        state: started
  handlers:
    - name: Reload_nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Le résultat avec ansible-lint (j’ai installé la dernière version 6.13.1):

ansible-lint deploy_rundeck.yml
WARNING  Listing 4 violation(s) that are fatal
name[play]: All plays should be named.
deploy_rundeck.yml:2

no-changed-when: Commands should not change things if nothing needs doing.
deploy_rundeck.yml:85 Task/Handler: Set as default locale

yaml[line-length]: Line too long (169 > 160 characters)
deploy_rundeck.yml:132

no-changed-when: Commands should not change things if nothing needs doing.
deploy_rundeck.yml:137 Task/Handler: Uncompress war

Read documentation for instructions on how to ignore specific rule violations.

                   Rule Violation Summary
 count tag               profile rule associated tags
     1 name[play]        basic   idiom
     1 yaml[line-length] basic   formatting, yaml
     2 no-changed-when   shared  command-shell, idempotency

Failed after min profile: 4 failure(s), 0 warning(s) on 1 files.

Le résultat avec spotter :

spotter scan deploy_rundeck.yml

deploy_rundeck.yml:32:7: WARNING: Default value for filter parameter changed between module versions 2.10.17 and 2.11.0 from "*" to []. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:34:7: WARNING: Default value for follow parameter changed between module versions 2.7.18 and 2.8.0 from "no" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:34:7: WARNING: Default value for get_md5 parameter changed between module versions 2.4.6 and 2.9.0 from "yes" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:34:7: WARNING: Default value for get_mime parameter changed between module versions 2.3.3 and 2.4.0, 2.7.18 and 2.8.0 from true to "yes", from "yes" to true. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:34:7: WARNING: Default value for get_checksum parameter changed between module versions 2.7.18 and 2.8.0 from "yes" to true. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:34:7: WARNING: Default value for get_attributes parameter changed between module versions 2.3.3 and 2.4.0, 2.7.18 and 2.8.0 from true to "yes", from "yes" to true. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:54:7: WARNING: Default value for force parameter changed between module versions 2.4.6 and 2.5.0 from "no" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:54:7: WARNING: Default value for remove parameter changed between module versions 2.4.6 and 2.5.0 from "no" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:54:7: WARNING: Default value for system parameter changed between module versions 2.4.6 and 2.5.0 from "no" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:54:7: WARNING: Default value for move_home parameter changed between module versions 2.4.6 and 2.5.0 from "no" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:54:7: WARNING: Default value for non_unique parameter changed between module versions 2.4.6 and 2.5.0 from "no" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:81:7: HINT: Collection community.general version from the requirements.yml 4.6.0 does not match installed collection version 6.3.0. Consider updating your collection.
deploy_rundeck.yml:85:7: WARNING: Task does not enforce a state. Use creates or removes parameters to inform Ansible under what conditions should the command be run. If the executed command is enforcing the desired state already, use the changed_when keyword to inform Ansible when the state changed. The last resort is adding a when clause to the task or converting the current task into a handler.
deploy_rundeck.yml:85:7: WARNING: Default value for warn parameter changed between module versions 2.10.17 and 2.11.0 from true to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:87:7: HINT: Collection community.general version from the requirements.yml 4.6.0 does not match installed collection version 6.3.0. Consider updating your collection.
deploy_rundeck.yml:90:7: HINT: Required collection ansible.posix is missing from requirements.yml or requirements.yml is missing.
deploy_rundeck.yml:96:7: HINT: Required collection ansible.posix is missing from requirements.yml or requirements.yml is missing.
deploy_rundeck.yml:103:7: WARNING: Use of with_items is discouraged. Consider using loop instead.
deploy_rundeck.yml:103:7: WARNING: Default value for follow parameter changed between module versions 2.5.1 and 2.5.2 from false to true. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:121:7: WARNING: Default value for follow parameter changed between module versions 2.5.1 and 2.5.2 from false to true. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:129:7: WARNING: Default value for force parameter changed between module versions 2.7.18 and 2.8.0 from "no" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:129:7: WARNING: Default value for use_proxy parameter changed between module versions 2.7.18 and 2.8.0 from "yes" to true. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:129:7: WARNING: Default value for validate_certs parameter changed between module versions 2.7.18 and 2.8.0 from "yes" to true. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:129:7: WARNING: Default value for force_basic_auth parameter changed between module versions 2.7.18 and 2.8.0 from "no" to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:137:7: WARNING: Default value for warn parameter changed between module versions 2.10.17 and 2.11.0 from true to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:145:7: WARNING: Default value for follow parameter changed between module versions 2.5.1 and 2.5.2 from false to true. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:161:7: WARNING: Default value for editable parameter changed between module versions 2.3.3 and 2.4.0 from true to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:172:7: WARNING: Default value for editable parameter changed between module versions 2.3.3 and 2.4.0 from true to false. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:184:7: ERROR: daemon_reload is not a valid parameter in module ansible.builtin.service.
deploy_rundeck.yml:198:7: WARNING: Default value for lock_timeout parameter changed between module versions 2.8.2 and 2.8.3 from 0 to 30. Consider setting value of the parameter explicitly.
deploy_rundeck.yml:213:7: WARNING: Use of with_items is discouraged. Consider using loop instead.
deploy_rundeck.yml:222:7: HINT: Required collection ansible.posix is missing from requirements.yml or requirements.yml is missing.
------------------------------------------------------------------------
Overall status: ERROR

En effet, on voit bien que spotter fournit beaucoup plus de recommandations qu’ansible-lint. On remarque que la promesse d’aider à gérer les montées de versions d’Ansible est tenu. Donc, je valide son utilisation.

Installation de l’extension Vscode

Une extension est disponible et comme ansible-lint permet de remonter les erreurs au moment de certains événements comme la sauvegarde du fichier.

Le lien

Conclusion

L’utilisation des deux outils est une combinaison gagnante et vous garantit que vous obtiendrez un code ansible plus fiable. Compte tenu du nombre limité de scans par mois pour la version libre, il faudra corriger toutes les erreurs remontées avec ansible-lint avant de lancer spotter. Pour ceux qui découvrent ce blog, il regorge de billets sur les outils devops et en particulier ansible.

Mots clés :

devops ansible tutorials infra as code formation ansible

Si vous avez apprécié cet article de blog, vous pouvez m'encourager à produire plus de contenu en m'offrant un café sur  Ko-Fi. Vous pouvez aussi passer votre prochaine commande sur amazon, sans que cela ne vous coûte plus cher, via  ce lien . Vous pouvez aussi partager le lien sur twitter ou Linkedin via les boutons ci-dessous. Je vous remercie pour votre soutien.

Autres Articles


Commentaires: