Testen der Ansible-Rolle für den RabbitMQ-Cluster unter Verwendung eines Moleküls

Molecule ist ein Framework zum Testen von Rollen in Ansible. Es gibt einige Artikel über Habré über das Testen mit Hilfe eines Moleküls, und fast alle Artikel befassen sich mit einigen "komplexen Testskripten für Ansible", und weiter in den Beispielen gibt es normalerweise einige einfache Rollen und Tests. Ich war daran interessiert, eine komplexere Rolle zu testen, zum Beispiel eine Rolle zum Erstellen eines RabbitMQ-Clusters.





Die Versionen der Programme, die zum Zeitpunkt dieses Schreibens verwendet wurden. Der korrekte Betrieb ist für die Molekülversion unter 3.3 nicht garantiert





Debian 10 Buster





ansible-3.4.0





Molekül-3.3.0





docker-ce-20.10.6





yamllint-1.26.1





ansible-lint-5.0.8





Installieren Sie Ansible und Molekül.





pip3 install --user ansible



(Wie genau zu installieren ist nicht so wichtig, im angegebenen Beispiel geht die Installation zum Homedir des Benutzers).





pip3 install --user molecule[docker]



(Wir werden den Docker-Treiber verwenden)





Installieren Sie Linters





pip3 install --user ansible-lint yamllint







Die Installation von Docker geht über den Rahmen dieses Artikels hinaus. Beachten Sie, dass Sie Docker auf demselben Computer installieren können, auf dem Sie das Molekül ausführen, oder Docker auf einem anderen Computer im Netzwerk installieren können (z. B. wenn der lokale Computer dies nicht hat genug Strom) oder verwenden Sie einen vorhandenen Docker-Server.





Im zweiten Fall müssen Sie nur den Docker-Client auf dem lokalen Computer installieren und die Variable DOCKER_HOST = "ssh: // ansible @ your_docker_server_address" setzen, wobei ansible ein Konto ist, das ssh-Zugriff auf den Server hat und unter dem Docker-Container wird erstellt. Das Konto muss auch Mitglied der Docker-Gruppe auf dem Docker-Server sein.





, , .









cd roles/role_rabbitmq
      
      



( ) .





.ansible-lint (Disclaimer: , skip_list , )





---
exclude_paths:
  - .cache/
  - .git/
  - molecule/
skip_list:
  - command-instead-of-module
  - git-latest
  - no-handler
  - package-latest
  - empty-string-compare
  - command-instead-of-shell
  - meta-no-info
  - no-relative-paths
  - risky-shell-pipe
  - role-name
  - unnamed-task
      
      



.yamllint





---
extends: default
ignore: |
  templates/
  sites/
  files/
  old/
  README.md
  LICENSE

rules:
  braces:
    min-spaces-inside: 0
    max-spaces-inside: 1

  brackets:
    min-spaces-inside: 0
    max-spaces-inside: 1

  comments:
    require-starting-space: false
    level: error

  indentation:
    spaces: 2
    indent-sequences: consistent

  line-length: disable
  truthy: disable
      
      



molecule/cluster .





mkdir molecule/cluster
      
      



molecule/cluster/Dockerfile.j2. . - .





FROM registry.company.net/debian/buster:latest

ENV DEBIAN_FRONTEND noninteractive
ENV pip_packages "ansible"
ENV http_proxy "http://10.10.0.1:8888"
ENV https_proxy "http://10.0.0.1:8888"
ENV no_proxy "127.0.0.1,localhost,*.company.net,10.0.0.0/8,192.168.0.0/16,172.0.0.0/8"

# Install dependencies.
RUN apt update \
  && apt-get install -y --no-install-recommends \
      sudo systemd systemd-sysv \
      build-essential wget libffi-dev libssl-dev \
      python3-apt python3-cryptography python3-pip python3-dev python3-setuptools python3-wheel \
      procps passwd curl lsof netcat gnupg ca-certificates openssh-client less vim iputils-ping iproute2 \
      debian-archive-keyring dnsutils \
  && rm -rf /var/lib/apt/lists/* \
  && rm -Rf /usr/share/doc && rm -Rf /usr/share/man \
  && apt-get clean

# Create ansible user
RUN groupadd --system ansible \
  && useradd --system --comment "Ansible remote management" --home-dir /home/ansible --create-home --gid ansible --shell /bin/bash --password "*" an
sible && echo "%ansible ALL = (ALL) NOPASSWD:ALL" > /etc/sudoers.d/ansible

# Add company repo
RUN curl -k "https://certs.company.net/ca.pem" > /usr/local/share/ca-certificates/ca.crt && update-ca-certificates \
  && curl -k "https://company.net/repos/keys/company_repo_key.gpg" | apt-key add \
  && echo "deb https://company.net/repos/buster buster-local main > /etc/apt/sources.list.d/company.list && apt-get update && pip3 install $pip_packages

# Install Ansible inventory file.
RUN mkdir -p /etc/ansible && echo "[local]\nlocalhost ansible_connection=local" > /etc/ansible/hosts

# Exclude /usr/share/doc
#      /usr/share/doc,        dpkg,    
RUN sed -i 's/path-exclude \/usr\/share\/doc/#path-exclude \/usr\/share\/doc/' /etc/dpkg/dpkg.cfg.d/docker

# Make sure systemd doesn't start agettys on tty[1-6].
RUN rm -f /lib/systemd/system/multi-user.target.wants/getty.target

VOLUME ["/sys/fs/cgroup"]
CMD ["/lib/systemd/systemd"]
      
      



molecule/cluster/prepare.yml. - pre-tasks. pika RabbitMQ.





---
- name: prepare
  hosts: all
  gather_facts: no  #       
  tasks:
    - name: update apt cache
      block:
        - name: update apt cache
          apt:
            update_cache: yes
        - name: perform upgrade of all packages to the latest version
          apt:
            upgrade: dist
            force_apt_get: yes
    - name: install python pika
      pip:
        name:
          - pika
        executable: pip3

      
      



molecule/cluster/converge.yml. . hosts, molecule.yml





---
- name: Converge
  hosts: rabbitmq_cluster

  roles:
    - role: role_rabbitmq
      
      



molecule/cluster/molecule.yml. , . cluster 192.168.0.0/24 - node01, node02, node03 192.168.0.1/2/3. RabbitMQ , .









inventory:
  links:
    group_vars: ../../../../files/molecule/group_vars/
      
      







groups:
  - rabbitmq_cluster
      
      



files/molecule/group_vars/rabbitmq_cluster.yml role_rabbitmq





---
rabbitmq_cluster: yes
certs_dir: /etc/rabbitmq/ssl
rabbitmq_ssl: yes
rabbitmq_ssl_certs:
  - "_.company.net"
rabbitmq_cookie: NJWHJPAOPYKSGTRGDLTN
#  ,      ,    molecule.yml
#            
rabbitmq_nodes:
  - node01
  - node02
  - node03

rabbitmq_master: rabbit@node01
rabbitmq_master_node: node01

rabbitmq_vhosts:
  - name: /test

rabbitmq_users:
  - user: test
    password: test
    vhost: /test

rabbitmq_exchanges:
  - name: test
    type: direct
    durable: yes
    vhost: /test

rabbitmq_queues:
  - name: test
    durable: yes
    vhost: /test

rabbitmq_bindings:
  - name: test
    destination: test
    destination_type: queue
    vhost: /test

rabbitmq_policies:
  - name: ha-replica
    vhost: /test
    tags:
      ha-mode: exactly
      ha-params: 2
      ha-sync-mode: automatic
      
      



molecule.yml





---
dependency:
  name: galaxy
  options:
    ignore-certs: True
driver:
  name: docker
platforms:
  - name: node01
    image: registry.company.net/debian/buster:latest
    # pre_build_image: true
    privileged: True
    tmpfs:
      - /run
      - /tmp
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket:ro
    capabilities:
      - SYS_ADMIN
    command: "/lib/systemd/systemd"
    dns_servers:
      - 10.0.0.1
    groups:
      - rabbitmq_cluster
    docker_networks:
      - name: cluster
        ipam_config:
          - subnet: "192.168.0.0/24"
            gateway: "192.168.0.254"
    networks:
      - name: cluster
        ipv4_address: "192.168.0.1"
    network_mode: default
  - name: node02
    image: registry.company.net/debian/buster:latest
    privileged: True
    tmpfs:
      - /run
      - /tmp
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket:ro
    capabilities:
      - SYS_ADMIN
    command: "/lib/systemd/systemd"
    dns_servers:
      - 10.0.0.1
    groups:
      - rabbitmq_cluster
    networks:
      - name: cluster
        ipv4_address: "192.168.0.2"
    network_mode: default
  - name: node03
    image: registry.company.net/debian/buster:latest
    privileged: True
    tmpfs:
      - /run
      - /tmp
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
      - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket:ro
    capabilities:
      - SYS_ADMIN
    command: "/lib/systemd/systemd"
    dns_servers:
      - 10.0.0.1
    groups:
      - rabbitmq_cluster
    networks:
      - name: cluster
        ipv4_address: "192.168.0.3"
    network_mode: default
provisioner:
  name: ansible
  config_options:
    defaults:  
      interpreter_python: auto_silent
      host_key_checking: False
      gathering: smart
      callback_whitelist: profile_tasks, timer, yaml
    ssh_connection:
      pipelining: True
  inventory:
    links:
      group_vars: ../../../../files/molecule/group_vars/
  ansible_args:
    - -e molecule_run=True
    - -e use_proxy=False
  env:
    MOLECULE_NO_LOG: 0
    ANSIBLE_VERBOSITY: 1
verifier:
  name: ansible
lint: |
  set -e
  ansible-lint .
scenario:
  name: cluster
  test_sequence:
    - dependency
    - lint
    - cleanup
    - destroy
    - syntax
    - create
    - prepare
    - converge
    - idempotence
    - side_effect
    - verify
    - cleanup
    - destroy
      
      



ansible_args





  ansible_args:
    - -e molecule_run=True
    - -e use_proxy=False
      
      



env environment. ( -v) ANSIBLE_VERBOSITY.





MOLECULE_NO_LOG , no_log=no ( no_log yes). no_log: "{{ molecule_no_log|d(False)|ternary(False, True) }}". molecule_no_log=0, no_log: no, no_log: yes. , .





  env:
    MOLECULE_NO_LOG: 0
    ANSIBLE_VERBOSITY: 1
      
      



ansible-lint yamllint, ansible-lint





lint: |
  set -e
  ansible-lint .
      
      



scenario cluster , . molecule matrix test.





side_effect verify, , - , molecule matrix.





scenario:
  name: cluster
  test_sequence:
    - dependency
    - lint
    - cleanup
    - destroy
    - syntax
    - create
    - prepare
    - converge
    - idempotence
    - side_effect
    - verify
    - cleanup
    - destroy
      
      



cluster, -s default





molecule test -s cluster > /tmp/log 2>&1





test_sequence , idempotence. , . ( , ), changed_when: no





/tmp/log "Idempotence completed successfully", ;). , -.





, converge, molecule converge -s cluster. , converge.yml destroy. "docker exec -it container_id /bin/bash" .





side-effect verify. side-effect (- chaos monkey). verify .





rabbitmq ( ).





molecule/cluster/side_effect.yml





---
- name: Side Effect
  serial: 1
  hosts: all
  gather_facts: no  #    
  tasks:
    - name: restart rabbitmq service
      block:
        - name: stop rabbitmq service
          systemd:
            name: rabbitmq-server
            state: stopped
          failed_when: no
        - name: pause
          pause:
            seconds: 15
        - name: start rabbitmq service
          systemd:
            name: rabbitmq-server
            state: started
          failed_when: no
      
      



Erstellen Sie ein Dateimolekül / cluster / verify.yml und fügen Sie verschiedene grundlegende Überprüfungen für unseren Cluster hinzu (auch hier schränkt nichts Ihre Vorstellungskraft ein).





---
- name: Verify
  hosts: all
  gather_facts: no
  tasks:
  - name: cluster status
    block:
      - name: get cluster status
        command: "rabbitmqctl cluster_status --formatter json"
        register: output
      - name: set facts
        set_fact:
          cluster_output: "{{ output.stdout|from_json }}"
      - name: print nodes
        debug:
          var: cluster_output.disk_nodes
      - name: verify fail
        fail:
          msg: "FAIL: number of nodes is less than 3"
        when:
          - cluster_output.disk_nodes | length < 3
    run_once: yes

  - name: check vhosts
    block:
      - name: get vhosts
        command: "rabbitmqctl list_vhosts --formatter json"
        register: output
      - name: set facts
        set_fact:
          vhost_output: "{{ output.stdout|from_json }}.name"
      - name: print vhosts
        debug:
          var: vhost_output
      - name: verify fail
        fail:
          msg: "FAIL: vhost is missing"
        when:
          - "'/test' not in vhost_output"
    run_once: yes

  - name: check users
    block:
      - name: get users
        command: "rabbitmqctl list_users --formatter json"
        register: output
      - name: set facts
        set_fact:
          user_output: "{{ output.stdout|from_json }}.user"
      - name: print users
        debug:
          var: user_output
      - name: verify fail
        fail:
          msg: "FAIL: user is missing"
        when:
          - "'test' not in user_output"
    run_once: yes

  - name: check queues
    block:
      - name: get queues
        command: "rabbitmqctl -p /test list_queues --formatter json"
        register: output
      - name: set facts
        set_fact:
          queue_output: "{{ output.stdout|from_json }}.name"
      - name: print queues
        debug:
          var: queue_output
      - name: verify fail
        fail:
          msg: "FAIL: queue is missing"
        when:
          - "'test' not in queue_output"
    run_once: yes

  - name: check exchanges
    block:
      - name: get exchanges
        command: "rabbitmqctl -p /test list_exchanges --formatter json"
        register: output
      - name: set facts
        set_fact:
          exchange_output: "{{ output.stdout|from_json }}.name"
      - name: print exchanges
        debug:
          var: exchange_output
      - name: verify fail
        fail:
          msg: "FAIL: exchange is missing"
        when:
          - "'test' not in exchange_output"
    run_once: yes

  - name: check bindings
    block:
      - name: get bindings
        command: "rabbitmqctl -p /test list_bindings --formatter json"
        register: output
      - name: set facts
        set_fact:
          binding_output: "{{ output.stdout|from_json }}.source_name"
      - name: print bindings
        debug:
          var: binding_output
      - name: verify fail
        fail:
          msg: "FAIL: binding is missing"
        when:
          - "'test' not in binding_output"
    run_once: yes

  - name: check policies
    block:
      - name: get policies
        command: "rabbitmqctl -p /test list_policies --formatter json"
        register: output
      - name: set facts
        set_fact:
          policy_output: "{{ output.stdout|from_json }}.name"
      - name: print policies
        debug:
          var: policy_output
      - name: verify fail
        fail:
          msg: "FAIL: policy is missing"
        when:
          - "'ha-replica' not in policy_output"
    run_once: yes

  - name: check publish
    block:
      - name: install consumer script
        copy:
          src: ../../../../files/molecule/scripts/consumer.py
          dest: /usr/local/bin/consumer.py
          owner: root
          mode: 0755
      - name: publish a message to a queue
        rabbitmq_publish:
          url: "amqp://test:test@localhost:5672/%2Ftest"
          queue: test
          body: "Test message"
          content_type: "text/plain"
          durable: yes
      - name: receive a message from the queue
        command: /usr/local/bin/consumer.py
    run_once: yes
      
      



Da ansible Lookup in einem Docker-Container nicht sehr gut funktioniert, erstellen wir files / molecular / scripts / consumer.py, ein kleines Python-Skript, das Nachrichten aus der Testwarteschlange druckt.





#!/usr/bin/python3

import pika, sys

url = 'amqp://test:test@localhost/%2ftest'
params = pika.URLParameters(url)
params.socket_timeout = 1

connection = pika.BlockingConnection(params)
channel = connection.channel()
channel.queue_declare(queue='test', durable=True)
method_frame, header_frame, body = channel.basic_get(queue = 'test')
if method_frame is None:
    connection.close()
    sys.exit('Queue is empty!')
else:
    channel.basic_ack(delivery_tag=method_frame.delivery_tag)
    connection.close()
    print(body)
      
      



Überprüfung der Nebenwirkung





molecule converge -s cluster
molecule side-effect -s cluster
      
      



Überprüfung überprüfen





molecule verify -s cluster
      
      



Wenn alles in Ordnung ist, führen Sie den vollständigen Test durch und überprüfen Sie das Protokoll.





molecule test -s cluster >/tmp/log 2>&1
tail -f /tmp/log
      
      






All Articles