class TestEndpointProvider(unittest.TestCase): def setUp(self): self.harness = Harness(EndpointProviderCharm, meta=PROVIDER_META) self.addCleanup(self.harness.cleanup) self.harness.set_leader(True) self.harness.begin() def test_provider_default_scrape_relations_not_in_meta(self): """Tests that the Provider raises exception when no promethes_scrape in meta.""" harness = Harness( EndpointProviderCharm, # No provider relation with `prometheus_scrape` as interface meta=""" name: provider-tester containers: prometheus: resource: prometheus-image prometheus-tester: {} provides: non-standard-name: interface: prometheus_scrape """, ) self.assertRaises(RelationNotFoundError, harness.begin) def test_provider_default_scrape_relation_wrong_interface(self): """Tests that Provider raises exception if the default relation has the wrong interface.""" harness = Harness( EndpointProviderCharm, # No provider relation with `prometheus_scrape` as interface meta=""" name: provider-tester containers: prometheus: resource: prometheus-image prometheus-tester: {} provides: metrics-endpoint: interface: not_prometheus_scrape """, ) self.assertRaises(RelationInterfaceMismatchError, harness.begin) def test_provider_default_scrape_relation_wrong_role(self): """Tests that Provider raises exception if the default relation has the wrong role.""" harness = Harness( EndpointProviderCharm, # No provider relation with `prometheus_scrape` as interface meta=""" name: provider-tester containers: prometheus: resource: prometheus-image prometheus-tester: {} requires: metrics-endpoint: interface: prometheus_scrape """, ) self.assertRaises(RelationRoleMismatchError, harness.begin) @patch("ops.testing._TestingModelBackend.network_get") def test_provider_sets_scrape_metadata(self, _): rel_id = self.harness.add_relation(RELATION_NAME, "provider") self.harness.add_relation_unit(rel_id, "provider/0") data = self.harness.get_relation_data(rel_id, self.harness.model.app.name) self.assertIn("scrape_metadata", data) scrape_metadata = data["scrape_metadata"] self.assertIn("model", scrape_metadata) self.assertIn("model_uuid", scrape_metadata) self.assertIn("application", scrape_metadata) @patch("ops.testing._TestingModelBackend.network_get") def test_provider_unit_sets_bind_address_on_pebble_ready( self, mock_net_get): bind_address = "192.0.8.2" fake_network = { "bind-addresses": [{ "interface-name": "eth0", "addresses": [{ "hostname": "prometheus-tester-0", "value": bind_address }], }] } mock_net_get.return_value = fake_network rel_id = self.harness.add_relation(RELATION_NAME, "provider") self.harness.container_pebble_ready("prometheus-tester") data = self.harness.get_relation_data(rel_id, self.harness.charm.unit.name) self.assertIn("prometheus_scrape_host", data) self.assertEqual(data["prometheus_scrape_host"], bind_address) @patch("ops.testing._TestingModelBackend.network_get") def test_provider_unit_sets_bind_address_on_relation_joined( self, mock_net_get): bind_address = "192.0.8.2" fake_network = { "bind-addresses": [{ "interface-name": "eth0", "addresses": [{ "hostname": "prometheus-tester-0", "value": bind_address }], }] } mock_net_get.return_value = fake_network rel_id = self.harness.add_relation(RELATION_NAME, "provider") self.harness.add_relation_unit(rel_id, "provider/0") data = self.harness.get_relation_data(rel_id, self.harness.charm.unit.name) self.assertIn("prometheus_scrape_host", data) self.assertEqual(data["prometheus_scrape_host"], bind_address) @patch("ops.testing._TestingModelBackend.network_get") def test_provider_supports_multiple_jobs(self, _): rel_id = self.harness.add_relation(RELATION_NAME, "provider") self.harness.add_relation_unit(rel_id, "provider/0") data = self.harness.get_relation_data(rel_id, self.harness.model.app.name) self.assertIn("scrape_jobs", data) jobs = json.loads(data["scrape_jobs"]) self.assertEqual(len(jobs), len(JOBS)) names = [job["job_name"] for job in jobs] job_names = [job["job_name"] for job in JOBS] self.assertListEqual(names, job_names) @patch("ops.testing._TestingModelBackend.network_get") def test_provider_sanitizes_jobs(self, _): rel_id = self.harness.add_relation(RELATION_NAME, "provider") self.harness.add_relation_unit(rel_id, "provider/0") data = self.harness.get_relation_data(rel_id, self.harness.model.app.name) self.assertIn("scrape_jobs", data) jobs = json.loads(data["scrape_jobs"]) for job in jobs: keys = set(job.keys()) self.assertTrue(keys.issubset(ALLOWED_KEYS)) @patch("ops.testing._TestingModelBackend.network_get") def test_each_alert_rule_is_topology_labeled(self, _): rel_id = self.harness.add_relation(RELATION_NAME, "provider") self.harness.add_relation_unit(rel_id, "provider/0") data = self.harness.get_relation_data(rel_id, self.harness.model.app.name) self.assertIn("alert_rules", data) alerts = json.loads(data["alert_rules"]) self.assertIn("groups", alerts) self.assertEqual(len(alerts["groups"]), 1) group = alerts["groups"][0] for rule in group["rules"]: self.assertIn("labels", rule) labels = rule["labels"] self.assertIn("juju_model", labels) self.assertIn("juju_application", labels) self.assertIn("juju_model_uuid", labels) @patch("ops.testing._TestingModelBackend.network_get") def test_each_alert_expression_is_topology_labeled(self, _): rel_id = self.harness.add_relation(RELATION_NAME, "provider") self.harness.add_relation_unit(rel_id, "provider/0") data = self.harness.get_relation_data(rel_id, self.harness.model.app.name) self.assertIn("alert_rules", data) alerts = json.loads(data["alert_rules"]) self.assertIn("groups", alerts) self.assertEqual(len(alerts["groups"]), 1) group = alerts["groups"][0] for rule in group["rules"]: self.assertIn("expr", rule) for labels in expression_labels(rule["expr"]): self.assertIn("juju_model", labels) self.assertIn("juju_model_uuid", labels) self.assertIn("juju_application", labels)
class TestCharm(unittest.TestCase): def setUp(self): self.harness = Harness(MongoDBCharm) self.addCleanup(self.harness.cleanup) mongo_resource = { "registrypath": "mongodb:4.4.1", "username": "******", "password": "******" } self.harness.add_oci_resource("mongodb-image", mongo_resource) self.harness.begin() self.peer_rel_id = self.harness.add_relation('mongodb', 'mongodb') @patch('ops.testing._TestingPebbleClient.pull') def test_replica_set_name_can_be_changed(self, _): self.harness.set_leader(True) self.harness.container_pebble_ready("mongodb") # check default replica set name plan = self.harness.get_container_pebble_plan("mongodb") self.assertEqual(replica_set_name(plan), "rs0") # check replica set name can be changed self.harness.update_config({"replica_set_name": "new_name"}) plan = self.harness.get_container_pebble_plan("mongodb") self.assertEqual(replica_set_name(plan), "new_name") @patch("mongoserver.MongoDB.reconfigure_replica_set") def test_replica_set_is_reconfigured_when_peer_joins(self, mock_reconf): self.harness.set_leader(True) self.harness.add_relation_unit(self.peer_rel_id, 'mongodb/1') self.harness.update_relation_data(self.peer_rel_id, 'mongodb/1', {'private-address': '10.0.0.1'}) peers = [ 'mongodb-k8s-0.mongodb-k8s-endpoints', 'mongodb-k8s-1.mongodb-k8s-endpoints' ] mock_reconf.assert_called_once_with(peers) def test_replica_set_uri_data_is_generated_correctly(self): self.harness.set_leader(True) replica_set_uri = self.harness.charm.mongo.replica_set_uri() data = self.harness.get_relation_data(self.peer_rel_id, self.harness.model.app.name) cred = "root:{}".format(data['root_password']) self.assertEqual( replica_set_uri, 'mongodb://{}@mongodb-k8s-0.mongodb-k8s-endpoints:27017/admin'. format(cred)) def test_leader_sets_key_and_root_credentials(self): self.harness.set_leader(False) self.harness.set_leader(True) data = self.harness.get_relation_data(self.peer_rel_id, self.harness.model.app.name) self.assertIsNotNone(data['root_password']) self.assertIsNotNone(data['security_key']) @patch('mongoserver.MongoDB.version') def test_charm_provides_version(self, mock_version): self.harness.set_leader(True) mock_version.return_value = "4.4.1" version = self.harness.charm.mongo.version() self.assertEqual(version, "4.4.1") @patch('mongoserver.MongoDB.is_ready') def test_start_is_deferred_if_monog_is_not_ready(self, is_ready): is_ready.return_value = False self.harness.set_leader(True) with self.assertLogs(level="DEBUG") as logger: self.harness.charm.on.start.emit() is_ready.assert_called() for message in sorted(logger.output): if "DEBUG:ops.framework:Deferring" in message: self.assertIn("StartEvent", message) @patch('mongoserver.MongoDB.initialize_replica_set') @patch('mongoserver.MongoDB.is_ready') def test_start_is_deffered_if_monog_is_not_initialized( self, is_ready, initialize): is_ready.return_value = True initialize.side_effect = RuntimeError("Not Initialized") self.harness.set_leader(True) with self.assertLogs(level="DEBUG") as logger: self.harness.charm.on.start.emit() is_ready.assert_called() self.assertIn( "INFO:charm:Deferring on_start since : error=Not Initialized", sorted(logger.output))
class TestSourceConsumer(unittest.TestCase): def setUp(self): self.harness = Harness(ConsumerCharm, meta=CONSUMER_META) self.addCleanup(self.harness.cleanup) self.harness.set_leader(True) self.harness.begin() @patch("ops.testing._TestingModelBackend.network_get") def test_consumer_sets_scrape_data(self, _): rel_id = self.harness.add_relation("grafana-source", "consumer") self.harness.add_relation_unit(rel_id, "consumer/0") data = self.harness.get_relation_data(rel_id, self.harness.model.app.name) self.assertIn("grafana_source_data", data) scrape_data = data["grafana_source_data"] self.assertIn("model", scrape_data) self.assertIn("model_uuid", scrape_data) self.assertIn("application", scrape_data) @patch("ops.testing._TestingModelBackend.network_get") def test_consumer_unit_sets_bind_address_on_pebble_ready( self, mock_net_get): bind_address = "1.2.3.4" fake_network = { "bind-addresses": [{ "interface-name": "eth0", "addresses": [{ "hostname": "grafana-tester-0", "value": bind_address }], }] } mock_net_get.return_value = fake_network rel_id = self.harness.add_relation("grafana-source", "consumer") self.harness.container_pebble_ready("grafana-tester") self.harness.add_relation_unit(rel_id, "consumer/0") data = self.harness.get_relation_data(rel_id, self.harness.charm.unit.name) self.assertIn("grafana_source_host", data) self.assertEqual(data["grafana_source_host"], f"{bind_address}:9090") @patch("ops.testing._TestingModelBackend.network_get") def test_consumer_unit_sets_bind_address_on_relation_joined( self, mock_net_get): bind_address = "1.2.3.4" fake_network = { "bind-addresses": [{ "interface-name": "eth0", "addresses": [{ "hostname": "grafana-tester-0", "value": bind_address }], }] } mock_net_get.return_value = fake_network rel_id = self.harness.add_relation("grafana-source", "consumer") self.harness.add_relation_unit(rel_id, "consumer/0") data = self.harness.get_relation_data(rel_id, self.harness.charm.unit.name) self.assertIn("grafana_source_host", data) self.assertEqual(data["grafana_source_host"], f"{bind_address}:9090")
class TestCharm(unittest.TestCase): @patch("charm.KubernetesServicePatch", lambda x, y: None) @patch_network_get(private_address="1.1.1.1") def setUp(self, *unused): self.harness = Harness(PrometheusCharm) self.addCleanup(self.harness.cleanup) self.harness.begin_with_initial_hooks() @patch_network_get(private_address="1.1.1.1") def test_grafana_is_provided_port_and_source(self, *unused): rel_id = self.harness.add_relation("grafana-source", "grafana") self.harness.add_relation_unit(rel_id, "grafana/0") grafana_host = self.harness.get_relation_data( rel_id, self.harness.model.unit.name)["grafana_source_host"] self.assertEqual(grafana_host, "{}:{}".format("1.1.1.1", "9090")) def test_default_cli_log_level_is_info(self): plan = self.harness.get_container_pebble_plan("prometheus") self.assertEqual(cli_arg(plan, "--log.level"), "info") def test_invalid_log_level_defaults_to_debug(self): bad_log_config = {"log_level": "bad-level"} with self.assertLogs(level="ERROR") as logger: self.harness.update_config(bad_log_config) expected_logs = [ "ERROR:root:Invalid loglevel: bad-level given, " "debug/info/warn/error/fatal allowed. " "defaulting to DEBUG loglevel." ] self.assertEqual(sorted(logger.output), expected_logs) plan = self.harness.get_container_pebble_plan("prometheus") self.assertEqual(cli_arg(plan, "--log.level"), "debug") def test_valid_log_level_is_accepted(self): valid_log_config = {"log_level": "warn"} self.harness.update_config(valid_log_config) plan = self.harness.get_container_pebble_plan("prometheus") self.assertEqual(cli_arg(plan, "--log.level"), "warn") @patch_network_get(private_address="1.1.1.1") def test_ingress_relation_not_set(self): self.harness.set_leader(True) plan = self.harness.get_container_pebble_plan("prometheus") self.assertEqual(cli_arg(plan, "--web.external-url"), "http://1.1.1.1:9090") @patch_network_get(private_address="1.1.1.1") def test_ingress_relation_set(self): self.harness.set_leader(True) rel_id = self.harness.add_relation("ingress", "traefik-ingress") self.harness.add_relation_unit(rel_id, "traefik-ingress/0") plan = self.harness.get_container_pebble_plan("prometheus") self.assertEqual(cli_arg(plan, "--web.external-url"), "http://1.1.1.1:9090") @patch_network_get(private_address="1.1.1.1") def test_web_external_url_has_precedence_over_ingress_relation(self): self.harness.set_leader(True) self.harness.update_config({"web_external_url": "http://*****:*****@patch_network_get(private_address="1.1.1.1") def test_web_external_url_set(self): self.harness.set_leader(True) self.harness.update_config({"web_external_url": "http://*****:*****@patch("prometheus_server.Prometheus.reload_configuration") def test_configuration_reload(self, trigger_configuration_reload): self.harness.container_pebble_ready("prometheus") trigger_configuration_reload.assert_called() self.harness.update_config({"log_level": "INFO"}) trigger_configuration_reload.assert_called()
class TestCharm(unittest.TestCase): @patch.object(CassandraOperatorCharm, "_goal_units", new=lambda x: 1) @patch.object(CassandraOperatorCharm, "_bind_address", new=lambda x: "1.1.1.1") @patch.object(ops.model.Container, "pull", new=fake_pull) @patch.object(ops.model.Container, "push", new=fake_push) def setUp(self): self.harness = Harness(CassandraOperatorCharm) self.addCleanup(self.harness.cleanup) self.harness.begin_with_initial_hooks() self.harness.set_leader(True) def tearDown(self): global FILES FILES = {} def test_relation_is_set(self): rel_id = self.harness.add_relation("database", "otherapp") self.assertIsInstance(rel_id, int) self.harness.add_relation_unit(rel_id, "otherapp/0") self.harness.update_relation_data(rel_id, "otherapp", {}) self.assertEqual( self.harness.get_relation_data(rel_id, self.harness.model.app.name)["port"], "9042", ) self.assertEqual( self.harness.get_relation_data(rel_id, self.harness.model.app.name)["address"], "cassandra-k8s-0.cassandra-k8s-endpoints.None.svc.cluster.local", ) def test_root_password_is_set(self): rel = self.harness.charm.model.get_relation("cassandra-peers") self.assertEqual(rel.data[self.harness.charm.app].get("root_password", None), None) self.assertEqual(bool(self.harness.charm.cassandra.root_password(None)), True) def test_config_file_is_set(self): self.harness.container_pebble_ready("cassandra") sample_content = yaml.safe_load(SAMPLE_CONFIG) content_str = ( self.harness.charm.unit.get_container("cassandra") .pull("/etc/cassandra/cassandra.yaml") .read() ) content = yaml.safe_load(content_str) assert content == sample_content @patch("ops.testing._TestingModelBackend.network_get") @patch("ops.testing._TestingPebbleClient.list_files") def test_prometheus_data_set(self, mock_net_get, mock_list_files): bind_address = "1.1.1.1" fake_network = { "bind-addresses": [ { "interface-name": "eth0", "addresses": [{"hostname": "cassandra-tester-0", "value": bind_address}], } ] } mock_net_get.return_value = fake_network rel_id = self.harness.add_relation("monitoring", "otherapp") self.assertIsInstance(rel_id, int) self.harness.add_relation_unit(rel_id, "otherapp/0") self.harness.update_relation_data(rel_id, "otherapp", {}) self.assertEqual( json.loads( self.harness.get_relation_data(rel_id, self.harness.model.app.name)["scrape_jobs"] )[0]["static_configs"][0]["targets"], ["*:9500"], ) @patch("ops.testing._TestingModelBackend.network_get") @patch("ops.testing._TestingPebbleClient.list_files") def test_heap_size_default(self, mock_net_get, mock_list_files): cassandra_environment = self._start_cassandra_and_get_pebble_service().environment self.assertEqual(cassandra_environment["JVM_OPTS"], "-Xms6G -Xmx6G") self.assertEqual(self.harness.model.unit.status, ops.model.ActiveStatus()) @patch("ops.testing._TestingModelBackend.network_get") @patch("ops.testing._TestingPebbleClient.list_files") def test_heap_size_config_success(self, mock_net_get, mock_list_files): self.harness.update_config({"heap_size": "1g"}) cassandra_environment = self._start_cassandra_and_get_pebble_service().environment self.assertEqual(cassandra_environment["JVM_OPTS"], "-Xms1g -Xmx1g") self.assertEqual(self.harness.model.unit.status, ops.model.ActiveStatus()) @patch("ops.testing._TestingModelBackend.network_get") @patch("ops.testing._TestingPebbleClient.list_files") def test_heap_size_config_invalid(self, mock_net_get, mock_list_files): self.harness.update_config({"heap_size": "0.5g"}) self.assertEqual( self.harness.model.unit.status, ops.model.BlockedStatus("Invalid Cassandra heap size setting: '0.5g'"), ) def _start_cassandra_and_get_pebble_service(self): container = self.harness.model.unit.get_container("cassandra") self.harness.charm.on.cassandra_pebble_ready.emit(container) pebble_plan = self.harness.get_container_pebble_plan("cassandra") return pebble_plan.services["cassandra"]