Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial cockroachdb-operator charm for review #1

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Prev Previous commit
Next Next commit
Add unit tests for the peer relation
This PR needs to go in
canonical/operator#196

to avoid

canonical/operator#202

when running tests.
  • Loading branch information
Dmitrii Shcherbakov committed Apr 1, 2020
commit 95c61ebf40eaf812eb46e4b90055f47f8545704e
2 changes: 1 addition & 1 deletion mod/ops-interface-proxy-listen-tcp
18 changes: 8 additions & 10 deletions src/cluster.py
Expand Up @@ -3,23 +3,21 @@

class CockroachDBCluster(Object):

state = StoredState()
stored = StoredState()

def __init__(self, charm, relation_name):
super().__init__(charm, relation_name)
self._relation_name = relation_name
self._relation = self.framework.model.get_relation(self._relation_name)
self.framework.observe(charm.on.cluster_initialized, self)

self.state.set_default(cluster_id=None)
self.stored.set_default(cluster_id=None)

@property
def _relations(self):
return self.framework.model.relations[self._relation_name]
def _relation(self):
return self.framework.model.get_relation(self._relation_name)

@property
def is_single(self):
return len(self._relations) == 1
return len(self.framework.model.relations[self._relation_name]) == 1

@property
def is_joined(self):
Expand All @@ -30,20 +28,20 @@ def on_cluster_initialized(self, event):
raise RuntimeError('The initial unit of a cluster must also be a leader.')

# A workaround for LP: #1859769.
self.state.cluster_id = event.cluster_id
self.stored.cluster_id = event.cluster_id
if not self.is_joined:
event.defer()
return

self._relation.data[self.model.app]['initial_unit'] = self.framework.model.unit.name
self._relation.data[self.model.app]['cluster_id'] = self.state.cluster_id
self._relation.data[self.model.app]['cluster_id'] = self.stored.cluster_id

@property
def is_cluster_initialized(self):
"""Determined by the presence of a cluster ID."""
if self.is_joined:
return self._relation.data[self.model.app].get('cluster_id') is not None
elif self.state.cluster_id:
elif self.stored.cluster_id:
return True
else:
return False
Expand Down
Empty file added test/__init__.py
Empty file.
128 changes: 128 additions & 0 deletions test/test_cluster.py
@@ -0,0 +1,128 @@
#!/usr/bin/env python3

import unittest
import yaml
import sys
sys.path.append('lib') # noqa
sys.path.append('src') # noqa

from charm import CockroachDBCharmEvents
from cluster import CockroachDBCluster
from ops import testing
from ops.charm import CharmBase


class TestCharmClass(CharmBase):
'''A test class that exposes the same set of events as the real CockroachDBCharm class.

CockroachDBCluster type expects the parent to expose an event called cluster_initialized.
'''

on = CockroachDBCharmEvents()


class TestCockroachDBCluster(unittest.TestCase):

realData = yaml.safe_load('''
ingress-address: 10.210.24.14
cluster_id: 9801e1d9-92e0-4d6a-aa25-fd0e8b236cfe
initial_unit: cockroachdb/0
''')

def setUp(self):
self.harness = testing.Harness(TestCharmClass, meta='''
name: cockroachdb
peers:
cluster:
interface: cockroachdb-peer
''')

self.harness.begin()
self.cluster = CockroachDBCluster(self.harness.charm, 'cluster')

def test_is_cluster_joined(self):
relation_id = self.harness.add_relation('cluster', 'cockroachdb')
self.harness.update_relation_data(
relation_id, 'cockroachdb/0', {'ingress-address': '192.0.2.1'})
self.assertTrue(self.cluster.is_joined)

def test_is_single(self):
relation_id = self.harness.add_relation('cluster', 'cockroachdb')
self.harness.update_relation_data(
relation_id, 'cockroachdb/0', {'ingress-address': '192.0.2.1'})
self.assertTrue(self.cluster.is_single)

def test_peer_addresses(self):
relation_id = self.harness.add_relation('cluster', 'cockroachdb')
self.harness.update_relation_data(
relation_id, 'cockroachdb/0', {'ingress-address': '192.0.2.1'})
self.harness.add_relation_unit(relation_id, 'cockroachdb/1',
{'ingress-address': '192.0.2.2'})
self.harness.add_relation_unit(relation_id, 'cockroachdb/2',
{'ingress-address': '192.0.2.3'})
# Relation units are stored in a set hence the result may not
# always be ordered in the same way.
self.assertEqual(set(self.cluster.peer_addresses), set(['192.0.2.2', '192.0.2.3']))

def test_initial_unit(self):
relation_id = self.harness.add_relation('cluster', 'cockroachdb')
self.assertIsNone(self.cluster.initial_unit)

self.harness.update_relation_data(
relation_id, 'cockroachdb', {
'cluster_id': '449ce7de-faea-48f1-925b-198032fdacc4',
'initial_unit': 'cockroachdb/1'
})
self.assertEqual(self.cluster.initial_unit, 'cockroachdb/1')

def test_advertise_addr(self):
# TODO: implement when network_get gets implemented for the test harness.
pass

def test_on_cluster_initialized_when_joined(self):
'''Test that the initial unit exposes a cluster id and reports the init state correctly.
'''
self.harness.set_leader()
relation_id = self.harness.add_relation('cluster', 'cockroachdb')
self.harness.update_relation_data(
relation_id, 'cockroachdb/0', {'ingress-address': '192.0.2.1'})
self.assertFalse(self.cluster.is_cluster_initialized)

cluster_id = '449ce7de-faea-48f1-925b-198032fdacc4'
self.harness.charm.on.cluster_initialized.emit(cluster_id)
self.assertTrue(self.cluster.is_cluster_initialized)

cluster_relation = self.harness.charm.model.get_relation('cluster')
app_data = cluster_relation.data[self.harness.charm.app]
self.assertEqual(app_data.get('cluster_id'), cluster_id)
self.assertEqual(app_data.get('initial_unit'), self.harness.charm.unit.name)

def test_on_cluster_initialized_when_not_joined(self):
'''Test a scenario when an initial unit generates cluster state without a peer relation.

This situation occurs on versions of Juju that do not have relation-created hooks fired
before the start event.
'''
self.harness.set_leader()
self.assertFalse(self.cluster.is_cluster_initialized)

cluster_id = '449ce7de-faea-48f1-925b-198032fdacc4'
self.harness.charm.on.cluster_initialized.emit(cluster_id)
self.assertTrue(self.cluster.is_cluster_initialized)
self.assertTrue(self.cluster.stored.cluster_id, cluster_id)

def test_on_cluster_initialized_not_leader(self):
'''Test that the handler raises an exception if erroneously used from a non-leader unit.
'''
self.harness.set_leader(is_leader=False)
relation_id = self.harness.add_relation('cluster', 'cockroachdb')
self.harness.update_relation_data(
relation_id, 'cockroachdb/0', {'ingress-address': '192.0.2.1'})
self.assertFalse(self.cluster.is_cluster_initialized)

with self.assertRaises(RuntimeError):
self.harness.charm.on.cluster_initialized.emit('449ce7de-faea-48f1-925b-198032fdacc4')


if __name__ == "__main__":
unittest.main()