Skip to content

Commit ab2f502

Browse files
committed
Add call to fetch units
1 parent a31929b commit ab2f502

File tree

6 files changed

+240
-14
lines changed

6 files changed

+240
-14
lines changed

fleet/client/fleet_fabric.py

+47-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from io import BytesIO
2+
23
from fleet.client import fleet_base
34

5+
46
__author__ = 'sukrit'
57

68
from fabric.api import run, settings, put, hide
@@ -147,6 +149,50 @@ def destroy_units_matching(self, service_prefix):
147149
% service_prefix,
148150
command_output=stream.getvalue())
149151

152+
def fetch_units_matching(self, service_prefix):
153+
"""
154+
Fetch units matching prefix.
155+
156+
:param service_prefix:
157+
:type service_prefix: str
158+
:return: list of units where each unit is represented as dict
159+
comprising of
160+
- unit : Name of fleet unit,
161+
- machine : Machine for the unit
162+
- active : Activation status ('activating', 'active')
163+
- sub : Current state of the unit
164+
"""
165+
with self._fabric_wrapper() as stream:
166+
with self._settings():
167+
try:
168+
units_raw = run('fleetctl list-units -no-legend '
169+
'-fields unit,machine,active,sub -full | '
170+
'grep %s | awk \'{{print $1}}\'' %
171+
service_prefix,
172+
stdout=stream, stderr=stream,
173+
warn_only=True)
174+
175+
units = []
176+
177+
for line in units_raw.splitlines():
178+
cols = line.split()
179+
if not cols:
180+
continue
181+
units.append({
182+
'unit': cols[0],
183+
'machine': cols[1],
184+
'active': cols[2],
185+
'sub': cols[3]
186+
})
187+
188+
return units
189+
190+
except SystemExit:
191+
raise FleetExecutionException(
192+
message='Failed to Fetch units with prefix: %s'
193+
% service_prefix,
194+
command_output=stream.getvalue())
195+
150196
def destroy(self, service):
151197
with self._fabric_wrapper() as stream:
152198
with self._settings():
@@ -165,7 +211,7 @@ def status(self, service_name):
165211
try:
166212
return run('fleetctl list-units | grep {} | '
167213
'awk \'{{{{print $4}}}}\''.format(service_name),
168-
stdout=stream, stderr=stream)
214+
stdout=stream, stderr=stream)
169215
except SystemExit:
170216
raise FleetExecutionException(
171217
message='Failed to get status for unit: %s'

fleet/deploy/deployer.py

+32-9
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,32 @@ def deploy(self):
8686
self._deploy(service_name_prefix)
8787

8888

89+
def _get_service_prefix(name, version, service_type):
90+
"""
91+
Gets the service prefix
92+
93+
:param name: Name of the application
94+
:type name: str
95+
:param version: Version of the application. If none, all versions are
96+
undeployed.
97+
:type version: str
98+
:param service_type: Service type (e.g. 'app', 'logger' etc)
99+
:type service_type: str
100+
:return: Service Prefix
101+
:rtype: str
102+
"""
103+
if not version and not service_type:
104+
return '%s-' % name
105+
elif not service_type:
106+
return '%s-%s-' % (name, version)
107+
else:
108+
return '%s-%s-%s@' % (name, version, service_type)
109+
110+
89111
def undeploy(fleet_provider, name, version=None, service_type=None):
90112
"""
91113
Un-deploys the application from the fleet cluster.
114+
92115
:param fleet_provider: Fleet provider for connecting to fleet cluster.
93116
:type fleet_provider: fleet.client.fleet_base.Provider
94117
:param name: Name of the application
@@ -98,23 +121,23 @@ def undeploy(fleet_provider, name, version=None, service_type=None):
98121
:type version: str
99122
:param service_type: Service type (e.g. 'app', 'logger' etc)
100123
:type service_type: str
101-
:return: None
124+
:return: List of fleet units (dict)
125+
:rtype: list
102126
"""
103-
def get_service_prefix():
104-
if not version and not service_type:
105-
return '%s-' % name
106-
elif not service_type:
107-
return '%s-%s-' % (name, version)
108-
else:
109-
return '%s-%s-%s@' % (name, version, service_type)
110127

111-
service_prefix = get_service_prefix()
128+
service_prefix = _get_service_prefix(name, version, service_type)
112129
fleet_provider.destroy_units_matching(service_prefix)
113130

114131

132+
def filter_units(fleet_provider, name, version=None, service_type=None):
133+
service_prefix = _get_service_prefix(name, version, service_type)
134+
return fleet_provider.fetch_units_matching(service_prefix)
135+
136+
115137
def status(fleet_provider, name, version, node_num, service_type='app'):
116138
"""
117139
Gets the status for a node in cluster.
140+
118141
:param fleet_provider:
119142
:param name:
120143
:param version:

tests/helper.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from nose.tools import ok_, eq_
2+
3+
__author__ = 'sukrit'
4+
5+
6+
def dict_compare(actual, expected, key_path='',):
7+
"""
8+
:param actual:
9+
:param expected:
10+
:param nested_path:
11+
:return: None
12+
13+
>>> dict_compare('A', 'B')
14+
Traceback (most recent call last):
15+
...
16+
AssertionError: Actual 'A' != Expected 'B' at path: ''
17+
18+
>>> dict_compare({}, 'B')
19+
Traceback (most recent call last):
20+
...
21+
AssertionError: Actual {} != Expected 'B' at path: ''
22+
23+
>>> dict_compare({}, {})
24+
True
25+
26+
>>> dict_compare({'key1':'value1'}, {'key2':'value2'})
27+
Traceback (most recent call last):
28+
...
29+
AssertionError: Key 'key1' was not expected at path: ''
30+
31+
>>> dict_compare({'key1':'value1', 'key2': 'value2'},
32+
... {'key2':'value2', 'key1': 'value1'})
33+
True
34+
35+
>>> dict_compare({'key1':{'key1.1': 'value1.1', 'key1.2': 'value1.2'}},
36+
... {'key1':{'key1.2': 'value1.2', 'key1.1': 'value1.1'}})
37+
True
38+
39+
>>> dict_compare({'key1':'value1'}, {'key2':'value2', 'key1':'value1'})
40+
Traceback (most recent call last):
41+
...
42+
AssertionError: Key 'key2' was not found in actual at path: ''
43+
44+
>>> dict_compare({'key1':'value1'}, {'key1':'value2'})
45+
Traceback (most recent call last):
46+
...
47+
AssertionError: Actual 'value1' != Expected 'value2' at path: '.key1'
48+
49+
"""
50+
51+
if isinstance(actual, dict) and isinstance(expected, dict):
52+
for key in actual:
53+
ok_(key in expected, 'Key \'%s\' was not expected at '
54+
'path: \'%s\'' % (key, key_path))
55+
dict_compare(actual[key], expected[key], '%s.%s' % (key_path, key))
56+
57+
for key in expected:
58+
ok_(key in actual, 'Key \'%s\' was not found in actual at '
59+
'path: \'%s\'' % (key, key_path))
60+
else:
61+
eq_(actual, expected, 'Actual %r != Expected %r at path: \'%s\''
62+
% (actual, expected, key_path))
63+
return True

tests/unit/client/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__author__ = 'sukrit'
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from mock import patch
2+
3+
from fleet.client.fleet_fabric import Provider
4+
from tests.helper import dict_compare
5+
6+
7+
__author__ = 'sukrit'
8+
9+
from nose.tools import eq_
10+
11+
12+
def _get_fleet_provider():
13+
return Provider()
14+
15+
16+
@patch('fleet.client.fleet_fabric.run')
17+
def test_fetch_units_matching_with_no_match(mock_run):
18+
"""
19+
Should return empty list when there are no matching units found
20+
"""
21+
# Given: Fleet provider
22+
provider = _get_fleet_provider()
23+
24+
# And no existing units for given service prefix
25+
mock_run.return_value = ''
26+
27+
# When: I try to fetch units with no matching unit
28+
units = provider.fetch_units_matching('non-existing-unit-')
29+
30+
# Then: Empty list is returned
31+
eq_(units, [])
32+
33+
34+
@patch('fleet.client.fleet_fabric.run')
35+
def test_fetch_units_matching_with_multiple_match(mock_run):
36+
"""
37+
Should return empty list when there are no matching units found
38+
"""
39+
# Given: Fleet provider
40+
provider = _get_fleet_provider()
41+
42+
# And no existing units for given service prefix
43+
mock_run.return_value = '''[email protected] 442337f12da14ad7830cda843079730b/10.249.0.235 active running
44+
[email protected] 0a5239ec591e4981905c792e99341f03/10.229.23.106 activating start-pre
45+
''' # noqa
46+
47+
# When: I try to fetch units with no matching unit
48+
units = provider.fetch_units_matching('cluster-deployer-develop-')
49+
50+
# Then: Empty list is returned
51+
eq_(len(units), 2, 'Expecting 2 units to be returned. Found: %d' %
52+
len(units))
53+
dict_compare(units[0], {
54+
'unit': '[email protected]',
55+
'machine': '442337f12da14ad7830cda843079730b/10.249.0.235',
56+
'active': 'active',
57+
'sub': 'running'
58+
})
59+
dict_compare(units[1], {
60+
'unit': '[email protected]',
61+
'machine': '0a5239ec591e4981905c792e99341f03/10.229.23.106',
62+
'active': 'activating',
63+
'sub': 'start-pre'
64+
})

tests/unit/deploy/test_deployer.py

+33-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,38 @@
1+
from nose.tools import eq_
2+
from fleet.deploy.deployer import _get_service_prefix
13

24

3-
def test_undeploy_with_defaults():
5+
def test_get_service_name_prefix_for_all_versions():
46
"""
5-
Should undeploy application with all versions and service types.
7+
Should get service name prefix for all versions
68
"""
79

8-
# Given: Instance of fleet provider
9-
pass
10+
# When: I get service name prefix for all version
11+
service_prefix = _get_service_prefix('test', None, None)
12+
13+
# Then: Expected value for service prefix is returned
14+
eq_(service_prefix, 'test')
15+
16+
17+
def test_get_service_name_prefix_for_given_version():
18+
"""
19+
Should get service name prefix for given version
20+
"""
21+
22+
# When: I get service name prefix for all version
23+
service_prefix = _get_service_prefix('test', 'v1', None)
24+
25+
# Then: Expected value for service prefix is returned
26+
eq_(service_prefix, 'test-v1')
27+
28+
29+
def test_get_service_name_prefix_for_given_type():
30+
"""
31+
Should get service name prefix for given version
32+
"""
33+
34+
# When: I get service name prefix for all version
35+
service_prefix = _get_service_prefix('test', 'v1', 'app')
36+
37+
# Then: Expected value for service prefix is returned
38+
eq_(service_prefix, 'test-v1-app@')

0 commit comments

Comments
 (0)