Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: localnet displays a warning when image is out of date #308

Merged
merged 12 commits into from
Aug 21, 2023
48 changes: 24 additions & 24 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/algokit/cli/localnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ def localnet_group() -> None:
def start_localnet() -> None:
sandbox = ComposeSandbox()
compose_file_status = sandbox.compose_file_status()
sandbox.check_docker_compose_for_new_image_versions()

if compose_file_status is ComposeFileStatus.MISSING:
logger.debug("Sandbox compose file does not exist yet; writing it out for the first time")
sandbox.write_compose_file()
Expand Down Expand Up @@ -101,6 +103,9 @@ def reset_localnet(*, update: bool) -> None:
sandbox.write_compose_file()
if update:
sandbox.pull()
else:
sandbox.check_docker_compose_for_new_image_versions()

sandbox.up()


Expand Down
52 changes: 50 additions & 2 deletions src/algokit/core/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,52 @@ def ps(self, service_name: str | None = None) -> list[dict[str, Any]]:
assert isinstance(data, list)
return cast(list[dict[str, Any]], data)

def _get_local_image_version(self, image_name: str) -> str:
"""
Get the local version of a Docker image
"""
arg = "{{.RepoDigests}}"
local_version = run(
["docker", "image", "inspect", image_name, "--format", arg],
cwd=self.directory,
bad_return_code_error_message="Failed to get image inspect",
)
# Remove brackets and split on '@' symbol and get the SHA hash
return local_version.output[1:-1].split("@")[1]
inaie-makerx marked this conversation as resolved.
Show resolved Hide resolved

def _get_latest_image_version(self, image_name: str) -> str | None:
"""
Get the latest version of a Docker image from Docker Hub
"""
args = image_name.split(":")
name = args[0]
tag = args[1] if len(args) > 1 else "latest"
url = f"https://registry.hub.docker.com/v2/repositories/{name}/tags/{tag}"
try:
data = httpx.get(url=url)
return str(data.json()["digest"])
except Exception as err:
logger.debug(f"Error checking indexer status: {err}", exc_info=True)
return None

def is_image_up_to_date(self, image_name: str) -> bool:
local_version = self._get_local_image_version(image_name)
latest_version = self._get_latest_image_version(image_name)
return local_version is None or local_version == latest_version

def check_docker_compose_for_new_image_versions(self) -> None:
is_indexer_up_to_date = self.is_image_up_to_date(INDEXER_IMAGE)
if is_indexer_up_to_date is False:
logger.warning(
"indexer has a new version available, run algokit localnet reset --update to get the latest version"
inaie-makerx marked this conversation as resolved.
Show resolved Hide resolved
)

is_algorand_up_to_date = self.is_image_up_to_date(ALGORAND_IMAGE)
if is_algorand_up_to_date is False:
logger.warning(
"algod has a new version available, run algokit localnet reset --update to get the latest version"
inaie-makerx marked this conversation as resolved.
Show resolved Hide resolved
)


DEFAULT_ALGOD_SERVER = "http://localhost"
DEFAULT_ALGOD_TOKEN = "a" * 64
Expand All @@ -128,6 +174,8 @@ def ps(self, service_name: str | None = None) -> list[dict[str, Any]]:
DEFAULT_WAIT_FOR_ALGOD = 60
DEFAULT_HEALTH_TIMEOUT = 1
ALGOD_HEALTH_URL = f"{DEFAULT_ALGOD_SERVER}:{DEFAULT_ALGOD_PORT}/v2/status"
INDEXER_IMAGE = "makerxau/algorand-indexer-dev:latest"
ALGORAND_IMAGE = "algorand/algod:latest"


def _wait_for_algod() -> bool:
Expand Down Expand Up @@ -171,7 +219,7 @@ def get_docker_compose_yml(
services:
algod:
container_name: {name}_algod
image: algorand/algod:latest
image: {ALGORAND_IMAGE}
ports:
- {algod_port}:8080
- {kmd_port}:7833
Expand All @@ -188,7 +236,7 @@ def get_docker_compose_yml(

indexer:
container_name: {name}_indexer
image: makerxau/algorand-indexer-dev:latest
image: {INDEXER_IMAGE}
ports:
- {indexer_port}:8980
restart: unless-stopped
Expand Down
58 changes: 57 additions & 1 deletion tests/localnet/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import pytest
from algokit.core.sandbox import ALGOD_HEALTH_URL
from algokit.core.sandbox import ALGOD_HEALTH_URL, ALGORAND_IMAGE, INDEXER_IMAGE
from pytest_httpx import HTTPXMock
from pytest_mock import MockerFixture

from tests.utils.proc_mock import ProcMock


@pytest.fixture(autouse=True)
def algod_health_fast_timings(mocker: MockerFixture) -> None: # noqa: ignore[PT004]
Expand All @@ -13,3 +15,57 @@ def algod_health_fast_timings(mocker: MockerFixture) -> None: # noqa: ignore[PT
@pytest.fixture()
def health_success(httpx_mock: HTTPXMock) -> None: # noqa: ignore[PT004]
httpx_mock.add_response(url=ALGOD_HEALTH_URL)


@pytest.fixture()
def _localnet_up_to_date(proc_mock: ProcMock, httpx_mock: HTTPXMock) -> None:
aorumbayev marked this conversation as resolved.
Show resolved Hide resolved
proc_mock.set_output(
["docker", "image", "inspect", ALGORAND_IMAGE, "--format", "{{.RepoDigests}}"],
["[algorand/algod@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]"],
)

proc_mock.set_output(
["docker", "image", "inspect", INDEXER_IMAGE, "--format", "{{.RepoDigests}}"],
["[makerxau/algorand-indexer-dev@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb]"],
)

httpx_mock.add_response(
url="https://registry.hub.docker.com/v2/repositories/makerxau/algorand-indexer-dev/tags/latest",
json={
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
},
)

httpx_mock.add_response(
url="https://registry.hub.docker.com/v2/repositories/algorand/algod/tags/latest",
json={
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
)


@pytest.fixture()
def _localnet_out_of_date(proc_mock: ProcMock, httpx_mock: HTTPXMock) -> None:
inaie-makerx marked this conversation as resolved.
Show resolved Hide resolved
proc_mock.set_output(
["docker", "image", "inspect", ALGORAND_IMAGE, "--format", "{{.RepoDigests}}"],
["[algorand/algod@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb]"],
)

proc_mock.set_output(
["docker", "image", "inspect", INDEXER_IMAGE, "--format", "{{.RepoDigests}}"],
["[makerxau/algorand-indexer-dev@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]"],
)

httpx_mock.add_response(
url="https://registry.hub.docker.com/v2/repositories/makerxau/algorand-indexer-dev/tags/latest",
json={
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
},
)

httpx_mock.add_response(
url="https://registry.hub.docker.com/v2/repositories/algorand/algod/tags/latest",
json={
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
)
4 changes: 2 additions & 2 deletions tests/localnet/test_localnet_reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_localnet_reset_without_existing_sandbox(app_dir_mock: AppDirs) -> None:
)


@pytest.mark.usefixtures("proc_mock", "health_success")
@pytest.mark.usefixtures("proc_mock", "health_success", "_localnet_up_to_date")
def test_localnet_reset_with_existing_sandbox_with_out_of_date_config(app_dir_mock: AppDirs) -> None:
(app_dir_mock.app_config_dir / "sandbox").mkdir()
(app_dir_mock.app_config_dir / "sandbox" / "docker-compose.yml").write_text("out of date config")
Expand All @@ -44,7 +44,7 @@ def test_localnet_reset_with_existing_sandbox_with_out_of_date_config(app_dir_mo
)


@pytest.mark.usefixtures("proc_mock", "health_success")
@pytest.mark.usefixtures("proc_mock", "health_success", "_localnet_up_to_date")
def test_localnet_reset_with_existing_sandbox_with_up_to_date_config(app_dir_mock: AppDirs) -> None:
(app_dir_mock.app_config_dir / "sandbox").mkdir()
(app_dir_mock.app_config_dir / "sandbox" / "docker-compose.yml").write_text(get_docker_compose_yml())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ DEBUG: Running 'docker compose down' in '{app_config}/sandbox'
DEBUG: docker: STDOUT
DEBUG: docker: STDERR
Sandbox definition is out of date; updating it to latest
DEBUG: Running 'docker image inspect makerxau/algorand-indexer-dev:latest --format {{.RepoDigests}}' in '{app_config}/sandbox'
DEBUG: docker: [makerxau/algorand-indexer-dev@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb]
DEBUG: HTTP Request: GET https://registry.hub.docker.com/v2/repositories/makerxau/algorand-indexer-dev/tags/latest "HTTP/1.1 200 OK"
DEBUG: Running 'docker image inspect algorand/algod:latest --format {{.RepoDigests}}' in '{app_config}/sandbox'
DEBUG: docker: [algorand/algod@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
DEBUG: HTTP Request: GET https://registry.hub.docker.com/v2/repositories/algorand/algod/tags/latest "HTTP/1.1 200 OK"
Starting AlgoKit LocalNet now...
DEBUG: Running 'docker compose up --detach --quiet-pull --wait' in '{app_config}/sandbox'
docker: STDOUT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ Deleting any existing LocalNet...
DEBUG: Running 'docker compose down' in '{app_config}/sandbox'
DEBUG: docker: STDOUT
DEBUG: docker: STDERR
DEBUG: Running 'docker image inspect makerxau/algorand-indexer-dev:latest --format {{.RepoDigests}}' in '{app_config}/sandbox'
DEBUG: docker: [makerxau/algorand-indexer-dev@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb]
DEBUG: HTTP Request: GET https://registry.hub.docker.com/v2/repositories/makerxau/algorand-indexer-dev/tags/latest "HTTP/1.1 200 OK"
DEBUG: Running 'docker image inspect algorand/algod:latest --format {{.RepoDigests}}' in '{app_config}/sandbox'
DEBUG: docker: [algorand/algod@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
DEBUG: HTTP Request: GET https://registry.hub.docker.com/v2/repositories/algorand/algod/tags/latest "HTTP/1.1 200 OK"
Starting AlgoKit LocalNet now...
DEBUG: Running 'docker compose up --detach --quiet-pull --wait' in '{app_config}/sandbox'
docker: STDOUT
Expand Down
Loading