]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: Add GrafanaSpec.initial_admin_password
authorSebastian Wagner <sewagner@redhat.com>
Fri, 16 Jul 2021 14:20:32 +0000 (16:20 +0200)
committerSebastian Wagner <sewagner@redhat.com>
Mon, 3 Jan 2022 14:00:09 +0000 (15:00 +0100)
By default, we're not creating any admin accout for Grafana now,
but we're adding an option to set the grafana password manually using:

```yaml
service_type: grafana
spec:
  initial_admin_password: mypassword
```

Users can then easily log into Grafana with the given password.

Fixes: https://tracker.ceph.com/issues/48291
Signed-off-by: Sebastian Wagner <sewagner@redhat.com>
(cherry picked from commit fdae665a2fe645d3f76b212f440ffe2c96281bc0)

Conflicts:
src/pybind/mgr/cephadm/tests/test_services.py

doc/cephadm/services/monitoring.rst
src/pybind/mgr/cephadm/services/monitoring.py
src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2
src/pybind/mgr/cephadm/tests/test_services.py
src/python-common/ceph/deployment/service_spec.py
src/python-common/ceph/tests/test_service_spec.py

index 91b8742f3cfb9fa3653424e2bd6f4834ca79bed4..5cb1537dbb4c3d9c453c8ccf8da5cad57a5774d2 100644 (file)
@@ -161,6 +161,8 @@ For example, if you had changed the prometheus image
 
           ceph config rm mgr mgr/cephadm/container_image_prometheus
 
+.. _cephadm-overwrite-jinja2-templates:
+
 Using custom configuration files
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -180,7 +182,7 @@ preserved and automatically applied on future deployments of these services.
 
   The configuration of the custom template is also preserved when the default
   configuration of cephadm changes. If the updated configuration is to be used,
-  the custom template needs to be migrated *manually*.
+  the custom template needs to be migrated *manually* after each upgrade of Ceph.
 
 Option names
 """"""""""""
@@ -338,6 +340,30 @@ update its configuration:
 
 The ``reconfig`` command also sets the proper URL for Ceph Dashboard.
 
+Setting the initial admin password
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default, Grafana will not create an initial
+admin user. In order to create the admin user, please create a file
+``grafana.yaml`` with this content:
+
+.. code-block:: yaml
+
+  service_type: grafana
+  spec:
+    initial_admin_password: mypassword
+
+Then apply this specification:
+
+.. code-block:: bash
+
+  ceph orch apply -i grafana.yaml
+  ceph orch redeploy grafana
+
+Grafana will now create an admin user called ``admin`` with the
+given password.
+
+
 Setting up Alertmanager
 -----------------------
 
index 3ec2a66789e391421ad1f0dbb792f13003a24f6d..627673d4dd65b1e09f2f798d923aff8a6f913236 100644 (file)
@@ -7,7 +7,7 @@ from urllib.parse import urlparse
 from mgr_module import HandleCommandResult
 
 from orchestrator import DaemonDescription
-from ceph.deployment.service_spec import AlertManagerSpec, ServiceSpec
+from ceph.deployment.service_spec import AlertManagerSpec, GrafanaSpec, ServiceSpec
 from cephadm.services.cephadmservice import CephadmService, CephadmDaemonDeploySpec
 from cephadm.services.ingress import IngressSpec
 from mgr_util import verify_tls, ServerConfigException, create_self_signed_cert, build_url
@@ -57,8 +57,11 @@ class GrafanaService(CephadmService):
                     'value': 'false',
                 })
 
+        spec: GrafanaSpec = cast(
+            GrafanaSpec, self.mgr.spec_store.active_specs[daemon_spec.service_name])
         grafana_ini = self.mgr.template.render(
             'services/grafana/grafana.ini.j2', {
+                'initial_admin_password': spec.initial_admin_password,
                 'http_port': daemon_spec.ports[0] if daemon_spec.ports else self.DEFAULT_SERVICE_PORT,
                 'http_addr': daemon_spec.ip if daemon_spec.ip else ''
             })
index 3d6303f4466c00acb406eed95386750879f4c0f8..642d0647f3fcd7b1511a51841730f7b87a310001 100644 (file)
   http_port = {{ http_port }}
   http_addr = {{ http_addr }}
 [security]
+{% if not initial_admin_password %}
+  disable_initial_admin_creation = true
+{% else %}
   admin_user = admin
-  admin_password = admin
+  admin_password = {{ initial_admin_password }}
+{% endif %}
   allow_embedding = true
index bccb2dc50b06062ebeb117904d38ede41576caef..c4f1e5b1c024bb3510887b01a4b1b53e4a9181a0 100644 (file)
@@ -4,7 +4,7 @@ import yaml
 
 import pytest
 
-from unittest.mock import MagicMock, call, patch
+from unittest.mock import MagicMock, call, patch, ANY
 
 from cephadm.serve import CephadmServe
 from cephadm.services.cephadmservice import MonService, MgrService, MdsService, RgwService, \
@@ -17,7 +17,7 @@ from cephadm.services.monitoring import GrafanaService, AlertmanagerService, Pro
 from cephadm.services.exporter import CephadmExporter
 from cephadm.module import CephadmOrchestrator
 from ceph.deployment.service_spec import IscsiServiceSpec, MonitoringSpec, AlertManagerSpec, \
-    ServiceSpec, RGWSpec
+    ServiceSpec, RGWSpec, GrafanaSpec
 from cephadm.tests.fixtures import with_host, with_service, _run_cephadm
 
 from orchestrator import OrchestratorError
@@ -335,7 +335,7 @@ class TestMonitoring:
             cephadm_module.set_store('grafana_crt', 'c')
             cephadm_module.set_store('grafana_key', 'k')
             with with_service(cephadm_module, MonitoringSpec('prometheus')) as _, \
-                    with_service(cephadm_module, MonitoringSpec('grafana')) as _:
+                    with_service(cephadm_module, GrafanaSpec('grafana')) as _:
                 files = {
                     'grafana.ini': dedent("""
                         # This file is generated by cephadm.
@@ -353,8 +353,7 @@ class TestMonitoring:
                           http_port = 3000
                           http_addr = 
                         [security]
-                          admin_user = admin
-                          admin_password = admin
+                          disable_initial_admin_creation = true
                           allow_embedding = true""").lstrip(),  # noqa: W291
                     'provisioning/datasources/ceph-dashboard.yml': dedent("""
                         # This file is generated by cephadm.
@@ -392,6 +391,47 @@ class TestMonitoring:
                     stdin=json.dumps({"files": files}),
                     image='')
 
+    @patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}'))
+    def test_grafana_initial_admin_pw(self, cephadm_module: CephadmOrchestrator):
+        with with_host(cephadm_module, 'test'):
+            with with_service(cephadm_module, GrafanaSpec(initial_admin_password='secure')):
+                out = cephadm_module.cephadm_services['grafana'].generate_config(
+                    CephadmDaemonDeploySpec('test', 'daemon', 'grafana'))
+                assert out == (
+                    {
+                        'files':
+                            {
+                                'certs/cert_file': ANY,
+                                'certs/cert_key': ANY,
+                                'grafana.ini':
+                                    '# This file is generated by cephadm.\n'
+                                    '[users]\n'
+                                    '  default_theme = light\n'
+                                    '[auth.anonymous]\n'
+                                    '  enabled = true\n'
+                                    "  org_name = 'Main Org.'\n"
+                                    "  org_role = 'Viewer'\n"
+                                    '[server]\n'
+                                    "  domain = 'bootstrap.storage.lab'\n"
+                                    '  protocol = https\n'
+                                    '  cert_file = /etc/grafana/certs/cert_file\n'
+                                    '  cert_key = /etc/grafana/certs/cert_key\n'
+                                    '  http_port = 3000\n'
+                                    '  http_addr = \n'
+                                    '[security]\n'
+                                    '  admin_user = admin\n'
+                                    '  admin_password = secure\n'
+                                    '  allow_embedding = true',
+                                'provisioning/datasources/ceph-dashboard.yml':
+                                    '# This file is generated by cephadm.\n'
+                                    'deleteDatasources:\n'
+                                    '\n'
+                                    'datasources:\n'
+                            }
+                    },
+                    [],
+                )
+
     @patch("cephadm.serve.CephadmServe._run_cephadm")
     def test_monitoring_ports(self, _run_cephadm, cephadm_module: CephadmOrchestrator):
         _run_cephadm.return_value = ('{}', '', 0)
index 56ca21a00051fae728d8d3589a2c1bad2ae5f3d0..b9493b2329b5753a9fcf89bd70957a93189c345e 100644 (file)
@@ -432,7 +432,7 @@ class ServiceSpec(object):
             'alertmanager': AlertManagerSpec,
             'ingress': IngressSpec,
             'container': CustomContainerSpec,
-            'grafana': MonitoringSpec,
+            'grafana': GrafanaSpec,
             'node-exporter': MonitoringSpec,
             'prometheus': MonitoringSpec,
         }.get(service_type, cls)
@@ -1093,3 +1093,27 @@ class AlertManagerSpec(MonitoringSpec):
 
 
 yaml.add_representer(AlertManagerSpec, ServiceSpec.yaml_representer)
+
+
+class GrafanaSpec(MonitoringSpec):
+    def __init__(self,
+                 service_type: str = 'grafana',
+                 service_id: Optional[str] = None,
+                 placement: Optional[PlacementSpec] = None,
+                 unmanaged: bool = False,
+                 preview_only: bool = False,
+                 config: Optional[Dict[str, str]] = None,
+                 networks: Optional[List[str]] = None,
+                 port: Optional[int] = None,
+                 initial_admin_password: Optional[str] = None
+                 ):
+        assert service_type == 'grafana'
+        super(GrafanaSpec, self).__init__(
+            'grafana', service_id=service_id,
+            placement=placement, unmanaged=unmanaged,
+            preview_only=preview_only, config=config, networks=networks, port=port)
+
+        self.initial_admin_password = initial_admin_password
+
+
+yaml.add_representer(GrafanaSpec, ServiceSpec.yaml_representer)
index b0541d653ef6e56b6d4cfd17a8c38d891fdd818c..e2e30944cebbf230eedbb6fa57480d88938fccce 100644 (file)
@@ -249,6 +249,12 @@ service_name: grafana
 spec:
   port: 1234
 ---
+service_type: grafana
+service_name: grafana
+spec:
+  initial_admin_password: secure
+  port: 1234
+---
 service_type: ingress
 service_id: rgw.foo
 service_name: ingress.rgw.foo