-
Notifications
You must be signed in to change notification settings - Fork 41
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
Use snapd REST api to change snap config #138
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
|
||
import logging | ||
import re | ||
import time | ||
from datetime import datetime, timedelta | ||
from subprocess import CalledProcessError, check_output, run | ||
|
||
|
@@ -65,7 +66,11 @@ def test_snap_refresh(): | |
def test_snap_set_and_get_with_typed(): | ||
cache = snap.SnapCache() | ||
lxd = cache["lxd"] | ||
lxd.ensure(snap.SnapState.Latest, channel="latest") | ||
try: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From Weii: this is to address instability in the GitHub Runners when running these tests. |
||
lxd.ensure(snap.SnapState.Latest, channel="latest") | ||
except snap.SnapError: | ||
time.sleep(60) | ||
lxd.ensure(snap.SnapState.Latest, channel="latest") | ||
configs = { | ||
"true": True, | ||
"false": False, | ||
|
@@ -137,7 +142,11 @@ def test_snap_set_and_get_with_typed(): | |
def test_snap_set_and_get_untyped(): | ||
cache = snap.SnapCache() | ||
lxd = cache["lxd"] | ||
lxd.ensure(snap.SnapState.Latest, channel="latest") | ||
try: | ||
lxd.ensure(snap.SnapState.Latest, channel="latest") | ||
except snap.SnapError: | ||
time.sleep(60) | ||
lxd.ensure(snap.SnapState.Latest, channel="latest") | ||
|
||
lxd.set({"foo": "true", "bar": True}, typed=False) | ||
assert lxd.get("foo", typed=False) == "true" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,10 @@ | |
# pyright: reportPrivateUsage=false | ||
|
||
import datetime | ||
import io | ||
import json | ||
import time | ||
import typing | ||
import unittest | ||
from subprocess import CalledProcessError | ||
from typing import Any, Dict, Iterable, Optional | ||
|
@@ -697,6 +700,141 @@ def test_request_raw_bad_response_raises_snapapierror(self): | |
finally: | ||
shutdown() | ||
|
||
def test_wait_changes(self): | ||
change_finished = False | ||
|
||
def _request_raw( | ||
method: str, | ||
path: str, | ||
query: Dict = None, | ||
headers: Dict = None, | ||
data: bytes = None, | ||
) -> typing.IO[bytes]: | ||
nonlocal change_finished | ||
if method == "PUT" and path == "snaps/test/conf": | ||
return io.BytesIO( | ||
json.dumps( | ||
{ | ||
"type": "async", | ||
"status-code": 202, | ||
"status": "Accepted", | ||
"result": None, | ||
"change": "97", | ||
} | ||
).encode("utf-8") | ||
) | ||
if method == "GET" and path == "changes/97" and not change_finished: | ||
change_finished = True | ||
return io.BytesIO( | ||
json.dumps( | ||
{ | ||
"type": "sync", | ||
"status-code": 200, | ||
"status": "OK", | ||
"result": { | ||
"id": "97", | ||
"kind": "configure-snap", | ||
"summary": 'Change configuration of "test" snap', | ||
"status": "Doing", | ||
"tasks": [ | ||
{ | ||
"id": "1029", | ||
"kind": "run-hook", | ||
"summary": 'Run configure hook of "test" snap', | ||
"status": "Doing", | ||
"progress": {"label": "", "done": 1, "total": 1}, | ||
"spawn-time": "2024-11-28T20:02:47.498399651+00:00", | ||
"data": {"affected-snaps": ["test"]}, | ||
} | ||
], | ||
"ready": False, | ||
"spawn-time": "2024-11-28T20:02:47.49842583+00:00", | ||
}, | ||
} | ||
).encode("utf-8") | ||
) | ||
if method == "GET" and path == "changes/97" and change_finished: | ||
return io.BytesIO( | ||
json.dumps( | ||
{ | ||
"type": "sync", | ||
"status-code": 200, | ||
"status": "OK", | ||
"result": { | ||
"id": "98", | ||
"kind": "configure-snap", | ||
"summary": 'Change configuration of "test" snap', | ||
"status": "Done", | ||
"tasks": [ | ||
{ | ||
"id": "1030", | ||
"kind": "run-hook", | ||
"summary": 'Run configure hook of "test" snap', | ||
"status": "Done", | ||
"progress": {"label": "", "done": 1, "total": 1}, | ||
"spawn-time": "2024-11-28T20:06:41.415929854+00:00", | ||
"ready-time": "2024-11-28T20:06:41.797437537+00:00", | ||
"data": {"affected-snaps": ["test"]}, | ||
} | ||
], | ||
"ready": True, | ||
"spawn-time": "2024-11-28T20:06:41.415955681+00:00", | ||
"ready-time": "2024-11-28T20:06:41.797440022+00:00", | ||
}, | ||
} | ||
).encode("utf-8") | ||
) | ||
raise RuntimeError("unknown request") | ||
|
||
client = snap.SnapClient() | ||
with patch.object(client, "_request_raw", _request_raw), patch.object(time, "sleep"): | ||
client._put_snap_conf("test", {"foo": "bar"}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess ideally we'd call the public |
||
|
||
def test_wait_failed(self): | ||
def _request_raw( | ||
method: str, | ||
path: str, | ||
query: Dict = None, | ||
headers: Dict = None, | ||
data: bytes = None, | ||
) -> typing.IO[bytes]: | ||
if method == "PUT" and path == "snaps/test/conf": | ||
return io.BytesIO( | ||
json.dumps( | ||
{ | ||
"type": "async", | ||
"status-code": 202, | ||
"status": "Accepted", | ||
"result": None, | ||
"change": "97", | ||
} | ||
).encode("utf-8") | ||
) | ||
if method == "GET" and path == "changes/97": | ||
return io.BytesIO( | ||
json.dumps( | ||
{ | ||
"type": "sync", | ||
"status-code": 200, | ||
"status": "OK", | ||
"result": { | ||
"id": "97", | ||
"kind": "configure-snap", | ||
"summary": 'Change configuration of "test" snap', | ||
"status": "Error", | ||
"ready": False, | ||
"spawn-time": "2024-11-28T20:02:47.49842583+00:00", | ||
}, | ||
} | ||
).encode("utf-8") | ||
) | ||
raise RuntimeError("unknown request") | ||
|
||
client = snap.SnapClient() | ||
with patch.object(client, "_request_raw", _request_raw), patch.object(time, "sleep"): | ||
with self.assertRaises(snap.SnapError): | ||
client._put_snap_conf("test", {"foo": "bar"}) | ||
|
||
|
||
class TestSnapBareMethods(unittest.TestCase): | ||
@patch("builtins.open", new_callable=mock_open, read_data="curl\n") | ||
|
@@ -902,28 +1040,24 @@ def fake_snap(command: str, optargs: Optional[Iterable[str]] = None) -> str: | |
with self.assertRaises(TypeError): | ||
foo.get(None) # pyright: ignore[reportArgumentType] | ||
|
||
@patch("charms.operator_libs_linux.v2.snap.subprocess.check_output") | ||
def test_snap_set_typed(self, mock_subprocess): | ||
@patch("charms.operator_libs_linux.v2.snap.SnapClient._put_snap_conf") | ||
def test_snap_set_typed(self, put_snap_conf): | ||
foo = snap.Snap("foo", snap.SnapState.Present, "stable", "1", "classic") | ||
|
||
config = {"n": 42, "s": "string", "d": {"nested": True}} | ||
|
||
foo.set(config, typed=True) | ||
mock_subprocess.assert_called_with( | ||
["snap", "set", "foo", "-t", "n=42", 's="string"', 'd={"nested": true}'], | ||
universal_newlines=True, | ||
) | ||
put_snap_conf.assert_called_with("foo", {"n": 42, "s": "string", "d": {"nested": True}}) | ||
|
||
@patch("charms.operator_libs_linux.v2.snap.subprocess.check_output") | ||
def test_snap_set_untyped(self, mock_subprocess): | ||
@patch("charms.operator_libs_linux.v2.snap.SnapClient._put_snap_conf") | ||
def test_snap_set_untyped(self, put_snap_conf): | ||
foo = snap.Snap("foo", snap.SnapState.Present, "stable", "1", "classic") | ||
|
||
config = {"n": 42, "s": "string", "d": {"nested": True}} | ||
|
||
foo.set(config, typed=False) | ||
mock_subprocess.assert_called_with( | ||
["snap", "set", "foo", "n=42", "s=string", "d={'nested': True}"], | ||
universal_newlines=True, | ||
put_snap_conf.assert_called_with( | ||
"foo", {"n": "42", "s": "string", "d": "{'nested': True}"} | ||
) | ||
|
||
@patch( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically this is a breaking change, but it previously returned whatever progress garbage that
snap set
printed to stdout, so charmers almost certainly weren't using it. So we're okay with changing this type to None. @james-garner-canonical to double check charm usage.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to private, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a look at the 400+ charms I know bout, and none of them use the return value, so this shouldn't break anyone (and it's a nice improvement)