Skip to content

Commit

Permalink
Merge branch 'main' into add-upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
SK1Y101 authored Dec 9, 2024
2 parents 683a843 + d370a51 commit e25c246
Show file tree
Hide file tree
Showing 15 changed files with 447 additions and 215 deletions.
3 changes: 1 addition & 2 deletions maas-agent/lib/charms/grafana_agent/v0/cos_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
Using the `COSAgentProvider` object only requires instantiating it,
typically in the `__init__` method of your charm (the one which sends telemetry).
The constructor of `COSAgentProvider` has only one required and ten optional parameters:
```python
def __init__(
Expand Down Expand Up @@ -253,7 +252,7 @@ class _MetricsEndpointDict(TypedDict):

LIBID = "dc15fa84cef84ce58155fb84f6c6213a"
LIBAPI = 0
LIBPATCH = 11
LIBPATCH = 12

PYDEPS = ["cosl", "pydantic"]

Expand Down
45 changes: 37 additions & 8 deletions maas-agent/lib/charms/operator_libs_linux/v2/snap.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import socket
import subprocess
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
Expand All @@ -83,7 +84,7 @@

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 7
LIBPATCH = 8


# Regex to locate 7-bit C1 ANSI sequences
Expand Down Expand Up @@ -332,19 +333,17 @@ def get(self, key: Optional[str], *, typed: bool = False) -> Any:

return self._snap("get", [key]).strip()

def set(self, config: Dict[str, Any], *, typed: bool = False) -> str:
def set(self, config: Dict[str, Any], *, typed: bool = False) -> None:
"""Set a snap configuration value.
Args:
config: a dictionary containing keys and values specifying the config to set.
typed: set to True to convert all values in the config into typed values while
configuring the snap (set with typed=True). Default is not to convert.
"""
if typed:
kv = [f"{key}={json.dumps(val)}" for key, val in config.items()]
return self._snap("set", ["-t"] + kv)

return self._snap("set", [f"{key}={val}" for key, val in config.items()])
if not typed:
config = {k: str(v) for k, v in config.items()}
self._snap_client._put_snap_conf(self._name, config)

def unset(self, key) -> str:
"""Unset a snap configuration value.
Expand Down Expand Up @@ -770,7 +769,33 @@ def _request(
headers["Content-Type"] = "application/json"

response = self._request_raw(method, path, query, headers, data)
return json.loads(response.read().decode())["result"]
response = json.loads(response.read().decode())
if response["type"] == "async":
return self._wait(response["change"])
return response["result"]

def _wait(self, change_id: str, timeout=300) -> JSONType:
"""Wait for an async change to complete.
The poll time is 100 milliseconds, the same as in snap clients.
"""
deadline = time.time() + timeout
while True:
if time.time() > deadline:
raise TimeoutError(f"timeout waiting for snap change {change_id}")
response = self._request("GET", f"changes/{change_id}")
status = response["status"]
if status == "Done":
return response.get("data")
if status == "Doing":
time.sleep(0.1)
continue
if status == "Wait":
logger.warning("snap change %s succeeded with status 'Wait'", change_id)
return response.get("data")
raise SnapError(
f"snap change {response.get('kind')!r} id {change_id} failed with status {status}"
)

def _request_raw(
self,
Expand Down Expand Up @@ -818,6 +843,10 @@ def get_installed_snap_apps(self, name: str) -> List:
"""Query the snap server for apps belonging to a named, currently installed snap."""
return self._request("GET", "apps", {"names": name, "select": "service"})

def _put_snap_conf(self, name: str, conf: Dict[str, Any]):
"""Set the configuration details for an installed snap."""
return self._request("PUT", f"snaps/{name}/conf", body=conf)


class SnapCache(Mapping):
"""An abstraction to represent installed/available packages.
Expand Down
79 changes: 43 additions & 36 deletions maas-agent/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,47 +1,54 @@
# Testing tools configuration
[tool.coverage.run]
branch = true
# Linting tools configuration

[tool.coverage.report]
show_missing = true
[tool.ruff]
target-version = "py38"
line-length = 99
extend-exclude = [ "*.egg_info", "__pycache__" ]

lint.select = [
"C",
"D",
"E",
"F",
"F401",
"I",
"I001",
"N",
"RUF",
"UP",
"W",
]
lint.ignore = [ "D107", "E501" ]
lint.extend-ignore = [
"D203",
"D204",
"D213",
"D215",
"D400",
"D404",
"D406",
"D407",
"D408",
"D409",
"D413",
]
lint.per-file-ignores = { "tests/*" = [ "D100", "D101", "D102", "D103", "D104" ] }
lint.mccabe.max-complexity = 10

[tool.codespell]
skip = "build,lib,venv,icon.svg,.tox,.git,.mypy_cache,.ruff_cache,.coverage"

[tool.pytest.ini_options]
minversion = "6.0"
log_cli_level = "INFO"

# Formatting tools configuration
[tool.black]
line-length = 99
target-version = ["py38"]

# Linting tools configuration
[tool.ruff]
line-length = 99
extend-exclude = ["__pycache__", "*.egg_info"]

[tool.ruff.lint]
select = ["E", "W", "F", "C", "N", "D", "I001"]
extend-ignore = [
"D203",
"D204",
"D213",
"D215",
"D400",
"D404",
"D406",
"D407",
"D408",
"D409",
"D413",
]
ignore = ["E501", "D107"]
per-file-ignores = { "tests/*" = ["D100", "D101", "D102", "D103", "D104"] }

[tool.ruff.lint.mccabe]
max-complexity = 10
[tool.coverage.run]
branch = true

[tool.codespell]
skip = "build,lib,venv,icon.svg,.tox,.git,.mypy_cache,.ruff_cache,.coverage"
[tool.coverage.report]
show_missing = true

[tool.pyright]
include = ["src/**.py"]
include = [ "src/**.py" ]
1 change: 0 additions & 1 deletion maas-agent/tests/unit/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@


class TestHelperSnapCache(unittest.TestCase):

def _setup_snap(
self,
mock_snap,
Expand Down
110 changes: 57 additions & 53 deletions maas-agent/tox.ini
Original file line number Diff line number Diff line change
@@ -1,105 +1,109 @@
# Copyright 2024 Canonical
# See LICENSE file for licensing details.

[tox]
no_package = True
skip_missing_interpreters = True
env_list = format, lint, static-{charm,lib}, unit, scenario
min_version = 4.0.0
requires =
tox>=4.2
env_list =
format
lint
static-{charm, lib}
unit
scenario
no_package = true
skip_missing_interpreters = true
basepython = py38

[vars]
src_path = {tox_root}/src
tests_path = {tox_root}/tests
lib_path = {tox_root}/lib/charms/maas_region/v0/
all_path = {[vars]src_path} {[vars]tests_path} {[vars]lib_path}

[testenv]
set_env =
PYTHONPATH = {tox_root}/lib:{[vars]src_path}
PYTHONBREAKPOINT=pdb.set_trace
PY_COLORS=1
pass_env =
PYTHONPATH
CHARM_BUILD_DIR
MODEL_SETTINGS
PYTHONPATH
set_env =
PYTHONBREAKPOINT = pdb.set_trace
PYTHONPATH = {tox_root}/lib:{[vars]src_path}
PY_COLORS = 1

[testenv:format]
description = Apply coding style standards to code
deps =
black
pyproject-fmt
ruff
tox-ini-fmt
commands =
black {[vars]all_path}
ruff format {[vars]all_path}
ruff check --fix {[vars]all_path}
- pyproject-fmt pyproject.toml
- tox-ini-fmt tox.ini

[testenv:lint]
description = Check code against coding style standards
deps =
black
ruff
codespell
pyproject-fmt
ruff
commands =
# if this charm owns a lib, uncomment "lib_path" variable
# and uncomment the following line
# codespell {[vars]lib_path}
codespell {tox_root}
ruff check {[vars]all_path}
black --check --diff {[vars]all_path}
pyproject-fmt --check pyproject.toml

[testenv:unit]
description = Run unit tests
deps =
pytest
coverage[toml]
-r {tox_root}/requirements.txt
coverage[toml]
pytest
commands =
coverage run --source={[vars]src_path} \
-m pytest \
--tb native \
-v \
-s \
{posargs} \
{[vars]tests_path}/unit
-m pytest \
--tb native \
-v \
-s \
{posargs} \
{[vars]tests_path}/unit
coverage report

[testenv:scenario]
description = Run scenario tests
deps =
-r {tox_root}/requirements.txt
cosl
ops-scenario
pytest
commands =
pytest -v -s --tb native {posargs} --log-cli-level=INFO {[vars]tests_path}/scenario

[testenv:static-{charm,lib}]
description = Run static analysis checks
deps =
pyright
typing-extensions
charm: -r{toxinidir}/requirements.txt
lib: ops
integration: {[testenv:integration]deps}
lib: jinja2
lib: ops
unit: {[testenv:unit]deps}
integration: {[testenv:integration]deps}
commands =
charm: pyright --pythonversion 3.8 {[vars]src_path} {posargs}
lib: pyright --pythonversion 3.8 {[vars]lib_path} {posargs}
lib: /usr/bin/env sh -c 'for m in $(git diff main --name-only --line-prefix=`git rev-parse --show-toplevel`/ {[vars]lib_path}); do if ! git diff main $m | grep -q "+LIBPATCH\|+LIBAPI"; then echo "You forgot to bump the version on $m!"; exit 1; fi; done'
allowlist_externals = /usr/bin/env
allowlist_externals =
/usr/bin/env

[testenv:integration]
description = Run integration tests
deps =
pytest
-r {tox_root}/requirements.txt
juju
pytest
pytest-operator
-r {tox_root}/requirements.txt
commands =
pytest -v \
-s \
--tb native \
--log-cli-level=INFO \
{posargs} \
{[vars]tests_path}/integration
-s \
--tb native \
--log-cli-level=INFO \
{posargs} \
{[vars]tests_path}/integration

[testenv:scenario]
description = Run scenario tests
deps =
pytest
cosl
ops-scenario
-r {tox_root}/requirements.txt
commands =
pytest -v -s --tb native {posargs} --log-cli-level=INFO {[vars]tests_path}/scenario
[vars]
src_path = {tox_root}/src
tests_path = {tox_root}/tests
lib_path = {tox_root}/lib/charms/maas_region/v0/
all_path = {[vars]src_path} {[vars]tests_path} {[vars]lib_path}
Binary file added maas-region/.charm_tracing_buffer.raw
Binary file not shown.
4 changes: 4 additions & 0 deletions maas-region/charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,7 @@ config:
default: ""
description: CA Certificates chain in PEM format
type: string
enable_prometheus_metrics:
default: true
description: Whether to enable Prometheus metrics for MAAS
type: boolean
3 changes: 1 addition & 2 deletions maas-region/lib/charms/grafana_agent/v0/cos_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
Using the `COSAgentProvider` object only requires instantiating it,
typically in the `__init__` method of your charm (the one which sends telemetry).
The constructor of `COSAgentProvider` has only one required and ten optional parameters:
```python
def __init__(
Expand Down Expand Up @@ -253,7 +252,7 @@ class _MetricsEndpointDict(TypedDict):

LIBID = "dc15fa84cef84ce58155fb84f6c6213a"
LIBAPI = 0
LIBPATCH = 11
LIBPATCH = 12

PYDEPS = ["cosl", "pydantic"]

Expand Down
Loading

0 comments on commit e25c246

Please sign in to comment.