Skip to content

Commit

Permalink
adding support for foxops 2.3 endpoints (#36)
Browse files Browse the repository at this point in the history
adding support for new patch endpoint
  • Loading branch information
defreng authored Nov 23, 2023
1 parent 79c6d4e commit 7535661
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 25 deletions.
45 changes: 36 additions & 9 deletions src/foxops_client/client_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,47 @@ async def delete_incarnation(self, incarnation_id: int):
self._handle_unexpected_response(resp)
raise ValueError("unexpected response")

async def update_incarnation(
async def patch_incarnation(
self,
incarnation_id: int,
automerge: bool,
template_repository_version: str | None = None,
template_data: dict[str, str] | None = None,
requested_version: str | None = None,
requested_data: dict[str, Any] | None = None,
):
data: dict[str, Any] = {
"automerge": automerge,
}
if requested_version is not None:
data["requested_version"] = requested_version
if requested_data is not None:
data["requested_data"] = requested_data

resp = await self.retry_function(self.client.patch)(f"/api/incarnations/{incarnation_id}", json=data)

match resp.status_code:
case httpx.codes.OK:
return IncarnationWithDetails.from_dict(resp.json())
case httpx.codes.NOT_FOUND:
raise IncarnationDoesNotExistError(resp.json()["message"])
case httpx.codes.BAD_REQUEST | httpx.codes.CONFLICT:
self.log.error(f"received error from FoxOps API: {resp.status_code} {resp.headers} {resp.text}")
raise FoxopsApiError(resp.json()["message"])

self._handle_unexpected_response(resp)
raise ValueError("unexpected response")

async def put_incarnation(
self,
incarnation_id: int,
automerge: bool,
template_repository_version: str,
template_data: dict[str, Any],
) -> IncarnationWithDetails:
data: dict[str, Any] = {
"automerge": automerge,
"template_repository_version": template_repository_version,
"template_data": template_data,
}
if template_repository_version is not None:
data["template_repository_version"] = template_repository_version
if template_data is not None:
data["template_data"] = template_data

resp = await self.retry_function(self.client.put)(f"/api/incarnations/{incarnation_id}", json=data)

Expand All @@ -132,7 +159,7 @@ async def create_incarnation(
incarnation_repository: str,
template_repository: str,
template_repository_version: str,
template_data: dict[str, str],
template_data: dict[str, Any],
target_directory: str | None = None,
automerge: bool | None = None,
) -> IncarnationWithDetails:
Expand All @@ -152,7 +179,7 @@ async def create_incarnation(
match resp.status_code:
case httpx.codes.CREATED:
return IncarnationWithDetails.from_dict(resp.json())
case httpx.codes.BAD_REQUEST:
case httpx.codes.BAD_REQUEST | httpx.codes.CONFLICT:
self.log.error(f"received error from FoxOps API: {resp.status_code} {resp.headers} {resp.text}")
raise FoxopsApiError(resp.json()["message"])

Expand Down
27 changes: 22 additions & 5 deletions src/foxops_client/client_sync.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
from typing import Any

from foxops_client.client_async import AsyncFoxopsClient
from foxops_client.types import Incarnation, IncarnationWithDetails
Expand Down Expand Up @@ -35,15 +36,31 @@ def get_incarnation(self, incarnation_id: int) -> IncarnationWithDetails:
def delete_incarnation(self, incarnation_id: int):
return self.loop.run_until_complete(self.client.delete_incarnation(incarnation_id))

def update_incarnation(
def patch_incarnation(
self,
incarnation_id: int,
automerge: bool,
template_repository_version: str | None = None,
template_data: dict[str, str] | None = None,
requested_version: str | None = None,
requested_data: dict[str, Any] | None = None,
):
return self.loop.run_until_complete(
self.client.patch_incarnation(
incarnation_id,
automerge,
requested_version=requested_version,
requested_data=requested_data,
)
)

def put_incarnation(
self,
incarnation_id: int,
automerge: bool,
template_repository_version: str,
template_data: dict[str, Any],
) -> IncarnationWithDetails:
return self.loop.run_until_complete(
self.client.update_incarnation(
self.client.put_incarnation(
incarnation_id,
automerge,
template_repository_version=template_repository_version,
Expand All @@ -56,7 +73,7 @@ def create_incarnation(
incarnation_repository: str,
template_repository: str,
template_repository_version: str,
template_data: dict[str, str],
template_data: dict[str, Any],
target_directory: str | None = None,
automerge: bool | None = None,
) -> IncarnationWithDetails:
Expand Down
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from infrastructure.foxops import (
FOXOPS_STATIC_TOKEN,
foxops_container,
foxops_database_initialization,
foxops_host_port,
foxops_host_url,
foxops_image,
Expand Down Expand Up @@ -35,6 +36,7 @@
foxops_host_url,
foxops_image,
foxops_secrets_volume,
foxops_database_initialization,
gitlab_access_token,
gitlab_access_token_binary,
gitlab_api_client,
Expand Down
13 changes: 10 additions & 3 deletions tests/infrastructure/foxops.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
FOXOPS_STATIC_TOKEN = "dummy"

# Images
foxops_image = fetch(repository="ghcr.io/roche/foxops:e61d4c1e6d7e77bca5df14dfe803030fcb52a0be")
foxops_image = fetch(repository="ghcr.io/roche/foxops:v2.3.1")

# Volumes
foxops_secrets_volume = volume(
name=f"{NAME_PREFIX}_foxops_secrets",
initial_content={
"foxops_gitlab_token": gitlab_access_token_binary,
"foxops_hoster_gitlab_token": gitlab_access_token_binary,
},
scope="session",
)
Expand All @@ -36,7 +36,8 @@ def ready(self):
image="{foxops_image.id}",
name=NAME_PREFIX + "_foxops",
environment={
"FOXOPS_GITLAB_ADDRESS": gitlab_docker_network_url,
"FOXOPS_HOSTER_TYPE": "gitlab",
"FOXOPS_HOSTER_GITLAB_ADDRESS": gitlab_docker_network_url,
"FOXOPS_STATIC_TOKEN": FOXOPS_STATIC_TOKEN,
},
volumes={
Expand All @@ -51,6 +52,12 @@ def ready(self):
)


@fixture(scope="session", autouse=True)
def foxops_database_initialization(foxops_container: FoxOpsContainer):
foxops_container.exec_run("rm /home/foxops/test.db")
foxops_container.exec_run("alembic upgrade head")


@fixture(scope="session")
def foxops_host_port(foxops_container: FoxOpsContainer):
return foxops_container.ports[FOXOPS_LISTEN_PORT][0]
Expand Down
2 changes: 1 addition & 1 deletion tests/infrastructure/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def ready(self):

# we want gitlab to return a 200 several times in a row.
# especially in the github runners it can act flaky otherwise.
for _ in range(3):
for _ in range(15):
response = httpx.get(self.host_url, follow_redirects=True)
if response.status_code != 200:
return False
Expand Down
22 changes: 15 additions & 7 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_create_incarnation_with_conflicting_existing_incarnation(incarnation, f
)

# THEN
assert e.value.message.find("already initialized") != -1
assert e.value.message.find("is already a foxops incarnation") != -1


def test_delete_incarnation(incarnation, foxops_client: FoxopsClient):
Expand Down Expand Up @@ -100,18 +100,26 @@ def test_list_incarnation_with_non_existing_incarnation(foxops_client):
foxops_client.list_incarnations(incarnation_repository="nonexisting", target_directory=".")


def test_update_incarnation_with_template_data_change_and_no_automerge(incarnation, foxops_client):
def test_put_incarnation_returns_error_when_variables_are_missing(incarnation, foxops_client):
# WHEN
response = foxops_client.update_incarnation(
with pytest.raises(FoxopsApiError):
foxops_client.put_incarnation(
incarnation_id=incarnation.id,
automerge=False,
template_repository_version="main",
template_data={},
)


def test_patch_incarnation_with_template_data_change_and_no_automerge(incarnation, foxops_client):
# WHEN
response = foxops_client.patch_incarnation(
incarnation_id=incarnation.id,
automerge=False,
template_data={"input_variable": "bar"},
requested_data={"input_variable": "bar"},
)

# THEN
# because we didn't merge, the template data should be the same as in the original incarnation details
assert response.template_data == incarnation.template_data

assert response.commit_sha is not None
assert response.commit_url is not None

Expand Down

0 comments on commit 7535661

Please sign in to comment.