diff options
Diffstat (limited to 'nixos/modules/services/search')
-rw-r--r-- | nixos/modules/services/search/elasticsearch-curator.nix | 95 | ||||
-rw-r--r-- | nixos/modules/services/search/elasticsearch.nix | 239 | ||||
-rw-r--r-- | nixos/modules/services/search/hound.nix | 127 | ||||
-rw-r--r-- | nixos/modules/services/search/kibana.nix | 213 | ||||
-rw-r--r-- | nixos/modules/services/search/meilisearch.md | 39 | ||||
-rw-r--r-- | nixos/modules/services/search/meilisearch.nix | 132 | ||||
-rw-r--r-- | nixos/modules/services/search/meilisearch.xml | 85 | ||||
-rw-r--r-- | nixos/modules/services/search/solr.nix | 110 |
8 files changed, 1040 insertions, 0 deletions
diff --git a/nixos/modules/services/search/elasticsearch-curator.nix b/nixos/modules/services/search/elasticsearch-curator.nix new file mode 100644 index 00000000000..bb2612322bb --- /dev/null +++ b/nixos/modules/services/search/elasticsearch-curator.nix @@ -0,0 +1,95 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.elasticsearch-curator; + curatorConfig = pkgs.writeTextFile { + name = "config.yaml"; + text = '' + --- + # Remember, leave a key empty if there is no value. None will be a string, + # not a Python "NoneType" + client: + hosts: ${builtins.toJSON cfg.hosts} + port: ${toString cfg.port} + url_prefix: + use_ssl: False + certificate: + client_cert: + client_key: + ssl_no_validate: False + http_auth: + timeout: 30 + master_only: False + logging: + loglevel: INFO + logfile: + logformat: default + blacklist: ['elasticsearch', 'urllib3'] + ''; + }; + curatorAction = pkgs.writeTextFile { + name = "action.yaml"; + text = cfg.actionYAML; + }; +in { + + options.services.elasticsearch-curator = { + + enable = mkEnableOption "elasticsearch curator"; + interval = mkOption { + description = "The frequency to run curator, a systemd.time such as 'hourly'"; + default = "hourly"; + type = types.str; + }; + hosts = mkOption { + description = "a list of elasticsearch hosts to connect to"; + type = types.listOf types.str; + default = ["localhost"]; + }; + port = mkOption { + description = "the port that elasticsearch is listening on"; + type = types.int; + default = 9200; + }; + actionYAML = mkOption { + description = "curator action.yaml file contents, alternatively use curator-cli which takes a simple action command"; + type = types.lines; + example = '' + --- + actions: + 1: + action: delete_indices + description: >- + Delete indices older than 45 days (based on index name), for logstash- + prefixed indices. Ignore the error if the filter does not result in an + actionable list of indices (ignore_empty_list) and exit cleanly. + options: + ignore_empty_list: True + disable_action: False + filters: + - filtertype: pattern + kind: prefix + value: logstash- + - filtertype: age + source: name + direction: older + timestring: '%Y.%m.%d' + unit: days + unit_count: 45 + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.elasticsearch-curator = { + startAt = cfg.interval; + serviceConfig = { + ExecStart = + "${pkgs.elasticsearch-curator}/bin/curator" + + " --config ${curatorConfig} ${curatorAction}"; + }; + }; + }; +} diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix new file mode 100644 index 00000000000..041d0b3c43f --- /dev/null +++ b/nixos/modules/services/search/elasticsearch.nix @@ -0,0 +1,239 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.elasticsearch; + + es7 = builtins.compareVersions cfg.package.version "7" >= 0; + + esConfig = '' + network.host: ${cfg.listenAddress} + cluster.name: ${cfg.cluster_name} + ${lib.optionalString cfg.single_node "discovery.type: single-node"} + ${lib.optionalString (cfg.single_node && es7) "gateway.auto_import_dangling_indices: true"} + + http.port: ${toString cfg.port} + transport.port: ${toString cfg.tcp_port} + + ${cfg.extraConf} + ''; + + configDir = cfg.dataDir + "/config"; + + elasticsearchYml = pkgs.writeTextFile { + name = "elasticsearch.yml"; + text = esConfig; + }; + + loggingConfigFilename = "log4j2.properties"; + loggingConfigFile = pkgs.writeTextFile { + name = loggingConfigFilename; + text = cfg.logging; + }; + + esPlugins = pkgs.buildEnv { + name = "elasticsearch-plugins"; + paths = cfg.plugins; + postBuild = "${pkgs.coreutils}/bin/mkdir -p $out/plugins"; + }; + +in +{ + + ###### interface + + options.services.elasticsearch = { + enable = mkOption { + description = "Whether to enable elasticsearch."; + default = false; + type = types.bool; + }; + + package = mkOption { + description = "Elasticsearch package to use."; + default = pkgs.elasticsearch; + defaultText = literalExpression "pkgs.elasticsearch"; + type = types.package; + }; + + listenAddress = mkOption { + description = "Elasticsearch listen address."; + default = "127.0.0.1"; + type = types.str; + }; + + port = mkOption { + description = "Elasticsearch port to listen for HTTP traffic."; + default = 9200; + type = types.int; + }; + + tcp_port = mkOption { + description = "Elasticsearch port for the node to node communication."; + default = 9300; + type = types.int; + }; + + cluster_name = mkOption { + description = "Elasticsearch name that identifies your cluster for auto-discovery."; + default = "elasticsearch"; + type = types.str; + }; + + single_node = mkOption { + description = "Start a single-node cluster"; + default = true; + type = types.bool; + }; + + extraConf = mkOption { + description = "Extra configuration for elasticsearch."; + default = ""; + type = types.str; + example = '' + node.name: "elasticsearch" + node.master: true + node.data: false + ''; + }; + + logging = mkOption { + description = "Elasticsearch logging configuration."; + default = '' + logger.action.name = org.elasticsearch.action + logger.action.level = info + + appender.console.type = Console + appender.console.name = console + appender.console.layout.type = PatternLayout + appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%m%n + + rootLogger.level = info + rootLogger.appenderRef.console.ref = console + ''; + type = types.str; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/elasticsearch"; + description = '' + Data directory for elasticsearch. + ''; + }; + + extraCmdLineOptions = mkOption { + description = "Extra command line options for the elasticsearch launcher."; + default = [ ]; + type = types.listOf types.str; + }; + + extraJavaOptions = mkOption { + description = "Extra command line options for Java."; + default = [ ]; + type = types.listOf types.str; + example = [ "-Djava.net.preferIPv4Stack=true" ]; + }; + + plugins = mkOption { + description = "Extra elasticsearch plugins"; + default = [ ]; + type = types.listOf types.package; + example = lib.literalExpression "[ pkgs.elasticsearchPlugins.discovery-ec2 ]"; + }; + + restartIfChanged = mkOption { + type = types.bool; + description = '' + Automatically restart the service on config change. + This can be set to false to defer restarts on a server or cluster. + Please consider the security implications of inadvertently running an older version, + and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option. + ''; + default = true; + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.elasticsearch = { + description = "Elasticsearch Daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = [ pkgs.inetutils ]; + inherit (cfg) restartIfChanged; + environment = { + ES_HOME = cfg.dataDir; + ES_JAVA_OPTS = toString cfg.extraJavaOptions; + ES_PATH_CONF = configDir; + }; + serviceConfig = { + ExecStart = "${cfg.package}/bin/elasticsearch ${toString cfg.extraCmdLineOptions}"; + User = "elasticsearch"; + PermissionsStartOnly = true; + LimitNOFILE = "1024000"; + Restart = "always"; + TimeoutStartSec = "infinity"; + }; + preStart = '' + ${optionalString (!config.boot.isContainer) '' + # Only set vm.max_map_count if lower than ES required minimum + # This avoids conflict if configured via boot.kernel.sysctl + if [ `${pkgs.procps}/bin/sysctl -n vm.max_map_count` -lt 262144 ]; then + ${pkgs.procps}/bin/sysctl -w vm.max_map_count=262144 + fi + ''} + + mkdir -m 0700 -p ${cfg.dataDir} + + # Install plugins + ln -sfT ${esPlugins}/plugins ${cfg.dataDir}/plugins + ln -sfT ${cfg.package}/lib ${cfg.dataDir}/lib + ln -sfT ${cfg.package}/modules ${cfg.dataDir}/modules + + # elasticsearch needs to create the elasticsearch.keystore in the config directory + # so this directory needs to be writable. + mkdir -m 0700 -p ${configDir} + + # Note that we copy config files from the nix store instead of symbolically linking them + # because otherwise X-Pack Security will raise the following exception: + # java.security.AccessControlException: + # access denied ("java.io.FilePermission" "/var/lib/elasticsearch/config/elasticsearch.yml" "read") + + cp ${elasticsearchYml} ${configDir}/elasticsearch.yml + # Make sure the logging configuration for old elasticsearch versions is removed: + rm -f "${configDir}/logging.yml" + cp ${loggingConfigFile} ${configDir}/${loggingConfigFilename} + mkdir -p ${configDir}/scripts + cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options + # redirect jvm logs to the data directory + mkdir -m 0700 -p ${cfg.dataDir}/logs + ${pkgs.sd}/bin/sd 'logs/gc.log' '${cfg.dataDir}/logs/gc.log' ${configDir}/jvm.options \ + + if [ "$(id -u)" = 0 ]; then chown -R elasticsearch:elasticsearch ${cfg.dataDir}; fi + ''; + postStart = '' + # Make sure elasticsearch is up and running before dependents + # are started + while ! ${pkgs.curl}/bin/curl -sS -f http://${cfg.listenAddress}:${toString cfg.port} 2>/dev/null; do + sleep 1 + done + ''; + }; + + environment.systemPackages = [ cfg.package ]; + + users = { + groups.elasticsearch.gid = config.ids.gids.elasticsearch; + users.elasticsearch = { + uid = config.ids.uids.elasticsearch; + description = "Elasticsearch daemon user"; + home = cfg.dataDir; + group = "elasticsearch"; + }; + }; + }; +} diff --git a/nixos/modules/services/search/hound.nix b/nixos/modules/services/search/hound.nix new file mode 100644 index 00000000000..ef62175b0a3 --- /dev/null +++ b/nixos/modules/services/search/hound.nix @@ -0,0 +1,127 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.hound; +in { + options = { + services.hound = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the hound code search daemon. + ''; + }; + + user = mkOption { + default = "hound"; + type = types.str; + description = '' + User the hound daemon should execute under. + ''; + }; + + group = mkOption { + default = "hound"; + type = types.str; + description = '' + Group the hound daemon should execute under. + ''; + }; + + extraGroups = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "dialout" ]; + description = '' + List of extra groups that the "hound" user should be a part of. + ''; + }; + + home = mkOption { + default = "/var/lib/hound"; + type = types.path; + description = '' + The path to use as hound's $HOME. If the default user + "hound" is configured then this is the home of the "hound" + user. + ''; + }; + + package = mkOption { + default = pkgs.hound; + defaultText = literalExpression "pkgs.hound"; + type = types.package; + description = '' + Package for running hound. + ''; + }; + + config = mkOption { + type = types.str; + description = '' + The full configuration of the Hound daemon. Note the dbpath + should be an absolute path to a writable location on disk. + ''; + example = literalExpression '' + ''' + { + "max-concurrent-indexers" : 2, + "dbpath" : "''${services.hound.home}/data", + "repos" : { + "nixpkgs": { + "url" : "https://www.github.com/NixOS/nixpkgs.git" + } + } + } + ''' + ''; + }; + + listen = mkOption { + type = types.str; + default = "0.0.0.0:6080"; + example = "127.0.0.1:6080 or just :6080"; + description = '' + Listen on this IP:port / :port + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.groups = optionalAttrs (cfg.group == "hound") { + hound.gid = config.ids.gids.hound; + }; + + users.users = optionalAttrs (cfg.user == "hound") { + hound = { + description = "hound code search"; + createHome = true; + home = cfg.home; + group = cfg.group; + extraGroups = cfg.extraGroups; + uid = config.ids.uids.hound; + }; + }; + + systemd.services.hound = { + description = "Hound Code Search"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + WorkingDirectory = cfg.home; + ExecStartPre = "${pkgs.git}/bin/git config --global --replace-all http.sslCAinfo /etc/ssl/certs/ca-certificates.crt"; + ExecStart = "${cfg.package}/bin/houndd" + + " -addr ${cfg.listen}" + + " -conf ${pkgs.writeText "hound.json" cfg.config}"; + + }; + path = [ pkgs.git pkgs.mercurial pkgs.openssh ]; + }; + }; + +} diff --git a/nixos/modules/services/search/kibana.nix b/nixos/modules/services/search/kibana.nix new file mode 100644 index 00000000000..e4ab85be9ef --- /dev/null +++ b/nixos/modules/services/search/kibana.nix @@ -0,0 +1,213 @@ +{ config, lib, options, pkgs, ... }: + +with lib; + +let + cfg = config.services.kibana; + opt = options.services.kibana; + + ge7 = builtins.compareVersions cfg.package.version "7" >= 0; + lt6_6 = builtins.compareVersions cfg.package.version "6.6" < 0; + + cfgFile = pkgs.writeText "kibana.json" (builtins.toJSON ( + (filterAttrsRecursive (n: v: v != null && v != []) ({ + server.host = cfg.listenAddress; + server.port = cfg.port; + server.ssl.certificate = cfg.cert; + server.ssl.key = cfg.key; + + kibana.index = cfg.index; + kibana.defaultAppId = cfg.defaultAppId; + + elasticsearch.url = cfg.elasticsearch.url; + elasticsearch.hosts = cfg.elasticsearch.hosts; + elasticsearch.username = cfg.elasticsearch.username; + elasticsearch.password = cfg.elasticsearch.password; + + elasticsearch.ssl.certificate = cfg.elasticsearch.cert; + elasticsearch.ssl.key = cfg.elasticsearch.key; + elasticsearch.ssl.certificateAuthorities = cfg.elasticsearch.certificateAuthorities; + } // cfg.extraConf) + ))); + +in { + options.services.kibana = { + enable = mkEnableOption "kibana service"; + + listenAddress = mkOption { + description = "Kibana listening host"; + default = "127.0.0.1"; + type = types.str; + }; + + port = mkOption { + description = "Kibana listening port"; + default = 5601; + type = types.int; + }; + + cert = mkOption { + description = "Kibana ssl certificate."; + default = null; + type = types.nullOr types.path; + }; + + key = mkOption { + description = "Kibana ssl key."; + default = null; + type = types.nullOr types.path; + }; + + index = mkOption { + description = "Elasticsearch index to use for saving kibana config."; + default = ".kibana"; + type = types.str; + }; + + defaultAppId = mkOption { + description = "Elasticsearch default application id."; + default = "discover"; + type = types.str; + }; + + elasticsearch = { + url = mkOption { + description = '' + Elasticsearch url. + + Defaults to <literal>"http://localhost:9200"</literal>. + + Don't set this when using Kibana >= 7.0.0 because it will result in a + configuration error. Use <option>services.kibana.elasticsearch.hosts</option> + instead. + ''; + default = null; + type = types.nullOr types.str; + }; + + hosts = mkOption { + description = '' + The URLs of the Elasticsearch instances to use for all your queries. + All nodes listed here must be on the same cluster. + + Defaults to <literal>[ "http://localhost:9200" ]</literal>. + + This option is only valid when using kibana >= 6.6. + ''; + default = null; + type = types.nullOr (types.listOf types.str); + }; + + username = mkOption { + description = "Username for elasticsearch basic auth."; + default = null; + type = types.nullOr types.str; + }; + + password = mkOption { + description = "Password for elasticsearch basic auth."; + default = null; + type = types.nullOr types.str; + }; + + ca = mkOption { + description = '' + CA file to auth against elasticsearch. + + It's recommended to use the <option>certificateAuthorities</option> option + when using kibana-5.4 or newer. + ''; + default = null; + type = types.nullOr types.path; + }; + + certificateAuthorities = mkOption { + description = '' + CA files to auth against elasticsearch. + + Please use the <option>ca</option> option when using kibana < 5.4 + because those old versions don't support setting multiple CA's. + + This defaults to the singleton list [ca] when the <option>ca</option> option is defined. + ''; + default = if cfg.elasticsearch.ca == null then [] else [ca]; + defaultText = literalExpression '' + if config.${opt.elasticsearch.ca} == null then [ ] else [ ca ] + ''; + type = types.listOf types.path; + }; + + cert = mkOption { + description = "Certificate file to auth against elasticsearch."; + default = null; + type = types.nullOr types.path; + }; + + key = mkOption { + description = "Key file to auth against elasticsearch."; + default = null; + type = types.nullOr types.path; + }; + }; + + package = mkOption { + description = "Kibana package to use"; + default = pkgs.kibana; + defaultText = literalExpression "pkgs.kibana"; + type = types.package; + }; + + dataDir = mkOption { + description = "Kibana data directory"; + default = "/var/lib/kibana"; + type = types.path; + }; + + extraConf = mkOption { + description = "Kibana extra configuration"; + default = {}; + type = types.attrs; + }; + }; + + config = mkIf (cfg.enable) { + assertions = [ + { + assertion = ge7 -> cfg.elasticsearch.url == null; + message = + "The option services.kibana.elasticsearch.url has been removed when using kibana >= 7.0.0. " + + "Please use option services.kibana.elasticsearch.hosts instead."; + } + { + assertion = lt6_6 -> cfg.elasticsearch.hosts == null; + message = + "The option services.kibana.elasticsearch.hosts is only valid for kibana >= 6.6."; + } + ]; + systemd.services.kibana = { + description = "Kibana Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "elasticsearch.service" ]; + environment = { BABEL_CACHE_PATH = "${cfg.dataDir}/.babelcache.json"; }; + serviceConfig = { + ExecStart = + "${cfg.package}/bin/kibana" + + " --config ${cfgFile}" + + " --path.data ${cfg.dataDir}"; + User = "kibana"; + WorkingDirectory = cfg.dataDir; + }; + }; + + environment.systemPackages = [ cfg.package ]; + + users.users.kibana = { + isSystemUser = true; + description = "Kibana service user"; + home = cfg.dataDir; + createHome = true; + group = "kibana"; + }; + users.groups.kibana = {}; + }; +} diff --git a/nixos/modules/services/search/meilisearch.md b/nixos/modules/services/search/meilisearch.md new file mode 100644 index 00000000000..98e7c542cb9 --- /dev/null +++ b/nixos/modules/services/search/meilisearch.md @@ -0,0 +1,39 @@ +# Meilisearch {#module-services-meilisearch} + +Meilisearch is a lightweight, fast and powerful search engine. Think elastic search with a much smaller footprint. + +## Quickstart + +the minimum to start meilisearch is + +```nix +services.meilisearch.enable = true; +``` + +this will start the http server included with meilisearch on port 7700. + +test with `curl -X GET 'http://localhost:7700/health'` + +## Usage + +you first need to add documents to an index before you can search for documents. + +### Add a documents to the `movies` index + +`curl -X POST 'http://127.0.0.1:7700/indexes/movies/documents' --data '[{"id": "123", "title": "Superman"}, {"id": 234, "title": "Batman"}]'` + +### Search documents in the `movies` index + +`curl 'http://127.0.0.1:7700/indexes/movies/search' --data '{ "q": "botman" }'` (note the typo is intentional and there to demonstrate the typo tolerant capabilities) + +## Defaults + +- The default nixos package doesn't come with the [dashboard](https://docs.meilisearch.com/learn/getting_started/quick_start.html#search), since the dashboard features makes some assets downloads at compile time. + +- Anonimized Analytics sent to meilisearch are disabled by default. + +- Default deployment is development mode. It doesn't require a secret master key. All routes are not protected and accessible. + +## Missing + +- the snapshot feature is not yet configurable from the module, it's just a matter of adding the relevant environment variables. diff --git a/nixos/modules/services/search/meilisearch.nix b/nixos/modules/services/search/meilisearch.nix new file mode 100644 index 00000000000..f6210f6f16e --- /dev/null +++ b/nixos/modules/services/search/meilisearch.nix @@ -0,0 +1,132 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.meilisearch; + +in +{ + + meta.maintainers = with maintainers; [ Br1ght0ne happysalada ]; + # Don't edit the docbook xml directly, edit the md and generate it: + # `pandoc meilisearch.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > meilisearch.xml` + meta.doc = ./meilisearch.xml; + + ###### interface + + options.services.meilisearch = { + enable = mkEnableOption "MeiliSearch - a RESTful search API"; + + package = mkOption { + description = "The package to use for meilisearch. Use this if you require specific features to be enabled. The default package has no features."; + default = pkgs.meilisearch; + defaultText = "pkgs.meilisearch"; + type = types.package; + }; + + listenAddress = mkOption { + description = "MeiliSearch listen address."; + default = "127.0.0.1"; + type = types.str; + }; + + listenPort = mkOption { + description = "MeiliSearch port to listen on."; + default = 7700; + type = types.port; + }; + + environment = mkOption { + description = "Defines the running environment of MeiliSearch."; + default = "development"; + type = types.enum [ "development" "production" ]; + }; + + # TODO change this to LoadCredentials once possible + masterKeyEnvironmentFile = mkOption { + description = '' + Path to file which contains the master key. + By doing so, all routes will be protected and will require a key to be accessed. + If no master key is provided, all routes can be accessed without requiring any key. + The format is the following: + MEILI_MASTER_KEY=my_secret_key + ''; + default = null; + type = with types; nullOr path; + }; + + noAnalytics = mkOption { + description = '' + Deactivates analytics. + Analytics allow MeiliSearch to know how many users are using MeiliSearch, + which versions and which platforms are used. + This process is entirely anonymous. + ''; + default = true; + type = types.bool; + }; + + logLevel = mkOption { + description = '' + Defines how much detail should be present in MeiliSearch's logs. + MeiliSearch currently supports four log levels, listed in order of increasing verbosity: + - 'ERROR': only log unexpected events indicating MeiliSearch is not functioning as expected + - 'WARN:' log all unexpected events, regardless of their severity + - 'INFO:' log all events. This is the default value + - 'DEBUG': log all events and including detailed information on MeiliSearch's internal processes. + Useful when diagnosing issues and debugging + ''; + default = "INFO"; + type = types.str; + }; + + maxIndexSize = mkOption { + description = '' + Sets the maximum size of the index. + Value must be given in bytes or explicitly stating a base unit. + For example, the default value can be written as 107374182400, '107.7Gb', or '107374 Mb'. + Default is 100 GiB + ''; + default = "107374182400"; + type = types.str; + }; + + payloadSizeLimit = mkOption { + description = '' + Sets the maximum size of accepted JSON payloads. + Value must be given in bytes or explicitly stating a base unit. + For example, the default value can be written as 107374182400, '107.7Gb', or '107374 Mb'. + Default is ~ 100 MB + ''; + default = "104857600"; + type = types.str; + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.meilisearch = { + description = "MeiliSearch daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + environment = { + MEILI_DB_PATH = "/var/lib/meilisearch"; + MEILI_HTTP_ADDR = "${cfg.listenAddress}:${toString cfg.listenPort}"; + MEILI_NO_ANALYTICS = toString cfg.noAnalytics; + MEILI_ENV = cfg.environment; + MEILI_DUMPS_DIR = "/var/lib/meilisearch/dumps"; + MEILI_LOG_LEVEL = cfg.logLevel; + MEILI_MAX_INDEX_SIZE = cfg.maxIndexSize; + }; + serviceConfig = { + ExecStart = "${cfg.package}/bin/meilisearch"; + DynamicUser = true; + StateDirectory = "meilisearch"; + EnvironmentFile = mkIf (cfg.masterKeyEnvironmentFile != null) cfg.masterKeyEnvironmentFile; + }; + }; + }; +} diff --git a/nixos/modules/services/search/meilisearch.xml b/nixos/modules/services/search/meilisearch.xml new file mode 100644 index 00000000000..c1a73f358c2 --- /dev/null +++ b/nixos/modules/services/search/meilisearch.xml @@ -0,0 +1,85 @@ +<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-meilisearch"> + <title>Meilisearch</title> + <para> + Meilisearch is a lightweight, fast and powerful search engine. Think + elastic search with a much smaller footprint. + </para> + <section xml:id="quickstart"> + <title>Quickstart</title> + <para> + the minimum to start meilisearch is + </para> + <programlisting language="bash"> +services.meilisearch.enable = true; +</programlisting> + <para> + this will start the http server included with meilisearch on port + 7700. + </para> + <para> + test with + <literal>curl -X GET 'http://localhost:7700/health'</literal> + </para> + </section> + <section xml:id="usage"> + <title>Usage</title> + <para> + you first need to add documents to an index before you can search + for documents. + </para> + <section xml:id="add-a-documents-to-the-movies-index"> + <title>Add a documents to the <literal>movies</literal> + index</title> + <para> + <literal>curl -X POST 'http://127.0.0.1:7700/indexes/movies/documents' --data '[{"id": "123", "title": "Superman"}, {"id": 234, "title": "Batman"}]'</literal> + </para> + </section> + <section xml:id="search-documents-in-the-movies-index"> + <title>Search documents in the <literal>movies</literal> + index</title> + <para> + <literal>curl 'http://127.0.0.1:7700/indexes/movies/search' --data '{ "q": "botman" }'</literal> + (note the typo is intentional and there to demonstrate the typo + tolerant capabilities) + </para> + </section> + </section> + <section xml:id="defaults"> + <title>Defaults</title> + <itemizedlist> + <listitem> + <para> + The default nixos package doesn’t come with the + <link xlink:href="https://docs.meilisearch.com/learn/getting_started/quick_start.html#search">dashboard</link>, + since the dashboard features makes some assets downloads at + compile time. + </para> + </listitem> + <listitem> + <para> + Anonimized Analytics sent to meilisearch are disabled by + default. + </para> + </listitem> + <listitem> + <para> + Default deployment is development mode. It doesn’t require a + secret master key. All routes are not protected and + accessible. + </para> + </listitem> + </itemizedlist> + </section> + <section xml:id="missing"> + <title>Missing</title> + <itemizedlist spacing="compact"> + <listitem> + <para> + the snapshot feature is not yet configurable from the module, + it’s just a matter of adding the relevant environment + variables. + </para> + </listitem> + </itemizedlist> + </section> +</chapter> diff --git a/nixos/modules/services/search/solr.nix b/nixos/modules/services/search/solr.nix new file mode 100644 index 00000000000..ea76bfc9298 --- /dev/null +++ b/nixos/modules/services/search/solr.nix @@ -0,0 +1,110 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.solr; + +in + +{ + options = { + services.solr = { + enable = mkEnableOption "Solr"; + + package = mkOption { + type = types.package; + default = pkgs.solr; + defaultText = literalExpression "pkgs.solr"; + description = "Which Solr package to use."; + }; + + port = mkOption { + type = types.int; + default = 8983; + description = "Port on which Solr is ran."; + }; + + stateDir = mkOption { + type = types.path; + default = "/var/lib/solr"; + description = "The solr home directory containing config, data, and logging files."; + }; + + extraJavaOptions = mkOption { + type = types.listOf types.str; + default = []; + description = "Extra command line options given to the java process running Solr."; + }; + + user = mkOption { + type = types.str; + default = "solr"; + description = "User under which Solr is ran."; + }; + + group = mkOption { + type = types.str; + default = "solr"; + description = "Group under which Solr is ran."; + }; + }; + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ cfg.package ]; + + systemd.services.solr = { + after = [ "network.target" "remote-fs.target" "nss-lookup.target" "systemd-journald-dev-log.socket" ]; + wantedBy = [ "multi-user.target" ]; + + environment = { + SOLR_HOME = "${cfg.stateDir}/data"; + LOG4J_PROPS = "${cfg.stateDir}/log4j2.xml"; + SOLR_LOGS_DIR = "${cfg.stateDir}/logs"; + SOLR_PORT = "${toString cfg.port}"; + }; + path = with pkgs; [ + gawk + procps + ]; + preStart = '' + mkdir -p "${cfg.stateDir}/data"; + mkdir -p "${cfg.stateDir}/logs"; + + if ! test -e "${cfg.stateDir}/data/solr.xml"; then + install -D -m0640 ${cfg.package}/server/solr/solr.xml "${cfg.stateDir}/data/solr.xml" + install -D -m0640 ${cfg.package}/server/solr/zoo.cfg "${cfg.stateDir}/data/zoo.cfg" + fi + + if ! test -e "${cfg.stateDir}/log4j2.xml"; then + install -D -m0640 ${cfg.package}/server/resources/log4j2.xml "${cfg.stateDir}/log4j2.xml" + fi + ''; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart="${cfg.package}/bin/solr start -f -a \"${concatStringsSep " " cfg.extraJavaOptions}\""; + ExecStop="${cfg.package}/bin/solr stop"; + }; + }; + + users.users = optionalAttrs (cfg.user == "solr") { + solr = { + group = cfg.group; + home = cfg.stateDir; + createHome = true; + uid = config.ids.uids.solr; + }; + }; + + users.groups = optionalAttrs (cfg.group == "solr") { + solr.gid = config.ids.gids.solr; + }; + + }; + +} |