Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/69724.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed the intermittent ``duplicate HTTP post method definition`` failure in the -W parallel docs builds (Prepare Release and Documentation jobs) by marking the HTTP routes documented on the rest_tornado and rest_wsgi pages with ``:noindex:``, leaving rest_cherrypy as the single indexed instance of each shared route.
45 changes: 45 additions & 0 deletions doc/_ext/salthttpanchors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
Keep HTML anchors on ``:noindex:``'d httpdomain directives.

sphinxcontrib-httpdomain's ``add_target_and_index`` intentionally splits its
two jobs: it always appends the ``#<method>--<path>`` anchor to the signature
node, and only gates the global route *registration* behind ``:noindex:``.
Sphinx's ``ObjectDescription.run`` however skips the whole method when
``noindex`` is set, so the anchor (and its permalink) is lost along with the
index entry. Anchors are per-page HTML ids and cannot collide across pages,
so restoring them is safe; only the global registration can produce the
parallel-build duplicate-route warnings.

Hide the option from Sphinx's outer gate and re-present it to httpdomain's
inner gate, so ``:noindex:`` means what httpdomain meant it to mean: no index
entry, anchor kept.
"""

from sphinxcontrib.httpdomain import HTTPDomain


def _make_anchored(cls):
class AnchoredHTTPResource(cls):
def run(self):
self._salt_noindex = "noindex" in self.options
self.options.pop("noindex", None)
return super().run()

def add_target_and_index(self, name_cls, sig, signode):
if self._salt_noindex:
self.options["noindex"] = None
try:
super().add_target_and_index(name_cls, sig, signode)
finally:
if self._salt_noindex:
self.options.pop("noindex", None)

AnchoredHTTPResource.__name__ = f"Anchored{cls.__name__}"
return AnchoredHTTPResource


def setup(app):
app.setup_extension("sphinxcontrib.httpdomain")
for name, cls in list(HTTPDomain.directives.items()):
app.add_directive_to_domain("http", name, _make_anchored(cls), override=True)
return {"parallel_read_safe": True, "parallel_write_safe": True}
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def _safe_urlsplit(url, scheme="", allow_fragments=True):
"sphinx.ext.imgconverter",
"sphinx.ext.intersphinx",
"sphinxcontrib.httpdomain",
"salthttpanchors",
"saltrepo",
"myst_parser",
#'saltautodoc', # Must be AFTER autodoc
Expand Down
10 changes: 10 additions & 0 deletions salt/netapi/rest_tornado/saltnado.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@ def get(self): # pylint: disable=arguments-differ
All logins are done over post, this is a parked endpoint

.. http:get:: /login
:noindex:

:status 401: |401|
:status 406: |406|
Expand Down Expand Up @@ -672,6 +673,7 @@ def post(self): # pylint: disable=arguments-differ
:ref:`Authenticate <rest_tornado-auth>` against Salt's eauth system

.. http:post:: /login
:noindex:

:reqheader X-Auth-Token: |req_token|
:reqheader Accept: |req_accept|
Expand Down Expand Up @@ -803,6 +805,7 @@ def get(self): # pylint: disable=arguments-differ
An endpoint to determine salt-api capabilities

.. http:get:: /
:noindex:

:reqheader Accept: |req_accept|

Expand Down Expand Up @@ -841,6 +844,7 @@ def post(self): # pylint: disable=arguments-differ
Send one or more Salt commands (lowstates) in the request body

.. http:post:: /
:noindex:

:reqheader X-Auth-Token: |req_token|
:reqheader Accept: |req_accept|
Expand Down Expand Up @@ -1219,6 +1223,7 @@ def get(self, mid=None): # pylint: disable=W0221
details

.. http:get:: /minions/(mid)
:noindex:

:reqheader X-Auth-Token: |req_token|
:reqheader Accept: |req_accept|
Expand Down Expand Up @@ -1266,6 +1271,7 @@ def post(self):
Start an execution command and immediately return the job id

.. http:post:: /minions
:noindex:

:reqheader X-Auth-Token: |req_token|
:reqheader Accept: |req_accept|
Expand Down Expand Up @@ -1345,6 +1351,7 @@ def get(self, jid=None): # pylint: disable=W0221
the return from a single job

.. http:get:: /jobs/(jid)
:noindex:

List jobs or show a single job from the job cache.

Expand Down Expand Up @@ -1445,6 +1452,7 @@ def post(self):
<rest_cherrypy-auth>`

.. http:post:: /run
:noindex:

This entry point is primarily for "one-off" commands. Each request
must pass full Salt authentication credentials. Otherwise this URL
Expand Down Expand Up @@ -1519,6 +1527,7 @@ def get(self):
event is formatted as JSON.

.. http:get:: /events
:noindex:

:status 200: |200|
:status 401: |401|
Expand Down Expand Up @@ -1678,6 +1687,7 @@ def post(self, tag_suffix=None): # pylint: disable=W0221
Fire an event in Salt with a custom event tag and data

.. http:post:: /hook
:noindex:

:status 200: |200|
:status 401: |401|
Expand Down
1 change: 1 addition & 0 deletions salt/netapi/rest_wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
==============
.. http:post:: /
:noindex:
**Example request** for a basic ``test.ping``::
Expand Down
Loading