summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorMaximilian Bosch <maximilian@mbosch.me>2022-11-02 11:54:07 +0100
committerMaximilian Bosch <maximilian@mbosch.me>2022-11-20 18:21:35 +0100
commit957e368f3d822a7892d18da9493a8c1d48ee8bef (patch)
treec5852004d90aa8e3b199da9de9bae88144341e63 /nixos
parent8c6c94de4cf1d529079113efda85ad43a0d1556d (diff)
nixos/grafana: `provision.{datasources,dashboards}` can't be a list anymore
The hack with `either` had the side-effect that the sub-options of the
submodule didn't appear in the manual. I decided to remove this because
the "migration" isn't that hard, you just need to fix some module
declarations.

However, `mkRenamedOptionModule` wouldn't work here because it'd create
a "virtual" option for the deprecated path (i.e.
`services.grafana.provision.{datasources,dashboards}`), but that's the
already a new option, i.e. the submodule for the new stuff.

To make sure that you still get errors, I implemented a small hack using
`coercedTo` which throws an error if a list is specified (as it would be
done on 22.05) which explains what to do instead to make the migration
easier.

Also, I linkified the options in the manual now to make it easier to
navigate between those.
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/services/monitoring/grafana.nix111
-rw-r--r--nixos/tests/grafana/provision/default.nix70
2 files changed, 67 insertions, 114 deletions
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 52d5cab9f5159..3ce52bc13e91c 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -13,21 +13,9 @@ let
   settingsFormatIni = pkgs.formats.ini {};
   configFile = settingsFormatIni.generate "config.ini" cfg.settings;
 
-  datasourceConfiguration = {
-    apiVersion = 1;
-    datasources = cfg.provision.datasources;
-  };
-
-  datasourceFileNew = if (cfg.provision.datasources.path == null) then provisioningSettingsFormat.generate "datasource.yaml" cfg.provision.datasources.settings else cfg.provision.datasources.path;
-  datasourceFile = if (builtins.isList cfg.provision.datasources) then provisioningSettingsFormat.generate "datasource.yaml" datasourceConfiguration else datasourceFileNew;
-
-  dashboardConfiguration = {
-    apiVersion = 1;
-    providers = cfg.provision.dashboards;
-  };
+  datasourceFile = if (cfg.provision.datasources.path == null) then provisioningSettingsFormat.generate "datasource.yaml" cfg.provision.datasources.settings else cfg.provision.datasources.path;
 
-  dashboardFileNew = if (cfg.provision.dashboards.path == null) then provisioningSettingsFormat.generate "dashboard.yaml" cfg.provision.dashboards.settings else cfg.provision.dashboards.path;
-  dashboardFile = if (builtins.isList cfg.provision.dashboards) then provisioningSettingsFormat.generate "dashboard.yaml" dashboardConfiguration else dashboardFileNew;
+  dashboardFile = if (cfg.provision.dashboards.path == null) then provisioningSettingsFormat.generate "dashboard.yaml" cfg.provision.dashboards.settings else cfg.provision.dashboards.path;
 
   notifierConfiguration = {
     apiVersion = 1;
@@ -60,6 +48,26 @@ let
   # Get a submodule without any embedded metadata:
   _filter = x: filterAttrs (k: v: k != "_module") x;
 
+  # FIXME(@Ma27) remove before 23.05. This is just a helper-type
+  # because `mkRenamedOptionModule` doesn't work if `foo.bar` is renamed
+  # to `foo.bar.baz`.
+  submodule' = module: types.coercedTo
+    (mkOptionType {
+      name = "grafana-provision-submodule";
+      description = "Wrapper-type for backwards compat of Grafana's declarative provisioning";
+      check = x:
+        if builtins.isList x then
+          throw ''
+            Provisioning dashboards and datasources declaratively by
+            setting `dashboards` or `datasources` to a list is not supported
+            anymore. Use `services.grafana.provision.datasources.settings.datasources`
+            (or `services.grafana.provision.dashboards.settings.providers`) instead.
+          ''
+        else isAttrs x || isFunction x;
+    })
+    id
+    (types.submodule module);
+
   # http://docs.grafana.org/administration/provisioning/#datasources
   grafanaTypes.datasourceConfig = types.submodule {
     freeformType = provisioningSettingsFormat.type;
@@ -564,17 +572,14 @@ in {
 
       datasources = mkOption {
         description = lib.mdDoc ''
-          Deprecated option for Grafana datasource configuration. Use either
-          `services.grafana.provision.datasources.settings` or
-          `services.grafana.provision.datasources.path` instead.
+          Declaratively provision Grafana's datasources.
         '';
-        default = [];
-        apply = x: if (builtins.isList x) then map _filter x else x;
-        type = with types; either (listOf grafanaTypes.datasourceConfig) (submodule {
+        default = {};
+        type = submodule' {
           options.settings = mkOption {
             description = lib.mdDoc ''
               Grafana datasource configuration in Nix. Can't be used with
-              `services.grafana.provision.datasources.path` simultaneously. See
+              [](#opt-services.grafana.provision.datasources.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources>
               for supported options.
             '';
@@ -630,28 +635,25 @@ in {
           options.path = mkOption {
             description = lib.mdDoc ''
               Path to YAML datasource configuration. Can't be used with
-              `services.grafana.provision.datasources.settings` simultaneously.
+              [](#opt-services.grafana.provision.datasources.settings) simultaneously.
             '';
             default = null;
             type = types.nullOr types.path;
           };
-        });
+        };
       };
 
 
       dashboards = mkOption {
         description = lib.mdDoc ''
-          Deprecated option for Grafana dashboard configuration. Use either
-          `services.grafana.provision.dashboards.settings` or
-          `services.grafana.provision.dashboards.path` instead.
+          Declaratively provision Grafana's dashboards.
         '';
-        default = [];
-        apply = x: if (builtins.isList x) then map _filter x else x;
-        type = with types; either (listOf grafanaTypes.dashboardConfig) (submodule {
+        default = {};
+        type = submodule' {
           options.settings = mkOption {
             description = lib.mdDoc ''
               Grafana dashboard configuration in Nix. Can't be used with
-              `services.grafana.provision.dashboards.path` simultaneously. See
+              [](#opt-services.grafana.provision.dashboards.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards>
               for supported options.
             '';
@@ -684,12 +686,12 @@ in {
           options.path = mkOption {
             description = lib.mdDoc ''
               Path to YAML dashboard configuration. Can't be used with
-              `services.grafana.provision.dashboards.settings` simultaneously.
+              [](#opt-services.grafana.provision.dashboards.settings) simultaneously.
             '';
             default = null;
             type = types.nullOr types.path;
           };
-        });
+        };
       };
 
 
@@ -706,7 +708,7 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML rules configuration. Can't be used with
-              `services.grafana.provision.alerting.rules.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.rules.settings) simultaneously.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -715,7 +717,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana rules configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.rules.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.rules.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#rules>
               for supported options.
             '';
@@ -829,7 +831,7 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML contact points configuration. Can't be used with
-              `services.grafana.provision.alerting.contactPoints.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.contactPoints.settings) simultaneously.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -838,7 +840,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana contact points configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.contactPoints.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.contactPoints.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#contact-points>
               for supported options.
             '';
@@ -909,7 +911,7 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML notification policies configuration. Can't be used with
-              `services.grafana.provision.alerting.policies.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.policies.settings) simultaneously.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -918,7 +920,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana notification policies configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.policies.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.policies.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#notification-policies>
               for supported options.
             '';
@@ -978,7 +980,7 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML templates configuration. Can't be used with
-              `services.grafana.provision.alerting.templates.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.templates.settings) simultaneously.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -987,7 +989,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana templates configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.templates.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.templates.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#templates>
               for supported options.
             '';
@@ -1059,7 +1061,7 @@ in {
           path = mkOption {
             description = lib.mdDoc ''
               Path to YAML mute timings configuration. Can't be used with
-              `services.grafana.provision.alerting.muteTimings.settings` simultaneously.
+              [](#opt-services.grafana.provision.alerting.muteTimings.settings) simultaneously.
             '';
             default = null;
             type = types.nullOr types.path;
@@ -1068,7 +1070,7 @@ in {
           settings = mkOption {
             description = lib.mdDoc ''
               Grafana mute timings configuration in Nix. Can't be used with
-              `services.grafana.provision.alerting.muteTimings.path` simultaneously. See
+              [](#opt-services.grafana.provision.alerting.muteTimings.path) simultaneously. See
               <https://grafana.com/docs/grafana/latest/administration/provisioning/#mute-timings>
               for supported options.
             '';
@@ -1168,8 +1170,8 @@ in {
       (optional (
         let
           checkOpts = opt: any (x: x.password != null || x.basicAuthPassword != null || x.secureJsonData != null) opt;
-          datasourcesUsed = if (cfg.provision.datasources.settings == null) then [] else cfg.provision.datasources.settings.datasources;
-        in if (builtins.isList cfg.provision.datasources) then checkOpts cfg.provision.datasources else checkOpts datasourcesUsed
+          datasourcesUsed = optionals (cfg.provision.datasources.settings != null) cfg.provision.datasources.settings.datasources;
+        in checkOpts datasourcesUsed
         ) ''
           Datasource passwords will be stored as plaintext in the Nix store!
           It is not possible to use file provider in provisioning; please provision
@@ -1179,20 +1181,6 @@ in {
         any (x: x.secure_settings != null) cfg.provision.notifiers
       ) "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.")
       (optional (
-        builtins.isList cfg.provision.datasources && cfg.provision.datasources != []
-      ) ''
-          Provisioning Grafana datasources with options has been deprecated.
-          Use `services.grafana.provision.datasources.settings` or
-          `services.grafana.provision.datasources.path` instead.
-        '')
-      (optional (
-        builtins.isList cfg.provision.datasources && cfg.provision.dashboards != []
-      ) ''
-          Provisioning Grafana dashboards with options has been deprecated.
-          Use `services.grafana.provision.dashboards.settings` or
-          `services.grafana.provision.dashboards.path` instead.
-        '')
-      (optional (
         cfg.provision.notifiers != []
         ) ''
             Notifiers are deprecated upstream and will be removed in Grafana 10.
@@ -1204,7 +1192,7 @@ in {
 
     assertions = [
       {
-        assertion = if (builtins.isList cfg.provision.datasources) then true else cfg.provision.datasources.settings == null || cfg.provision.datasources.path == null;
+        assertion = cfg.provision.datasources.settings == null || cfg.provision.datasources.path == null;
         message = "Cannot set both datasources settings and datasources path";
       }
       {
@@ -1213,12 +1201,11 @@ in {
           ({ type, access, ... }: type == "prometheus" -> access != "direct")
           opt;
         in
-          if (builtins.isList cfg.provision.datasources) then prometheusIsNotDirect cfg.provision.datasources
-          else cfg.provision.datasources.settings == null || prometheusIsNotDirect cfg.provision.datasources.settings.datasources;
+          cfg.provision.datasources.settings == null || prometheusIsNotDirect cfg.provision.datasources.settings.datasources;
         message = "For datasources of type `prometheus`, the `direct` access mode is not supported anymore (since Grafana 9.2.0)";
       }
       {
-        assertion = if (builtins.isList cfg.provision.dashboards) then true else cfg.provision.dashboards.settings == null || cfg.provision.dashboards.path == null;
+        assertion = cfg.provision.dashboards.settings == null || cfg.provision.dashboards.path == null;
         message = "Cannot set both dashboards settings and dashboards path";
       }
       {
diff --git a/nixos/tests/grafana/provision/default.nix b/nixos/tests/grafana/provision/default.nix
index 7a707ab9fed1f..c4cb9a54daa5f 100644
--- a/nixos/tests/grafana/provision/default.nix
+++ b/nixos/tests/grafana/provision/default.nix
@@ -28,29 +28,6 @@ let
   };
 
   extraNodeConfs = {
-    provisionOld = {
-      services.grafana.provision = {
-        datasources = [{
-          name = "Test Datasource";
-          type = "testdata";
-          access = "proxy";
-          uid = "test_datasource";
-        }];
-
-        dashboards = [{ options.path = "/var/lib/grafana/dashboards"; }];
-
-        notifiers = [{
-          uid = "test_notifiers";
-          name = "Test Notifiers";
-          type = "email";
-          settings = {
-            singleEmail = true;
-            addresses = "test@test.com";
-          };
-        }];
-      };
-    };
-
     provisionNix = {
       services.grafana.provision = {
         datasources.settings = {
@@ -172,57 +149,46 @@ in {
   testScript = ''
     start_all()
 
-    nodeOld = ("Nix (old format)", provisionOld)
     nodeNix = ("Nix (new format)", provisionNix)
     nodeYaml = ("Nix (YAML)", provisionYaml)
 
-    for nodeInfo in [nodeOld, nodeNix, nodeYaml]:
-        with subtest(f"Should start provision node: {nodeInfo[0]}"):
-            nodeInfo[1].wait_for_unit("grafana.service")
-            nodeInfo[1].wait_for_open_port(3000)
+    for description, machine in [nodeNix, nodeYaml]:
+        with subtest(f"Should start provision node: {description}"):
+            machine.wait_for_unit("grafana.service")
+            machine.wait_for_open_port(3000)
 
-        with subtest(f"Successful datasource provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful datasource provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/datasources/uid/test_datasource | grep Test\ Datasource"
             )
 
-        with subtest(f"Successful dashboard provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful dashboard provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/dashboards/uid/test_dashboard | grep Test\ Dashboard"
             )
 
-
-
-    with subtest(f"Successful notifiers provision with {nodeOld[0]}"):
-        nodeOld[1].succeed(
-            "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/alert-notifications/uid/test_notifiers | grep Test\ Notifiers"
-        )
-
-
-
-    for nodeInfo in [nodeNix, nodeYaml]:
-        with subtest(f"Successful rule provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful rule provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/alert-rules/test_rule | grep Test\ Rule"
             )
 
-        with subtest(f"Successful contact point provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful contact point provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/contact-points | grep Test\ Contact\ Point"
             )
 
-        with subtest(f"Successful policy provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful policy provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/policies | grep Test\ Contact\ Point"
             )
 
-        with subtest(f"Successful template provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest(f"Successful template provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/templates | grep Test\ Template"
             )
 
-        with subtest("Successful mute timings provision with {nodeInfo[0]}"):
-            nodeInfo[1].succeed(
+        with subtest("Successful mute timings provision with {description}"):
+            machine.succeed(
                 "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1:3000/api/v1/provisioning/mute-timings | grep Test\ Mute\ Timing"
             )
   '';