def test_pod_spec_is_generated(self): # Exercise spec = domain.build_juju_pod_spec(app_name=self.mock_app_name, charm_config=self.mock_config, image_meta=self.mock_image_meta) # Assertions assert type(spec) == dict assert spec == { 'containers': [{ 'name': self.mock_app_name, 'imageDetails': { 'imagePath': self.mock_image_meta.image_path, 'username': self.mock_image_meta.repo_username, 'password': self.mock_image_meta.repo_password }, 'ports': [{ 'containerPort': self.mock_advertised_port, 'protocol': 'TCP' }], 'readinessProbe': { 'httpGet': { 'path': '/api/health', 'port': self.mock_advertised_port }, 'initialDelaySeconds': 10, 'timeoutSeconds': 30 } }] }
def set_juju_pod_spec(fw_adapter, alerting_config=None): # Mutable defaults bug as described in https://bit.ly/3cF0k0w if not alerting_config: alerting_config = dict() if not fw_adapter.unit_is_leader(): logging.debug("Unit is not a leader, skip pod spec configuration") # Although PodSpec will not be altered, the pod provisioning process # still have to continue return True if alerting_config: logger.debug("Got alerting config: {} {}".format( type(alerting_config), alerting_config)) logging.debug("Building Juju pod spec") try: juju_pod_spec = build_juju_pod_spec( app_name=fw_adapter.get_app_name(), charm_config=fw_adapter.get_config(), prom_image_meta=fw_adapter.get_image_meta('prometheus-image'), nginx_image_meta=fw_adapter.get_image_meta('nginx-image'), alerting_config=alerting_config) pod_spec = juju_pod_spec.to_dict() except CharmError as e: fw_adapter.set_unit_status( BlockedStatus("Pod spec build failure: {0}".format(e))) return False logging.debug("Configuring pod: set PodSpec to: {0}".format(pod_spec)) fw_adapter.set_pod_spec(pod_spec) fw_adapter.set_unit_status(MaintenanceStatus("Configuring pod")) return True
def test_pod_spec_with_prometheus_config_is_generated(self): # Exercise spec = domain.build_juju_pod_spec( app_name=self.mock_app_name, charm_config=self.mock_config, image_meta=self.mock_image_meta, prometheus_server_details=self.mock_prometheus_server_details) # Assertions assert type(spec) == dict prom_host = self.mock_prometheus_server_details.host prom_port = self.mock_prometheus_server_details.port assert spec == { 'containers': [{ 'name': self.mock_app_name, 'imageDetails': { 'imagePath': self.mock_image_meta.image_path, 'username': self.mock_image_meta.repo_username, 'password': self.mock_image_meta.repo_password }, 'ports': [{ 'containerPort': self.mock_advertised_port, 'protocol': 'TCP' }], 'readinessProbe': { 'httpGet': { 'path': '/api/health', 'port': self.mock_advertised_port }, 'initialDelaySeconds': 10, 'timeoutSeconds': 30 }, 'files': [{ 'name': 'prometheus-ds', 'mountPath': '/etc/grafana/provisioning/datasources', 'files': { 'prometheus.yaml': textwrap.dedent(f""" apiVersion: 1 datasources: - name: Prometheus type: prometheus access: proxy url: http://{prom_host}:{prom_port} isDefault: true editable: false """) } }] }] }
def on_start_handler(event, fw_adapter): if not fw_adapter.am_i_leader(): return juju_pod_spec = build_juju_pod_spec( app_name=fw_adapter.get_app_name(), charm_config=fw_adapter.get_config(), image_meta=fw_adapter.get_image_meta('grafana-image'), ) fw_adapter.set_pod_spec(juju_pod_spec) fw_adapter.set_unit_status(MaintenanceStatus("Configuring pod"))
def on_prom_available_handler(event, fw_adapter): if not fw_adapter.am_i_leader(): return juju_pod_spec = build_juju_pod_spec( app_name=fw_adapter.get_app_name(), charm_config=fw_adapter.get_config(), image_meta=fw_adapter.get_image_meta('grafana-image'), prometheus_server_details=event.server_details, ) fw_adapter.set_pod_spec(juju_pod_spec) fw_adapter.set_unit_status(MaintenanceStatus("Configuring pod"))
def test_pod_spec_is_generated(self): # Set up mock_app_name = f'{uuid4()}' mock_advertised_port = random.randint(1, 65535) mock_config = { 'advertised-port': mock_advertised_port, } mock_image_meta = ImageMeta({ 'registrypath': str(uuid4()), 'username': str(uuid4()), 'password': str(uuid4()), }) # Exercise spec = domain.build_juju_pod_spec(app_name=mock_app_name, charm_config=mock_config, image_meta=mock_image_meta) # Assertions assert type(spec) == dict assert spec == { 'containers': [{ 'name': mock_app_name, 'imageDetails': { 'imagePath': mock_image_meta.image_path, 'username': mock_image_meta.repo_username, 'password': mock_image_meta.repo_password }, 'ports': [{ 'containerPort': mock_advertised_port, 'protocol': 'TCP' }], 'readinessProbe': { 'httpGet': { 'path': '/api/health', 'port': mock_advertised_port }, 'initialDelaySeconds': 10, 'timeoutSeconds': 30 } }] }
def on_server_new_relation_handler(event, state, fw_adapter): log.debug("Got event {}".format(event)) if not fw_adapter.am_i_leader(): return mysql_details = \ interface_mysql.MySQLServerDetails.restore(state.mysql_server_details) prometheus_details = \ interface_http.ServerDetails.restore(state.prometheus_server_details) juju_pod_spec = build_juju_pod_spec( app_name=fw_adapter.get_app_name(), charm_config=fw_adapter.get_config(), image_meta=fw_adapter.get_image_meta('grafana-image'), mysql_server_details=mysql_details, prometheus_server_details=prometheus_details, ) log.info("Updating juju podspec with new backend details") fw_adapter.set_pod_spec(juju_pod_spec) fw_adapter.set_unit_status(MaintenanceStatus("Configuring pod"))
def set_juju_pod_spec(fw_adapter): if not fw_adapter.am_i_leader(): logging.debug("Unit is not a leader, skip pod spec configuration") return charm_config = fw_adapter.get_config() logging.debug("Building AlertManager config file") alertmanager_config = build_alertmanager_config( base64_config_yaml=charm_config["alertmanager-config"], base64_secrets_yaml=charm_config["alertmanager-secrets"]) logging.debug("Building Juju pod spec") juju_pod_spec = build_juju_pod_spec( app_name=fw_adapter.get_app_name(), charm_config=charm_config, image_meta=fw_adapter.get_image_meta('alertmanager-image'), alertmanager_config=alertmanager_config) logging.debug("Configuring pod") fw_adapter.set_pod_spec(juju_pod_spec.to_dict()) fw_adapter.set_unit_status(MaintenanceStatus("Configuring pod"))
def set_juju_pod_spec(fw_adapter, alerting_config={}): if not fw_adapter.unit_is_leader(): logging.debug("Unit is not a leader, skip pod spec configuration") return if alerting_config: logger.debug("Got alerting config: {} {}".format( type(alerting_config), alerting_config)) logging.debug("Building Juju pod spec") try: juju_pod_spec = build_juju_pod_spec( app_name=fw_adapter.get_app_name(), charm_config=fw_adapter.get_config(), image_meta=fw_adapter.get_image_meta('prometheus-image'), alerting_config=alerting_config) except CharmError as e: fw_adapter.set_unit_status( BlockedStatus("Pod spec build failure: {0}".format(e))) return logging.debug("Configuring pod") fw_adapter.set_pod_spec(juju_pod_spec.to_dict()) fw_adapter.set_unit_status(MaintenanceStatus("Configuring pod"))
def test_pod_spec_with_mysql_and_prometheus_config_is_generated(self): # Exercise spec = domain.build_juju_pod_spec( app_name=self.mock_app_name, charm_config=self.mock_config, image_meta=self.mock_image_meta, prometheus_server_details=self.mock_prometheus_server_details, mysql_server_details=self.mock_mysql_server_details) # Assertions assert type(spec) == dict prom_host = self.mock_prometheus_server_details.host prom_port = self.mock_prometheus_server_details.port assert spec == { 'containers': [{ 'name': self.mock_app_name, 'imageDetails': { 'imagePath': self.mock_image_meta.image_path, 'username': self.mock_image_meta.repo_username, 'password': self.mock_image_meta.repo_password }, 'ports': [{ 'containerPort': self.mock_advertised_port, 'protocol': 'TCP' }], 'readinessProbe': { 'httpGet': { 'path': '/api/health', 'port': self.mock_advertised_port }, 'initialDelaySeconds': 10, 'timeoutSeconds': 30 }, 'files': [{ 'name': 'prometheus-ds', 'mountPath': '/etc/grafana/provisioning/datasources', 'files': { 'prometheus.yaml': textwrap.dedent(f""" apiVersion: 1 datasources: - name: Prometheus type: prometheus access: proxy url: http://{prom_host}:{prom_port} isDefault: true editable: false """) } }, { 'name': 'mysql-db-config', 'mountPath': '/etc/grafana', 'files': { 'grafana.ini': textwrap.dedent(f""" [database] type = mysql host = {self.mock_mysql_server_details.address} name = {self.mock_mysql_server_details.database} user = {self.mock_mysql_server_details.username} password = {self.mock_mysql_server_details.password} ;ca_cert_path = ;client_key_path = ;client_cert_path = ;server_cert_name = # Max idle conn setting default is 2 ;max_idle_conn = 2 # Max conn setting default is 0 (mean not set) ;max_open_conn = # Connection Max Lifetime default is 14400 # (means 14400 seconds or 4 hours) ;conn_max_lifetime = 14400 # Set to true to log the sql calls and execution times. ;log_queries = """) } }] }] }
def test__pod_spec_is_generated(self): # Set up mock_app_name = str(uuid4()) mock_external_labels = { str(uuid4()): str(uuid4()), str(uuid4()): str(uuid4()), str(uuid4()): str(uuid4()), } mock_config = { 'external-labels': json.dumps(mock_external_labels), 'monitor-k8s': False } mock_image_meta = ImageMeta({ 'registrypath': str(uuid4()), 'username': str(uuid4()), 'password': str(uuid4()), }) am_config = domain.AlertManagerConfigFile({str(uuid4()): str(uuid4())}) # Exercise juju_pod_spec = domain.build_juju_pod_spec( app_name=mock_app_name, charm_config=mock_config, image_meta=mock_image_meta, alertmanager_config=am_config) # Assertions assert isinstance(juju_pod_spec, domain.AlertManagerJujuPodSpec) assert juju_pod_spec.to_dict() == { 'containers': [{ 'name': mock_app_name, 'imageDetails': { 'imagePath': mock_image_meta.image_path, 'username': mock_image_meta.repo_username, 'password': mock_image_meta.repo_password }, 'args': [ '--config.file=/etc/alertmanager/alertmanager.yml', '--storage.path=/alertmanager', '--cluster.listen-address=0.0.0.0:9094', '--cluster.peer=alertmanager-0.alertmanager-endpoints:9094' ], 'ports': [{ 'name': 'web', 'containerPort': 9093, 'protocol': 'TCP' }, { 'name': 'peering-tcp', 'containerPort': 9094, 'protocol': 'TCP' }, { 'name': 'peering-udp', 'containerPort': 9094, 'protocol': 'UDP' }], 'readinessProbe': { 'httpGet': { 'path': '/-/ready', 'port': 9093 }, 'initialDelaySeconds': 10, 'timeoutSeconds': 30 }, 'livenessProbe': { 'httpGet': { 'path': '/-/healthy', 'port': 9093 }, 'initialDelaySeconds': 30, 'timeoutSeconds': 30 }, 'files': [{ 'name': 'config', 'mountPath': '/etc/alertmanager', 'files': { 'alertmanager.yml': am_config.yaml_dump() } }] }] }
def test__pod_spec_is_generated(): # Set up mock_app_name = str(uuid4()) mock_external_labels = { str(uuid4()): str(uuid4()), str(uuid4()): str(uuid4()), str(uuid4()): str(uuid4()), } mock_config = get_default_charm_config() mock_config['external-labels'] = json.dumps(mock_external_labels) mock_image_meta = ImageMeta({ 'registrypath': str(uuid4()), 'username': str(uuid4()), 'password': str(uuid4()), }) mock_args_config = domain.build_prometheus_cli_args(mock_config) # Exercise juju_pod_spec = domain.build_juju_pod_spec(app_name=mock_app_name, charm_config=mock_config, image_meta=mock_image_meta) # Assertions assert isinstance(juju_pod_spec, domain.PrometheusJujuPodSpec) assert juju_pod_spec.to_dict() == { 'containers': [{ 'name': mock_app_name, 'imageDetails': { 'imagePath': mock_image_meta.image_path, 'username': mock_image_meta.repo_username, 'password': mock_image_meta.repo_password }, 'args': mock_args_config, 'ports': [{ 'containerPort': 9090, 'protocol': 'TCP' }], 'readinessProbe': { 'httpGet': { 'path': '/-/ready', 'port': 9090 }, 'initialDelaySeconds': 10, 'timeoutSeconds': 30 }, 'livenessProbe': { 'httpGet': { 'path': '/-/healthy', 'port': 9090 }, 'initialDelaySeconds': 30, 'timeoutSeconds': 30 }, 'files': [{ 'name': 'config', 'mountPath': '/etc/prometheus', 'files': { 'prometheus.yml': yaml.dump({ 'global': { 'scrape_interval': '15s', 'scrape_timeout': '10s', 'evaluation_interval': '1m', 'external_labels': mock_external_labels }, 'scrape_configs': [{ 'job_name': 'prometheus', 'scrape_interval': '5s', 'static_configs': [{ 'targets': ['localhost:9090'] }] }], 'alerting': {} }) } }] }] }
def test__pod_spec_is_generated(self): # Set up mock_app_name = str(uuid4()) mock_external_labels = { str(uuid4()): str(uuid4()), str(uuid4()): str(uuid4()), str(uuid4()): str(uuid4()), } mock_config = get_default_charm_config() mock_config['external-labels'] = json.dumps(mock_external_labels) mock_image_meta = ImageMeta({ 'registrypath': str(uuid4()), 'username': str(uuid4()), 'password': str(uuid4()), }) mock_args_config = domain.build_prometheus_cli_args(mock_config) # Exercise juju_pod_spec = domain.build_juju_pod_spec( app_name=mock_app_name, charm_config=mock_config, prom_image_meta=mock_image_meta, nginx_image_meta=mock_image_meta) expected_nginx_config = textwrap.dedent("""\ server { listen 80; server_name _; access_log /var/log/nginx/prometheus-http.access.log main; error_log /var/log/nginx/prometheus-http.error.log; location / { proxy_pass http://localhost:9090; } }""") # Assertions assert isinstance(juju_pod_spec, domain.PrometheusJujuPodSpec) self.assertEqual( juju_pod_spec.to_dict(), { 'containers': [{ 'name': mock_app_name, 'imageDetails': { 'imagePath': mock_image_meta.image_path, 'username': mock_image_meta.repo_username, 'password': mock_image_meta.repo_password }, 'args': mock_args_config, 'readinessProbe': { 'httpGet': { 'path': '/-/ready', 'port': 9090 }, 'initialDelaySeconds': 10, 'timeoutSeconds': 30 }, 'livenessProbe': { 'httpGet': { 'path': '/-/healthy', 'port': 9090 }, 'initialDelaySeconds': 30, 'timeoutSeconds': 30 }, 'files': [{ 'name': 'prom-config', 'mountPath': '/etc/prometheus', 'files': { 'prometheus.yml': yaml.dump({ 'global': { 'scrape_interval': '15s', 'scrape_timeout': '10s', 'evaluation_interval': '1m', 'external_labels': mock_external_labels }, 'scrape_configs': [{ 'metrics_path': '/metrics', 'honor_timestamps': True, 'scheme': 'http', 'job_name': 'prometheus', 'scrape_interval': '5s', 'scrape_timeout': '5s', 'static_configs': [{ 'targets': ['localhost:9090'] }] }], 'alerting': {} }) } }] }, { 'name': '{0}-nginx'.format(mock_app_name), 'imageDetails': { 'imagePath': mock_image_meta.image_path, 'username': mock_image_meta.repo_username, 'password': mock_image_meta.repo_password }, 'ports': [{ 'containerPort': 80, 'name': 'nginx-http', 'protocol': 'TCP' }, { 'containerPort': 443, 'name': 'nginx-https', 'protocol': 'TCP' }], 'files': [{ 'name': 'nginx-config', 'mountPath': '/etc/nginx/conf.d', 'files': { 'default.conf': expected_nginx_config } }] }] })