Skip to content

Commit 80ac55d

Browse files
committed
Merge branch 'pr1-add-gpg-operations' into pr2-add-deb822-support
2 parents 40dbd46 + 37fc991 commit 80ac55d

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

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(
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"args": [],
3+
"kwargs": {
4+
"keyserver": "hkps://keyserver.ubuntu.com",
5+
"keyid": ["0xD88E42B4"],
6+
"dest": "/etc/apt/keyrings/vendor.gpg"
7+
},
8+
"facts": {
9+
"files.Directory": {
10+
"path=/etc/apt/keyrings": {
11+
"mode": "755",
12+
"user": "root",
13+
"group": "root"
14+
},
15+
"path=/tmp/pyinfra-gpg-empfile_": null
16+
},
17+
"files.File": {
18+
"path=/etc/apt/keyrings/vendor.gpg": {
19+
"mode": "644",
20+
"user": "root",
21+
"group": "root"
22+
}
23+
},
24+
"gpg.GpgKeyrings": {
25+
"directories=['/etc/apt/keyrings']": {
26+
"/etc/apt/keyrings/vendor.gpg": {
27+
"format": "gpg",
28+
"keys": {
29+
"D88E42B4": {
30+
"length": 4096,
31+
"uid": "Test Key <[email protected]>"
32+
}
33+
}
34+
}
35+
}
36+
}
37+
},
38+
"commands": [
39+
"chmod 644 /etc/apt/keyrings/vendor.gpg"
40+
],
41+
"noop_description": "GPG keys ['0xD88E42B4'] already exist in /etc/apt/keyrings/vendor.gpg"
42+
}

0 commit comments

Comments
 (0)