diff --git a/clanServices/hello-world/default.nix b/clanServices/hello-world/default.nix index 15e53a7b3..f322030cd 100644 --- a/clanServices/hello-world/default.nix +++ b/clanServices/hello-world/default.nix @@ -1,3 +1,9 @@ +# Example clan service. See https://docs.clan.lol/guides/services/community/ +# for more details + +# The test for this module in ./tests/vm/default.nix shows an example of how +# the service is used. + { packages }: { ... }: { @@ -5,30 +11,94 @@ manifest.name = "clan-core/hello-word"; manifest.description = "This is a test"; - roles.peer = { + # This service provides two roles: "morning" and "evening". Roles can be + # defined in this file directly (e.g. the "morning" role) or split up into a + # separate file (e.g. the "evening" role) + roles.morning = { interface = { lib, ... }: { - options.foo = lib.mkOption { + # Here we define the settings for this role. They will be accessible + # via `roles.morning.settings` in the role + + options.greeting = lib.mkOption { type = lib.types.str; - # default = ""; - description = "Some option"; + default = "Good morning"; + description = "The greeting to use"; }; }; + # Maps over all instances and produces one result per instance. + perInstance = + { + # Role settings for this machine/instance + settings, + + # The name of this instance of the service + instanceName, + + # The current machine + machine, + + # All roles of this service, with their assigned machines + roles, + ... + }: + { + # Analog to 'perSystem' of flake-parts. + # For every instance of this service we will add a nixosModule to a morning-machine + nixosModule = + { config, ... }: + { + # Interaction examples what you could do here: + # - Get some settings of this machine + # settings.ipRanges + # + # - Get all evening names: + # allEveningNames = lib.attrNames roles.evening.machines + # + # - Get all roles of the machine: + # machine.roles + # + # - Get the settings that where applied to a specific evening machine: + # roles.evening.machines.peer1.settings + imports = [ ]; + environment.etc.hello.text = "${settings.greeting} World!"; + }; + }; }; + # The impnlementation of the evening role is in a separate file. We have kept + # the interface here, so we can see all settings of the service in one place, + # but you can also move it to the respective file + roles.evening = { + interface = + { lib, ... }: + { + options.greeting = lib.mkOption { + type = lib.types.str; + default = "Good evening"; + description = "The greeting to use"; + }; + }; + }; + imports = [ ./evening.nix ]; + + # This part gets applied to all machines, regardless of their role. perMachine = { machine, ... }: { - nixosModule = { - clan.core.vars.generators.hello = { - files.hello = { - secret = false; - }; - script = '' - echo "Hello world from ${machine.name}" > $out/hello - ''; + nixosModule = + { pkgs, ... }: + { + environment.systemPackages = [ + (pkgs.writeShellScriptBin "greet-world" '' + #!${pkgs.bash}/bin/bash + set -euo pipefail + + cat /etc/hello + echo " I'm ${machine.name}" + '') + ]; }; - }; }; } diff --git a/clanServices/hello-world/evening.nix b/clanServices/hello-world/evening.nix new file mode 100644 index 000000000..53707def9 --- /dev/null +++ b/clanServices/hello-world/evening.nix @@ -0,0 +1,12 @@ +{ + roles.evening.perInstance = + { settings, ... }: + { + nixosModule = + { ... }: + { + imports = [ ]; + environment.etc.hello.text = "${settings.greeting} World!"; + }; + }; +} diff --git a/clanServices/hello-world/tests/eval-tests.nix b/clanServices/hello-world/tests/eval-tests.nix index becd0f169..1cca5c54b 100644 --- a/clanServices/hello-world/tests/eval-tests.nix +++ b/clanServices/hello-world/tests/eval-tests.nix @@ -27,20 +27,10 @@ let module.name = "hello-world"; module.input = "self"; - roles.peer.machines.jon = { }; + roles.evening.machines.jon = { }; }; }; }; - # NOTE: - # If you wonder why 'self-zerotier-redux': - # A local module has prefix 'self', otherwise it is the name of the 'input' - # The rest is the name of the service as in the instance 'module.name'; - # - # -> ${module.input}-${module.name} - # In this case it is 'self-zerotier-redux' - # This is usually only used internally, but we can use it to test the evaluation of service module in isolation - # evaluatedService = - # testFlake.clanInternals.inventoryClass.distributedServices.importedModulesEvaluated.self-zerotier-redux.config; in { test_simple = { diff --git a/clanServices/hello-world/tests/vm/default.nix b/clanServices/hello-world/tests/vm/default.nix index 3777772bf..0f641444b 100644 --- a/clanServices/hello-world/tests/vm/default.nix +++ b/clanServices/hello-world/tests/vm/default.nix @@ -5,22 +5,35 @@ directory = ./.; inventory = { machines.peer1 = { }; + machines.peer2 = { }; instances."test" = { module.name = "hello-service"; module.input = "self"; - roles.peer.machines.peer1 = { }; + + # Assign the roles to the two machines + roles.morning.machines.peer1 = { }; + + roles.evening.machines.peer2 = { + # Set roles settings for the peers, where we want to differ from + # the role defaults + settings = { + greeting = "Good night"; + }; + }; }; }; }; testScript = - { nodes, ... }: + { ... }: '' start_all() - # peer1 should have the 'hello' file - value = peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.hello.files.hello.path}") - assert value.strip() == "Hello world from peer1", value + value = peer1.succeed("greet-world") + assert value.strip() == "Good morning World! I'm peer1", value + + value = peer2.succeed("greet-world") + assert value.strip() == "Good night World! I'm peer2", value ''; } diff --git a/formatter.nix b/formatter.nix index 51d68dd7b..5dbbc7754 100644 --- a/formatter.nix +++ b/formatter.nix @@ -61,6 +61,7 @@ "pkgs/clan-cli/clan_cli/tests/data/sshd_config" "pkgs/clan-vm-manager/.vscode/lhebendanz.weaudit" "pkgs/clan-vm-manager/bin/clan-vm-manager" + "clanServices/hello-world/default.nix" "sops/secrets/test-backup-age.key/secret" ]; treefmt.settings.formatter.ruff-format.includes = [