Skip to content

Commit de3aca9

Browse files
committed
feat(pkg): add trusted/untrusted states
This states allow to trust and untrust packages and sources when using homebrew.
1 parent 3ce7b9c commit de3aca9

3 files changed

Lines changed: 287 additions & 3 deletions

File tree

salt/states/pkg.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3936,6 +3936,120 @@ def held(name, version=None, pkgs=None, replace=False, **kwargs):
39363936
return ret
39373937

39383938

3939+
def trusted(name, **kwargs):
3940+
"""
3941+
Ensure a package source or component is marked as trusted by the package
3942+
manager. Only available for package managers that implement a trust model
3943+
(e.g. Homebrew on macOS).
3944+
3945+
.. versionadded:: 3008.2
3946+
3947+
name
3948+
The identifier of the package source or component to trust. The exact
3949+
format depends on the package manager (e.g. a tap name, a formula, a
3950+
URL).
3951+
3952+
kwargs
3953+
Additional keyword arguments are passed through to the underlying
3954+
``pkg.trust`` and ``pkg.is_trusted`` module functions, allowing
3955+
package-manager-specific options to be supplied. For example, on macOS
3956+
with Homebrew, ``type`` can be set to ``tap``, ``formula``, ``cask``
3957+
or ``command`` to disambiguate the target.
3958+
3959+
Examples:
3960+
3961+
.. code-block:: yaml
3962+
3963+
# Homebrew: trust a third-party tap
3964+
cdalvaro/tap:
3965+
pkg.trusted:
3966+
- type: tap
3967+
3968+
# Homebrew: trust a specific formula from a third-party tap
3969+
cdalvaro/tap/salt:
3970+
pkg.trusted:
3971+
- type: formula
3972+
"""
3973+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
3974+
3975+
if "pkg.trust" not in __salt__:
3976+
ret["result"] = False
3977+
ret["comment"] = "`trust` is not available for this package manager."
3978+
return ret
3979+
3980+
if __salt__["pkg.is_trusted"](name, **kwargs):
3981+
ret["comment"] = f"{name} is already trusted."
3982+
return ret
3983+
3984+
if __opts__["test"]:
3985+
ret["result"] = None
3986+
ret["comment"] = f"{name} would be trusted."
3987+
return ret
3988+
3989+
if not __salt__["pkg.trust"](name, **kwargs):
3990+
ret["result"] = False
3991+
ret["comment"] = f"Failed to trust {name}."
3992+
return ret
3993+
3994+
ret["changes"] = {name: {"old": "untrusted", "new": "trusted"}}
3995+
ret["comment"] = f"{name} is now trusted."
3996+
return ret
3997+
3998+
3999+
def untrusted(name, **kwargs):
4000+
"""
4001+
Ensure a package source or component is not marked as trusted by the
4002+
package manager. Only available for package managers that implement a
4003+
trust model (e.g. Homebrew on macOS).
4004+
4005+
.. versionadded:: 3008.2
4006+
4007+
name
4008+
The identifier of the package source or component to untrust. The
4009+
exact format depends on the package manager.
4010+
4011+
kwargs
4012+
Additional keyword arguments are passed through to the underlying
4013+
``pkg.untrust`` and ``pkg.is_trusted`` module functions, allowing
4014+
package-manager-specific options to be supplied. For example, on macOS
4015+
with Homebrew, ``type`` can be set to ``tap``, ``formula``, ``cask``
4016+
or ``command`` to disambiguate the target.
4017+
4018+
Example:
4019+
4020+
.. code-block:: yaml
4021+
4022+
# Homebrew: remove trust from a third-party tap
4023+
cdalvaro/tap:
4024+
pkg.untrusted:
4025+
- type: tap
4026+
"""
4027+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
4028+
4029+
if "pkg.untrust" not in __salt__:
4030+
ret["result"] = False
4031+
ret["comment"] = "`untrust` is not available for this package manager."
4032+
return ret
4033+
4034+
if not __salt__["pkg.is_trusted"](name, **kwargs):
4035+
ret["comment"] = f"{name} is already not trusted."
4036+
return ret
4037+
4038+
if __opts__["test"]:
4039+
ret["result"] = None
4040+
ret["comment"] = f"{name} would be untrusted."
4041+
return ret
4042+
4043+
if not __salt__["pkg.untrust"](name, **kwargs):
4044+
ret["result"] = False
4045+
ret["comment"] = f"Failed to untrust {name}."
4046+
return ret
4047+
4048+
ret["changes"] = {name: {"old": "trusted", "new": "untrusted"}}
4049+
ret["comment"] = f"{name} is no longer trusted."
4050+
return ret
4051+
4052+
39394053
def unheld(name, version=None, pkgs=None, all=False, **kwargs):
39404054
"""
39414055
.. versionadded:: 3005

tests/pytests/unit/modules/test_mac_brew_pkg.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,7 +1793,5 @@ def test_is_trusted_with_type_not_found():
17931793
"""
17941794
Tests is_trusted with a type filter returns False when not in list.
17951795
"""
1796-
with patch(
1797-
"salt.modules.mac_brew_pkg.list_trusted", return_value=["other/tap"]
1798-
):
1796+
with patch("salt.modules.mac_brew_pkg.list_trusted", return_value=["other/tap"]):
17991797
assert mac_brew.is_trusted("thirdparty/foo", type="tap") is False

tests/pytests/unit/states/test_pkg.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,3 +1345,175 @@ def test_latest_no_change_windows():
13451345
with patch.dict(pkg.__salt__, salt_dict):
13461346
ret = pkg.latest(pkg_name)
13471347
assert ret.get("result", False) is True
1348+
1349+
1350+
# 'trusted' state tests
1351+
1352+
1353+
def test_trusted_not_available():
1354+
"""
1355+
Test pkg.trusted when pkg.trust is not available for the package manager.
1356+
"""
1357+
with patch.dict(pkg.__salt__, {}):
1358+
ret = pkg.trusted("cdalvaro/tap")
1359+
assert ret["result"] is False
1360+
assert "not available" in ret["comment"]
1361+
assert ret["changes"] == {}
1362+
1363+
1364+
def test_trusted_already_trusted():
1365+
"""
1366+
Test pkg.trusted when the item is already trusted.
1367+
"""
1368+
with patch.dict(
1369+
pkg.__salt__,
1370+
{
1371+
"pkg.trust": MagicMock(return_value=True),
1372+
"pkg.is_trusted": MagicMock(return_value=True),
1373+
},
1374+
):
1375+
ret = pkg.trusted("cdalvaro/tap", type="tap")
1376+
assert ret["result"] is True
1377+
assert "already trusted" in ret["comment"]
1378+
assert ret["changes"] == {}
1379+
1380+
1381+
def test_trusted_test_mode():
1382+
"""
1383+
Test pkg.trusted in test mode when the item would be trusted.
1384+
"""
1385+
with patch.dict(
1386+
pkg.__salt__,
1387+
{
1388+
"pkg.trust": MagicMock(return_value=True),
1389+
"pkg.is_trusted": MagicMock(return_value=False),
1390+
},
1391+
), patch.dict(pkg.__opts__, {"test": True}):
1392+
ret = pkg.trusted("cdalvaro/tap")
1393+
assert ret["result"] is None
1394+
assert "would be trusted" in ret["comment"]
1395+
assert ret["changes"] == {}
1396+
1397+
1398+
def test_trusted_success():
1399+
"""
1400+
Test pkg.trusted successfully trusts an item.
1401+
"""
1402+
trust_mock = MagicMock(return_value=True)
1403+
with patch.dict(
1404+
pkg.__salt__,
1405+
{
1406+
"pkg.trust": trust_mock,
1407+
"pkg.is_trusted": MagicMock(return_value=False),
1408+
},
1409+
):
1410+
ret = pkg.trusted("cdalvaro/tap", type="tap")
1411+
assert ret["result"] is True
1412+
assert ret["changes"] == {
1413+
"cdalvaro/tap": {"old": "untrusted", "new": "trusted"}
1414+
}
1415+
assert "now trusted" in ret["comment"]
1416+
trust_mock.assert_called_once_with("cdalvaro/tap", type="tap")
1417+
1418+
1419+
def test_trusted_failure():
1420+
"""
1421+
Test pkg.trusted when the brew trust command fails.
1422+
"""
1423+
with patch.dict(
1424+
pkg.__salt__,
1425+
{
1426+
"pkg.trust": MagicMock(return_value=False),
1427+
"pkg.is_trusted": MagicMock(return_value=False),
1428+
},
1429+
):
1430+
ret = pkg.trusted("cdalvaro/tap")
1431+
assert ret["result"] is False
1432+
assert "Failed to trust" in ret["comment"]
1433+
assert ret["changes"] == {}
1434+
1435+
1436+
# 'untrusted' state tests
1437+
1438+
1439+
def test_untrusted_not_available():
1440+
"""
1441+
Test pkg.untrusted when pkg.untrust is not available for the package manager.
1442+
"""
1443+
with patch.dict(pkg.__salt__, {}):
1444+
ret = pkg.untrusted("cdalvaro/tap")
1445+
assert ret["result"] is False
1446+
assert "not available" in ret["comment"]
1447+
assert ret["changes"] == {}
1448+
1449+
1450+
def test_untrusted_already_not_trusted():
1451+
"""
1452+
Test pkg.untrusted when the item is already not trusted.
1453+
"""
1454+
with patch.dict(
1455+
pkg.__salt__,
1456+
{
1457+
"pkg.untrust": MagicMock(return_value=True),
1458+
"pkg.is_trusted": MagicMock(return_value=False),
1459+
},
1460+
):
1461+
ret = pkg.untrusted("cdalvaro/tap", type="tap")
1462+
assert ret["result"] is True
1463+
assert "already not trusted" in ret["comment"]
1464+
assert ret["changes"] == {}
1465+
1466+
1467+
def test_untrusted_test_mode():
1468+
"""
1469+
Test pkg.untrusted in test mode when the item would be untrusted.
1470+
"""
1471+
with patch.dict(
1472+
pkg.__salt__,
1473+
{
1474+
"pkg.untrust": MagicMock(return_value=True),
1475+
"pkg.is_trusted": MagicMock(return_value=True),
1476+
},
1477+
), patch.dict(pkg.__opts__, {"test": True}):
1478+
ret = pkg.untrusted("cdalvaro/tap")
1479+
assert ret["result"] is None
1480+
assert "would be untrusted" in ret["comment"]
1481+
assert ret["changes"] == {}
1482+
1483+
1484+
def test_untrusted_success():
1485+
"""
1486+
Test pkg.untrusted successfully untrusts an item.
1487+
"""
1488+
untrust_mock = MagicMock(return_value=True)
1489+
with patch.dict(
1490+
pkg.__salt__,
1491+
{
1492+
"pkg.untrust": untrust_mock,
1493+
"pkg.is_trusted": MagicMock(return_value=True),
1494+
},
1495+
):
1496+
ret = pkg.untrusted("cdalvaro/tap", type="tap")
1497+
assert ret["result"] is True
1498+
assert ret["changes"] == {
1499+
"cdalvaro/tap": {"old": "trusted", "new": "untrusted"}
1500+
}
1501+
assert "no longer trusted" in ret["comment"]
1502+
untrust_mock.assert_called_once_with("cdalvaro/tap", type="tap")
1503+
1504+
1505+
def test_untrusted_failure():
1506+
"""
1507+
Test pkg.untrusted when the brew untrust command fails.
1508+
"""
1509+
with patch.dict(
1510+
pkg.__salt__,
1511+
{
1512+
"pkg.untrust": MagicMock(return_value=False),
1513+
"pkg.is_trusted": MagicMock(return_value=True),
1514+
},
1515+
):
1516+
ret = pkg.untrusted("cdalvaro/tap")
1517+
assert ret["result"] is False
1518+
assert "Failed to untrust" in ret["comment"]
1519+
assert ret["changes"] == {}

0 commit comments

Comments
 (0)