nginx/ansible-role-nginx-config

Listen directive in servers['core'] not workign properly

Closed this issue · 6 comments

Hello everyone,

I'm currently setting up Gitea in a VM using ansible. In addition to Gitea I'm setting up Nginx as proxy to terminate ssl and handle 80 and 443 connections.

The idea is to get nginx up and running ( that's done using your nginx install role ) and then configure it ( that's why I'm here ) to deploy a configuration that 1. upgrade http connections to https ( 301 rewrite ) and 2. proxy_pass' https connections to the gitea UI.

Problem:

I'm having HUGE problems getting a working example.. Here's my current 'testing' version:

---
- name: Install and Configure Nginx
  hosts: localhost
  become: true
  tasks:
    - name: Configure Nginx
      ansible.builtin.include_role:
        name: nginxinc.nginx_config
      vars:
        nginx_config_http_template_enable: true
        nginx_config_http_template:
          - template_file: http/default.conf.j2
            deployment_location: /etc/nginx/conf.d/git.my.domain.conf
            config:
              servers:
                - core:
                    listen: # double intended ??
                      - port: 80
                    server_name: git.my.domain
                  locations:
                    - location: /
                      rewrite:
                        code: 301
                        text: https://git.my.domain

This causes a item x exepcted str found Unexpected even if I get it to roll it out the resulting conf file is empty except for the

location / {

}

I have no idea how to properly use your role.. The documentation isn't helpful sadly.. It just states examples but doesn't explain what's required to get it working. ( I had trouble even realizing that listen: - port: 80 was double inteded because it's an actual child of core and not on the same level as core ( like location is )

My question would be: Can you help me set this up properly? If I get good enough with the role I might be able to lend a hand with documentation and such.

Thansk for reading, thanks for your great work.. The roles itself seem good, I just have starting problems :D

Also added a JSON in the discussion.

For now it seems to be like the template is misbehaving.

Originally posted by @StoffelCPR in #435

Creating a templating engine in Ansible isn't trivial given the Jinja2 restrictions, which is why you see some potentially confusing behavior when trying to use the template capabilities. Jinja2 errors are also fundamentally useless, sadly.

That being said, your use case should quite feasible. Can you share your full error log and which version of Jinja2 are you using? Can you also perchance try to wrap both your server_name and text parameters (...git.my.domain) in double quotes just in case Jinja2 is getting confused?

---
- hosts: myhost
  become: true
  tasks:
    - name: "Install nginx"
      ansible.builtin.include_role:
        name: nginxinc.nginx
      vars:
        nginx_enable: true
        nginx_type: "opensource"
    - name: "Configure nginx"
      ansible.builtin.include_role:
        name: nginxinc.nginx_config
      vars:
        nginx_config_debug_output: true
        nginx_config_http_template_enable: true
        nginx_config_http_template:
          - template_file: http/default.conf.j2
            deployment_location: /etc/nginx/conf.d/git.my.domain.conf
            config:
              servers:
                - core:
                    server_name: "git.my.domain"
                    listen:
                      - default_server: "true"
                        port: "80"
                        address: "0.0.0.0"
                  locations:
                    - location: /
                      rewrite:
                        code: "301"
                        text: "https://git.my.domain"
    - name: "Install and configure Gitea"
      ansible.builtin.include_role:
        name: gitea

Done it. Result is:

TASK [nginxinc.nginx_config : Dynamically generate NGINX HTTP config files] ***************************************************************************************************************************************************************************************************
task path: /home/ansible/ansible/roles/nginxinc.nginx_config/tasks/config/template-config.yml:80
<git.my.domain> ESTABLISH SSH CONNECTION FOR USER: ansible
<git.my.domain> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r -o ServerAliveInterval=30 -o ServerAliveCountMax=20 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="ansible"' -o ConnectTimeout=120 git.my.domain '/bin/sh -c '"'"'echo ~ansible && sleep 0'"'"''
<git.my.domain> (0, b'/home/ansible\n', b"OpenSSH_8.9p1 Ubuntu-3ubuntu0.7, OpenSSL 3.0.2 15 Mar 2022\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files\r\ndebug1: /etc/ssh/ssh_config line 21: Applying options for *\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/ansible/.ssh/known_hosts'\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/home/ansible/.ssh/known_hosts2'\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 3660650\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n")
<git.my.domain> ESTABLISH SSH CONNECTION FOR USER: ansible
<git.my.domain> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r -o ServerAliveInterval=30 -o ServerAliveCountMax=20 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="ansible"' -o ConnectTimeout=120 git.my.domain '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /home/ansible/.ansible/tmp `"&& mkdir "` echo /home/ansible/.ansible/tmp/ansible-tmp-1718977189.4231527-3660896-13764742764474 `" && echo ansible-tmp-1718977189.4231527-3660896-13764742764474="` echo /home/ansible/.ansible/tmp/ansible-tmp-1718977189.4231527-3660896-13764742764474 `" ) && sleep 0'"'"''
<git.my.domain> (0, b'ansible-tmp-1718977189.4231527-3660896-13764742764474=/home/ansible/.ansible/tmp/ansible-tmp-1718977189.4231527-3660896-13764742764474\n', b"OpenSSH_8.9p1 Ubuntu-3ubuntu0.7, OpenSSL 3.0.2 15 Mar 2022\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files\r\ndebug1: /etc/ssh/ssh_config line 21: Applying options for *\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/ansible/.ssh/known_hosts'\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/home/ansible/.ssh/known_hosts2'\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 3660650\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n")
<git.my.domain> ESTABLISH SSH CONNECTION FOR USER: ansible
<git.my.domain> SSH: EXEC ssh -vvv -o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r -o ServerAliveInterval=30 -o ServerAliveCountMax=20 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="ansible"' -o ConnectTimeout=120 git.my.domain '/bin/sh -c '"'"'rm -f -r /home/ansible/.ansible/tmp/ansible-tmp-1718977189.4231527-3660896-13764742764474/ > /dev/null 2>&1 && sleep 0'"'"''
<git.my.domain> (0, b'', b"OpenSSH_8.9p1 Ubuntu-3ubuntu0.7, OpenSSL 3.0.2 15 Mar 2022\r\ndebug1: Reading configuration data /etc/ssh/ssh_config\r\ndebug1: /etc/ssh/ssh_config line 19: include /etc/ssh/ssh_config.d/*.conf matched no files\r\ndebug1: /etc/ssh/ssh_config line 21: Applying options for *\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/home/ansible/.ssh/known_hosts'\r\ndebug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/home/ansible/.ssh/known_hosts2'\r\ndebug1: auto-mux: Trying existing master\r\ndebug2: fd 3 setting O_NONBLOCK\r\ndebug2: mux_client_hello_exchange: master version 4\r\ndebug3: mux_client_forwards: request forwardings: 0 local, 0 remote\r\ndebug3: mux_client_request_session: entering\r\ndebug3: mux_client_request_alive: entering\r\ndebug3: mux_client_request_alive: done pid = 3660650\r\ndebug3: mux_client_request_session: session request sent\r\ndebug1: mux_client_request_session: master session id: 2\r\ndebug3: mux_client_read_packet: read header failed: Broken pipe\r\ndebug2: Received exit status from master 0\r\n")
The full traceback is:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/ansible/template/__init__.py", line 1010, in do_template
    res = myenv.concat(rf)
  File "/usr/lib/python3/dist-packages/ansible/template/native_helpers.py", line 83, in ansible_concat
    return ''.join([to_text(v) for v in nodes])
  File "/usr/lib/python3/dist-packages/ansible/template/native_helpers.py", line 83, in <listcomp>
    return ''.join([to_text(v) for v in nodes])
  File "<template>", line 406, in root
  File "/usr/lib/python3/dist-packages/ansible/template/__init__.py", line 381, in call
    return super().call(obj, *args, **kwargs)
  File "/usr/lib/python3/dist-packages/jinja2/runtime.py", line 349, in call
    return __obj(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/jinja2/runtime.py", line 814, in __call__
    return self._invoke(arguments, autoescape)
  File "/usr/lib/python3/dist-packages/jinja2/runtime.py", line 828, in _invoke
    rv = self._func(*arguments)
  File "/home/ansible/ansible/roles/nginxinc.nginx_config/templates/http/core.j2", line 764, in macro
TypeError: sequence item 4: expected str instance, Undefined found

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-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 "/usr/lib/python3/dist-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 }}

{% if item['config']['upstreams'] is defined %}
{% from 'http/upstream.j2' import upstream with context %}
{{ upstream(item['config']['upstreams']) }}
{%- endif %}
{% if item['config']['core'] is defined %}
{% from 'http/core.j2' import core with context %}
{{ core(item['config']['core']) }}
{%- endif %}
{% if item['config']['http2'] is defined %}
{% from 'http/modules.j2' import http2 with context %}
{{ http2(item['config']['http2'], 'http') }}
{%- endif %}
{% if item['config']['http3'] is defined %}
{% from 'http/modules.j2' import http3 with context %}
{{ http3(item['config']['http3']) }}
{%- endif %}
{% if item['config']['quic'] is defined %}
{% from 'http/modules.j2' import quic with context %}
{{ quic(item['config']['quic']) }}
{%- endif %}
{% if item['config']['ssl'] is defined %}
{% from 'http/ssl.j2' import ssl with context %}
{{ ssl(item['config']['ssl']) }}
{%- endif %}
{% if item['config']['app_protect_waf'] is defined %}
{% from 'http/app_protect.j2' import app_protect_waf with context %}
{{ app_protect_waf(item['config']['app_protect_waf']) }}
{%- endif %}
{% if item['config']['app_protect_dos'] is defined %}
{% from 'http/app_protect.j2' import app_protect_dos with context %}
{{ app_protect_dos(item['config']['app_protect_dos']) }}
{%- endif %}
{% if item['config']['proxy'] is defined %}
{% from 'http/proxy.j2' import proxy with context %}
{{ proxy(item['config']['proxy']) }}
{%- endif %}
{% if item['config']['grpc'] is defined %}
{% from 'http/grpc.j2' import grpc with context %}
{{ grpc(item['config']['grpc']) }}
{%- endif %}
{% if item['config']['access'] is defined %}
{% from 'http/auth.j2' import access with context %}
{{ access(item['config']['access']) }}
{%- endif %}
{% if item['config']['auth_basic'] is defined %}
{% from 'http/auth.j2' import auth_basic with context %}
{{ auth_basic(item['config']['auth_basic']) }}
{%- endif %}
{% if item['config']['auth_request'] is defined %}
{% from 'http/auth.j2' import auth_request with context %}
{{ auth_request(item['config']['auth_request']) }}
{%- endif %}
{% if item['config']['auth_jwt'] is defined %}
{% from 'http/auth.j2' import auth_jwt with context %}
{{ auth_jwt(item['config']['auth_jwt']) }}
{%- endif %}
{% if item['config']['api'] is defined %}
{% from 'http/modules.j2' import api with context %}
{{ api(item['config']['api']) }}
{%- endif %}
{% if item['config']['stub_status'] is defined %}
{% from 'http/modules.j2' import stub_status with context %}
{{ stub_status(item['config']['stub_status']) }}
{%- endif %}
{% if item['config']['autoindex'] is defined %}
{% from 'http/modules.j2' import autoindex with context %}
{{ autoindex(item['config']['autoindex']) }}
{%- endif %}
{% if item['config']['gunzip'] is defined %}
{% from 'http/modules.j2' import gunzip with context %}
{{ gunzip(item['config']['gunzip']) }}
{%- endif %}
{% if item['config']['gzip'] is defined %}
{% from 'http/modules.j2' import gzip with context %}
{{ gzip(item['config']['gzip']) }}
{%- endif %}
{% if item['config']['headers'] is defined %}
{% from 'http/modules.j2' import headers with context %}
{{ headers(item['config']['headers']) }}
{%- endif %}
{% if item['config']['health_check'] is defined %}
{% from 'http/modules.j2' import health_check with context %}
{{ health_check(item['config']['health_check']) }}
{%- endif %}
{% if item['config']['keyval'] is defined %}
{% from 'http/modules.j2' import keyval with context %}
{{ keyval(item['config']['keyval']) }}
{%- endif %}
{% if item['config']['limit_req'] is defined %}
{% from 'http/modules.j2' import limit_req with context %}
{{ limit_req(item['config']['limit_req']) }}
{%- endif %}
{% if item['config']['log'] is defined %}
{% from 'http/modules.j2' import log with context %}
{{ log(item['config']['log']) }}
{%- endif %}
{% if item['config']['map'] is defined %}
{% from 'http/modules.j2' import map with context %}
{{ map(item['config']['map']) }}
{%- endif %}
{% if item['config']['mirror'] is defined %}
{% from 'http/modules.j2' import mirror with context %}
{{ mirror(item['config']['mirror']) }}
{%- endif %}
{% if item['config']['realip'] is defined %}
{% from 'http/modules.j2' import realip with context %}
{{ realip(item['config']['realip']) }}
{%- endif %}
{% if item['config']['rewrite'] is defined %}
{% from 'http/modules.j2' import rewrite with context %}
{{ rewrite(item['config']['rewrite']) }}
{%- endif %}
{% if item['config']['split_clients'] is defined %}
{% from 'http/modules.j2' import split_clients with context %}
{{ split_clients(item['config']['split_clients']) }}
{%- endif %}
{% if item['config']['sub_filter'] is defined %}
{% from 'http/modules.j2' import sub_filter with context %}
{{ sub_filter(item['config']['sub_filter']) }}
{%- endif %}
{% if item['config']['custom_directives'] is defined and item['config']['custom_directives'] is not mapping %}
{% for directive in item['config']['custom_directives'] if item['config']['custom_directives'] is not string %}
{{ directive }}
{% else %}
{{ item['config']['custom_directives'] }}
{% endfor %}
{% endif %}
{% if item['config']['servers'] is defined %}
{% for server in item['config']['servers'] %}
server {
{% if server['core'] is defined %}
{% from 'http/core.j2' import core with context %}
{% filter indent(4) %}
    {{ core(server['core']) }}
{%- endfilter %}
{% endif %}
{% if server['http2'] is defined %}
{% from 'http/modules.j2' import http2 with context %}
{% filter indent(4) %}
    {{ http2(server['http2'], 'server') }}
{%- endfilter %}
{% endif %}
{% if server['http3'] is defined %}
{% from 'http/modules.j2' import http3 with context %}
{% filter indent(4) %}
    {{ http3(server['http3']) }}
{%- endfilter %}
{% endif %}
{% if server['quic'] is defined %}
{% from 'http/modules.j2' import quic with context %}
{% filter indent(4) %}
    {{ quic(server['quic']) }}
{%- endfilter %}
{% endif %}

{% if server['ssl'] is defined %}
{% from 'http/ssl.j2' import ssl with context %}
{% filter indent(4) %}
    {{ ssl(server['ssl']) }}
{%- endfilter %}
{% endif %}
{% if server['app_protect_waf'] is defined %}
{% from 'http/app_protect.j2' import app_protect_waf with context %}
{% filter indent(4) %}
    {{ app_protect_waf(server['app_protect_waf']) }}
{%- endfilter %}
{% endif %}
{% if server['app_protect_dos'] is defined %}
{% from 'http/app_protect.j2' import app_protect_dos with context %}
{% filter indent(4) %}
    {{ app_protect_dos(server['app_protect_dos']) }}
{%- endfilter %}
{% endif %}
{% if server['proxy'] is defined %}
{% from 'http/proxy.j2' import proxy with context %}
{% filter indent(4) %}
    {{ proxy(server['proxy']) }}
{%- endfilter %}
{% endif %}
{% if server['grpc'] is defined %}
{% from 'http/grpc.j2' import grpc with context %}
{% filter indent(4) %}
    {{ grpc(server['grpc']) }}
{%- endfilter %}
{% endif %}
{% if server['access'] is defined %}
{% from 'http/auth.j2' import access with context %}
{% filter indent(4) %}
    {{ access(server['access']) }}
{%- endfilter %}
{% endif %}
{% if server['auth_request'] is defined %}
{% from 'http/auth.j2' import auth_request with context %}
{% filter indent(4) %}
    {{ auth_request(server['auth_request']) }}
{%- endfilter %}
{% endif %}
{% if server['auth_basic'] is defined %}
{% from 'http/auth.j2' import auth_basic with context %}
{% filter indent(4) %}
    {{ auth_basic(server['auth_basic']) }}
{%- endfilter %}
{% endif %}
{% if server['auth_jwt'] is defined %}
{% from 'http/auth.j2' import auth_jwt with context %}
{% filter indent(4) %}
    {{ auth_jwt(server['auth_jwt']) }}
{%- endfilter %}
{% endif %}
{% if server['api'] is defined %}
{% from 'http/modules.j2' import api with context %}
{% filter indent(4) %}
    {{ api(server['api']) }}
{%- endfilter %}
{% endif %}
{% if server['stub_status'] is defined %}
{% from 'http/modules.j2' import stub_status with context %}
    {{ stub_status(server['stub_status']) }}
{%- endif %}
{% if server['autoindex'] is defined %}
{% from 'http/modules.j2' import autoindex with context %}
{% filter indent(4) %}
    {{ autoindex(server['autoindex']) }}
{%- endfilter %}
{% endif %}
{% if server['gunzip'] is defined %}
{% from 'http/modules.j2' import gunzip with context %}
{% filter indent(4) %}
    {{ gunzip(server['gunzip']) }}
{%- endfilter %}
{% endif %}
{% if server['gzip'] is defined %}
{% from 'http/modules.j2' import gzip with context %}
{% filter indent(4) %}
    {{ gzip(server['gzip']) }}
{%- endfilter %}
{% endif %}
{% if server['headers'] is defined %}
{% from 'http/modules.j2' import headers with context %}
{% filter indent(4) %}
    {{ headers(server['headers']) }}
{%- endfilter %}
{% endif %}
{% if server['health_check'] is defined %}
{% from 'http/modules.j2' import health_check with context %}
{% filter indent(4) %}
    {{ health_check(server['health_check']) }}
{% endfilter %}
{% endif %}
{% if server['keyval'] is defined %}
{% from 'http/modules.j2' import keyval with context %}
{% filter indent(4) %}
    {{ keyval(server['keyval']) }}
{% endfilter %}
{% endif %}
{% if server['limit_req'] is defined %}
{% from 'http/modules.j2' import limit_req with context %}
{% filter indent(4) %}
    {{ limit_req(server['limit_req']) }}
{%- endfilter %}
{% endif %}
{% if server['log'] is defined %}
{% from 'http/modules.j2' import log with context %}
{% filter indent(4) %}
    {{ log(server['log']) }}
{%- endfilter %}
{% endif %}
{% if server['mirror'] is defined %}
{% from 'http/modules.j2' import mirror with context %}
{% filter indent(4) %}
    {{ mirror(server['mirror']) }}
{%- endfilter %}
{% endif %}
{% if server['realip'] is defined %}
{% from 'http/modules.j2' import realip with context %}
{% filter indent(4) %}
    {{ realip(server['realip']) }}
{%- endfilter %}
{% endif %}
{% if server['rewrite'] is defined %}
{% from 'http/modules.j2' import rewrite with context %}
{% filter indent(4) %}
    {{ rewrite(server['rewrite']) }}
{%- endfilter %}
{% endif %}
{% if server['sub_filter'] is defined %}
{% from 'http/modules.j2' import sub_filter with context %}
{% filter indent(4) %}
    {{ sub_filter(server['sub_filter']) }}
{%- endfilter %}
{% endif %}
{% if server['custom_directives'] is defined and server['custom_directives'] is not mapping %}
{% for directive in server['custom_directives'] if server['custom_directives'] is not string %}
{% filter indent(4) %}
    {{ directive }}
{% endfilter %}
{% else %}
    {{ server['custom_directives'] }}
{% endfor %}
{% endif %}
{% if server['locations'] is defined %}
{% for location in server['locations'] %}
    location {{ location['location'] }} {
{% if location['core'] is defined %}
{% from 'http/core.j2' import core with context %}
{% filter indent(8) %}
        {{ core(location['core']) }}
{%- endfilter %}
{% endif %}
{% if location['http2'] is defined %}
{% from 'http/modules.j2' import http2 with context %}
{% filter indent(8) %}
        {{ http2(location['http2'], 'location') }}
{%- endfilter %}
{% endif %}
{% if location['app_protect_waf'] is defined %}
{% from 'http/app_protect.j2' import app_protect_waf with context %}
{% filter indent(8) %}
        {{ app_protect_waf(location['app_protect_waf']) }}
{%- endfilter %}
{% endif %}
{% if location['app_protect_dos'] is defined %}
{% from 'http/app_protect.j2' import app_protect_dos with context %}
{% filter indent(8) %}
        {{ app_protect_dos(location['app_protect_dos']) }}
{%- endfilter %}
{% endif %}
{% if location['proxy'] is defined %}
{% from 'http/proxy.j2' import proxy with context %}
{% filter indent(8) %}
        {{ proxy(location['proxy']) }}
{%- endfilter %}
{% endif %}
{% if location['grpc'] is defined %}
{% from 'http/grpc.j2' import grpc with context %}
{% filter indent(8) %}
        {{ grpc(location['grpc']) }}
{%- endfilter %}
{% endif %}
{% if location['access'] is defined %}
{% from 'http/auth.j2' import access with context %}
{% filter indent(8) %}
        {{ access(location['access']) }}
{%- endfilter %}
{% endif %}
{% if location['auth_request'] is defined %}
{% from 'http/auth.j2' import auth_request with context %}
{% filter indent(8) %}
        {{ auth_request(location['auth_request']) }}
{%- endfilter %}
{% endif %}
{% if location['auth_basic'] is defined %}
{% from 'http/auth.j2' import auth_basic with context %}
{% filter indent(8) %}
        {{ auth_basic(location['auth_basic']) }}
{%- endfilter %}
{% endif %}
{% if location['auth_jwt'] is defined %}
{% from 'http/auth.j2' import auth_jwt with context %}
{% filter indent(8) %}
        {{ auth_jwt(location['auth_jwt']) }}
{%- endfilter %}
{% endif %}
{% if location['api'] is defined %}
{% from 'http/modules.j2' import api with context %}
{% filter indent(8) %}
        {{ api(location['api']) }}
{%- endfilter %}
{% endif %}
{% if location['stub_status'] is defined %}
{% from 'http/modules.j2' import stub_status with context %}
        {{ stub_status(location['stub_status']) }}
{%- endif %}
{% if location['autoindex'] is defined %}
{% from 'http/modules.j2' import autoindex with context %}
{% filter indent(8) %}
        {{ autoindex(location['autoindex']) }}
{%- endfilter %}
{% endif %}
{% if location['gunzip'] is defined %}
{% from 'http/modules.j2' import gunzip with context %}
{% filter indent(8) %}
        {{ gunzip(location['gunzip']) }}
{%- endfilter %}
{% endif %}
{% if location['gzip'] is defined %}
{% from 'http/modules.j2' import gzip with context %}
{% filter indent(8) %}
        {{ gzip(location['gzip']) }}
{%- endfilter %}
{% endif %}
{% if location['headers'] is defined %}
{% from 'http/modules.j2' import headers with context %}
{% filter indent(8) %}
        {{ headers(location['headers']) }}
{%- endfilter %}
{% endif %}
{% if location['health_check'] is defined %}
{% from 'http/modules.j2' import health_check with context %}
{% filter indent(8) %}
        {{ health_check(location['health_check']) }}
{%- endfilter %}
{% endif %}
{% if location['keyval'] is defined %}
{% from 'http/modules.j2' import keyval with context %}
{% filter indent(8) %}
        {{ keyval(location['keyval']) }}
{%- endfilter %}
{% endif %}
{% if location['limit_req'] is defined %}
{% from 'http/modules.j2' import limit_req with context %}
{% filter indent(8) %}
        {{ limit_req(location['limit_req']) }}
{%- endfilter %}
{% endif %}
{% if location['log'] is defined %}
{% from 'http/modules.j2' import log with context %}
{% filter indent(8) %}
        {{ log(location['log']) }}
{%- endfilter %}
{% endif %}
{% if location['mirror'] is defined %}
{% from 'http/modules.j2' import mirror with context %}
{% filter indent(8) %}
        {{ mirror(location['mirror']) }}
{%- endfilter %}
{% endif %}
{% if location['realip'] is defined %}
{% from 'http/modules.j2' import realip with context %}
{% filter indent(8) %}
        {{ realip(location['realip']) }}
{%- endfilter %}
{% endif %}
{% if location['rewrite'] is defined %}
{% from 'http/modules.j2' import rewrite with context %}
{% filter indent(8) %}
        {{ rewrite(location['rewrite']) }}
{%- endfilter %}
{% endif %}
{% if location['sub_filter'] is defined %}
{% from 'http/modules.j2' import sub_filter with context %}
{% filter indent(8) %}
        {{ sub_filter(location['sub_filter']) }}
{%- endfilter %}
{% endif %}
{% if location['custom_directives'] is defined and location['custom_directives'] is not mapping %}
{% for directive in location['custom_directives'] if location['custom_directives'] is not string %}
{% filter indent(8) %}
        {{ directive }}
{% endfilter %}
{% else %}
        {{ location['custom_directives'] }}
{% endfor %}
{% endif %}

    }
{% endfor %}
{% endif %}
}
{% endfor %}
{% endif %}
): sequence item 4: expected str instance, Undefined found. sequence item 4: expected str instance, Undefined found
failed: [git.my.domain] (item=/etc/nginx/conf.d/git.my.domain.conf) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "config": {
            "servers": [
                {
                    "core": {
                        "listen": [
                            {
                                "address": "0.0.0.0",
                                "default_server": "true",
                                "port": "80"
                            }
                        ],
                        "server_name": "git.my.domain"
                    },
                    "locations": [
                        {
                            "location": "/",
                            "rewrite": {
                                "code": "301",
                                "text": "https://git.my.domain"
                            }
                        }
                    ]
                }
            ]
        },
        "deployment_location": "/etc/nginx/conf.d/git.my.domain.conf",
        "template_file": "http/default.conf.j2"
    },
    "msg": "AnsibleError: Unexpected templating type error occurred on ({{ ansible_managed | comment }}\n\n{% if item['config']['upstreams'] is defined %}\n{% from 'http/upstream.j2' import upstream with context %}\n{{ upstream(item['config']['upstreams']) }}\n{%- endif %}\n{% if item['config']['core'] is defined %}\n{% from 'http/core.j2' import core with context %}\n{{ core(item['config']['core']) }}\n{%- endif %}\n{% if item['config']['http2'] is defined %}\n{% from 'http/modules.j2' import http2 with context %}\n{{ http2(item['config']['http2'], 'http') }}\n{%- endif %}\n{% if item['config']['http3'] is defined %}\n{% from 'http/modules.j2' import http3 with context %}\n{{ http3(item['config']['http3']) }}\n{%- endif %}\n{% if item['config']['quic'] is defined %}\n{% from 'http/modules.j2' import quic with context %}\n{{ quic(item['config']['quic']) }}\n{%- endif %}\n{% if item['config']['ssl'] is defined %}\n{% from 'http/ssl.j2' import ssl with context %}\n{{ ssl(item['config']['ssl']) }}\n{%- endif %}\n{% if item['config']['app_protect_waf'] is defined %}\n{% from 'http/app_protect.j2' import app_protect_waf with context %}\n{{ app_protect_waf(item['config']['app_protect_waf']) }}\n{%- endif %}\n{% if item['config']['app_protect_dos'] is defined %}\n{% from 'http/app_protect.j2' import app_protect_dos with context %}\n{{ app_protect_dos(item['config']['app_protect_dos']) }}\n{%- endif %}\n{% if item['config']['proxy'] is defined %}\n{% from 'http/proxy.j2' import proxy with context %}\n{{ proxy(item['config']['proxy']) }}\n{%- endif %}\n{% if item['config']['grpc'] is defined %}\n{% from 'http/grpc.j2' import grpc with context %}\n{{ grpc(item['config']['grpc']) }}\n{%- endif %}\n{% if item['config']['access'] is defined %}\n{% from 'http/auth.j2' import access with context %}\n{{ access(item['config']['access']) }}\n{%- endif %}\n{% if item['config']['auth_basic'] is defined %}\n{% from 'http/auth.j2' import auth_basic with context %}\n{{ auth_basic(item['config']['auth_basic']) }}\n{%- endif %}\n{% if item['config']['auth_request'] is defined %}\n{% from 'http/auth.j2' import auth_request with context %}\n{{ auth_request(item['config']['auth_request']) }}\n{%- endif %}\n{% if item['config']['auth_jwt'] is defined %}\n{% from 'http/auth.j2' import auth_jwt with context %}\n{{ auth_jwt(item['config']['auth_jwt']) }}\n{%- endif %}\n{% if item['config']['api'] is defined %}\n{% from 'http/modules.j2' import api with context %}\n{{ api(item['config']['api']) }}\n{%- endif %}\n{% if item['config']['stub_status'] is defined %}\n{% from 'http/modules.j2' import stub_status with context %}\n{{ stub_status(item['config']['stub_status']) }}\n{%- endif %}\n{% if item['config']['autoindex'] is defined %}\n{% from 'http/modules.j2' import autoindex with context %}\n{{ autoindex(item['config']['autoindex']) }}\n{%- endif %}\n{% if item['config']['gunzip'] is defined %}\n{% from 'http/modules.j2' import gunzip with context %}\n{{ gunzip(item['config']['gunzip']) }}\n{%- endif %}\n{% if item['config']['gzip'] is defined %}\n{% from 'http/modules.j2' import gzip with context %}\n{{ gzip(item['config']['gzip']) }}\n{%- endif %}\n{% if item['config']['headers'] is defined %}\n{% from 'http/modules.j2' import headers with context %}\n{{ headers(item['config']['headers']) }}\n{%- endif %}\n{% if item['config']['health_check'] is defined %}\n{% from 'http/modules.j2' import health_check with context %}\n{{ health_check(item['config']['health_check']) }}\n{%- endif %}\n{% if item['config']['keyval'] is defined %}\n{% from 'http/modules.j2' import keyval with context %}\n{{ keyval(item['config']['keyval']) }}\n{%- endif %}\n{% if item['config']['limit_req'] is defined %}\n{% from 'http/modules.j2' import limit_req with context %}\n{{ limit_req(item['config']['limit_req']) }}\n{%- endif %}\n{% if item['config']['log'] is defined %}\n{% from 'http/modules.j2' import log with context %}\n{{ log(item['config']['log']) }}\n{%- endif %}\n{% if item['config']['map'] is defined %}\n{% from 'http/modules.j2' import map with context %}\n{{ map(item['config']['map']) }}\n{%- endif %}\n{% if item['config']['mirror'] is defined %}\n{% from 'http/modules.j2' import mirror with context %}\n{{ mirror(item['config']['mirror']) }}\n{%- endif %}\n{% if item['config']['realip'] is defined %}\n{% from 'http/modules.j2' import realip with context %}\n{{ realip(item['config']['realip']) }}\n{%- endif %}\n{% if item['config']['rewrite'] is defined %}\n{% from 'http/modules.j2' import rewrite with context %}\n{{ rewrite(item['config']['rewrite']) }}\n{%- endif %}\n{% if item['config']['split_clients'] is defined %}\n{% from 'http/modules.j2' import split_clients with context %}\n{{ split_clients(item['config']['split_clients']) }}\n{%- endif %}\n{% if item['config']['sub_filter'] is defined %}\n{% from 'http/modules.j2' import sub_filter with context %}\n{{ sub_filter(item['config']['sub_filter']) }}\n{%- endif %}\n{% if item['config']['custom_directives'] is defined and item['config']['custom_directives'] is not mapping %}\n{% for directive in item['config']['custom_directives'] if item['config']['custom_directives'] is not string %}\n{{ directive }}\n{% else %}\n{{ item['config']['custom_directives'] }}\n{% endfor %}\n{% endif %}\n{% if item['config']['servers'] is defined %}\n{% for server in item['config']['servers'] %}\nserver {\n{% if server['core'] is defined %}\n{% from 'http/core.j2' import core with context %}\n{% filter indent(4) %}\n    {{ core(server['core']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['http2'] is defined %}\n{% from 'http/modules.j2' import http2 with context %}\n{% filter indent(4) %}\n    {{ http2(server['http2'], 'server') }}\n{%- endfilter %}\n{% endif %}\n{% if server['http3'] is defined %}\n{% from 'http/modules.j2' import http3 with context %}\n{% filter indent(4) %}\n    {{ http3(server['http3']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['quic'] is defined %}\n{% from 'http/modules.j2' import quic with context %}\n{% filter indent(4) %}\n    {{ quic(server['quic']) }}\n{%- endfilter %}\n{% endif %}\n\n{% if server['ssl'] is defined %}\n{% from 'http/ssl.j2' import ssl with context %}\n{% filter indent(4) %}\n    {{ ssl(server['ssl']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['app_protect_waf'] is defined %}\n{% from 'http/app_protect.j2' import app_protect_waf with context %}\n{% filter indent(4) %}\n    {{ app_protect_waf(server['app_protect_waf']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['app_protect_dos'] is defined %}\n{% from 'http/app_protect.j2' import app_protect_dos with context %}\n{% filter indent(4) %}\n    {{ app_protect_dos(server['app_protect_dos']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['proxy'] is defined %}\n{% from 'http/proxy.j2' import proxy with context %}\n{% filter indent(4) %}\n    {{ proxy(server['proxy']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['grpc'] is defined %}\n{% from 'http/grpc.j2' import grpc with context %}\n{% filter indent(4) %}\n    {{ grpc(server['grpc']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['access'] is defined %}\n{% from 'http/auth.j2' import access with context %}\n{% filter indent(4) %}\n    {{ access(server['access']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['auth_request'] is defined %}\n{% from 'http/auth.j2' import auth_request with context %}\n{% filter indent(4) %}\n    {{ auth_request(server['auth_request']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['auth_basic'] is defined %}\n{% from 'http/auth.j2' import auth_basic with context %}\n{% filter indent(4) %}\n    {{ auth_basic(server['auth_basic']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['auth_jwt'] is defined %}\n{% from 'http/auth.j2' import auth_jwt with context %}\n{% filter indent(4) %}\n    {{ auth_jwt(server['auth_jwt']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['api'] is defined %}\n{% from 'http/modules.j2' import api with context %}\n{% filter indent(4) %}\n    {{ api(server['api']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['stub_status'] is defined %}\n{% from 'http/modules.j2' import stub_status with context %}\n    {{ stub_status(server['stub_status']) }}\n{%- endif %}\n{% if server['autoindex'] is defined %}\n{% from 'http/modules.j2' import autoindex with context %}\n{% filter indent(4) %}\n    {{ autoindex(server['autoindex']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['gunzip'] is defined %}\n{% from 'http/modules.j2' import gunzip with context %}\n{% filter indent(4) %}\n    {{ gunzip(server['gunzip']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['gzip'] is defined %}\n{% from 'http/modules.j2' import gzip with context %}\n{% filter indent(4) %}\n    {{ gzip(server['gzip']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['headers'] is defined %}\n{% from 'http/modules.j2' import headers with context %}\n{% filter indent(4) %}\n    {{ headers(server['headers']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['health_check'] is defined %}\n{% from 'http/modules.j2' import health_check with context %}\n{% filter indent(4) %}\n    {{ health_check(server['health_check']) }}\n{% endfilter %}\n{% endif %}\n{% if server['keyval'] is defined %}\n{% from 'http/modules.j2' import keyval with context %}\n{% filter indent(4) %}\n    {{ keyval(server['keyval']) }}\n{% endfilter %}\n{% endif %}\n{% if server['limit_req'] is defined %}\n{% from 'http/modules.j2' import limit_req with context %}\n{% filter indent(4) %}\n    {{ limit_req(server['limit_req']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['log'] is defined %}\n{% from 'http/modules.j2' import log with context %}\n{% filter indent(4) %}\n    {{ log(server['log']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['mirror'] is defined %}\n{% from 'http/modules.j2' import mirror with context %}\n{% filter indent(4) %}\n    {{ mirror(server['mirror']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['realip'] is defined %}\n{% from 'http/modules.j2' import realip with context %}\n{% filter indent(4) %}\n    {{ realip(server['realip']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['rewrite'] is defined %}\n{% from 'http/modules.j2' import rewrite with context %}\n{% filter indent(4) %}\n    {{ rewrite(server['rewrite']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['sub_filter'] is defined %}\n{% from 'http/modules.j2' import sub_filter with context %}\n{% filter indent(4) %}\n    {{ sub_filter(server['sub_filter']) }}\n{%- endfilter %}\n{% endif %}\n{% if server['custom_directives'] is defined and server['custom_directives'] is not mapping %}\n{% for directive in server['custom_directives'] if server['custom_directives'] is not string %}\n{% filter indent(4) %}\n    {{ directive }}\n{% endfilter %}\n{% else %}\n    {{ server['custom_directives'] }}\n{% endfor %}\n{% endif %}\n{% if server['locations'] is defined %}\n{% for location in server['locations'] %}\n    location {{ location['location'] }} {\n{% if location['core'] is defined %}\n{% from 'http/core.j2' import core with context %}\n{% filter indent(8) %}\n        {{ core(location['core']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['http2'] is defined %}\n{% from 'http/modules.j2' import http2 with context %}\n{% filter indent(8) %}\n        {{ http2(location['http2'], 'location') }}\n{%- endfilter %}\n{% endif %}\n{% if location['app_protect_waf'] is defined %}\n{% from 'http/app_protect.j2' import app_protect_waf with context %}\n{% filter indent(8) %}\n        {{ app_protect_waf(location['app_protect_waf']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['app_protect_dos'] is defined %}\n{% from 'http/app_protect.j2' import app_protect_dos with context %}\n{% filter indent(8) %}\n        {{ app_protect_dos(location['app_protect_dos']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['proxy'] is defined %}\n{% from 'http/proxy.j2' import proxy with context %}\n{% filter indent(8) %}\n        {{ proxy(location['proxy']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['grpc'] is defined %}\n{% from 'http/grpc.j2' import grpc with context %}\n{% filter indent(8) %}\n        {{ grpc(location['grpc']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['access'] is defined %}\n{% from 'http/auth.j2' import access with context %}\n{% filter indent(8) %}\n        {{ access(location['access']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['auth_request'] is defined %}\n{% from 'http/auth.j2' import auth_request with context %}\n{% filter indent(8) %}\n        {{ auth_request(location['auth_request']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['auth_basic'] is defined %}\n{% from 'http/auth.j2' import auth_basic with context %}\n{% filter indent(8) %}\n        {{ auth_basic(location['auth_basic']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['auth_jwt'] is defined %}\n{% from 'http/auth.j2' import auth_jwt with context %}\n{% filter indent(8) %}\n        {{ auth_jwt(location['auth_jwt']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['api'] is defined %}\n{% from 'http/modules.j2' import api with context %}\n{% filter indent(8) %}\n        {{ api(location['api']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['stub_status'] is defined %}\n{% from 'http/modules.j2' import stub_status with context %}\n        {{ stub_status(location['stub_status']) }}\n{%- endif %}\n{% if location['autoindex'] is defined %}\n{% from 'http/modules.j2' import autoindex with context %}\n{% filter indent(8) %}\n        {{ autoindex(location['autoindex']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['gunzip'] is defined %}\n{% from 'http/modules.j2' import gunzip with context %}\n{% filter indent(8) %}\n        {{ gunzip(location['gunzip']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['gzip'] is defined %}\n{% from 'http/modules.j2' import gzip with context %}\n{% filter indent(8) %}\n        {{ gzip(location['gzip']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['headers'] is defined %}\n{% from 'http/modules.j2' import headers with context %}\n{% filter indent(8) %}\n        {{ headers(location['headers']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['health_check'] is defined %}\n{% from 'http/modules.j2' import health_check with context %}\n{% filter indent(8) %}\n        {{ health_check(location['health_check']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['keyval'] is defined %}\n{% from 'http/modules.j2' import keyval with context %}\n{% filter indent(8) %}\n        {{ keyval(location['keyval']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['limit_req'] is defined %}\n{% from 'http/modules.j2' import limit_req with context %}\n{% filter indent(8) %}\n        {{ limit_req(location['limit_req']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['log'] is defined %}\n{% from 'http/modules.j2' import log with context %}\n{% filter indent(8) %}\n        {{ log(location['log']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['mirror'] is defined %}\n{% from 'http/modules.j2' import mirror with context %}\n{% filter indent(8) %}\n        {{ mirror(location['mirror']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['realip'] is defined %}\n{% from 'http/modules.j2' import realip with context %}\n{% filter indent(8) %}\n        {{ realip(location['realip']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['rewrite'] is defined %}\n{% from 'http/modules.j2' import rewrite with context %}\n{% filter indent(8) %}\n        {{ rewrite(location['rewrite']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['sub_filter'] is defined %}\n{% from 'http/modules.j2' import sub_filter with context %}\n{% filter indent(8) %}\n        {{ sub_filter(location['sub_filter']) }}\n{%- endfilter %}\n{% endif %}\n{% if location['custom_directives'] is defined and location['custom_directives'] is not mapping %}\n{% for directive in location['custom_directives'] if location['custom_directives'] is not string %}\n{% filter indent(8) %}\n        {{ directive }}\n{% endfilter %}\n{% else %}\n        {{ location['custom_directives'] }}\n{% endfor %}\n{% endif %}\n\n    }\n{% endfor %}\n{% endif %}\n}\n{% endfor %}\n{% endif %}\n): sequence item 4: expected str instance, Undefined found. sequence item 4: expected str instance, Undefined found"
}

Entire error log.

Package               Version
--------------------- ----------------
ansible               9.6.0
ansible-core          2.16.7
Jinja2                3.0.3

That's all I have ^^

Yeah the jinja stuff is a little sad.

Ideally I would like to extend the config file to include ssl certificate and the proxy pass to gitea with upstream and all.

So the basic web server configuration for an nginx site should work

Couple suggestions:

  • Can you update your version of Jinja2? 3.1 introduced some features and bug fixes that this role uses and they might be related to your issues. (See ansible/ansible#77272 (comment) for one of those issues/fixes.)
  • Can you update your block to this (you might need to tweak the whitespaces, it's hard to write YAML/Python here 😅 ):
servers:
  - core:
      server_name: "git.my.domain"
      listen:
        - default_server: true
          port: 80
          address: 0.0.0.0
    locations:
      - location: /
        rewrite:
          code: 301
          text: "https://git.my.domain/"

It passes now.

#
# Ansible managed
#

server {
    listen 0.0.0.0:80 default_server;
    server_name git.my.domain;


    location / {


    }
}

Prints this so the rewrite isn't working properly yet

But that is because rewrite is missing the return:

rewrite:
  return:
    code: 301
    text: "https://$host$request_uri"

This is save to close now. I'll continue developing my role and will answer my underlying discussion myself later for documentation of the Jinja version and such. Thank you so much

For posterity sake, looks like the issue was related to running an old version of Jinja2.