Unverified Commit eba81e89 authored by Maximilian Bosch's avatar Maximilian Bosch Committed by GitHub
Browse files

grafana-image-renderer: 4.0.14 -> 5.0.10 (#461983)

parents 40651935 3e45e1b3
Loading
Loading
Loading
Loading
+187 −95
Original line number Diff line number Diff line
@@ -2,108 +2,186 @@
  lib,
  pkgs,
  config,
  utils,
  ...
}:
let
  cfg = config.services.grafana-image-renderer;

  format = pkgs.formats.json { };

  configFile = format.generate "grafana-image-renderer-config.json" cfg.settings;
  format = {
    type =
      with lib.types;
      attrsOf (
        attrsOf (oneOf [
          str
          int
          bool
          (listOf (oneOf [
            str
            int
          ]))
        ])
      );

    generate = lib.flip lib.pipe [
      # Remove legacy option prefixes that only exist for backwards-compat
      (lib.flip builtins.removeAttrs [
        "service"
        "rendering"
        "assertions"
        "warnings"
      ])

      # Normalize CLI args from a nested attr-set to `section.option = value`
      (lib.concatMapAttrs (block: lib.mapAttrs' (option: lib.nameValuePair "${block}.${option}")))

      # Turn attr-set into a list of arguments, denormalize list options.
      (lib.mapAttrsToList (
        option: value:
        if lib.isBool value then
          lib.optional value "--${option}"
        else if lib.isList value then
          map (v: [
            "--${option}"
            v
          ]) value
        else
          [
            "--${option}"
            (toString value)
          ]
      ))
      lib.flatten

      # Turn into a string
      utils.escapeSystemdExecArgs
    ];
  };
in
{
  imports = [
    (lib.mkChangedOptionModule
      [
        "services"
        "grafana-image-renderer"
        "chromium"
      ]
      [
        "services"
        "grafana-image-renderer"
        "settings"
        "browser"
        "path"
      ]
      (config: lib.getExe config.services.grafana-image-renderer.chromium)
    )
    (lib.mkRemovedOptionModule
      [
        "services"
        "grafana-image-renderer"
        "verbose"
      ]
      ''
        Use `services.grafana-image-renderer.settings.log.level = "debug"` instead.
      ''
    )
  ];

  options.services.grafana-image-renderer = {
    enable = lib.mkEnableOption "grafana-image-renderer";

    chromium = lib.mkOption {
      type = lib.types.package;
      description = ''
        The chromium to use for image rendering.
      '';
    };

    verbose = lib.mkEnableOption "verbosity for the service";

    provisionGrafana = lib.mkEnableOption "Grafana configuration for grafana-image-renderer";

    settings = lib.mkOption {
      type = lib.types.submodule {
        freeformType = format.type;
        imports = [
          ../../misc/assertions.nix
          (lib.mkRenamedOptionModule
            [
              "rendering"
              "width"
            ]
            [
              "browser"
              "min-width"
            ]
          )
          (lib.mkRenamedOptionModule
            [
              "rendering"
              "height"
            ]
            [
              "browser"
              "min-height"
            ]
          )
          (lib.mkRenamedOptionModule
            [
              "rendering"
              "args"
            ]
            [

              "browser"
              "flag"
            ]
          )
          (lib.mkChangedOptionModule
            [
              "service"
              "port"
            ]
            [
              "server"
              "addr"
            ]
            (config: "0.0.0.0:${toString config.service.port}")
          )
          (lib.mkRemovedOptionModule
            [
              "rendering"
              "mode"
            ]
            ''
              This option is obsolete.
            ''
          )
          (lib.mkRenamedOptionModule
            [
              "service"
              "logging"
            ]
            [
              "log"
            ]
          )
        ];

        options = {
          service = {
            port = lib.mkOption {
              type = lib.types.port;
              default = 8081;
          server.addr = lib.mkOption {
            type = lib.types.str;
            default = "localhost:8081";
            description = ''
                The TCP port to use for the rendering server.
              Listen address of the service.
            '';
          };
            logging.level = lib.mkOption {
              type = lib.types.enum [
                "error"
                "warning"
                "info"
                "debug"
              ];
              default = "info";
          browser.path = lib.mkOption {
            type = lib.types.path;
            default = lib.getExe pkgs.chromium;
            defaultText = lib.literalExpression "lib.getExe pkgs.chromium";
            description = ''
                The log-level of the {file}`grafana-image-renderer.service`-unit.
              Path to the executable of the chromium to use.
            '';
          };
        };
          rendering = {
            width = lib.mkOption {
              default = 1000;
              type = lib.types.ints.positive;
              description = ''
                Width of the PNG used to display the alerting graph.
              '';
            };
            height = lib.mkOption {
              default = 500;
              type = lib.types.ints.positive;
              description = ''
                Height of the PNG used to display the alerting graph.
              '';
            };
            mode = lib.mkOption {
              default = "default";
              type = lib.types.enum [
                "default"
                "reusable"
                "clustered"
              ];
              description = ''
                Rendering mode of `grafana-image-renderer`:

                - `default:` Creates on browser-instance
                  per rendering request.
                - `reusable:` One browser instance
                  will be started and reused for each rendering request.
                - `clustered:` allows to precisely
                  configure how many browser-instances are supposed to be used. The values
                  for that mode can be declared in `rendering.clustering`.
              '';
            };
            args = lib.mkOption {
              type = lib.types.listOf lib.types.str;
              default = [ "--no-sandbox" ];
              description = ''
                List of CLI flags passed to `chromium`.
              '';
            };
          };
        };
      };

      default = { };

      description = ''
        Configuration attributes for `grafana-image-renderer`.

        See <https://github.com/grafana/grafana-image-renderer/blob/ce1f81438e5f69c7fd7c73ce08bab624c4c92e25/default.json>
        for supported values.
      '';
    };
  };
@@ -120,39 +198,53 @@ in
    ];

    services.grafana.settings.rendering = lib.mkIf cfg.provisionGrafana {
      server_url = "http://localhost:${toString cfg.settings.service.port}/render";
      server_url = "http://${toString cfg.settings.server.addr}/render";
      callback_url = "http://${config.services.grafana.settings.server.http_addr}:${toString config.services.grafana.settings.server.http_port}";
    };

    services.grafana-image-renderer.chromium = lib.mkDefault pkgs.chromium;

    services.grafana-image-renderer.settings = {
      rendering = lib.mapAttrs (lib.const lib.mkDefault) {
        chromeBin = "${cfg.chromium}/bin/chromium";
        verboseLogging = cfg.verbose;
        timezone = config.time.timeZone;
      };

      service = {
        logging.level = lib.mkIf cfg.verbose (lib.mkDefault "debug");
        metrics.enabled = lib.mkDefault false;
      };
      browser.timezone = lib.mkIf (config.time.timeZone != null) (lib.mkDefault config.time.timeZone);
    };

    systemd.services.grafana-image-renderer = {
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];
      wants = [ "network-online.target" ];
      after = [ "network-online.target" ];
      description = "Grafana backend plugin that handles rendering of panels & dashboards to PNGs using headless browser (Chromium/Chrome)";

      environment = {
        PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "true";
      };

      serviceConfig = {
        DynamicUser = true;
        PrivateTmp = true;
        ExecStart = "${pkgs.grafana-image-renderer}/bin/grafana-image-renderer server --config=${configFile}";
        ExecStart = "${lib.getExe pkgs.grafana-image-renderer} server ${format.generate cfg.settings}";
        Restart = "always";
        AmbientCapabilities = "";
        CapabilityBoundingSet = "";
        LockPersonality = true;
        MountAPIVFS = true;
        NoNewPrivileges = true;
        PrivateDevices = true;
        PrivateMounts = true;
        PrivateUsers = true;
        ProtectClock = true;
        ProtectControlGroups = "strict";
        ProtectHome = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectProc = "invisible";
        ProtectSystem = "full";
        RemoveIPC = true;
        RestrictAddressFamilies = [
          "AF_UNIX"
          "AF_INET"
          "AF_INET6"
        ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;
        SystemCallArchitectures = "native";
        UMask = 27;
      };
    };
  };
+0 −109
Original line number Diff line number Diff line
{
  "name": "renderer",
  "version": "1.0.0",
  "author": "Grafana Labs",
  "license": "Apache-2.0",
  "repository": {
    "type": "git",
    "url": "https://github.com/grafana/grafana-image-renderer.git"
  },
  "scripts": {
    "eslint": "eslint .",
    "typecheck": "tsc --noEmit",
    "prettier:check": "prettier --list-different \"**/*.ts\"",
    "prettier:write": "prettier --list-different \"**/*.ts\" --write",
    "precommit": "npm run eslint & npm run typecheck",
    "watch": "tsc-watch --onSuccess \"node build/app.js server --config=dev.json\"",
    "watch:debug": "tsc-watch --onSuccess \"cross-env DEBUG=puppeteer-cluster:* node build/app.js server --config=dev.json\"",
    "build": "tsc",
    "start": "node build/app.js server --config=dev.json",
    "create-gcom-plugin-json": "ts-node scripts/createGcomPluginJson.ts ./scripts/tmp",
    "push-to-gcom": "sh ./scripts/push-to-gcom.sh",
    "test-update": "cross-env UPDATE_GOLDEN=true jest",
    "test": "sh ./scripts/run_tests.sh",
    "test-ci": "jest",
    "test-diff": "cross-env SAVE_DIFF=true jest"
  },
  "dependencies": {
    "@grpc/grpc-js": "^1.8.22",
    "@grpc/proto-loader": "^0.7.2",
    "@hapi/boom": "^10.0.0",
    "@opentelemetry/api": "^1.9.0",
    "@opentelemetry/auto-instrumentations-node": "^0.49.0",
    "@opentelemetry/exporter-trace-otlp-http": "^0.52.1",
    "@opentelemetry/resources": "^1.25.1",
    "@opentelemetry/sdk-node": "^0.52.1",
    "@opentelemetry/semantic-conventions": "^1.25.1",
    "@puppeteer/browsers": "^2.3.1",
    "chokidar": "^3.5.2",
    "dompurify": "^3.2.4",
    "express": "^4.21.1",
    "express-prom-bundle": "^6.5.0",
    "ioredis": "^5.6.1",
    "jimp": "^0.22.12",
    "jsdom": "20.0.0",
    "lodash": "^4.17.21",
    "minimist": "^1.2.6",
    "morgan": "^1.9.0",
    "multer": "^2.0.2",
    "on-finished": "^2.3.0",
    "poolpeteer": "^0.24.0",
    "prom-client": "^14.1.0",
    "puppeteer": "^22.8.2",
    "puppeteer-cluster": "^0.24.0",
    "rate-limiter-flexible": "^7.0.0",
    "tar-fs": "^3.0.9",
    "unique-filename": "^2.0.1",
    "winston": "^3.8.2"
  },
  "devDependencies": {
    "@eslint/js": "^9.31.0",
    "@grafana/eslint-config": "^8.1.0",
    "@grafana/sign-plugin": "^3.1.3",
    "@stylistic/eslint-plugin-ts": "^4.4.1",
    "@types/content-disposition": "^0.5.9",
    "@types/express": "^4.17.14",
    "@types/jest": "^29.5.12",
    "@types/jsdom": "20.0.0",
    "@types/lodash": "^4.17.20",
    "@types/minimist": "^1.2.5",
    "@types/morgan": "^1.9.10",
    "@types/multer": "^1.4.7",
    "@types/node": "^20.17.27",
    "@types/pixelmatch": "^5.2.6",
    "@types/supertest": "^2.0.15",
    "@typescript-eslint/eslint-plugin": "^8.37.0",
    "@typescript-eslint/parser": "^8.37.0",
    "@yao-pkg/pkg": "^6.3.0",
    "axios": "1.8.2",
    "cross-env": "7.0.3",
    "eslint": "^9.31.0",
    "eslint-config-prettier": "^10.1.5",
    "eslint-plugin-jsdoc": "^51.4.1",
    "eslint-plugin-react": "^7.37.5",
    "eslint-plugin-react-hooks": "^5.2.0",
    "fast-png": "^6.2.0",
    "jest": "^29.7.0",
    "jsonwebtoken": "^9.0.2",
    "lint-staged": "13.0.3",
    "prettier": "2.7.1",
    "supertest": "^7.0.0",
    "ts-jest": "^29.1.1",
    "ts-node": "10.9.1",
    "tsc-watch": "5.0.3",
    "typescript": "^5.8.3",
    "typescript-eslint": "^8.37.0"
  },
  "lint-staged": {
    "*.ts": [
      "prettier --write"
    ]
  },
  "pkg": {
    "assets": "proto/*"
  },
  "bin": "build/app.js",
  "engines": {
    "node": ">= 22"
  }
}
+8 −54
Original line number Diff line number Diff line
{
  lib,
  mkYarnPackage,
  buildGoModule,
  fetchFromGitHub,
  fetchYarnDeps,
  nodejs,
  runtimeShell,
}:

# Notes for the upgrade:
# * Download the tarball of the new version to use.
# * Replace new `package.json` here.
# * Update `version`+`hash` and rebuild.

mkYarnPackage rec {
buildGoModule (finalAttrs: {
  pname = "grafana-image-renderer";
  version = "4.0.14";
  version = "5.0.10";

  src = fetchFromGitHub {
    owner = "grafana";
    repo = "grafana-image-renderer";
    rev = "v${version}";
    hash = "sha256-CoQTOzQ7h31B3U0yvJYsgC3uaSyjNNLpD+8uMN+naiQ=";
  };

  offlineCache = fetchYarnDeps {
    yarnLock = src + "/yarn.lock";
    hash = "sha256-xZrIoQlPeyGTbFRUQ0M8Tc6YpzsC5IACW0bbZ+HnsOQ=";
    tag = "v${finalAttrs.version}";
    hash = "sha256-oWJlb1mV1sNgN7EQ8L4msfnKps5oV60JgwZYpAJQaq4=";
  };

  packageJSON = ./package.json;

  buildPhase = ''
    runHook preBuild

    pushd deps/renderer
    yarn run build
    popd

    runHook postBuild
  '';

  dontInstall = true;

  distPhase = ''
    runHook preDist

    shopt -s extglob

    pushd deps/renderer
    install_path="$out/libexec/grafana-image-renderer"
    mkdir -p $install_path
    cp -R ../../node_modules $install_path
    cp -R ./!(node_modules) $install_path
    popd

    mkdir -p $out/bin
    cat >$out/bin/grafana-image-renderer <<EOF
    #! ${runtimeShell}
    ${nodejs}/bin/node $install_path/build/app.js \$@
    EOF
    chmod +x $out/bin/grafana-image-renderer
  vendorHash = "sha256-wA1XeLO2bYwq7HZOQ5UNcdqqJdEWRUxFoAQucXAj48k=";

    runHook postDist
  '';
  subPackages = [ "." ];

  meta = with lib; {
    homepage = "https://github.com/grafana/grafana-image-renderer";
@@ -70,6 +25,5 @@ mkYarnPackage rec {
    mainProgram = "grafana-image-renderer";
    license = licenses.asl20;
    maintainers = with maintainers; [ ma27 ];
    platforms = platforms.all;
  };
}
})