Skip to content

Commit

Permalink
Merge pull request #476 from Tecnativa/fix-ci-error
Browse files Browse the repository at this point in the history
[IMP] Traeffik 3 compatibility
  • Loading branch information
pedrobaeza authored Jul 2, 2024
2 parents e2f4efd + b231192 commit 911397e
Show file tree
Hide file tree
Showing 9 changed files with 443 additions and 38 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ jobs:
odoo-version: 17.0
- python-version: 3.11
odoo-version: 17.0
traefik_version:
- 1
- 2
- 3

steps:
# Shared steps
Expand Down Expand Up @@ -92,7 +96,9 @@ jobs:
- run: poetry run invoke test
env:
SELECTED_ODOO_VERSIONS: ${{ matrix.odoo-version }}
TRAEFIK_VERSION: ${{ matrix.traefik_version }}
# Concurrent tests (isolated)
- run: poetry run invoke test --sequential
env:
SELECTED_ODOO_VERSIONS: ${{ matrix.odoo-version }}
TRAEFIK_VERSION: ${{ matrix.traefik_version }}
108 changes: 108 additions & 0 deletions _traefik3_labels.yml.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{%- import "_macros.jinja" as macros -%}
{# Echo all domains of a group, in a Traefik rule #}
{%- macro domains_rule(hosts, path_prefixes=(), paths=()) -%}
Host(`{{ hosts | join("`) || Host(`") }}`)
{%- if path_prefixes or paths -%}
{{" "}}&& (
{%- if path_prefixes -%}
{{ path_prefix_rule(path_prefixes) }}
{%- if paths %} || {% endif %}
{%- endif %}
{%- if paths -%}
{{ path_rule(paths) }}
{%- endif -%}
)
{%- endif %}
{%- endmacro %}

{# Echo all path prefixes in a Traefik rule #}
{%- macro path_prefix_rule(path_prefixes) -%}
{%- for path in path_prefixes -%}
{%- if path.endswith("/") -%}
PathPrefix(`{{ path }}`)
{%- else -%}
PathPrefix(`{{ path }}`) || Path(`{{ path }}`)
{%- endif -%}
{%- if not loop.last %} || {% endif %}
{%- endfor %}
{%- endmacro %}

{# Echo all paths in a Traefik rule #}
{%- macro path_rule(paths) -%}
{%- for path in paths -%}
Path(`{{ path }}`)
{%- if not loop.last %} || {% endif %}
{%- endfor %}
{%- endmacro %}

{%- macro odoo(domain_groups_list, paths_without_crawlers, odoo_version, traefik_version) %}
traefik.domain: {{ macros.first_main_domain(domain_groups_list)|tojson }}
{%- call(domain_group) macros.domains_loop_grouped(domain_groups_list) %}

{#- Route redirections #}
{%- if domain_group.redirect_to %}
traefik.alt-{{ domain_group.loop.index0 }}.frontend.redirect.regex: ^(.*)://([^/]+)/(.*)$$
traefik.alt-{{ domain_group.loop.index0 }}.frontend.redirect.replacement: $$1://{{ domain_group.redirect_to }}/$$3
{{-
router(
prefix="alt",
index0=domain_group.loop.index0,
rule=domains_rule(domain_group.hosts, domain_group.path_prefixes),
entrypoints=domain_group.entrypoints,
)
}}
{%- else %}

{#- Forbidden crawler routers #}
{%- if traefik_version < 3%}
{%- if paths_without_crawlers and not domain_group.path_prefixes %}
traefik.forbiddenCrawlers-{{ domain_group.loop.index0 }}.frontend.headers.customResponseHeaders:
"X-Robots-Tag:noindex, nofollow"
{{-
router(
prefix="forbiddenCrawlers",
index0=domain_group.loop.index0,
rule=domains_rule(domain_group.hosts, paths_without_crawlers),
entrypoints=domain_group.entrypoints,
)
}}
{%- endif %}
{%- endif %}

{#- Normal routers #}
{%- if paths_without_crawlers != ["/"] or domain_group.path_prefixes %}
{{-
router(
prefix="main",
index0=domain_group.loop.index0,
rule=domains_rule(domain_group.hosts, domain_group.path_prefixes),
entrypoints=domain_group.entrypoints,
)
}}
{%- endif %}
{%- if not domain_group.path_prefixes %}
{%- set longpolling_route = "/longpolling/" if odoo_version < 16 else "/websocket" -%}
{{-
router(
prefix="longpolling",
index0=domain_group.loop.index0,
rule=domains_rule(domain_group.hosts, [longpolling_route]),
entrypoints=domain_group.entrypoints,
port=8072,
)
}}
{%- endif %}
{%- endif %}
{%- endcall %}
{%- endmacro %}
{#- Basic labels for a single router #}
{%- macro router(prefix, index0, rule, entrypoints=(), port=none) %}
traefik.{{ prefix }}-{{ index0 }}.frontend.rule: {{ rule }}
{%- if entrypoints %}
traefik.{{ prefix }}-{{ index0 }}.frontend.entryPoints:
{{ entrypoints|sort|join(",") }}
{%- endif %}
{%- if port %}
traefik.{{ prefix }}-{{ index0 }}.port: {{ port }}
{%- endif %}
{%- endmacro %}
243 changes: 243 additions & 0 deletions _traefik3_paths_labels.yml.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
{%- import "_macros.jinja" as macros -%}
{#
Note: indentation of 6 spaces is important because that's the depth level
of container labels in a docker-compose file.
#}

{# Echo all path prefixes in a Traefik rule #}
{%- macro path_prefix_rule(path_prefixes) %}
{%- set _ns = namespace(with_slash=[], without_slash=[]) %}
{%- for prefix in path_prefixes %}
{%- if prefix.endswith("/") %}
{%- set _ns.with_slash = _ns.with_slash + [prefix] %}
{%- else %}
{%- set _ns.with_slash = _ns.with_slash + ["%s/" % prefix] %}
{%- set _ns.without_slash = _ns.without_slash + [prefix] %}
{%- endif %}
{%- endfor -%}
(PathPrefix(
{%- for path in _ns.with_slash -%}
`{{ path }}`
{%- if not loop.last %}) || PathPrefix({% endif %}
{%- endfor -%}
)
{%- if _ns.without_slash %} || Path(
{%- for path in _ns.without_slash -%}
`{{ path }}`
{%- if not loop.last %}) || Path({% endif %}
{%- endfor -%}
)
{%- endif -%}
)
{%- endmacro %}

{# Echo all domains of a group, in a Traefik rule #}
{%- macro domains_rule(domain_group) -%}
Host(
{%- for host in domain_group.hosts -%}
`{{ host }}`
{%- if not loop.last %}, {% endif %}
{%- endfor -%}
)
{%- if domain_group.path_prefixes %} && {{ path_prefix_rule(domain_group.path_prefixes) }}
{%- endif %}
{%- endmacro %}

{%- macro key(project_name, odoo_version, suffix) %}
{{- '%s-%.1f-%s'|format(project_name, odoo_version, suffix)|replace('.', '-') }}
{%- endmacro %}

{#- Basic labels for a single router #}
{%- macro router(domain_group, key, suffix, rule=none, service=none, middlewares=()) %}
traefik.http.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.rule:
{{ rule|default(domains_rule(domain_group), true) }}
traefik.http.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.service:
{{ key }}-{{ service|default("main", true) }}
{%- if domain_group.entrypoints %}
traefik.http.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.entrypoints:
{{ domain_group.entrypoints|sort|join(", ") }}
{%- endif %}
{%- if middlewares %}
traefik.http.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.middlewares:
{{ key }}-{{ middlewares|sort|join(", %s-" % key) }}
{%- endif %}

{%- if domain_group.cert_resolver %}

{#- Add TLS configuration #}
{%- if suffix.endswith("-secure") %}
traefik.http.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.tls: "true"
{%- if domain_group.cert_resolver is string %}
traefik.http.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.tls.certResolver:
{{ domain_group.cert_resolver }}
{%- endif %}

{#- Create TLS-only router;
HACK https://github.com/containous/traefik/issues/7235 #}
{%- else %}
{{- router(domain_group, key, "%s-secure" % suffix, rule, service, middlewares) }}
{%- endif %}

{%- endif %}
{%- endmacro %}

{%- macro common_middlewares(key, cidr_whitelist) %}
{#- Common middlewares #}
traefik.http.middlewares.{{ key }}-buffering.buffering.retryExpression:
IsNetworkError() && Attempts() < 5
traefik.http.middlewares.{{ key }}-compress.compress: "true"
? traefik.http.middlewares.{{ key }}-forbid-crawlers.headers.customResponseHeaders.X-Robots-Tag
: "noindex, nofollow"
traefik.http.middlewares.{{ key }}-addSTS.headers.forceSTSHeader: "true"
traefik.http.middlewares.{{ key }}-forceSecure.redirectScheme.scheme: https
traefik.http.middlewares.{{ key }}-forceSecure.redirectScheme.permanent: "true"
{%- if cidr_whitelist %}
{#- Declare whitelist middleware #}
? traefik.http.middlewares.{{ key }}-whitelist.IPWhiteList.sourceRange
: {% for cidr in cidr_whitelist -%}
{{ cidr }}{% if not loop.last %}, {% endif %}
{%- endfor %}
{%- endif %}
{%- endmacro %}

{%- macro odoo(domain_groups_list, cidr_whitelist, key, odoo_version,
paths_without_crawlers, project_name) %}
{#- Service #}
traefik.http.services.{{ key }}-main.loadbalancer.server.port: 8069
traefik.http.services.{{ key }}-longpolling.loadbalancer.server.port: 8072

{%- call(domain_group) macros.domains_loop_grouped(domain_groups_list) %}
{#- Remember basic middlewares for this domain group #}
{%- set _ns = namespace(basic_middlewares=[]) -%}
{%- if cidr_whitelist %}
{%- set _ns.basic_middlewares = _ns.basic_middlewares + ["whitelist"] %}
{%- endif %}
{%- if domain_group.cert_resolver %}
{%- set _ns.basic_middlewares = _ns.basic_middlewares + ["addSTS", "forceSecure"] %}
{%- endif %}

{%- if domain_group.redirect_to %}
{#- Redirections #}
{%- if domain_group.redirect_permanent %}
traefik.http.middlewares.{{ key }}-redirect-{{ domain_group.loop.index0 }}.redirectRegex.permanent: "true"
{%- endif %}
traefik.http.middlewares.{{ key }}-redirect-{{ domain_group.loop.index0 }}.redirectRegex.regex: ^(.*)://([^/]+)/(.*)$$
traefik.http.middlewares.{{ key }}-redirect-{{ domain_group.loop.index0 }}.redirectRegex.replacement: $$1://{{ domain_group.redirect_to }}/$$3
{{-
router(
domain_group=domain_group,
key=key,
suffix="redirect",
middlewares=_ns.basic_middlewares + [
"compress",
"redirect-%d" % domain_group.loop.index0,
],
)
}}
{%- else %}

{#- When removing crawlers for /, this router is the same as forbiddenCrawlers, so no need to duplicate #}
{%- if paths_without_crawlers != ["/"] or domain_group.path_prefixes %}
{#- Main router #}
{{-
router(
domain_group=domain_group,
key=key,
suffix="main",
middlewares=_ns.basic_middlewares + [
"buffering",
"compress",
],
)
}}
{%- endif %}

{#- Longpolling router #}
{%- if not domain_group.path_prefixes %}
{%- set longpolling_route = "PathPrefix(`/longpolling/`)" if odoo_version < 16 else "Path(`/websocket`)" -%}
{{-
router(
domain_group=domain_group,
key=key,
suffix="longpolling",
rule="%s && %s" % (domains_rule(domain_group), longpolling_route),
service="longpolling",
middlewares=_ns.basic_middlewares,
)
}}
{%- endif %}

{#- Forbidden crawlers router #}
{%- if paths_without_crawlers and not domain_group.path_prefixes %}
{{-
router(
domain_group=domain_group,
key=key,
suffix="forbiddenCrawlers",
rule="%s && %s" % (
domains_rule(domain_group),
path_prefix_rule(paths_without_crawlers),
),
middlewares=_ns.basic_middlewares + [
"buffering",
"compress",
"forbid-crawlers",
],
)
}}
{%- endif %}
{%- endif %}
{%- endcall %}
{%- endmacro %}

{#- Basic labels for a single router #}
{%- macro router_tcp(domain_group, key, suffix, rule=none, service=none, middlewares=(), port=none) %}
{%- if port %}
traefik.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.port: {{ port }}
{%- endif %}
traefik.tcp.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.rule:
{{ rule|default(domains_rule(domain_group), true) }}
traefik.tcp.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.service:
{{ key }}-{{ service|default("main", true) }}
{%- if domain_group.entrypoints %}
traefik.tcp.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.entrypoints:
{{ domain_group.entrypoints|sort|join(", ") }}
{%- endif %}
{%- if middlewares %}
traefik.tcp.routers.{{ key }}-{{ suffix }}-{{ domain_group.loop.index0 }}.middlewares:
{{ key }}-{{ middlewares|sort|join(", %s-" % key) }}
{%- endif %}
{%- endmacro %}

{%- macro database(domain_groups_list, cidr_whitelist, key, port, project_name) %}
{#- Service #}
traefik.tcp.services.{{ key }}-database.loadbalancer.server.port: 5432

{%- if cidr_whitelist %}
{#- Declare whitelist middleware #}
? traefik.tcp.middlewares.{{ key }}-whitelist.IPWhiteList.sourceRange
: {% for cidr in cidr_whitelist -%}
{{ cidr }}{% if not loop.last %}, {% endif %}
{%- endfor %}
{%- endif %}

{%- call(domain_group) macros.domains_loop_grouped(domain_groups_list) %}
{#- Remember basic middlewares for this domain group #}
{%- set _ns = namespace(basic_middlewares=[]) -%}
{%- if cidr_whitelist %}
{%- set _ns.basic_middlewares = _ns.basic_middlewares + ["whitelist"] %}
{%- endif %}

{#- database router #}
{{-
router_tcp(
domain_group=domain_group,
key=key,
suffix="database",
service="database",
middlewares=_ns.basic_middlewares,
port=port,
)
}}
{%- endcall %}
{%- endmacro %}
11 changes: 11 additions & 0 deletions copier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ odoo_proxy:
Which proxy will you use to deploy odoo?
traefik_version:
type: int
default: 2
when: "{{ odoo_proxy == 'traefik' }}"
help: >
Indicate Traefik version( v2 recommended )
choices:
v1.7: 1
v2.4: 2
v3.0: 3

odoo_initial_lang:
default: en_US
type: str
Expand Down
1 change: 1 addition & 0 deletions docs/scaffolding2copier.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ copier $CUSTOM_COPIER_FLAGS \
-d postgres_version="$DB_VERSION" \
-d postgres_username="$DB_USER" \
-d postgres_dbname="prod" \
-d traefik_version="$TRAEFIK_VERSION" \
-d smtp_default_from="$SMTP_DEFAULT_FROM" \
-d smtp_relay_host="$SMTP_REAL_RELAY_HOST" \
-d smtp_relay_port="$SMTP_REAL_RELAY_PORT" \
Expand Down
Loading

0 comments on commit 911397e

Please sign in to comment.