# ansible

* [environment](#environment)
  * [install](#install)
  * [upgrade](#upgrade)
  * [completion](#completion)
* [ansible-vault](#ansible-vault)
  * [encrypted files](#encrypted-files)
  * [prompt for the password](#prompt-for-the-password)
  * [reset key](#reset-key)
  * [encrypt](#encrypt)
  * [decrypt](#decrypt)
  * [view](#view)
* [ansible-galaxy](#ansible-galaxy)
* [ansible-playbook](#ansible-playbook)
  * [tags](#tags)
* [vault CLI](#vault-cli)
  * [environment variables](#environment-variables)
  * [kv get](#kv-get)
  * [kv list](#kv-list)
* [ansible-config](#ansible-config)
  * [get all default](#get-all-default)
* [ansible-lint](#ansible-lint)
* [ansible-tools](#ansible-tools)
* [plugin](#plugin)
  * [lookup](#lookup)
  * [Become](#become)
* [troubleshooting](#troubleshooting)

> \[!NOTE|label:references:]
>
> * [Ansible中文权威指南](http://www.ansible.com.cn/index.html)
>   * [Vault](http://www.ansible.com.cn/docs/playbooks_vault.html)
>   * [Playbooks](http://www.ansible.com.cn/docs/playbooks.html)
> * [Ansible Documentation](https://docs.ansible.com/ansible/latest/index.html)
>   * [Installing Ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html)
>   * [Tips and tricks](https://docs.ansible.com/ansible/3/user_guide/playbooks_best_practices.html#tip-for-variables-and-vaults)
>   * [ansible.builtin.template](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html)
> * [Sample Ansible setup](https://docs.ansible.com/ansible/latest/tips_tricks/sample_setup.html)
> * [How to build your inventory](https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html)
> * [\* Filters](https://docs.ansible.com/ansible/2.9/user_guide/playbooks_filters.html#filters)
> * [用 Ansible 实现网络自动化](https://linux.cn/article-9964-1.html)

### environment

```bash
$ sudo dnf install wget yum-utils make gcc openssl-devel bzip2-devel libffi-devel zlib-devel
$ sudo dnf groupinstall 'development tools
$ sudo dnf -y install epel-release
$ sudo dnf install python39
$ sudo update-alternatives --config python
$ sudo update-alternatives --config python3
```

#### install

```bash
$ python -m pip install --user ansible

# or via pipx
$ python3 -m pip install pipx
$ pipx ensurepath
$ pipx install ansible --include-deps
```

* install for development

  ```bash
  $ python -m pip install --user https://github.com/ansible/ansible/archive/devel.tar.gz
  ```

#### upgrade

```bash
$ python -m pip install --user --upgrade ansible

# with pipx
$ pipx upgrade --include-injected ansible
```

#### [completion](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#adding-ansible-command-shell-completion)

```bash
$ python -m pip install --user argcomplete
$ cat >> ~/.bashrc << EOF
  command -v ansible > /dev/null && eval $(register-python-argcomplete ansible)           &&
                                    eval $(register-python-argcomplete ansible-config)    &&
                                    eval $(register-python-argcomplete ansible-console)   &&
                                    eval $(register-python-argcomplete ansible-doc)       &&
                                    eval $(register-python-argcomplete ansible-galaxy)    &&
                                    eval $(register-python-argcomplete ansible-inventory) &&
                                    eval $(register-python-argcomplete ansible-playbook)  &&
                                    eval $(register-python-argcomplete ansible-pull)      &&
                                    eval $(register-python-argcomplete ansible-vault)
EOF
```

* [completion.bash](https://github.com/dysosmus/ansible-completion)

  ```bash
  $ mkdir -p ~/.marslo/.completion
  # older version
  $ git clone git@github.com:dysosmus/ansible-completion.git ~/.marslo/.completion/ansible-completion

  # new version
  $ git clone git@github.com:marslo/ansible-completion.git   ~/.marslo/.completion/ansible-completion

  $ cat >> ~/.bashrc << EOF
  [ -d '~/.marslo/.completion/ansible-completion' ] && eval $(cat ~/.marslo/.completion/ansible-completion/*.bash)
  EOF
  ```

  * or via `ln`

    ```bash
    $ ls -1 --color=none /path/to/ansible-completion/*.bash |
         xargs -t -I{} bash -c "ln -svf {} /usr/local/share/bash-completion/completions/\$(basename {} | awk -F'.' '{print \$1}')"
    ```

### ansible-vault

> \[!NOTE]
>
> * [\* iMarslo : check line ending](https://github.com/marslo/ibook/blob/marslo/docs/cheatsheet/text-processing/text-processing.html#check-line-ending)
> * [\* iMarslo : remove the ending '\n'](https://github.com/marslo/ibook/blob/marslo/docs/cheatsheet/text-processing/text-processing.html#remove-the-ending-\n)
> * [Ansible Documentation](https://docs.ansible.com/ansible/latest/index.html)
>   * [\* Ansible Vault](https://docs.ansible.com/ansible/2.8/user_guide/vault.html)
>   * [Protecting sensitive data with Ansible vault](https://docs.ansible.com/ansible/latest/vault_guide/index.html)
>   * [Encrypting content with Ansible Vault](https://docs.ansible.com/ansible/3/user_guide/vault.html)
>   * [Using Vault in playbooks](https://docs.ansible.com/ansible/2.8/user_guide/playbooks_vault.html)
>   * [Managing vault passwords](https://docs.ansible.com/ansible/latest/vault_guide/vault_managing_passwords.html)
>   * [Community.Hashi\_Vault](https://docs.ansible.com/ansible/latest/collections/community/hashi_vault/index.html)
>   * [community.hashi\_vault.hashi\_vault lookup - Retrieve secrets from HashiCorp’s Vault](https://docs.ansible.com/ansible/latest/collections/community/hashi_vault/hashi_vault_lookup.html)
> * [10 ansible vault examples to decrypt/encrypt string & files](https://www.golinuxcloud.com/ansible-vault-example-encrypt-string-playbook/)
> * [Ansible 详解（四）：Ansible-vault](https://zhuanlan.zhihu.com/p/39158667)
> * [Ansible: How to pass multiple password files to playbook](https://stackoverflow.com/a/65684902/2940319)

#### [encrypted files](https://docs.ansible.com/ansible/3/user_guide/vault.html#creating-encrypted-files)

> \[!NOTE|label:references:]
>
> * [Why should text files end with a newline?](https://stackoverflow.com/q/729692/2940319)
> * [Removing a newline character at the end of a file](https://stackoverflow.com/a/27274234/2940319)
>
>   ```bash
>   $ truncate -s -1 /path/to/yaml
>   ```
> * [sed solution](https://stackoverflow.com/a/63777386/2940319)
>
>   ```bash
>   $ sed -z s/.$// pw.txt | od -c
>   0000000   a   b   c
>   0000003
>   ```
> * [printf solution](https://stackoverflow.com/a/12148703/2940319)
>
>   ```bash
>   $ printf %s "$(< pw.txt)" | od -c
>   0000000   a   b   c
>   0000003
>   ```
> * [head solution](https://stackoverflow.com/a/12579554/2940319)
>
>   ```bash
>   $ head -c -1 pw.txt | od -c
>   0000000   a   b   c  \n   e   f   g
>   0000007
>   ```

```bash
$ truncate -s -1 foo.yml

## encrypt file
$ ansible-vault encrypt --vault-id @prompt foo.yml
New vault password (default):
Confirm new vault password (default):

## create new file
$ ansible-vault create --vault-id @prompt foo.yml
New vault password (default):
Confirm new vault password (default):

$ cat foo.yml
$ANSIBLE_VAULT;1.1;AES256
65393763393937353538636266646432646265643531343530623436373462633663333234653032
6131396532663939376339306261616637316561343531350a393536353331343837653265383037
30343839316531666530336134623135313535336136653232653533643131303364306265393336
3234366662313332640a613963633766663061643064356530643863373138393039326466333638
3638
```

* create with name

  ```bash
  $ ansible-vault create --vault-id test@prompt foo.yml
  New vault password (test):
  Confirm new vault password (test):

  $ cat foo.yml
  $ANSIBLE_VAULT;1.2;AES256;test
  33303164313336626433376532306266633237333038653931386531616637666637626238346339
  3764383262343066636236626666613562363130636565630a313966376138323931333635333266
  32633330356132626637663534633165356133653639653130303839336338336261316362343065
  3964613438623337630a663735313836353566326333323732303232303864393063646432353463
  3631
  ```

#### [prompt for the password](https://docs.ansible.com/ansible/3/user_guide/vault.html#using-vault-id-without-a-vault-id)

> \[!TIP] via `--vault-id @prompt`
>
> \
> \- \[How to see special characters?]\(<https://www.unix.com/unix-for-dummies-questions-and-answers/151193-how-see-special-characters.html>) - be aware of the \`echo\` will automatically appending the \`\n\` in the end of the line: \`\`\`bash $ echo 'abc' | od -c 0000000 a b c \n 0000004 \`\`\` - with \`echo -n\` \`\`\`bash $ echo -n 'abc' | od -c 0000000 a b c 0000003 \`\`\`

```bash
$ echo -n 'Test123!' | ansible-vault encrypt_string --vault-id @prompt
New vault password (default):
Confirm new vault password (default):
Reading plaintext input from stdin. (ctrl-d to end input, twice if your content does not already have a newline)

Encryption successful
!vault |
          $ANSIBLE_VAULT;1.1;AES256
          62306630653236616438653236353135623936626332636337396432346235376364386233363938
          3930663634396138373139643031396433386339353634640a323938323431356330323363353335
          61636134636539326539623665393261643462396239653864313861393761633762313161386464
          3166333136366465370a323765386238646539613438333334633434613533373565326464383836
          6464
```

* or

  ```bash
  $ ansible-vault encrypt_string
  New Vault password:
  Confirm New Vault password:
  Reading plaintext input from stdin. (ctrl-d to end input, twice if your content does not already have a newline)
  abc^D                          # ctrl-d twice
  Encryption successful
  !vault |
            $ANSIBLE_VAULT;1.1;AES256
            39323234633365393633306135386362373463356636633937336236643763616232383832396333
            3136343265346534306638343738363435393964353262330a313331323161653832656365336331
            36356564653565613664666631346434306366666163393463633030363732336436346364613638
            3038303934366166320a633064326333623062663362343031633065333138313762353534643530
            633
  ```

#### reset key

```bash
$ ansible-vault rekey --vault-id @prompt /path/to/file
```

#### encrypt

```bash
$ ansible-vault encrypt --vault-id @prompt /path/to/file
```

#### decrypt

```bash
$ ansible-vault decrypt --vault-id @prompt /path/to/file
```

* decrypt from string

  ```bash
  # encrypt
  $ echo -n 'a' | ansible-vault encrypt_string
  Reading plaintext input from stdin. (ctrl-d to end input, twice if your content does not already have a newline)

  Encryption successful
  !vault |
            $ANSIBLE_VAULT;1.1;AES256
            63666334316139653431343330386139346466356439373263643566373062613666653362353738
            3630386133363464313964666230313062666662396161650a313165353966393136643932643434
            64306666613835333130613866303730623538313136323236653732663461623532343035626262
            3932643631653739350a306131666337633831653233623638396438386535623938626133653332
            3464

  # decrypt
  $ echo -n '$ANSIBLE_VAULT;1.1;AES256
  63666334316139653431343330386139346466356439373263643566373062613666653362353738
  3630386133363464313964666230313062666662396161650a313165353966393136643932643434
  64306666613835333130613866303730623538313136323236653732663461623532343035626262
  3932643631653739350a306131666337633831653233623638396438386535623938626133653332
  3464' | ansible-vault decrypt
  Decryption successful
  a
  ```

#### view

```bash
$ ansible-vault view --vault-id @prompt /path/to/file
```

### ansible-galaxy

> \[!NOTE|label:references:]
>
> * [\* Kubernetes Collection for Ansible](https://galaxy.ansible.com/kubernetes/core)
>   * [ansible-collections/kubernetes.core](https://github.com/ansible-collections/kubernetes.core)
>   * [kubernetes.core.k8s](https://github.com/ansible-collections/kubernetes.core/blob/main/docs/kubernetes.core.k8s_inventory.rst)
> * [kubernetes.core](https://galaxy.ansible.com/kubernetes/core)
>   * [kubernetes.core.k8s](https://docs.ansible.com/ansible/latest/collections/kubernetes/core/k8s_module.html#ansible-collections-kubernetes-core-k8s-module)
> * [Collections in the Kubernetes Namespace](https://docs.ansible.com/ansible/latest/collections/kubernetes/index.html)
> * [Kubernetes.Core](https://docs.ansible.com/ansible/latest/collections/kubernetes/core/index.html#plugins-in-kubernetes-core)
> * [installing roles and collections from the same requirements.yml file](https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#installing-roles-and-collections-from-the-same-requirements-yml-file)
> * [helm – Manages Kubernetes packages with the Helm package manager](https://docs.ansible.com/ansible/2.9/modules/helm_module.html)
> * [The Inside Playbook](https://www.ansible.com/blog)
>   * [Creating Kubernetes Dynamic Inventories with kubernetes.core Modules](https://www.ansible.com/blog/creating-kubernetes-dynamic-inventories-with-kubernetes.core-modules)
>   * [\* Automating Helm using Ansible](https://www.ansible.com/blog/automating-helm-using-ansible)
>   * [k8s\_taint](https://www.ansible.com/blog/whats-new-in-the-ansible-content-collection-for-kubernetes-2.3)
>   * [Kubernetes Meets Event-Driven Ansible](https://www.ansible.com/blog/kubernetes-meets-event-driven-ansible)

```bash
$ ansible-galaxy collection install kubernetes.core

# or
$ ansible-galaxy install -r requirements.yml
```

* [example](https://docs.ansible.com/ansible/latest/collections/kubernetes/core/helm_module.html#examples)

  ```bash
  - name: Deploy latest version of Prometheus chart inside monitoring namespace (and create it)
    kubernetes.core.helm:
      name: test
      chart_ref: stable/prometheus
      release_namespace: monitoring
      create_namespace: true

  # From repository
  - name: Add stable chart repo
    kubernetes.core.helm_repository:
      name: stable
      repo_url: "https://kubernetes.github.io/ingress-nginx"

  - name: Deploy latest version of Grafana chart inside monitoring namespace with values
    kubernetes.core.helm:
      name: test
      chart_ref: stable/grafana
      release_namespace: monitoring
      values:
        replicas: 2

  - name: Deploy Grafana chart on 5.0.12 with values loaded from template
    kubernetes.core.helm:
      name: test
      chart_ref: stable/grafana
      chart_version: 5.0.12
      values: "{{ lookup('template', 'somefile.yaml') | from_yaml }}"

  - name: Deploy Grafana chart using values files on target
    kubernetes.core.helm:
      name: test
      chart_ref: stable/grafana
      release_namespace: monitoring
      values_files:
        - /path/to/values.yaml

  - name: Remove test release and waiting suppression ending
    kubernetes.core.helm:
      name: test
      state: absent
      wait: true

  - name: Separately update the repository cache
    kubernetes.core.helm:
      name: dummy
      namespace: kube-system
      state: absent
      update_repo_cache: true

  - name: Deploy Grafana chart using set values on target
    kubernetes.core.helm:
      name: test
      chart_ref: stable/grafana
      release_namespace: monitoring
      set_values:
        - value: phase=prod
          value_type: string

  # From git
  - name: Git clone stable repo on HEAD
    ansible.builtin.git:
      repo: "http://github.com/helm/charts.git"
      dest: /tmp/helm_repo

  - name: Deploy Grafana chart from local path
    kubernetes.core.helm:
      name: test
      chart_ref: /tmp/helm_repo/stable/grafana
      release_namespace: monitoring

  # From url
  - name: Deploy Grafana chart on 5.6.0 from url
    kubernetes.core.helm:
      name: test
      chart_ref: "https://github.com/grafana/helm-charts/releases/download/grafana-5.6.0/grafana-5.6.0.tgz"
      release_namespace: monitoring

  # Using complex Values
  - name: Deploy new-relic client chart
    kubernetes.core.helm:
      name: newrelic-bundle
      chart_ref: newrelic/nri-bundle
      release_namespace: default
      force: True
      wait: True
      replace: True
      update_repo_cache: True
      disable_hook: True
      values:
        global:
          licenseKey: "{{ nr_license_key }}"
          cluster: "{{ site_name }}"
        newrelic-infrastructure:
          privileged: True
        ksm:
          enabled: True
        prometheus:
          enabled: True
        kubeEvents:
          enabled: True
        logging:
          enabled: True
  ```

### ansible-playbook

> \[!NOTE|label:references:]
>
> * [passwordless via environment variables](https://docs.ansible.com/archive/ansible/2.5/user_guide/playbooks_vault.html)
> * <https://docs.ansible.com/ansible/latest/plugins/become.html#become-plugins>
> * sample:
>   * [gikeymarcia/ansible-role-neovim](https://github.com/gikeymarcia/ansible-role-neovim/blob/master/tasks/main.yml)

```bash
# without password
$ export ANSIBLE_VAULT_PASSWORD_FILE=/path/to/password.txt
$ ansible-playbook -i hosts /path/to/yaml

# with password
$ ansible-playbook -i hosts /path/to/yaml --vault-id @prompt
```

#### tags

> \[!NOTE|label:references]
>
> * [Special tags: always and never](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tags.html#special-tags-always-and-never)
> * [Use Ansible tags to save time on playbook runs](https://www.redhat.com/sysadmin/ansible-tags-fast-playbook-runs)

**never**

```yaml
$ cat sample.yaml
---

- hosts: localhost
  gather_facts: False
  tasks:

    - name: Hello tag example
      debug:
        msg: "Hello!"
      tags:
        - hello

    - name: No tag example
      debug:
        msg: "How are you?"

    - name: Goodbye tag example
      debug:
        msg: "Goodbye!"
      tags:
        - goodbye
        - never                        # will not be executed unless using `--tags goodbye`
```

* result

  ```bash
  $ ansible-playbook sample.yaml

  PLAY [localhost] *************************************************************************
  TASK [Hello tag example] *****************************************************************
  ok: [localhost] => {
      "msg": "Hello!"
  }

  TASK [No tag example] ********************************************************************
  ok: [localhost] => {
      "msg": "How are you?"
  }

  PLAY RECAP *******************************************************************************
  localhost           : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  ```
* result with `--tags`

  ```bash
  $ ansible-playbook sample.yaml --tags goodbye

  PLAY [localhost] *************************************************************************

  TASK [Goodbye tag example] ***************************************************************
  ok: [localhost] => {
      "msg": "Goodbye!"
  }

  PLAY RECAP *******************************************************************************
  localhost           : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
  ```

### vault CLI

> \[!NOTE|label:references:]
>
> * [Vault CLI commands](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-vault-cli)
> * [Vault commands (CLI)](https://developer.hashicorp.com/vault/docs/commands)
> * [Using Ansible command line tools](https://docs.ansible.com/ansible/latest/command_guide/index.html)

#### environment variables

```bash
$ export VAULT_ADDR=https://vault.example.com
$ export VAULT_TOKEN=s.s**********************K
```

#### [kv get](https://developer.hashicorp.com/vault/docs/commands/kv/get)

```bash
$ vault kv get -mount=project service-account/account
============= Secret Path =============
project/data/service-account/account

======= Metadata =======
Key                Value
---                -----
created_time       2024-06-19T06:51:35.036120169Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            9

============= Data =============
Key                       Value
---                       -----
email                     account@sample.com
password                  eW7[xRF(rgA@x)n£LN75?R5b.k2z1+
username                  account

# with json foramt
$ vault kv get -format=json -mount=project service-account/account
```

#### [kv list](https://developer.hashicorp.com/vault/docs/commands/kv/list)

```bash
$ vault kv list project/service-account
Keys
----
account
```

### [ansible-config](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#inventory-unparsed-warning)

> \[!NOTE|label:referencs:]
>
> * [v2.4 Configuration file](https://docs.ansible.com/archive/ansible/2.4/intro_configuration.html)
> * [Ansible Configuration Settings](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#envvar-ANSIBLE_VAULT_PAS)

#### get all default

```bash
$ ansible-config init --disabled -t all
```

* disable localhost warning

  ```bash
  $ cat ansible.cfg
  [defaults]
  localhost_warning=false
  ```

### ansible-lint

> \[!NOET|label:references:]
>
> * [Yamllint Indentation Warning on Valid Ansible code](https://stackoverflow.com/q/47417743/2940319) | [`indent-sequences: false`](http://yamllint.readthedocs.io/en/latest/rules.html#module-yamllint.rules.indentation)
> * [ansible-lint - Installing](https://ansible.readthedocs.io/projects/lint/installing/)
> * vim/nvim plugins
>   * [jctanner/ansible-tools](https://github.com/jctanner/ansible-tools)

* [Configuration](https://ansible.readthedocs.io/projects/lint/configuring/)
  * `.ansible-lint`
  * `.config/ansible-lint.yml`
  * `.config/ansible-lint.yaml`

### ansible-tools

> \[!NOTE|label:references:]
>
> * [jctanner/ansible-tools](https://github.com/jctanner/ansible-tools)

### plugin

#### [lookup](https://docs.ansible.com/ansible/latest/plugins/lookup.html)

> \[!NOTE|label:references:]
>
> * [自动化运维 | Ansible lookup](http://www.manongjc.com/detail/57-jamkqmdgyzomkhf.html)
> * sample code
>
>   ```bash
>   $ ls --color=none lookup* | xargs -n1 -t cat
>   cat lookup-content.txt
>   hello world
>
>   cat lookup.yaml
>   ---
>   - hosts: localhost
>     tasks:
>     vars:
>        contents: "{{ lookup('file', '/home/marslo/iMarslo/study/code/ansible/lookup-content.txt')}}"
>     tasks:
>        - debug: msg="the content of file lookup-content.txt is {{contents}}"
>   ```

```bash
$ ansible-playbook lookup.yaml
PLAY [localhost] *************************************************************************

TASK [Gathering Facts] *******************************************************************
Monday 24 July 2023  17:43:51 -0700 (0:00:00.007)       0:00:00.007 ***********
ok: [localhost]

TASK [debug] *****************************************************************************
Monday 24 July 2023  17:43:51 -0700 (0:00:00.935)       0:00:00.943 ***********
ok: [localhost] => {}

MSG:

the content of file lookup-content.txt is hello world

PLAY RECAP *******************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Monday 24 July 2023  17:43:52 -0700 (0:00:00.030)       0:00:00.974 ***********
===============================================================================
Gathering Facts ------------------------------------------------------------------- 0.94s
debug ----------------------------------------------------------------------------- 0.03s
```

#### [Become](https://docs.ansible.com/ansible/latest/plugins/become.html#become-plugins)

> \[!NOTE|label:references:]
>
> * [Understanding privilege escalation: become](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_privilege_escalation.html#using-become)
> * [Ansible Become](https://learning-ocean.com/tutorials/ansible/ansible-become/)

### troubleshooting

* [generate the final yaml via `ansible.builtin.template`](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html)

  ```yaml
  # in tasks/main.yaml
  - name: Template a file to local
    ansible.builtin.template:
      src: file_in_template.yaml
      dest: /local/path/to/yaml
  ```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://imarslo.gitbook.io/handbook/awesomeshell/ansible.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
