diff --git a/flake.nix b/flake.nix index be6a1db8c..341c5b848 100644 --- a/flake.nix +++ b/flake.nix @@ -53,6 +53,8 @@ ./nixosModules/flake-module.nix ./pkgs/flake-module.nix ./templates/flake-module.nix + + ./inventory/flake-module.nix ]; } ); diff --git a/inventory/.envrc b/inventory/.envrc new file mode 100644 index 000000000..8d1c6842b --- /dev/null +++ b/inventory/.envrc @@ -0,0 +1,5 @@ +source_up + +watch_file flake-module.nix + +use flake .#inventory-schema --builders '' diff --git a/inventory/.vscode/settings.json b/inventory/.vscode/settings.json new file mode 100644 index 000000000..82a476e34 --- /dev/null +++ b/inventory/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "cue.toolsPath": "/nix/store/x9471mp522cdi4c9gc8dchvyx6v01b3f-cue-0.8.2/bin/cue", + "[cue]": { + "editor.formatOnSave": false + } +} \ No newline at end of file diff --git a/inventory/README.md b/inventory/README.md new file mode 100644 index 000000000..318d1bf09 --- /dev/null +++ b/inventory/README.md @@ -0,0 +1,57 @@ +# Inventory + +This part provides a specification for the inventory. + +It is used for design phase and as validation helper. + +> Cue is less verbose and easier to understand and maintain than json-schema. +> Json-schema, if needed can be easily generated on-the fly. + +## Checking validity + +Directly check a json against the schema + +`cue vet inventory.json root.cue -d '#Root'` + +## Json schema + +Export the json-schema i.e. for usage in python / javascript / nix + +`cue export --out openapi root.cue` + +## Usage + +Comments are rendered as descriptions in the json schema. + +```cue +// A name of the clan (primarily shown by the UI) +name: string +``` + +Cue open sets. In the following `foo = {...}` means that the key `foo` can contain any arbitrary json object. + +```cue +foo: { ... } +``` + +Cue dynamic keys. + +```cue +[string]: { + attr: string +} +``` + +This is the schema of + +```json +{ + "a": { + "attr": "foo" + }, + "b": { + "attr": "bar" + } + // ... Indefinitely more dynamic keys of type "string" +} +``` diff --git a/inventory/example_flake.nix b/inventory/example_flake.nix new file mode 100644 index 000000000..b6f7fca67 --- /dev/null +++ b/inventory/example_flake.nix @@ -0,0 +1,119 @@ +{ + description = ""; + + inputs.clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz"; + + outputs = + { clan-core, ... }: + let + pkgs = clan-core.inputs.nixpkgs.legacyPackages.${system}; + # Usage see: https://docs.clan.lol + + # nice_flake_interface -> buildClan() -> inventory -> buildClanFromInventory() -> nixosConfigurations + + system = "x86_64-linux"; + + clan = + clan-core.lib.buildClanFromInventory [ + # Inventory 0 (loads the json file managed by the Python API) + (builtins.fromJSON (builtins.readFile ./inventory.json)) + # -> + # { + # services."backups_1".autoIncludeMachines = true; + # services."backups_1".module = "borgbackup"; + # ... etc. + # } + ] + ++ buildInventory { + clanName = "nice_flake_interface"; + description = "A nice flake interface"; + icon = "assets/icon.png"; + machines = { + jon = { + # Just regular nixos/clan configuration ? + # config = { + # imports = [ + # ./modules/shared.nix + # ./machines/jon/configuration.nix + # ]; + # nixpkgs.hostPlatform = system; + # # Set this for clan commands use ssh i.e. `clan machines update` + # # If you change the hostname, you need to update this line to root@ + # # This only works however if you have avahi running on your admin machine else use IP + # clan.networking.targetHost = pkgs.lib.mkDefault "root@jon"; + # # ssh root@flash-installer.local lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT + # disko.devices.disk.main = { + # device = "/dev/disk/by-id/__CHANGE_ME__"; + # }; + # # IMPORTANT! Add your SSH key here + # # e.g. > cat ~/.ssh/id_ed25519.pub + # users.users.root.openssh.authorizedKeys.keys = throw '' + # Don't forget to add your SSH key here! + # users.users.root.openssh.authorizedKeys.keys = [ "" ] + # ''; + # # Zerotier needs one controller to accept new nodes. Once accepted + # # the controller can be offline and routing still works. + # clan.networking.zerotier.controller.enable = true; + # }; + }; + }; + } + ++ [ + # Low level inventory overrides (comes at the end) + { + services."backups_2".autoIncludeMachines = true; + services."backups_2".module = "borgbackup"; + } + ]; + + /* + # Type + + buildInventory :: { + clanName :: string + machines :: { + ${name} :: { + config :: { + # NixOS configuration + }; + }; + }; + # ... More mapped inventory options + # i.e. shared config for all machines + } -> Inventory + */ + buildInventory = + options: + let + # TODO: Map over machines + name = "jon"; + inventory = { + # Set the clan meta + meta.name = options.clanName; + meta.description = options.description; + meta.icon = options.icon; + # Declare the services + # This "nixos" module simply provides the usual NixOS configuration options. + services."nixos".module = "nixos"; + services."nixos".machineConfig.${name}.config = options.machines.${name}.config; + + # Declare the machines + machines.${name} = { + name = options.machines.${name}.meta.name; + description = options.machines.${name}.meta.description; + icon = options.machines.${name}.meta.icon; + system = options.machines.${name}.config.nixpkgs.hostPlatform; + }; + }; + in + inventory; + in + { + # all machines managed by Clan + inherit (clan) nixosConfigurations clanInternals; + # add the Clan cli tool to the dev shell + devShells.${system}.default = pkgs.mkShell { + packages = [ clan-core.packages.${system}.clan-cli ]; + }; + }; +} diff --git a/inventory/flake-module.nix b/inventory/flake-module.nix new file mode 100644 index 000000000..4519f8e2e --- /dev/null +++ b/inventory/flake-module.nix @@ -0,0 +1,18 @@ +{ ... }: +{ + perSystem = + { pkgs, config, ... }: + { + packages.inventory-schema = pkgs.stdenv.mkDerivation { + name = "inventory-schema"; + src = ./src; + + buildInputs = [ pkgs.cue ]; + + installPhase = '' + mkdir -p $out + ''; + }; + devShells.inventory-schema = pkgs.mkShell { inputsFrom = [ config.packages.inventory-schema ]; }; + }; +} diff --git a/inventory/inventory.json b/inventory/inventory.json new file mode 100644 index 000000000..0ee272e7e --- /dev/null +++ b/inventory/inventory.json @@ -0,0 +1,34 @@ +{ + "meta": { + "name": "My clan", + "description": "My clan description", + "icon": "assets/clan-icon.png" + }, + "services": { + "service_ref": { + "meta": { + "name": "backup" + }, + "autoIncludeMachines": true, + "module": "core" + } + }, + "machines": { + "jon_machine": { + "name": "jon_machine", + "description": "Jon's machine", + "icon": "assets/icon.png", + "system": "x86_64-linux" + } + }, + "users": { + "mic": { + "autoInclude": false, + "schemas": ["ssh-user"], + "config": { + "sshKey": "...", + "username": "mic92" + } + } + } +} diff --git a/inventory/src/cue.mod/module.cue b/inventory/src/cue.mod/module.cue new file mode 100644 index 000000000..773cae9d3 --- /dev/null +++ b/inventory/src/cue.mod/module.cue @@ -0,0 +1,2 @@ +module: "clan.lol/inventory" +language: version: "v0.8.2" \ No newline at end of file diff --git a/inventory/src/machines/machines.cue b/inventory/src/machines/machines.cue new file mode 100644 index 000000000..36d77e378 --- /dev/null +++ b/inventory/src/machines/machines.cue @@ -0,0 +1,8 @@ +package machines + +#machine: machines: [string]: { + "name": string, + "description": string, + "icon": string, + "system": string +} \ No newline at end of file diff --git a/inventory/src/root.cue b/inventory/src/root.cue new file mode 100644 index 000000000..c3b7496a4 --- /dev/null +++ b/inventory/src/root.cue @@ -0,0 +1,28 @@ +package inventory + +import ( + "clan.lol/inventory/services" + "clan.lol/inventory/machines" + "clan.lol/inventory/users" +) + +@jsonschema(schema="http://json-schema.org/schema#") +#Root: { + meta: { + // A name of the clan (primarily shown by the UI) + name: string + // A description of the clan + description: string + // The icon path + icon: string + } + + // A map of services + services.#service + + // A map of machines + machines.#machine + + // A map of users + users.#user +} diff --git a/inventory/src/services/services.cue b/inventory/src/services/services.cue new file mode 100644 index 000000000..c24ef69f4 --- /dev/null +++ b/inventory/src/services/services.cue @@ -0,0 +1,21 @@ +package services + +#service: services: [string]: { + autoIncludeMachines: bool, + meta: { + name: string, + }, + // TODO: this should be the list of avilable modules + module: string, + machineConfig: { + [string]: { + config: { + defaultUser?: string + } + } + }, + globalConfig: { + // Should be one of the avilable users + defaultUser?: string, + } +} \ No newline at end of file diff --git a/inventory/src/users/users.cue b/inventory/src/users/users.cue new file mode 100644 index 000000000..bdd54067d --- /dev/null +++ b/inventory/src/users/users.cue @@ -0,0 +1,9 @@ +package users + +#user: users: [string]: { + "autoInclude": bool, + "schemas": [ string ], + "config": { + ... + } +} \ No newline at end of file