Skip to content

Commit 74fe55e

Browse files
committed
Merge branch 'pr2-add-deb822-support' into pr3-manage-apt-key-removal
2 parents 360ec2f + 80ac55d commit 74fe55e

2 files changed

Lines changed: 62 additions & 2 deletions

File tree

src/pyinfra/operations/gpg.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pyinfra import host
99
from pyinfra.api import OperationError, operation
1010
from pyinfra.facts.gpg import GpgKeyrings
11+
from pyinfra.facts import files as file_facts
1112

1213
from . import files
1314

@@ -277,6 +278,64 @@ def key(
277278
# After validation, we know dest is not None for installation
278279
assert dest is not None, "dest should not be None after validation"
279280

281+
# Check if key already exists (for idempotence)
282+
if keyid:
283+
# If we have keyid(s), check if they exist in the destination keyring
284+
try:
285+
dest_dir = str(PurePosixPath(dest).parent)
286+
keyring_fact = host.get_fact(GpgKeyrings, [dest_dir])
287+
288+
# keyring_fact contains keyring paths as keys
289+
# Check if our destination keyring exists in the fact
290+
keyring_info = keyring_fact.get(dest)
291+
292+
if keyring_info:
293+
existing_keys = keyring_info["keys"]
294+
keyids_to_check = keyid if isinstance(keyid, list) else [keyid]
295+
296+
# Check if all requested keys already exist
297+
all_keys_exist = True
298+
for kid in keyids_to_check:
299+
# Remove 0x prefix if present for comparison
300+
clean_keyid = kid.replace("0x", "").replace("0X", "").upper()
301+
key_exists = any(
302+
clean_keyid in existing_key_id.upper()
303+
or existing_key_id.upper().endswith(clean_keyid)
304+
for existing_key_id in existing_keys.keys()
305+
)
306+
if not key_exists:
307+
all_keys_exist = False
308+
break
309+
310+
if all_keys_exist:
311+
# All keys already exist, ensure file permissions are correct
312+
yield from files.file._inner(
313+
path=dest,
314+
mode=mode,
315+
present=True,
316+
)
317+
host.noop(f"GPG keys {keyid} already exist in {dest}")
318+
return
319+
except (KeyError, AttributeError):
320+
# Fact not available or incomplete, proceed with installation
321+
pass
322+
else:
323+
# If no keyid specified, check if destination file exists (for file/URL sources)
324+
try:
325+
file_fact = host.get_fact(file_facts.File, dest)
326+
if file_fact:
327+
# File exists, ensure permissions are correct
328+
yield from files.file._inner(
329+
path=dest,
330+
mode=mode,
331+
present=True,
332+
)
333+
host.noop(f"GPG keyring {dest} already exists")
334+
return
335+
except (KeyError, AttributeError):
336+
# Fact not available, proceed with installation
337+
pass
338+
280339
# Ensure destination directory exists
281340
dest_dir = str(PurePosixPath(dest).parent)
282341
yield from files.directory._inner(

tests/operations/gpg.key/keyserver_single_idempotent.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"mode": "755",
1212
"user": "root",
1313
"group": "root"
14-
}
14+
},
15+
"path=/tmp/pyinfra-gpg-empfile_": null
1516
},
1617
"files.File": {
1718
"path=/etc/apt/keyrings/vendor.gpg": {
@@ -21,7 +22,7 @@
2122
}
2223
},
2324
"gpg.GpgKeyrings": {
24-
"/etc/apt/keyrings": {
25+
"directories=['/etc/apt/keyrings']": {
2526
"/etc/apt/keyrings/vendor.gpg": {
2627
"format": "gpg",
2728
"keys": {

0 commit comments

Comments
 (0)