8
8
These tests use the Amulet test helpers:
9
9
see https://jujucharms.com/docs/stable/tools-amulet
11
Connection tests are only run on local environments where the internal unit
12
addresses are reachable.
13
15
from pkg_resources import resource_filename
20
22
# Allow importing modules and packages from the hooks directory.
21
23
sys.path.append(resource_filename(__name__, '../hooks'))
26
# Define the charm name.
30
class RedisClient(object):
31
"""A very simple and naive telnet redis client used for tests."""
33
def __init__(self, host, port=6379):
34
"""Initialize the client."""
39
def connect(self, password=None):
40
"""Connect to the client."""
41
self._client = telnetlib.Telnet(self._host, self._port)
42
if password is not None:
43
self._client.write('AUTH {}\n'.format(password))
44
response = self._readline()
46
raise ValueError('authentication error: {}'.format(response))
49
"""Close the client connection."""
50
if self._client is not None:
54
def set(self, key, value):
55
"""Set a key in the redis database, with the given value."""
56
self._client.write('SET {} {}\n'.format(key, value))
57
response = self._readline()
59
raise ValueError('unexpected response: {}'.format(response))
62
"""Return the value corresponding to key from the redis database.
64
Return None if the key is not found.
66
self._client.write('GET {}\n'.format(key))
67
response = self._readline()
70
return self._readline()
73
"""Read next line from the client connection."""
74
return self._client.read_until('\r\n').strip()
77
_counter = itertools.count()
80
def get_service_name():
81
"""Return an incremental redis service name."""
82
return 'redis{}'.format(next(_counter))
85
def deploy(options=None):
86
"""Deploy one unit of the given service using the redis charm.
88
Return the Amulet deployment and the unit object.
90
deployment = amulet.Deployment(series='trusty')
91
service_name = get_service_name()
92
deployment.add(service_name, charm=CHARM_NAME)
93
if options is not None:
94
deployment.configure(service_name, options)
95
deployment.expose(service_name)
97
deployment.setup(timeout=900)
98
deployment.sentry.wait()
99
except amulet.helpers.TimeoutError:
101
amulet.FAIL, msg='Environment was not stood up in time.')
102
return deployment, deployment.sentry.unit[service_name + '/0']
105
def deploy_master_slave(master_options=None, slave_options=None):
106
"""Deploy two redis services related in a master-slave relationship.
108
Return the Amulet deployment and the two unit objects.
110
deployment = amulet.Deployment(series='trusty')
111
master, slave = get_service_name(), get_service_name()
112
deployment.add(master, charm=CHARM_NAME)
113
deployment.add(slave, charm=CHARM_NAME)
114
if master_options is not None:
115
deployment.configure(master, master_options)
116
if slave_options is not None:
117
deployment.configure(slave, slave_options)
118
deployment.relate(master + ':master', slave + ':slave')
119
deployment.expose(master)
120
deployment.expose(slave)
122
deployment.setup(timeout=900)
123
deployment.sentry.wait()
124
except amulet.helpers.TimeoutError:
126
amulet.FAIL, msg='Environment was not stood up in time.')
127
units = deployment.sentry.unit
128
return deployment, units[master + '/0'], units[slave + '/0']
28
# Define a test decorator for running the test only if the current environment
30
only_on_local_environments = unittest.skipIf(
31
helpers.get_environment_type() != 'local',
32
'only available whe using a local environment')
131
35
class TestDeployment(unittest.TestCase):
141
45
cls.deployment.remove_service(cls.unit.info['service'])
143
47
def test_config_file(self):
48
address = helpers.get_private_address(self.unit)
144
49
expected_content = (
146
52
'logfile /var/log/redis/redis-server.log\n'
147
53
'loglevel notice\n'
149
).format(self.unit.info['public-address'])
152
self.unit.file_contents(configfile.REDIS_CONF))
60
self.unit.file_contents(settings.REDIS_CONF))
62
@only_on_local_environments
154
63
def test_connection(self):
155
client = RedisClient(self.unit.info['public-address'])
64
client = helpers.RedisClient(self.unit.info['public-address'])
157
66
self.addCleanup(client.close)
158
67
self.assertIsNone(client.get('my-key'))
180
91
cls.deployment.remove_service(cls.unit.info['service'])
182
93
def test_config_file(self):
94
address = helpers.get_private_address(self.unit)
183
95
expected_content = (
185
98
'logfile /tmp/redis.log\n'
186
99
'loglevel verbose\n'
188
101
'requirepass secret\n'
189
).format(self.unit.info['public-address'])
190
105
self.assertEqual(
191
106
expected_content,
192
self.unit.file_contents(configfile.REDIS_CONF))
107
self.unit.file_contents(settings.REDIS_CONF))
109
@only_on_local_environments
194
110
def test_connection(self):
195
client = RedisClient(
111
client = helpers.RedisClient(
196
112
self.unit.info['public-address'], port=self.options['port'])
197
113
client.connect(password=self.options['password'])
198
114
self.addCleanup(client.close)
217
135
def test_master_config_file(self):
218
136
expected_content = (
220
139
'logfile /var/log/redis/redis-server.log\n'
221
140
'loglevel notice\n'
223
).format(self.master.info['public-address'])
144
).format(self.master_address)
224
145
self.assertEqual(
225
146
expected_content,
226
self.master.file_contents(configfile.REDIS_CONF))
147
self.master.file_contents(settings.REDIS_CONF))
228
149
def test_slave_config_file(self):
229
150
expected_content = (
231
153
'logfile /var/log/redis/redis-server.log\n'
232
154
'loglevel notice\n'
234
156
'slaveof {} 6379\n'
236
self.slave.info['public-address'],
237
self.master.info['public-address'])
159
).format(self.slave_address, self.master_address)
238
160
self.assertEqual(
239
161
expected_content,
240
self.slave.file_contents(configfile.REDIS_CONF))
162
self.slave.file_contents(settings.REDIS_CONF))
164
@only_on_local_environments
242
165
def test_connection(self):
243
master_client = RedisClient(self.master.info['public-address'])
166
master_client = helpers.RedisClient(self.master.info['public-address'])
244
167
master_client.connect()
245
168
self.addCleanup(master_client.close)
246
169
master_client.set('my-key', '42')
170
# Wait for master and slave synchronization.
247
172
# Retrieve the value from the slave.
248
slave_client = RedisClient(self.slave.info['public-address'])
173
slave_client = helpers.RedisClient(self.slave.info['public-address'])
249
174
slave_client.connect()
250
175
self.addCleanup(slave_client.close)
251
176
self.assertEqual('42', slave_client.get('my-key'))
254
179
class TestMasterSlaveRelationOptions(unittest.TestCase):
256
master_options = {'password': 'secret'}
257
slave_options = {'port': 4747, 'loglevel': 'warning'}
181
master_options = {'databases': 5, 'password': 'secret'}
182
slave_options = {'port': 4747, 'loglevel': 'warning', 'timeout': 42}
260
185
def setUpClass(cls):
261
186
# Set up the environment and deploy the charm.
262
cls.deployment, cls.master, cls.slave = deploy_master_slave(
187
cls.deployment, cls.master, cls.slave = helpers.deploy_master_slave(
263
188
master_options=cls.master_options,
264
189
slave_options=cls.slave_options)
190
cls.master_address = helpers.get_private_address(cls.master)
191
cls.slave_address = helpers.get_private_address(cls.slave)
267
194
def tearDownClass(cls):
272
199
def test_master_config_file(self):
273
200
expected_content = (
275
203
'logfile /var/log/redis/redis-server.log\n'
276
204
'loglevel notice\n'
278
206
'requirepass secret\n'
279
).format(self.master.info['public-address'])
209
).format(self.master_address)
280
210
self.assertEqual(
281
211
expected_content,
282
self.master.file_contents(configfile.REDIS_CONF))
212
self.master.file_contents(settings.REDIS_CONF))
284
214
def test_slave_config_file(self):
285
215
expected_content = (
287
218
'logfile /var/log/redis/redis-server.log\n'
288
219
'loglevel warning\n'
289
220
'masterauth secret\n'
291
222
'slaveof {} 6379\n'
293
self.slave.info['public-address'],
294
self.master.info['public-address'])
225
).format(self.slave_address, self.master_address)
295
226
self.assertEqual(
296
227
expected_content,
297
self.slave.file_contents(configfile.REDIS_CONF))
228
self.slave.file_contents(settings.REDIS_CONF))
230
@only_on_local_environments
299
231
def test_connection(self):
300
master_client = RedisClient(self.master.info['public-address'])
232
master_client = helpers.RedisClient(self.master.info['public-address'])
301
233
master_client.connect(password=self.master_options['password'])
302
234
self.addCleanup(master_client.close)
303
235
master_client.set('my-key', '42')
236
# Wait for master and slave synchronization.
304
238
# Retrieve the value from the slave.
305
slave_client = RedisClient(
239
slave_client = helpers.RedisClient(
306
240
self.slave.info['public-address'], port=self.slave_options['port'])
307
241
slave_client.connect()
308
242
self.addCleanup(slave_client.close)