class TestEndpointAggregator(unittest.TestCase): def setUp(self): self.harness = Harness(EndpointAggregatorCharm, meta=AGGREGATOR_META) self.harness.set_model_info(name="testmodel", uuid="1234567890") self.addCleanup(self.harness.cleanup) self.harness.set_leader(True) self.harness.begin_with_initial_hooks() def test_adding_prometheus_then_target_forwards_a_labeled_scrape_job(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") target_rel_id = self.harness.add_relation(SCRAPE_TARGET_RELATION, "target-app") self.harness.add_relation_unit(target_rel_id, "target-app/0") hostname = "scrape_target_0" port = "1234" self.harness.update_relation_data( target_rel_id, "target-app/0", { "hostname": f"{hostname}", "port": f"{port}", }, ) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) scrape_jobs = json.loads(prometheus_rel_data.get("scrape_jobs", "[]")) expected_jobs = [{ "job_name": "juju_testmodel_1234567_target-app_prometheus_scrape", "static_configs": [{ "targets": ["scrape_target_0:1234"], "labels": { "juju_model": "testmodel", "juju_model_uuid": "1234567890", "juju_application": "target-app", "juju_unit": "target-app/0", "host": "scrape_target_0", }, }], "relabel_configs": [RELABEL_INSTANCE_CONFIG], }] self.assertListEqual(scrape_jobs, expected_jobs) def test_adding_prometheus_then_target_forwards_a_labeled_alert_rule(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") alert_rules_rel_id = self.harness.add_relation(ALERT_RULES_RELATION, "rules-app") self.harness.add_relation_unit(alert_rules_rel_id, "rules-app/0") self.harness.update_relation_data(alert_rules_rel_id, "rules-app/0", {"groups": ALERT_RULE_1}) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) alert_rules = json.loads(prometheus_rel_data.get("alert_rules", "{}")) groups = alert_rules.get("groups", []) self.assertEqual(len(groups), 1) group = groups[0] expected_group = { "name": "juju_testmodel_1234567_rules-app_alert_rules", "rules": [{ "alert": "CPU_Usage", "expr": 'cpu_usage_idle{is_container!="True", group="promoagents-juju"} < 10', "for": "5m", "labels": { "override_group_by": "host", "severity": "page", "cloud": "juju", "juju_model": "testmodel", "juju_model_uuid": "1234567", "juju_application": "rules-app", "juju_unit": "rules-app/0", }, "annotations": { "description": "Host {{ $labels.host }} has had < 10% idle cpu for the last 5m\n", "summary": "Host {{ $labels.host }} CPU free is less than 10%", }, }], } self.assertDictEqual(group, expected_group) def test_adding_target_then_prometheus_forwards_a_labeled_scrape_job(self): target_rel_id = self.harness.add_relation(SCRAPE_TARGET_RELATION, "target-app") self.harness.add_relation_unit(target_rel_id, "target-app/0") hostname = "scrape_target_0" port = "1234" self.harness.update_relation_data( target_rel_id, "target-app/0", { "hostname": f"{hostname}", "port": f"{port}", }, ) prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) scrape_jobs = json.loads(prometheus_rel_data.get("scrape_jobs", "[]")) expected_jobs = [{ "job_name": "juju_testmodel_1234567_target-app_prometheus_scrape", "static_configs": [{ "targets": ["scrape_target_0:1234"], "labels": { "juju_model": "testmodel", "juju_model_uuid": "1234567890", "juju_application": "target-app", "juju_unit": "target-app/0", "host": "scrape_target_0", }, }], "relabel_configs": [RELABEL_INSTANCE_CONFIG], }] self.assertListEqual(scrape_jobs, expected_jobs) def test_adding_target_then_prometheus_forwards_a_labeled_alert_rule(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") alert_rules_rel_id = self.harness.add_relation(ALERT_RULES_RELATION, "rules-app") self.harness.add_relation_unit(alert_rules_rel_id, "rules-app/0") self.harness.update_relation_data(alert_rules_rel_id, "rules-app/0", {"groups": ALERT_RULE_1}) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) alert_rules = json.loads(prometheus_rel_data.get("alert_rules", "{}")) groups = alert_rules.get("groups", []) self.assertEqual(len(groups), 1) group = groups[0] expected_group = { "name": "juju_testmodel_1234567_rules-app_alert_rules", "rules": [{ "alert": "CPU_Usage", "expr": 'cpu_usage_idle{is_container!="True", group="promoagents-juju"} < 10', "for": "5m", "labels": { "override_group_by": "host", "severity": "page", "cloud": "juju", "juju_model": "testmodel", "juju_model_uuid": "1234567", "juju_application": "rules-app", "juju_unit": "rules-app/0", }, "annotations": { "description": "Host {{ $labels.host }} has had < 10% idle cpu for the last 5m\n", "summary": "Host {{ $labels.host }} CPU free is less than 10%", }, }], } self.assertDictEqual(group, expected_group) def test_scrape_jobs_from_multiple_target_applications_are_forwarded(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") target_rel_id_1 = self.harness.add_relation(SCRAPE_TARGET_RELATION, "target-app-1") self.harness.add_relation_unit(target_rel_id_1, "target-app-1/0") self.harness.update_relation_data( target_rel_id_1, "target-app-1/0", { "hostname": "scrape_target_0", "port": "1234", }, ) target_rel_id_2 = self.harness.add_relation(SCRAPE_TARGET_RELATION, "target-app-2") self.harness.add_relation_unit(target_rel_id_2, "target-app-2/0") self.harness.update_relation_data( target_rel_id_2, "target-app-2/0", { "hostname": "scrape_target_1", "port": "5678", }, ) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) scrape_jobs = json.loads(prometheus_rel_data.get("scrape_jobs", "[]")) self.assertEqual(len(scrape_jobs), 2) expected_jobs = [ { "job_name": "juju_testmodel_1234567_target-app-1_prometheus_scrape", "static_configs": [{ "targets": ["scrape_target_0:1234"], "labels": { "juju_model": "testmodel", "juju_model_uuid": "1234567890", "juju_application": "target-app-1", "juju_unit": "target-app-1/0", "host": "scrape_target_0", }, }], "relabel_configs": [RELABEL_INSTANCE_CONFIG], }, { "job_name": "juju_testmodel_1234567_target-app-2_prometheus_scrape", "static_configs": [{ "targets": ["scrape_target_1:5678"], "labels": { "juju_model": "testmodel", "juju_model_uuid": "1234567890", "juju_application": "target-app-2", "juju_unit": "target-app-2/0", "host": "scrape_target_1", }, }], "relabel_configs": [RELABEL_INSTANCE_CONFIG], }, ] self.assertListEqual(scrape_jobs, expected_jobs) def test_alert_rules_from_multiple_target_applications_are_forwarded(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") alert_rules_rel_id_1 = self.harness.add_relation( ALERT_RULES_RELATION, "rules-app-1") self.harness.add_relation_unit(alert_rules_rel_id_1, "rules-app-1/0") self.harness.update_relation_data( alert_rules_rel_id_1, "rules-app-1/0", {"groups": ALERT_RULE_1}, ) alert_rules_rel_id_2 = self.harness.add_relation( ALERT_RULES_RELATION, "rules-app-2") self.harness.add_relation_unit(alert_rules_rel_id_2, "rules-app-2/0") self.harness.update_relation_data( alert_rules_rel_id_2, "rules-app-2/0", {"groups": ALERT_RULE_2}, ) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) alert_rules = json.loads(prometheus_rel_data.get("alert_rules", "{}")) groups = alert_rules.get("groups", []) self.assertEqual(len(groups), 2) expected_groups = [ { "name": "juju_testmodel_1234567_rules-app-1_alert_rules", "rules": [{ "alert": "CPU_Usage", "expr": 'cpu_usage_idle{is_container!="True", group="promoagents-juju"} < 10', "for": "5m", "labels": { "override_group_by": "host", "severity": "page", "cloud": "juju", "juju_model": "testmodel", "juju_model_uuid": "1234567", "juju_application": "rules-app-1", "juju_unit": "rules-app-1/0", }, "annotations": { "description": "Host {{ $labels.host }} has had < 10% idle cpu for the last 5m\n", "summary": "Host {{ $labels.host }} CPU free is less than 10%", }, }], }, { "name": "juju_testmodel_1234567_rules-app-2_alert_rules", "rules": [{ "alert": "DiskFull", "expr": 'disk_free{is_container!="True", fstype!~".*tmpfs|squashfs|overlay"} <1024', "for": "5m", "labels": { "override_group_by": "host", "severity": "page", "juju_model": "testmodel", "juju_model_uuid": "1234567", "juju_application": "rules-app-2", "juju_unit": "rules-app-2/0", }, "annotations": { "description": "Host {{ $labels.host}} {{ $labels.path }} is full\nsummary: Host {{ $labels.host }} {{ $labels.path}} is full\n" }, }], }, ] self.assertListEqual(groups, expected_groups) def test_scrape_job_removal_differentiates_between_applications(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") target_rel_id_1 = self.harness.add_relation("prometheus-target", "target-app-1") self.harness.add_relation_unit(target_rel_id_1, "target-app-1/0") self.harness.update_relation_data( target_rel_id_1, "target-app-1/0", { "hostname": "scrape_target_0", "port": "1234", }, ) target_rel_id_2 = self.harness.add_relation("prometheus-target", "target-app-2") self.harness.add_relation_unit(target_rel_id_2, "target-app-2/0") self.harness.update_relation_data( target_rel_id_2, "target-app-2/0", { "hostname": "scrape_target_1", "port": "5678", }, ) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) scrape_jobs = json.loads(prometheus_rel_data.get("scrape_jobs", "[]")) self.assertEqual(len(scrape_jobs), 2) self.harness.remove_relation_unit(target_rel_id_2, "target-app-2/0") scrape_jobs = json.loads(prometheus_rel_data.get("scrape_jobs", "[]")) self.assertEqual(len(scrape_jobs), 1) expected_jobs = [{ "job_name": "juju_testmodel_1234567_target-app-1_prometheus_scrape", "static_configs": [{ "targets": ["scrape_target_0:1234"], "labels": { "juju_model": "testmodel", "juju_model_uuid": "1234567890", "juju_application": "target-app-1", "juju_unit": "target-app-1/0", "host": "scrape_target_0", }, }], "relabel_configs": [RELABEL_INSTANCE_CONFIG], }] self.assertListEqual(scrape_jobs, expected_jobs) def test_alert_rules_removal_differentiates_between_applications(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") alert_rules_rel_id_1 = self.harness.add_relation( "prometheus-rules", "rules-app-1") self.harness.add_relation_unit(alert_rules_rel_id_1, "rules-app-1/0") self.harness.update_relation_data( alert_rules_rel_id_1, "rules-app-1/0", {"groups": ALERT_RULE_1}, ) alert_rules_rel_id_2 = self.harness.add_relation( "prometheus-rules", "rules-app-2") self.harness.add_relation_unit(alert_rules_rel_id_2, "rules-app-2/0") self.harness.update_relation_data( alert_rules_rel_id_2, "rules-app-2/0", {"groups": ALERT_RULE_2}, ) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) alert_rules = json.loads(prometheus_rel_data.get("alert_rules", "{}")) groups = alert_rules.get("groups", []) self.assertEqual(len(groups), 2) self.harness.remove_relation_unit(alert_rules_rel_id_2, "rules-app-2/0") alert_rules = json.loads(prometheus_rel_data.get("alert_rules", "{}")) groups = alert_rules.get("groups", []) self.assertEqual(len(groups), 1) expected_groups = [ { "name": "juju_testmodel_1234567_rules-app-1_alert_rules", "rules": [{ "alert": "CPU_Usage", "expr": 'cpu_usage_idle{is_container!="True", group="promoagents-juju"} < 10', "for": "5m", "labels": { "override_group_by": "host", "severity": "page", "cloud": "juju", "juju_model": "testmodel", "juju_model_uuid": "1234567", "juju_application": "rules-app-1", "juju_unit": "rules-app-1/0", }, "annotations": { "description": "Host {{ $labels.host }} has had < 10% idle cpu for the last 5m\n", "summary": "Host {{ $labels.host }} CPU free is less than 10%", }, }], }, ] self.assertListEqual(groups, expected_groups) def test_removing_scrape_jobs_differentiates_between_units(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") target_rel_id = self.harness.add_relation("prometheus-target", "target-app") self.harness.add_relation_unit(target_rel_id, "target-app/0") self.harness.update_relation_data( target_rel_id, "target-app/0", { "hostname": "scrape_target_0", "port": "1234", }, ) self.harness.add_relation_unit(target_rel_id, "target-app/1") self.harness.update_relation_data( target_rel_id, "target-app/1", { "hostname": "scrape_target_1", "port": "5678", }, ) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) scrape_jobs = json.loads(prometheus_rel_data.get("scrape_jobs", "[]")) self.assertEqual(len(scrape_jobs), 1) self.assertEqual(len(scrape_jobs[0].get("static_configs")), 2) self.harness.remove_relation_unit(target_rel_id, "target-app/1") scrape_jobs = json.loads(prometheus_rel_data.get("scrape_jobs", "[]")) self.assertEqual(len(scrape_jobs), 1) self.assertEqual(len(scrape_jobs[0].get("static_configs")), 1) expected_jobs = [{ "job_name": "juju_testmodel_1234567_target-app_prometheus_scrape", "static_configs": [{ "targets": ["scrape_target_0:1234"], "labels": { "juju_model": "testmodel", "juju_model_uuid": "1234567890", "juju_application": "target-app", "juju_unit": "target-app/0", "host": "scrape_target_0", }, }], "relabel_configs": [RELABEL_INSTANCE_CONFIG], }] self.assertListEqual(scrape_jobs, expected_jobs) def test_removing_alert_rules_differentiates_between_units(self): prometheus_rel_id = self.harness.add_relation(PROMETHEUS_RELATION, "prometheus") self.harness.add_relation_unit(prometheus_rel_id, "prometheus/0") alert_rules_rel_id = self.harness.add_relation("prometheus-rules", "rules-app") self.harness.add_relation_unit(alert_rules_rel_id, "rules-app/0") self.harness.update_relation_data( alert_rules_rel_id, "rules-app/0", {"groups": ALERT_RULE_1}, ) self.harness.add_relation_unit(alert_rules_rel_id, "rules-app/1") self.harness.update_relation_data( alert_rules_rel_id, "rules-app/1", {"groups": ALERT_RULE_2}, ) prometheus_rel_data = self.harness.get_relation_data( prometheus_rel_id, self.harness.model.app.name) alert_rules = json.loads(prometheus_rel_data.get("alert_rules", "{}")) groups = alert_rules.get("groups", []) self.assertEqual(len(groups), 1) self.harness.remove_relation_unit(alert_rules_rel_id, "rules-app/1") alert_rules = json.loads(prometheus_rel_data.get("alert_rules", "{}")) groups = alert_rules.get("groups", []) self.assertEqual(len(groups), 1) expected_groups = [ { "name": "juju_testmodel_1234567_rules-app_alert_rules", "rules": [{ "alert": "CPU_Usage", "expr": 'cpu_usage_idle{is_container!="True", group="promoagents-juju"} < 10', "for": "5m", "labels": { "override_group_by": "host", "severity": "page", "cloud": "juju", "juju_model": "testmodel", "juju_model_uuid": "1234567", "juju_application": "rules-app", "juju_unit": "rules-app/0", }, "annotations": { "description": "Host {{ $labels.host }} has had < 10% idle cpu for the last 5m\n", "summary": "Host {{ $labels.host }} CPU free is less than 10%", }, }], }, ] self.assertListEqual(groups, expected_groups)
class TestSharedDBRelation(unittest.TestCase): def setUp(self): self.harness = Harness(MySQLOperatorCharm) self.addCleanup(self.harness.cleanup) self.harness.begin() self.peer_relation_id = self.harness.add_relation( "database-peers", "database-peers") self.harness.add_relation_unit(self.peer_relation_id, "mysql/1") self.shared_db_relation_id = self.harness.add_relation( LEGACY_DB_SHARED, "other-app") self.harness.add_relation_unit(self.shared_db_relation_id, "other-app/0") self.charm = self.harness.charm @patch_network_get(private_address="1.1.1.1") @patch("mysqlsh_helpers.MySQL.get_cluster_primary_address", return_value="1.1.1.1") @patch("relations.shared_db.generate_random_password", return_value="super_secure_password") @patch("mysqlsh_helpers.MySQL.create_application_database_and_scoped_user") def test_shared_db_relation_changed( self, _create_application_database_and_scoped_user, _generate_random_password, _get_cluster_primary_address, ): # run start-up events to enable usage of the helper class self.harness.set_leader(True) self.charm.on.config_changed.emit() # confirm that the relation databag is empty shared_db_relation_databag = self.harness.get_relation_data( self.shared_db_relation_id, self.harness.charm.app) shared_db_relation = self.charm.model.get_relation(LEGACY_DB_SHARED) app_unit = list(shared_db_relation.units)[0] self.assertEqual(shared_db_relation_databag, {}) self.assertEqual(shared_db_relation.data.get(app_unit), {}) self.assertEqual(shared_db_relation.data.get(self.charm.unit), {}) # update the app leader unit data to trigger shared_db_relation_changed event self.harness.update_relation_data( self.shared_db_relation_id, "other-app/0", { "database": "shared_database", "hostname": "1.1.1.2", "username": "******", "private-address": "1.1.1.3", }, ) # 2 calls during start-up events, and 1 calls during the shared_db_relation_changed event self.assertEqual(_generate_random_password.call_count, 1) _create_application_database_and_scoped_user.assert_called_once_with( "shared_database", "shared_user", "super_secure_password", "1.1.1.3", "other-app/0") # confirm that the relation databag is populated self.assertEqual( shared_db_relation.data.get(self.charm.unit), { "db_host": "1.1.1.1", "db_port": "3306", "wait_timeout": "3600", "password": "******", "allowed_units": "other-app/0", }, ) @patch_network_get(private_address="1.1.1.1") @patch("utils.generate_random_password", return_value="super_secure_password") @patch("mysqlsh_helpers.MySQL.create_application_database_and_scoped_user") def test_shared_db_relation_changed_error_on_user_creation( self, _create_application_database_and_scoped_user, _generate_random_password): # run start-up events to enable usage of the helper class self.harness.set_leader(True) self.charm.on.config_changed.emit() _create_application_database_and_scoped_user.side_effect = ( MySQLCreateApplicationDatabaseAndScopedUserError( "Can't create user")) # update the app leader unit data to trigger shared_db_relation_changed event self.harness.update_relation_data( self.shared_db_relation_id, "other-app/0", { "database": "shared_database", "hostname": "1.1.1.2", "username": "******", }, ) self.assertTrue( isinstance(self.harness.model.unit.status, BlockedStatus)) @patch_network_get(private_address="1.1.1.1") @patch("mysqlsh_helpers.MySQL.get_cluster_primary_address", return_value="1.1.1.1:3306") @patch("mysqlsh_helpers.MySQL.delete_users_for_unit") @patch("relations.shared_db.generate_random_password", return_value="super_secure_password") @patch("mysqlsh_helpers.MySQL.create_application_database_and_scoped_user") def test_shared_db_relation_departed( self, _create_application_database_and_scoped_user, _generate_random_password, _delete_users_for_unit, _get_cluster_primary_address, ): # run start-up events to enable usage of the helper class self.harness.set_leader(True) self.charm.on.config_changed.emit() # update the app leader unit data to trigger shared_db_relation_changed event self.harness.update_relation_data( self.shared_db_relation_id, "other-app/0", { "database": "shared_database", "hostname": "1.1.1.2", "username": "******", "private-address": "1.1.1.2", }, ) # Workaround ops.testing not setting `departing_unit` in v1.5.0 # ref https://github.com/canonical/operator/pull/790 shared_db_relation = self.charm.model.get_relation(LEGACY_DB_SHARED) with patch.object(RelationDepartedEvent, "departing_unit", new_callable=PropertyMock) as mock_departing_unit: mock_departing_unit.return_value = list( shared_db_relation.units)[0] self.harness.remove_relation_unit(self.shared_db_relation_id, "other-app/0") _delete_users_for_unit.assert_called_once_with("other-app/0")