Skip to content

Commit 5960904

Browse files
nocloud: handle dict-of-strings public-keys format
The third branch of NoCloudConfigDriveService.get_public_keys() assumed public-keys was a dict of {<name>: {'openssh-key': <key>}} (OpenStack config drive shape) and called .get('openssh-key') on each value. KubeVirt's accessCredentials sshPublicKey noCloud propagation method serialises the keys as map[string]string, producing {'key0': '<ssh-rsa ...>', 'key1': '<ssh-rsa ...>'} - dict whose values are plain strings. With that input the plugin crashed with: ERROR cloudbaseinit.init [-] plugin 'SetUserSSHPublicKeysPlugin' failed with error "'str' object has no attribute 'get'" File "...nocloudservice.py", line 698, in get_public_keys public_keys = service.get_public_keys() Accept both shapes: if the dict value is a string treat it as the SSH key directly, otherwise keep the previous OpenStack-style extraction. Reproduced on Windows Server 2022/2025 deployed on KubeVirt 1.8.x + cloudbase-init 1.1.8 with the Cozystack vm-instance chart that wires .Values.sshKeys into accessCredentials sshPublicKey + noCloud propagation. KubeVirt source for the format: pkg/cloud-init/cloud-init.go, type NoCloudMetadata PublicSSHKeys map[string]string. Adds a regression unit test test_get_public_keys_dict_of_strings mirroring the existing test_get_public_keys / test_get_public_keys_alt_fmt pattern. Signed-off-by: mattia-eleuteri <mattia@hidora.io>
1 parent c48d759 commit 5960904

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

cloudbaseinit/metadata/services/nocloudservice.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,14 @@ def get_public_keys(self):
695695
if isinstance(raw_ssh_keys, list):
696696
return raw_ssh_keys
697697

698-
return [raw_ssh_keys[key].get('openssh-key') for key in raw_ssh_keys]
698+
# raw_ssh_keys may be a dict in two shapes:
699+
# 1) {<key-name>: {'openssh-key': <key>}} (OpenStack-style)
700+
# 2) {<key-name>: <key>} (KubeVirt-style)
701+
# KubeVirt serialises NoCloudMetadata.PublicSSHKeys as
702+
# map[string]string, hitting case 2; treat string values directly so
703+
# we don't crash with "'str' object has no attribute 'get'".
704+
return [v if isinstance(v, str) else v.get('openssh-key')
705+
for v in raw_ssh_keys.values()]
699706

700707
def get_network_details(self):
701708
debian_net_config = self._get_meta_data().get('network-interfaces')

cloudbaseinit/tests/metadata/services/test_nocloudservice.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,23 @@ def test_get_public_keys_alt_fmt(self, mock_get_metadata):
516516
result = self._config_drive.get_public_keys()
517517
self.assertEqual(result, expected_result)
518518

519+
@mock.patch(MODULE_PATH + '.NoCloudConfigDriveService._get_meta_data')
520+
def test_get_public_keys_dict_of_strings(self, mock_get_metadata):
521+
# KubeVirt serialises accessCredentials sshPublicKey noCloud as
522+
# map[string]string, which deserialises to a dict whose values are
523+
# plain SSH key strings (not the OpenStack {'openssh-key': ...}
524+
# shape). The plugin must accept both formats.
525+
fake_key0 = 'ssh-rsa AAAA0 user0@host'
526+
fake_key1 = 'ssh-rsa AAAA1 user1@host'
527+
mock_get_metadata.return_value = {
528+
'public-keys': {
529+
'key0': fake_key0,
530+
'key1': fake_key1,
531+
}
532+
}
533+
result = sorted(self._config_drive.get_public_keys())
534+
self.assertEqual(result, sorted([fake_key0, fake_key1]))
535+
519536
@ddt.data(('', ('V2 network metadata is empty', None)),
520537
('1', ('V2 network metadata is not a dictionary', None)),
521538
('{}', ('V2 network metadata is empty', None)),

0 commit comments

Comments
 (0)