|
17 | 17 | from pyinfra.connectors.util import remove_any_sudo_askpass_file |
18 | 18 | from pyinfra.facts.files import Directory, FindInFile, Link |
19 | 19 | from pyinfra.facts.server import ( |
| 20 | + AuthorizedKeys, |
20 | 21 | Groups, |
21 | 22 | Home, |
22 | 23 | Hostname, |
@@ -801,34 +802,49 @@ def read_any_pub_key_file(key): |
801 | 802 |
|
802 | 803 | authorized_key_file = f"{authorized_key_directory}/{authorized_key_filename}" |
803 | 804 |
|
804 | | - if delete_keys: |
805 | | - # Create a whole new authorized_keys file |
806 | | - keys_file = StringIO( |
807 | | - "{0}\n".format( |
808 | | - "\n".join(public_keys), |
809 | | - ), |
810 | | - ) |
| 805 | + # Pull the currently installed keys once; individual files.line calls otherwise |
| 806 | + # issue one FindInFile fact per key, which dominates the cost for users with many |
| 807 | + # keys. |
| 808 | + current_keys = host.get_fact(AuthorizedKeys, user=user, path=authorized_key_file) |
811 | 809 |
|
812 | | - # And ensure it exists |
813 | | - yield from files.put._inner( |
814 | | - src=keys_file, |
815 | | - dest=authorized_key_file, |
816 | | - user=user, |
817 | | - group=group or user, |
818 | | - mode=600, |
819 | | - ) |
| 810 | + if delete_keys: |
| 811 | + if current_keys == public_keys: |
| 812 | + # Still ensure the file and its ownership/mode stay correct. |
| 813 | + yield from files.file._inner( |
| 814 | + path=authorized_key_file, |
| 815 | + user=user, |
| 816 | + group=group or user, |
| 817 | + mode=600, |
| 818 | + ) |
| 819 | + else: |
| 820 | + keys_file = StringIO( |
| 821 | + "{0}\n".format( |
| 822 | + "\n".join(public_keys), |
| 823 | + ), |
| 824 | + ) |
| 825 | + yield from files.put._inner( |
| 826 | + src=keys_file, |
| 827 | + dest=authorized_key_file, |
| 828 | + user=user, |
| 829 | + group=group or user, |
| 830 | + mode=600, |
| 831 | + ) |
820 | 832 |
|
821 | 833 | else: |
822 | | - # Ensure authorized_keys exists |
| 834 | + # Ensure authorized_keys exists with the right ownership and mode. |
823 | 835 | yield from files.file._inner( |
824 | 836 | path=authorized_key_file, |
825 | 837 | user=user, |
826 | 838 | group=group or user, |
827 | 839 | mode=600, |
828 | 840 | ) |
829 | 841 |
|
830 | | - # And every public key is present |
| 842 | + # Only append the keys that the fact says are missing; an empty fact result |
| 843 | + # also covers the "file does not exist yet" case. |
| 844 | + current_key_set = set(current_keys) |
831 | 845 | for key in public_keys: |
| 846 | + if key in current_key_set: |
| 847 | + continue |
832 | 848 | yield from files.line._inner(path=authorized_key_file, line=key, ensure_newline=True) |
833 | 849 |
|
834 | 850 |
|
|
0 commit comments