Skip to content

Commit 3ae3d38

Browse files
committed
Cache all calls to kubernetes and docker when doing service discovery
Particularly this goes from O(4-5N) calls to the kubernetes API to 1.
1 parent fb0ea7c commit 3ae3d38

File tree

3 files changed

+142
-125
lines changed

3 files changed

+142
-125
lines changed

tests/core/test_service_discovery.py

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from utils.service_discovery.etcd_config_store import EtcdStore
1313
from utils.service_discovery.abstract_config_store import AbstractConfigStore, CONFIG_FROM_KUBE
1414
from utils.service_discovery.sd_backend import get_sd_backend
15-
from utils.service_discovery.sd_docker_backend import SDDockerBackend
15+
from utils.service_discovery.sd_docker_backend import SDDockerBackend, _SDDockerBackendConfigFetchState
1616

1717

1818
def clear_singletons(agentConfig):
@@ -34,7 +34,7 @@ def raise_for_status(self):
3434

3535
def _get_container_inspect(c_id):
3636
"""Return a mocked container inspect dict from self.container_inspects."""
37-
for co, _, _, _ in TestServiceDiscovery.container_inspects:
37+
for co, _, _, _, _ in TestServiceDiscovery.container_inspects:
3838
if co.get('Id') == c_id:
3939
return co
4040
return None
@@ -178,7 +178,10 @@ def setUp(self):
178178

179179
@mock.patch('utils.http.requests.get')
180180
@mock.patch('utils.kubernetes.kubeutil.check_yaml')
181-
def test_get_host_address(self, mock_check_yaml, mock_get):
181+
@mock.patch.object(AbstractConfigStore, '__init__', return_value=None)
182+
@mock.patch('utils.dockerutil.DockerUtil.client', return_value=None)
183+
@mock.patch('utils.kubernetes.kubeutil.get_conf_path', return_value=None)
184+
def test_get_host_address(self, mock_check_yaml, mock_get, *args):
182185
kubernetes_config = {'instances': [{'kubelet_port': 1337}]}
183186
pod_list = {
184187
'items': [{
@@ -234,35 +237,34 @@ def test_get_host_address(self, mock_check_yaml, mock_get):
234237
mock_get.return_value = Response(pod_list)
235238

236239
for c_ins, tpl_var, expected_ip in ip_address_inspects:
237-
with mock.patch.object(AbstractConfigStore, '__init__', return_value=None):
238-
with mock.patch('utils.dockerutil.DockerUtil.client', return_value=None):
239-
with mock.patch('utils.kubernetes.kubeutil.get_conf_path', return_value=None):
240-
sd_backend = get_sd_backend(agentConfig=self.auto_conf_agentConfig)
241-
self.assertEquals(sd_backend._get_host_address(c_ins, tpl_var), expected_ip)
242-
clear_singletons(self.auto_conf_agentConfig)
243-
244-
def test_get_port(self):
245-
with mock.patch('utils.dockerutil.DockerUtil.client', return_value=None):
246-
for c_ins, _, var_tpl, expected_ports, _ in self.container_inspects:
247-
sd_backend = get_sd_backend(agentConfig=self.auto_conf_agentConfig)
248-
if isinstance(expected_ports, str):
249-
self.assertEquals(sd_backend._get_port(c_ins, var_tpl), expected_ports)
250-
else:
251-
self.assertRaises(expected_ports, sd_backend._get_port, c_ins, var_tpl)
252-
clear_singletons(self.auto_conf_agentConfig)
240+
state = _SDDockerBackendConfigFetchState(lambda _: c_ins)
241+
sd_backend = get_sd_backend(agentConfig=self.auto_conf_agentConfig)
242+
self.assertEquals(sd_backend._get_host_address(state, 'container id', tpl_var), expected_ip)
243+
clear_singletons(self.auto_conf_agentConfig)
244+
245+
@mock.patch('utils.dockerutil.DockerUtil.client', return_value=None)
246+
def test_get_port(self, _):
247+
for c_ins, _, var_tpl, expected_ports, _ in self.container_inspects:
248+
state = _SDDockerBackendConfigFetchState(lambda _: c_ins)
249+
sd_backend = get_sd_backend(agentConfig=self.auto_conf_agentConfig)
250+
if isinstance(expected_ports, str):
251+
self.assertEquals(sd_backend._get_port(state, 'container id', var_tpl), expected_ports)
252+
else:
253+
self.assertRaises(expected_ports, sd_backend._get_port, state, 'c_id', var_tpl)
254+
clear_singletons(self.auto_conf_agentConfig)
253255

254256
@mock.patch('utils.dockerutil.DockerUtil.client', return_value=None)
255257
@mock.patch.object(SDDockerBackend, '_get_host_address', return_value='127.0.0.1')
256258
@mock.patch.object(SDDockerBackend, '_get_port', return_value='1337')
257-
@mock.patch('docker.Client.inspect_container', side_effect=_get_container_inspect)
258259
@mock.patch.object(SDDockerBackend, '_get_config_templates', side_effect=_get_conf_tpls)
259260
def test_get_check_configs(self, *args):
260261
"""Test get_check_config with mocked container inspect and config template"""
261262
c_id = self.docker_container_inspect.get('Id')
262263
for image in self.mock_templates.keys():
263264
sd_backend = get_sd_backend(agentConfig=self.auto_conf_agentConfig)
265+
state = _SDDockerBackendConfigFetchState(_get_container_inspect)
264266
self.assertEquals(
265-
sd_backend._get_check_configs(c_id, image)[0],
267+
sd_backend._get_check_configs(state, c_id, image)[0],
266268
self.mock_templates[image][1])
267269
clear_singletons(self.auto_conf_agentConfig)
268270

@@ -317,7 +319,10 @@ def test_render_template(self):
317319
self.assertEquals(config, None)
318320
clear_singletons(agentConfig)
319321

320-
def test_fill_tpl(self):
322+
@mock.patch('utils.dockerutil.DockerUtil.client', return_value=None)
323+
@mock.patch.object(EtcdStore, 'get_client', return_value=None)
324+
@mock.patch.object(ConsulStore, 'get_client', return_value=None)
325+
def test_fill_tpl(self, *args):
321326
"""Test _fill_tpl with mocked docker client"""
322327

323328
valid_configs = [
@@ -468,32 +473,28 @@ def test_fill_tpl(self):
468473
)
469474
]
470475

471-
with mock.patch('utils.dockerutil.DockerUtil.client', return_value=None):
472-
with mock.patch.object(EtcdStore, 'get_client', return_value=None):
473-
with mock.patch.object(ConsulStore, 'get_client', return_value=None):
474-
for ac in self.agentConfigs:
475-
sd_backend = get_sd_backend(agentConfig=ac)
476-
try:
477-
for co in valid_configs + edge_cases:
478-
inspect, tpl, variables, tags = co[0]
479-
instance_tpl, var_values = sd_backend._fill_tpl(inspect, tpl, variables, tags)
480-
for key in instance_tpl.keys():
481-
if isinstance(instance_tpl[key], list):
482-
self.assertEquals(len(instance_tpl[key]), len(co[1][0].get(key)))
483-
for elem in instance_tpl[key]:
484-
self.assertTrue(elem in co[1][0].get(key))
485-
else:
486-
self.assertEquals(instance_tpl[key], co[1][0].get(key))
487-
self.assertEquals(var_values, co[1][1])
488-
489-
for co in invalid_config:
490-
inspect, tpl, variables, tags = co[0]
491-
self.assertRaises(co[1], sd_backend._fill_tpl(inspect, tpl, variables, tags))
492-
493-
clear_singletons(ac)
494-
except Exception:
495-
clear_singletons(ac)
496-
raise
476+
for ac in self.agentConfigs:
477+
sd_backend = get_sd_backend(agentConfig=ac)
478+
try:
479+
for co in valid_configs + edge_cases:
480+
inspect, tpl, variables, tags = co[0]
481+
state = _SDDockerBackendConfigFetchState(lambda _: inspect)
482+
instance_tpl, var_values = sd_backend._fill_tpl(state, 'c_id', tpl, variables, tags)
483+
for key in instance_tpl.keys():
484+
if isinstance(instance_tpl[key], list):
485+
self.assertEquals(len(instance_tpl[key]), len(co[1][0].get(key)))
486+
for elem in instance_tpl[key]:
487+
self.assertTrue(elem in co[1][0].get(key))
488+
else:
489+
self.assertEquals(instance_tpl[key], co[1][0].get(key))
490+
self.assertEquals(var_values, co[1][1])
491+
492+
for co in invalid_config:
493+
inspect, tpl, variables, tags = co[0]
494+
state = _SDDockerBackendConfigFetchState(lambda _: inspect)
495+
self.assertRaises(co[1], sd_backend._fill_tpl(state, 'c_id', tpl, variables, tags))
496+
finally:
497+
clear_singletons(ac)
497498

498499
# config_stores tests
499500

@@ -546,9 +547,9 @@ def test_get_check_tpls_kube(self, mock_client_read):
546547
kube_pod_name=image,
547548
kube_container_name='foo',
548549
kube_annotations=dict(zip(
549-
['sd.datadoghq.com/foo/check_names',
550-
'sd.datadoghq.com/foo/init_configs',
551-
'sd.datadoghq.com/foo/instances'],
550+
['service-discovery.datadoghq.com/foo.check_names',
551+
'service-discovery.datadoghq.com/foo.init_configs',
552+
'service-discovery.datadoghq.com/foo.instances'],
552553
self.mock_tpls[image][0]))))
553554

554555
def test_get_config_id(self):

utils/service_discovery/abstract_config_store.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
INSTANCES = 'instances'
2828
KUBE_ANNOTATIONS = 'kube_annotations'
2929
KUBE_CONTAINER_NAME = 'kube_container_name'
30-
KUBE_ANNOTATION_PREFIX = 'sd.datadoghq.com'
30+
KUBE_ANNOTATION_PREFIX = 'service-discovery.datadoghq.com'
3131

3232

3333
class KeyNotFound(Exception):
@@ -101,7 +101,7 @@ def _populate_identifier_to_checks(self):
101101

102102
def _get_kube_config(self, identifier, kube_annotations, kube_container_name):
103103
try:
104-
prefix = '{}/{}/'.format(KUBE_ANNOTATION_PREFIX, kube_container_name)
104+
prefix = '{}/{}.'.format(KUBE_ANNOTATION_PREFIX, kube_container_name)
105105
check_names = json.loads(kube_annotations[prefix + CHECK_NAMES])
106106
init_config_tpls = json.loads(kube_annotations[prefix + INIT_CONFIGS])
107107
instance_tpls = json.loads(kube_annotations[prefix + INSTANCES])

0 commit comments

Comments
 (0)