Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: do not use namespace in node exporter systemd template #390

Closed

Conversation

bigjazzsound
Copy link

I'm using ansible-core version 2.16.7 and the latest version of the collection.

When I run the task to install/config node-exporter:

- name: Manage node-exporter
  ansible.builtin.include_role:
    name: prometheus.prometheus.node_exporter

I get this error:

TASK [prometheus.prometheus.node_exporter : Copy the node_exporter systemd service file] *****************************************************************************
task path: <redacted>/collections/ansible_collections/prometheus/prometheus/roles/node_exporter/tasks/configure.yml:2
<redacted>
The full traceback is:
Traceback (most recent call last):
  File "<redacted>/venv/lib/python3.12/site-packages/ansible/template/__init__.py", line 1010, in do_template
    res = myenv.concat(rf)
          ^^^^^^^^^^^^^^^^
  File "<redacted>/venv/lib/python3.12/site-packages/ansible/template/native_helpers.py", line 83, in ansible_concat
    return ''.join([to_text(v) for v in nodes])
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<template>", line 156, in root
  File "<redacted>/venv/lib/python3.12/site-packages/ansible/template/__init__.py", line 381, in call
    return super().call(obj, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/venv/lib/python3.12/site-packages/jinja2/runtime.py", line 303, in call
    return __obj(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'str' object is not callable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<redacted>/venv/lib/python3.12/site-packages/ansible/plugins/action/template.py", line 152, in run
    resultant = templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False, overrides=overrides)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<redacted>/venv/lib/python3.12/site-packages/ansible/template/__init__.py", line 1021, in do_template
    raise AnsibleError("Unexpected templating type error occurred on (%s): %s" % (to_native(data), to_native(te)), orig_exc=te)
ansible.errors.AnsibleError: Unexpected templating type error occurred on ({{ ansible_managed | comment }}

[Unit]
Description=Prometheus Node Exporter
After=network-online.target

[Service]
Type=simple
User={{ node_exporter_system_user }}
Group={{ node_exporter_system_group }}
ExecStart={{ node_exporter_binary_install_dir }}/node_exporter \
{% for collector in node_exporter_enabled_collectors -%}
{%   if not collector is mapping %}
    '--collector.{{ collector }}' \
{%   else -%}
{%     set name, options = (collector.items()|list)[0] -%}
    '--collector.{{ name }}' \
{%     for k,v in options|dictsort %}
    '--collector.{{ name }}.{{ k }}={{ v }}' \
{%     endfor -%}
{%   endif -%}
{% endfor -%}
{% for collector in node_exporter_disabled_collectors %}
    '--no-collector.{{ collector }}' \
{% endfor %}
{% if node_exporter_tls_server_config | length > 0 or node_exporter_http_server_config | length > 0 or node_exporter_basic_auth_users | length > 0 %}
    {% if node_exporter_version is version('1.5.0', '>=') %}
    '--web.config.file=/etc/node_exporter/config.yaml' \
    {% else %}
    '--web.config=/etc/node_exporter/config.yaml' \
    {% endif %}
{% endif %}
{% if node_exporter_web_disable_exporter_metrics %}
    '--web.disable-exporter-metrics' \
{% endif %}
{% if node_exporter_version is version('1.5.0', '>=') and
      node_exporter_web_listen_address is iterable and
      node_exporter_web_listen_address is not mapping and
      node_exporter_web_listen_address is not string %}
{%   for address in node_exporter_web_listen_address %}
    '--web.listen-address={{ address }}' \
{%   endfor %}
{% else %}
    '--web.listen-address={{ node_exporter_web_listen_address }}' \
{% endif %}
    '--web.telemetry-path={{ node_exporter_web_telemetry_path }}'

SyslogIdentifier=node_exporter
Restart=always
RestartSec=1
StartLimitInterval=0

{% set ns = namespace(protect_home = 'yes') %}
{% for m in ansible_mounts if m.mount.startswith('/home') %}
{%   set ns.protect_home = 'read-only' %}
{% endfor %}
{% if node_exporter_textfile_dir.startswith('/home') %}
{%   set ns.protect_home = 'read-only' %}
{% endif %}
ProtectHome={{ ns.protect_home }}
NoNewPrivileges=yes

{% if (ansible_facts.packages.systemd | first).version is version('232', '>=') %}
ProtectSystem=strict
ProtectControlGroups=true
ProtectKernelModules=true
ProtectKernelTunables=yes
{% else %}
ProtectSystem=full
{% endif %}

[Install]
WantedBy=multi-user.target
): 'str' object is not callable. 'str' object is not callable
fatal: [dev-factory-svc-tenable-scan-candidate]: FAILED! => changed=false 
  msg: |-
    AnsibleError: Unexpected templating type error occurred on ({{ ansible_managed | comment }}
  
    [Unit]
    Description=Prometheus Node Exporter
    After=network-online.target
  
    [Service]
    Type=simple
    User={{ node_exporter_system_user }}
    Group={{ node_exporter_system_group }}
    ExecStart={{ node_exporter_binary_install_dir }}/node_exporter \
    {% for collector in node_exporter_enabled_collectors -%}
    {%   if not collector is mapping %}
        '--collector.{{ collector }}' \
    {%   else -%}
    {%     set name, options = (collector.items()|list)[0] -%}
        '--collector.{{ name }}' \
    {%     for k,v in options|dictsort %}
        '--collector.{{ name }}.{{ k }}={{ v }}' \
    {%     endfor -%}
    {%   endif -%}
    {% endfor -%}
    {% for collector in node_exporter_disabled_collectors %}
        '--no-collector.{{ collector }}' \
    {% endfor %}
    {% if node_exporter_tls_server_config | length > 0 or node_exporter_http_server_config | length > 0 or node_exporter_basic_auth_users | length > 0 %}
        {% if node_exporter_version is version('1.5.0', '>=') %}
        '--web.config.file=/etc/node_exporter/config.yaml' \
        {% else %}
        '--web.config=/etc/node_exporter/config.yaml' \
        {% endif %}
    {% endif %}
    {% if node_exporter_web_disable_exporter_metrics %}
        '--web.disable-exporter-metrics' \
    {% endif %}
    {% if node_exporter_version is version('1.5.0', '>=') and
          node_exporter_web_listen_address is iterable and
          node_exporter_web_listen_address is not mapping and
          node_exporter_web_listen_address is not string %}
    {%   for address in node_exporter_web_listen_address %}
        '--web.listen-address={{ address }}' \
    {%   endfor %}
    {% else %}
        '--web.listen-address={{ node_exporter_web_listen_address }}' \
    {% endif %}
        '--web.telemetry-path={{ node_exporter_web_telemetry_path }}'
  
    SyslogIdentifier=node_exporter
    Restart=always
    RestartSec=1
    StartLimitInterval=0
  
    {% set ns = namespace(protect_home = 'yes') %}
    {% for m in ansible_mounts if m.mount.startswith('/home') %}
    {%   set ns.protect_home = 'read-only' %}
    {% endfor %}
    {% if node_exporter_textfile_dir.startswith('/home') %}
    {%   set ns.protect_home = 'read-only' %}
    {% endif %}
    ProtectHome={{ ns.protect_home }}
    NoNewPrivileges=yes
  
    {% if (ansible_facts.packages.systemd | first).version is version('232', '>=') %}
    ProtectSystem=strict
    ProtectControlGroups=true
    ProtectKernelModules=true
    ProtectKernelTunables=yes
    {% else %}
    ProtectSystem=full
    {% endif %}
  
    [Install]
    WantedBy=multi-user.target
    ): 'str' object is not callable. 'str' object is not callable

Removing the use of jinja namespaces fixes the error.

Copy link
Member

@gardar gardar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine by me, not sure why we needed the namespace to begin with, although I'm not sure why you are running into this issue. What jinja2 version do you have installed?

@bigjazzsound
Copy link
Author

Looks fine by me, not sure why we needed the namespace to begin with, although I'm not sure why you are running into this issue. What jinja2 version do you have installed?

3.1.4

@gardar gardar requested a review from SuperQ October 23, 2024 17:55
@gardar
Copy link
Member

gardar commented Oct 23, 2024

The namespace is also used in the pushgateway and process_exporter roles, I guess we should get rid of it in them too.

@floored1585
Copy link

I'm also running into this issue. Is there a work-around?

@floored1585
Copy link

I made these changes locally to allow the collection to work, but it would be nice to have them merged.

@floored1585
Copy link

Don't merge this as-is. The namespace is used because jinja2 vars modified inside loops don't update the global state. I'm still not sure what the actual issue is but this PR breaks the actual logic.

@floored1585
Copy link

The actual issue, in our environment, was a hostvar or group var "namespace" being set which conflicts with the jinja2 "namespace" object

@SuperQ
Copy link
Contributor

SuperQ commented Dec 12, 2024

Ok, it seems like we need this namespace function. I guess we should close this.

@bigjazzsound
Copy link
Author

The actual issue, in our environment, was a hostvar or group var "namespace" being set which conflicts with the jinja2 "namespace" object

Good catch. Turns out my team was doing the same thing.

@bigjazzsound bigjazzsound deleted the fix-node-exporter branch December 12, 2024 14:00
Copy link
Contributor

Docs Build 📝

This PR is closed and any previously published docsite has been unpublished.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants