Skip to content

Commit

Permalink
Find job_template by name
Browse files Browse the repository at this point in the history
Previously we were using controller's named_url endpoints to access
job_templates. This url can lead to 404 error for job templates having
selective characters even with correct escalating encoding.

Switch back to old method by listing all job template and find the
right job template by matching the name and organization. Add a filter
query parameter to limit the size of returned list.

Resolves AAP-11309: run_job_template fails at some special template names

Temporarily remove isort from the pre-commit-config because the setting
up itself keeps on failing and thus blocks the git commit
  • Loading branch information
bzwei committed Apr 18, 2023
1 parent 9b00d47 commit 7840644
Show file tree
Hide file tree
Showing 6 changed files with 5,432 additions and 111 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ repos:
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.10.1
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
Expand Down
5 changes: 5 additions & 0 deletions ansible_rulebook/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,8 @@ class ControllerNeededException(Exception):
class InvalidFilterNameException(Exception):

pass


class JobTemplateNotFoundException(Exception):

pass
58 changes: 36 additions & 22 deletions ansible_rulebook/job_template_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,19 @@
import ssl
from functools import cached_property
from typing import Any, Callable, Union
from urllib.parse import parse_qsl, quote, urljoin, urlparse
from urllib.parse import parse_qsl, urljoin, urlparse

import aiohttp
import dpath

from ansible_rulebook.exception import ControllerApiException
from ansible_rulebook.exception import (
ControllerApiException,
JobTemplateNotFoundException,
)

logger = logging.getLogger(__name__)


# https://docs.ansible.com/ansible-tower/latest/html/towerapi/access_resources.html#access-resources
URL_PATH_RESERVED_CHARSET = {}
for c in ";/?:@=&[]":
URL_PATH_RESERVED_CHARSET[c] = quote(c, safe="")
URL_PATH_RESERVED_CHARSET["+"] = "[+]"


def _encode_uri(text: str) -> str:
for c in URL_PATH_RESERVED_CHARSET:
if c in text:
text = text.replace(c, URL_PATH_RESERVED_CHARSET[c])
return text


class JobTemplateRunner:
JOB_TEMPLATE_SLUG = "/api/v2/job_templates"
VALID_POST_CODES = [200, 201, 202]
Expand Down Expand Up @@ -91,6 +80,35 @@ def _sslcontext(self) -> Union[bool, ssl.SSLContext]:
return ssl.create_default_context(cafile=self.verify_ssl)
return False

async def _get_job_template_id(self, name: str, organization: str) -> int:
slug = f"{self.JOB_TEMPLATE_SLUG}/"
params = {"name": name}

async with aiohttp.ClientSession(
headers=self._auth_headers()
) as session:
while True:
response = await self._get_page(session, slug, params)
json_body = json.loads(response["body"])
for jt in json_body["results"]:
if (
jt["name"] == name
and dpath.get(
jt, "summary_fields.organization.name", "."
)
== organization
):
return jt["id"]

if json_body.get("next", None):
params["page"] = params.get("page", 1) + 1
else:
break

raise JobTemplateNotFoundException(
f"{name} in organization {organization}"
)

async def run_job_template(
self,
name: str,
Expand Down Expand Up @@ -138,12 +156,8 @@ async def run_job_template(
async def launch(
self, name: str, organization: str, job_params: dict
) -> dict:
name_uri = _encode_uri(name)
org_uri = _encode_uri(organization)
resource_uri = f"{name_uri}++{org_uri}"
url = urljoin(
self.host, f"{self.JOB_TEMPLATE_SLUG}/{resource_uri}/launch/"
)
jt_id = await self._get_job_template_id(name, organization)
url = urljoin(self.host, f"{self.JOB_TEMPLATE_SLUG}/{jt_id}/launch/")

async with aiohttp.ClientSession(
headers=self._auth_headers()
Expand Down
Loading

0 comments on commit 7840644

Please sign in to comment.