Add metadata interface for instance data

Some plugins, like the userdata execution plugin, need a standard model
for the instance data, which can be applied by templating engines like
jinja to the userdata.

This way, a user can use this feature to customize her userdata scripts
with values that are specific to that instance runtime env or cloud platform.

The instance data structure is based on the cloud-init specifications:
https://cloudinit.readthedocs.io/en/latest/topics/instancedata.html

The v1 namespace contains a subset of the cloud-init specs for the instance
data.

The ds.meta_data namespace contains all the values the v1 namespace
contains, in order to be compatible with cloud-init, plus a subset
of other instance data.

Change-Id: I5c529498f06fe3c86f7fa3c20fdf3091840c4041
This commit is contained in:
Adrian Vladu 2019-12-12 13:18:49 +02:00
parent c31c5b99f1
commit 551c4eb318
2 changed files with 72 additions and 0 deletions

View File

@ -14,6 +14,7 @@
import abc
import copy
import gzip
import io
import time
@ -221,6 +222,45 @@ class BaseMetadataService(object):
def get_ephemeral_disk_data_loss_warning(self):
raise NotExistingMetadataException()
def get_instance_data(self):
"""Returns a dictionary with instance data from the metadata source
The instance data structure is based on the cloud-init specifications:
https://cloudinit.readthedocs.io/en/latest/topics/instancedata.html
The v1 namespace contains a subset of the cloud-init standard
for the instance data. In the future, it should reach parity with the
cloud-init standard.
The ds.meta_data namespace contains all the values the v1 namespace
contains, in order to be compatible with cloud-init, plus a subset of
other instance data.
The ds namespace can change without prior notice and should not be
used in production.
"""
instance_id = self.get_instance_id()
hostname = self.get_host_name()
v1_data = {
"instance_id": instance_id,
"local_hostname": hostname,
"public_ssh_keys": self.get_public_keys()
}
# Copy the v1 data to the ds.meta_data and add more fields
ds_meta_data = copy.deepcopy(v1_data)
ds_meta_data.update({
"hostname": hostname
})
return {
"v1": v1_data,
"ds": {
"meta_data": ds_meta_data,
}
}
class BaseHTTPMetadataService(BaseMetadataService):

View File

@ -61,6 +61,38 @@ class TestBase(unittest.TestCase):
result = self._service.get_user_pwd_encryption_key()
self.assertEqual(result, mock_get_public_keys.return_value[0])
@mock.patch('cloudbaseinit.metadata.services.base.'
'BaseMetadataService.get_public_keys')
@mock.patch('cloudbaseinit.metadata.services.base.'
'BaseMetadataService.get_host_name')
@mock.patch('cloudbaseinit.metadata.services.base.'
'BaseMetadataService.get_instance_id')
def test_get_instance_data(self, mock_instance_id, mock_hostname,
mock_public_keys):
fake_instance_id = 'id'
mock_instance_id.return_value = fake_instance_id
fake_hostname = 'host'
mock_hostname.return_value = fake_hostname
fake_keys = ['ssh1', 'ssh2']
mock_public_keys.return_value = fake_keys
expected_response = {
'v1': {
"instance_id": fake_instance_id,
"local_hostname": fake_hostname,
"public_ssh_keys": fake_keys
},
'ds': {
'meta_data': {
"instance_id": fake_instance_id,
"local_hostname": fake_hostname,
"public_ssh_keys": fake_keys,
"hostname": fake_hostname
},
}
}
self.assertEqual(expected_response, self._service.get_instance_data())
class TestBaseHTTPMetadataService(unittest.TestCase):