Merge "Added method for creating k8s events object."
This commit is contained in:
commit
21a6bae62a
|
@ -13,6 +13,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import contextlib
|
||||
import datetime
|
||||
import functools
|
||||
import itertools
|
||||
import os
|
||||
|
@ -23,6 +24,7 @@ import urllib3
|
|||
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
import pytz
|
||||
import requests
|
||||
from requests import adapters
|
||||
|
||||
|
@ -445,3 +447,55 @@ class K8sClient(object):
|
|||
params.get('resourceVersion'))
|
||||
time.sleep(t)
|
||||
attempt += 1
|
||||
|
||||
def add_event(self, resource, reason, message, type_='Normal'):
|
||||
"""Create an Event object for the provided resource."""
|
||||
involved_object = {'apiVersion': resource['apiVersion'],
|
||||
'kind': resource['kind'],
|
||||
'name': resource['metadata']['name'],
|
||||
'namespace': resource['metadata']['namespace'],
|
||||
'uid': resource['metadata']['uid']}
|
||||
|
||||
# This is needed for Event date, otherwise LAST SEEN/Age will be empty
|
||||
# and misleading.
|
||||
now = datetime.datetime.utcnow().replace(tzinfo=pytz.UTC)
|
||||
date_time = now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
name = ".".join((resource['metadata']['name'],
|
||||
self._get_hex_timestamp(now)))
|
||||
|
||||
event = {'kind': 'Event',
|
||||
'apiVersion': 'v1',
|
||||
'firstTimestamp': date_time,
|
||||
'metadata': {'name': name},
|
||||
'reason': reason,
|
||||
'message': message,
|
||||
'type': type_,
|
||||
'involvedObject': involved_object}
|
||||
|
||||
try:
|
||||
return self.post(f'{constants.K8S_API_BASE}/namespaces/'
|
||||
f'{resource["metadata"]["namespace"]}/events',
|
||||
event)
|
||||
except exc.K8sClientException:
|
||||
LOG.warning(f'There was non critical error during creating an '
|
||||
'Event for resource: "{resource}", with reason: '
|
||||
f'"{reason}", message: "{message}" and type: '
|
||||
f'"{type_}"')
|
||||
return {}
|
||||
|
||||
def _get_hex_timestamp(self, datetimeobj):
|
||||
"""Get hex representation for timestamp.
|
||||
|
||||
In Kuberenets, Event name is constructed name of the bounded object
|
||||
and timestamp in hexadecimal representation.
|
||||
Note, that Python timestamp is represented as floating figure:
|
||||
1631622163.8534190654754638671875
|
||||
while those which origin from K8s, after change to int:
|
||||
1631622163915909162
|
||||
so, to get similar integer, we need to multiply the float by
|
||||
100000000 to get the same precision and cast to integer, to get rid
|
||||
of the fractures, and finally convert it to hex representation.
|
||||
"""
|
||||
timestamp = datetime.datetime.timestamp(datetimeobj)
|
||||
return format(int(timestamp * 100000000), 'x')
|
||||
|
|
|
@ -163,7 +163,8 @@ def get_sgr_obj(sgr_id='7621d1e0-a2d2-4496-94eb-ffd375d20877',
|
|||
return os_sgr.SecurityGroupRule(**sgr_data)
|
||||
|
||||
|
||||
def get_k8s_pod():
|
||||
def get_k8s_pod(name='pod-5bb648d658-55n76', namespace='namespace',
|
||||
uid='683da866-6bb1-4da2-bf6a-a5f4137c38e7'):
|
||||
|
||||
return {'apiVersion': 'v1',
|
||||
'kind': 'Pod',
|
||||
|
@ -173,9 +174,9 @@ def get_k8s_pod():
|
|||
'labels': {'app': 'pod',
|
||||
'pod-template-hash': '5bb648d658'},
|
||||
'operation': 'Update',
|
||||
'name': 'pod-5bb648d658-55n76',
|
||||
'namespace': 'default',
|
||||
'name': name,
|
||||
'namespace': namespace,
|
||||
'resourceVersion': '19416',
|
||||
'uid': '683da866-6bb1-4da2-bf6a-a5f4137c38e7'},
|
||||
'uid': uid},
|
||||
'spec': {},
|
||||
'status': {}}
|
||||
|
|
|
@ -23,6 +23,7 @@ import requests
|
|||
from kuryr_kubernetes import exceptions as exc
|
||||
from kuryr_kubernetes import k8s_client
|
||||
from kuryr_kubernetes.tests import base as test_base
|
||||
from kuryr_kubernetes.tests import fake
|
||||
|
||||
|
||||
class TestK8sClient(test_base.TestCase):
|
||||
|
@ -482,3 +483,43 @@ class TestK8sClient(test_base.TestCase):
|
|||
m_resp.status_code = 500
|
||||
self.assertRaises(exc.K8sClientException,
|
||||
self.client._raise_from_response, m_resp)
|
||||
|
||||
def test_add_event(self):
|
||||
self.client.post = mock.MagicMock()
|
||||
get_hex_ts = self.client._get_hex_timestamp = mock.MagicMock()
|
||||
get_hex_ts.return_value = 'deadc0de'
|
||||
|
||||
namespace = 'n1'
|
||||
uid = 'deadbeef'
|
||||
name = 'pod-123'
|
||||
pod = fake.get_k8s_pod(name=name, namespace=namespace, uid=uid)
|
||||
event_name = f'{name}.deadc0de'
|
||||
|
||||
self.client.add_event(pod, 'reason', 'message')
|
||||
|
||||
# Event path
|
||||
url = self.client.post.call_args[0][0]
|
||||
data = self.client.post.call_args[0][1]
|
||||
self.assertEqual(url, f'/api/v1/namespaces/{namespace}/events')
|
||||
|
||||
# Event fields
|
||||
self.assertEqual(data['metadata']['name'], event_name)
|
||||
self.assertEqual(data['reason'], 'reason')
|
||||
self.assertEqual(data['message'], 'message')
|
||||
self.assertEqual(data['type'], 'Normal')
|
||||
|
||||
# involvedObject
|
||||
self.assertDictEqual(data['involvedObject'],
|
||||
{'apiVersion': pod['apiVersion'],
|
||||
'kind': pod['kind'],
|
||||
'name': name,
|
||||
'namespace': namespace,
|
||||
'uid': uid})
|
||||
|
||||
def test_add_event_k8s_exception(self):
|
||||
self.client.post = mock.MagicMock()
|
||||
self.client.post.side_effect = exc.K8sClientException
|
||||
pod = fake.get_k8s_pod()
|
||||
|
||||
self.assertDictEqual(self.client.add_event(pod, 'reason1', 'message2'),
|
||||
{})
|
||||
|
|
Loading…
Reference in New Issue