forked from fediversity/fediversity
		
	
		
			
				
	
	
		
			712 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			712 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{
 | 
						|
  pkgs,
 | 
						|
  lib,
 | 
						|
  config,
 | 
						|
  inputs,
 | 
						|
  sources,
 | 
						|
  ...
 | 
						|
}:
 | 
						|
let
 | 
						|
  inherit (lib) mkOption types;
 | 
						|
  inherit (lib.types)
 | 
						|
    attrTag
 | 
						|
    attrsOf
 | 
						|
    deferredModuleWith
 | 
						|
    functionTo
 | 
						|
    nullOr
 | 
						|
    optionType
 | 
						|
    raw
 | 
						|
    str
 | 
						|
    submodule
 | 
						|
    ;
 | 
						|
  inherit (pkgs.callPackage ./utils.nix { }) toBash;
 | 
						|
  withPackages = packages: {
 | 
						|
    makeWrapperArgs = [
 | 
						|
      "--prefix"
 | 
						|
      "PATH"
 | 
						|
      ":"
 | 
						|
      "${lib.makeBinPath packages}"
 | 
						|
    ];
 | 
						|
  };
 | 
						|
  writeConfig =
 | 
						|
    {
 | 
						|
      system,
 | 
						|
      caller,
 | 
						|
      root-path,
 | 
						|
      deployment-type,
 | 
						|
      deployment-name,
 | 
						|
      args,
 | 
						|
    }:
 | 
						|
    # having a `caller` location and (serializable) `args`, we know
 | 
						|
    # enough to call it again to extract different info elsewhere later.
 | 
						|
    # we use this to make a deployment script using the desired nixos config,
 | 
						|
    # which would otherwise not be serializable, while nix also makes it hard to
 | 
						|
    # produce its derivation to pass thru without a `nix-instantiate` call,
 | 
						|
    # which in turn would need to be passed the (unserializable) nixos config.
 | 
						|
    builtins.toString (
 | 
						|
      pkgs.writers.writeText "configuration.nix" ''
 | 
						|
        import ${root-path}/deployment/nixos.nix {
 | 
						|
          system = "${system}";
 | 
						|
          configuration = (import "${root-path}/${caller}" (builtins.fromJSON "${
 | 
						|
            lib.replaceStrings [ "\"" ] [ "\\\"" ] (lib.strings.toJSON args)
 | 
						|
          }")).${deployment-name}.${deployment-type}.nixos-configuration;
 | 
						|
        }
 | 
						|
      ''
 | 
						|
    );
 | 
						|
 | 
						|
  functionType = submodule ./function.nix;
 | 
						|
  application-resources = submodule {
 | 
						|
    options.resources = mkOption {
 | 
						|
      # TODO: maybe transpose, and group the resources by type instead
 | 
						|
      type = attrsOf (
 | 
						|
        attrTag (
 | 
						|
          lib.mapAttrs (_name: resource: mkOption { type = submodule resource.request; }) config.resources
 | 
						|
        )
 | 
						|
      );
 | 
						|
    };
 | 
						|
  };
 | 
						|
  nixops4Deployment = types.deferredModuleWith {
 | 
						|
    staticModules = [
 | 
						|
      inputs.nixops4.modules.nixops4Deployment.default
 | 
						|
 | 
						|
      {
 | 
						|
        _class = "nixops4Deployment";
 | 
						|
        _module.args = {
 | 
						|
          resourceProviderSystem = pkgs.system;
 | 
						|
          resources = { };
 | 
						|
        };
 | 
						|
      }
 | 
						|
    ];
 | 
						|
  };
 | 
						|
  nixos-configuration = mkOption {
 | 
						|
    description = "A NixOS configuration.";
 | 
						|
    type = raw;
 | 
						|
  };
 | 
						|
  host-ssh = mkOption {
 | 
						|
    description = "SSH connection info to connect to a single host.";
 | 
						|
    type = submodule {
 | 
						|
      options = {
 | 
						|
        host = mkOption {
 | 
						|
          description = "the host to access by SSH";
 | 
						|
          type = str;
 | 
						|
        };
 | 
						|
        username = mkOption {
 | 
						|
          description = "the SSH user to use";
 | 
						|
          type = nullOr str;
 | 
						|
          default = null;
 | 
						|
        };
 | 
						|
        key-file = mkOption {
 | 
						|
          description = "path to the user's SSH private key";
 | 
						|
          type = nullOr str;
 | 
						|
          example = "/root/.ssh/id_ed25519";
 | 
						|
        };
 | 
						|
        sshOpts = mkOption {
 | 
						|
          description = "Extra SSH options (`-o`) to use.";
 | 
						|
          type = types.listOf str;
 | 
						|
          default = [ ];
 | 
						|
          example = "ConnectTimeout=60";
 | 
						|
        };
 | 
						|
      };
 | 
						|
    };
 | 
						|
  };
 | 
						|
  # FIXME allow custom deployment types
 | 
						|
  # FIXME make deployments environment resources?
 | 
						|
  deployment-type = attrTag {
 | 
						|
    ssh-host = mkOption {
 | 
						|
      description = "A deployment by SSH to update a single existing NixOS host.";
 | 
						|
      type = submodule (ssh-host: {
 | 
						|
        options = {
 | 
						|
          system = mkOption {
 | 
						|
            description = "The architecture of the system to deploy to.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          inherit nixos-configuration;
 | 
						|
          ssh = host-ssh;
 | 
						|
          caller = mkOption {
 | 
						|
            description = "The calling module to obtain the NixOS configuration from.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          args = mkOption {
 | 
						|
            description = "The arguments with which to call the module to obtain the NixOS configuration.";
 | 
						|
            type = types.attrs;
 | 
						|
          };
 | 
						|
          deployment-name = mkOption {
 | 
						|
            description = "The name of the deployment for which to obtain the NixOS configuration.";
 | 
						|
            type = types.str;
 | 
						|
            default = "default";
 | 
						|
          };
 | 
						|
          root-path = mkOption {
 | 
						|
            description = "The path to the root of the repository.";
 | 
						|
            type = types.path;
 | 
						|
          };
 | 
						|
          run = mkOption {
 | 
						|
            type = types.package;
 | 
						|
            # error: The option `ssh-deployment.ssh-host.run' is read-only, but it's set multiple times.
 | 
						|
            # readOnly = true;
 | 
						|
            default =
 | 
						|
              let
 | 
						|
                inherit (ssh-host.config)
 | 
						|
                  system
 | 
						|
                  ssh
 | 
						|
                  caller
 | 
						|
                  args
 | 
						|
                  deployment-name
 | 
						|
                  root-path
 | 
						|
                  ;
 | 
						|
                inherit (ssh)
 | 
						|
                  host
 | 
						|
                  username
 | 
						|
                  key-file
 | 
						|
                  sshOpts
 | 
						|
                  ;
 | 
						|
                environment = {
 | 
						|
                  key_file = key-file;
 | 
						|
                  ssh_opts = sshOpts;
 | 
						|
                  inherit
 | 
						|
                    host
 | 
						|
                    username
 | 
						|
                    ;
 | 
						|
                  nixos_conf = writeConfig {
 | 
						|
                    inherit
 | 
						|
                      system
 | 
						|
                      caller
 | 
						|
                      args
 | 
						|
                      deployment-name
 | 
						|
                      root-path
 | 
						|
                      ;
 | 
						|
                    deployment-type = "ssh-host";
 | 
						|
                  };
 | 
						|
                };
 | 
						|
              in
 | 
						|
              pkgs.writers.writeBashBin "deploy-sh.sh"
 | 
						|
                (withPackages [
 | 
						|
                  pkgs.jq
 | 
						|
                ])
 | 
						|
                ''
 | 
						|
                  env ${
 | 
						|
                    toString (lib.mapAttrsToList (k: v: "${k}=\"${toBash v}\"") environment)
 | 
						|
                  } bash ./deployment/run/ssh-single-host/run.sh
 | 
						|
                '';
 | 
						|
          };
 | 
						|
        };
 | 
						|
      });
 | 
						|
    };
 | 
						|
    nixops4 = mkOption {
 | 
						|
      description = "A NixOps4 NixOS deployment. For an example, see https://github.com/nixops4/nixops4-nixos/blob/main/example/deployment.nix.";
 | 
						|
      type = nixops4Deployment;
 | 
						|
    };
 | 
						|
    tf-host = mkOption {
 | 
						|
      description = "A Terraform deployment by SSH to update a single existing NixOS host.";
 | 
						|
      type = submodule (tf-host: {
 | 
						|
        options = {
 | 
						|
          system = mkOption {
 | 
						|
            description = "The architecture of the system to deploy to.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          inherit nixos-configuration;
 | 
						|
          ssh = host-ssh;
 | 
						|
          caller = mkOption {
 | 
						|
            description = "The calling module to obtain the NixOS configuration from.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          args = mkOption {
 | 
						|
            description = "The arguments with which to call the module to obtain the NixOS configuration.";
 | 
						|
            type = types.attrs;
 | 
						|
          };
 | 
						|
          deployment-name = mkOption {
 | 
						|
            description = "The name of the deployment for which to obtain the NixOS configuration.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          root-path = mkOption {
 | 
						|
            description = "The path to the root of the repository.";
 | 
						|
            type = types.path;
 | 
						|
          };
 | 
						|
          httpBackend = mkOption {
 | 
						|
            description = "environment variables to configure the TF HTTP back-end, see <https://developer.hashicorp.com/terraform/language/backend/http#configuration-variables>";
 | 
						|
            type = types.attrsOf (types.either types.str types.int);
 | 
						|
          };
 | 
						|
          run = mkOption {
 | 
						|
            type = types.package;
 | 
						|
            # error: The option `tf-deployment.tf-host.run' is read-only, but it's set multiple times.
 | 
						|
            # readOnly = true;
 | 
						|
            default =
 | 
						|
              let
 | 
						|
                inherit (tf-host.config)
 | 
						|
                  system
 | 
						|
                  ssh
 | 
						|
                  caller
 | 
						|
                  args
 | 
						|
                  deployment-name
 | 
						|
                  root-path
 | 
						|
                  httpBackend
 | 
						|
                  ;
 | 
						|
                inherit (ssh)
 | 
						|
                  host
 | 
						|
                  username
 | 
						|
                  key-file
 | 
						|
                  sshOpts
 | 
						|
                  ;
 | 
						|
                environment = {
 | 
						|
                  key_file = key-file;
 | 
						|
                  ssh_opts = sshOpts;
 | 
						|
                  inherit
 | 
						|
                    host
 | 
						|
                    username
 | 
						|
                    ;
 | 
						|
                  nixos_conf = writeConfig {
 | 
						|
                    inherit
 | 
						|
                      system
 | 
						|
                      caller
 | 
						|
                      args
 | 
						|
                      deployment-name
 | 
						|
                      root-path
 | 
						|
                      ;
 | 
						|
                    deployment-type = "tf-host";
 | 
						|
                  };
 | 
						|
                };
 | 
						|
                tf-env = pkgs.callPackage ./run/tf-env.nix {
 | 
						|
                  inherit httpBackend;
 | 
						|
                  tfPackage = pkgs.callPackage ./run/tf-single-host/tf.nix { };
 | 
						|
                  tfDirs = [ "deployment/run/tf-single-host" ];
 | 
						|
                };
 | 
						|
              in
 | 
						|
              pkgs.writers.writeBashBin "deploy-tf.sh"
 | 
						|
                (withPackages [
 | 
						|
                  pkgs.jq
 | 
						|
                  (pkgs.callPackage ./run/tf-single-host/tf.nix { })
 | 
						|
                ])
 | 
						|
                ''
 | 
						|
                  env ${toString (lib.mapAttrsToList (k: v: "TF_VAR_${k}=\"${toBash v}\"") environment)} \
 | 
						|
                  ${toString (lib.mapAttrsToList (k: v: "${k}=\"${toBash v}\"") httpBackend)} \
 | 
						|
                  tf_env=${tf-env} bash ./deployment/run/tf-single-host/run.sh
 | 
						|
                '';
 | 
						|
          };
 | 
						|
        };
 | 
						|
      });
 | 
						|
    };
 | 
						|
    tf-proxmox-host = mkOption {
 | 
						|
      description = "A Terraform deployment by SSH to update a single existing NixOS host.";
 | 
						|
      type = submodule (tf-host: {
 | 
						|
        options = {
 | 
						|
          system = mkOption {
 | 
						|
            description = "The architecture of the system to deploy to.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          inherit nixos-configuration;
 | 
						|
          ssh = host-ssh;
 | 
						|
          # TODO: add proxmox info
 | 
						|
          module = mkOption {
 | 
						|
            description = "The module to call to obtain the NixOS configuration from.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          args = mkOption {
 | 
						|
            description = "The arguments with which to call the module to obtain the NixOS configuration.";
 | 
						|
            type = types.attrs;
 | 
						|
          };
 | 
						|
          deployment-name = mkOption {
 | 
						|
            description = "The name of the deployment for which to obtain the NixOS configuration.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          root-path = mkOption {
 | 
						|
            description = "The path to the root of the repository.";
 | 
						|
            type = types.path;
 | 
						|
          };
 | 
						|
          proxmox-user = mkOption {
 | 
						|
            description = "The ProxmoX user to use.";
 | 
						|
            type = types.str;
 | 
						|
            default = "root@pam";
 | 
						|
          };
 | 
						|
          # TODO: is sensitivity here handled properly?
 | 
						|
          proxmox-password = mkOption {
 | 
						|
            description = "The ProxmoX password to use.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          node-name = mkOption {
 | 
						|
            description = "the name of the ProxmoX node to use.";
 | 
						|
            type = types.str;
 | 
						|
          };
 | 
						|
          httpBackend = mkOption {
 | 
						|
            description = "environment variables to configure the TF HTTP back-end, see <https://developer.hashicorp.com/terraform/language/backend/http#configuration-variables>";
 | 
						|
            type = types.attrsOf (types.either types.str types.int);
 | 
						|
          };
 | 
						|
          bridge = mkOption {
 | 
						|
            description = "The name of the network bridge (defaults to vmbr0).";
 | 
						|
            type = types.str;
 | 
						|
            default = "vmbr0";
 | 
						|
          };
 | 
						|
          vlanId = mkOption {
 | 
						|
            description = "The VLAN identifier.";
 | 
						|
            type = types.int;
 | 
						|
            default = 0;
 | 
						|
          };
 | 
						|
          imageDatastoreId = mkOption {
 | 
						|
            description = "ID of the datastore of the image.";
 | 
						|
            type = types.str;
 | 
						|
            default = "local";
 | 
						|
          };
 | 
						|
          vmDatastoreId = mkOption {
 | 
						|
            description = "ID of the datastore of the VM.";
 | 
						|
            type = types.str;
 | 
						|
            default = "local";
 | 
						|
          };
 | 
						|
          cdDatastoreId = mkOption {
 | 
						|
            description = "ID of the datastore of the virtual CD-rom drive to use for cloud-init.";
 | 
						|
            type = types.str;
 | 
						|
            default = "local";
 | 
						|
          };
 | 
						|
          ipv4Gateway = mkOption {
 | 
						|
            description = "Gateway for IPv4.";
 | 
						|
            type = types.str;
 | 
						|
            default = "";
 | 
						|
          };
 | 
						|
          ipv4Address = mkOption {
 | 
						|
            description = "IPv4 address.";
 | 
						|
            type = types.str;
 | 
						|
            default = "";
 | 
						|
          };
 | 
						|
          ipv6Gateway = mkOption {
 | 
						|
            description = "Gateway for IPv6.";
 | 
						|
            type = types.str;
 | 
						|
            default = "";
 | 
						|
          };
 | 
						|
          ipv6Address = mkOption {
 | 
						|
            description = "IPv6 address.";
 | 
						|
            type = types.str;
 | 
						|
            default = "";
 | 
						|
          };
 | 
						|
          run = mkOption {
 | 
						|
            type = types.package;
 | 
						|
            # error: The option `tf-deployment.tf-host.run' is read-only, but it's set multiple times.
 | 
						|
            # readOnly = true;
 | 
						|
            default =
 | 
						|
              let
 | 
						|
                inherit (tf-host.config)
 | 
						|
                  system
 | 
						|
                  ssh
 | 
						|
                  module
 | 
						|
                  args
 | 
						|
                  deployment-name
 | 
						|
                  httpBackend
 | 
						|
                  root-path
 | 
						|
                  proxmox-user
 | 
						|
                  proxmox-password
 | 
						|
                  node-name
 | 
						|
                  bridge
 | 
						|
                  vlanId
 | 
						|
                  imageDatastoreId
 | 
						|
                  vmDatastoreId
 | 
						|
                  cdDatastoreId
 | 
						|
                  ipv4Gateway
 | 
						|
                  ipv4Address
 | 
						|
                  ipv6Gateway
 | 
						|
                  ipv6Address
 | 
						|
                  ;
 | 
						|
                inherit (ssh)
 | 
						|
                  host
 | 
						|
                  username
 | 
						|
                  key-file
 | 
						|
                  sshOpts
 | 
						|
                  ;
 | 
						|
                deployment-type = "tf-proxmox-host";
 | 
						|
                nixos_conf = writeConfig {
 | 
						|
                  inherit
 | 
						|
                    system
 | 
						|
                    module
 | 
						|
                    args
 | 
						|
                    deployment-name
 | 
						|
                    root-path
 | 
						|
                    deployment-type
 | 
						|
                    ;
 | 
						|
                };
 | 
						|
                # machine = import nixos_conf;
 | 
						|
                machine = import ./nixos.nix {
 | 
						|
                  inherit sources system;
 | 
						|
                  configuration = tf-host.config.nixos-configuration;
 | 
						|
                  # configuration = { ... }: {
 | 
						|
                  #   imports = [
 | 
						|
                  #     tf-host.config.nixos-configuration
 | 
						|
                  #     ../infra/common/nixos/repart.nix
 | 
						|
                  #   ];
 | 
						|
                  # };
 | 
						|
                };
 | 
						|
                # inherit (machine.config.boot.uki) name;
 | 
						|
                name = "monkey";
 | 
						|
 | 
						|
                # # systemd-repart
 | 
						|
                # better for cross-compilation, worse for pre-/post-processing, doesn't support MBR: https://github.com/nix-community/disko/issues/550#issuecomment-2503736973
 | 
						|
                # raw = "${machine.config.system.build.image}/${name}.raw";
 | 
						|
 | 
						|
                # disko
 | 
						|
                # worse for cross-compilation, better for pre-/post-processing, needs manual `imageSize`, random failures: https://github.com/nix-community/disko/issues/550#issuecomment-2503736973
 | 
						|
                raw = "${machine.config.system.build.diskoImages}/main.raw";
 | 
						|
 | 
						|
                # # nixos-generators: note it can straight-up do qcow2 as well, if we settle for nixos-generators
 | 
						|
                # # `mount: /run/nixos-etc-metadata.J3iARWBtna: failed to setup loop device for /nix/store/14ka2bmx6lcnyr8ah2yl787sqcgxz5ni-etc-metadata.erofs.`
 | 
						|
                # # [`Error: Failed to parse os-release`](https://github.com/NixOS/nixpkgs/blob/5b1861820a3bc4ef2f60b0afcffb71ea43f5d000/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs#L151)
 | 
						|
                # raw = let
 | 
						|
                # # TODO parameterize things to let this flow into the terraform
 | 
						|
                # # btw qcow can be made by nixos-generators (qcow, qcow-efi) or by `image.repart`
 | 
						|
                # # wait, so i generate an image for the nixos config from the data model? how would i then propagate that to deploy?
 | 
						|
                # gen = import "${pkgs.nixos-generators}/share/nixos-generator/nixos-generate.nix" {
 | 
						|
                #   inherit system formatConfig;
 | 
						|
                #   inherit (sources) nixpkgs;
 | 
						|
                #   configuration = tf-host.config.nixos-configuration;
 | 
						|
                # };
 | 
						|
                # in
 | 
						|
                #   "${gen.config.system.build.${formatAttr}}/nixos${fileExtension}";
 | 
						|
 | 
						|
                environment = {
 | 
						|
                  key_file = key-file;
 | 
						|
                  ssh_opts = sshOpts;
 | 
						|
                  inherit
 | 
						|
                    host
 | 
						|
                    nixos_conf
 | 
						|
                    bridge
 | 
						|
                    ;
 | 
						|
                  node_name = node-name;
 | 
						|
                  proxmox_user = proxmox-user;
 | 
						|
                  proxmox_password = proxmox-password;
 | 
						|
                  ssh_user = username;
 | 
						|
                  vlan_id = vlanId;
 | 
						|
                  image_datastore_id = imageDatastoreId;
 | 
						|
                  vm_datastore_id = vmDatastoreId;
 | 
						|
                  cd_datastore_id = cdDatastoreId;
 | 
						|
                  ipv4_gateway = ipv4Gateway;
 | 
						|
                  ipv4_address = ipv4Address;
 | 
						|
                  ipv6_gateway = ipv6Gateway;
 | 
						|
                  ipv6_address = ipv6Address;
 | 
						|
                };
 | 
						|
                tf-env = pkgs.callPackage ./run/tf-env.nix {
 | 
						|
                  inherit httpBackend;
 | 
						|
                  tfPackage = pkgs.callPackage ./run/tf-proxmox/tf.nix { };
 | 
						|
                  tfDirs = [
 | 
						|
                    "deployment/run/tf-single-host"
 | 
						|
                    "deployment/run/tf-proxmox"
 | 
						|
                  ];
 | 
						|
                };
 | 
						|
                vm_name = "test14";
 | 
						|
              in
 | 
						|
              lib.trace (lib.strings.toJSON environment) pkgs.writers.writeBashBin "deploy-tf-proxmox.sh"
 | 
						|
                (withPackages [
 | 
						|
                  pkgs.jq
 | 
						|
                  pkgs.qemu
 | 
						|
                  pkgs.nixos-generators
 | 
						|
                  pkgs.httpie
 | 
						|
                  (pkgs.callPackage ./run/tf-proxmox/tf.nix { inherit sources; })
 | 
						|
                ])
 | 
						|
                ''
 | 
						|
                  set -e
 | 
						|
 | 
						|
                  # TODO after install: $nix_host_keys
 | 
						|
                  # cp $tmpdir/${vm_name}_host_key /mnt/etc/ssh/ssh_host_ed25519_key
 | 
						|
                  # chmod 600 /mnt/etc/ssh/ssh_host_ed25519_key
 | 
						|
                  # cp $tmpdir/${vm_name}_host_key.pub /mnt/etc/ssh/ssh_host_ed25519_key.pub
 | 
						|
                  # chmod 644 /mnt/etc/ssh/ssh_host_ed25519_key.pub
 | 
						|
 | 
						|
                  # nixos-generate gives the burden of building revisions, while systemd-repart handles partitioning ~~at the burden of version revisions~~
 | 
						|
                  # .qcow2 is around half the size of .raw, on top of supporting backups - be it apparently at the cost of performance
 | 
						|
                  qemu-img convert -f raw -O qcow2 -C "${raw}" /tmp/${name}.qcow2
 | 
						|
 | 
						|
                  # ls -l ${raw}
 | 
						|
                  # ls -l /tmp/${name}.qcow2
 | 
						|
 | 
						|
                  env ${toString (lib.mapAttrsToList (k: v: "TF_VAR_${k}=\"${toBash v}\"") environment)} \
 | 
						|
                  ${toString (lib.mapAttrsToList (k: v: "${k}=\"${toBash v}\"") httpBackend)} \
 | 
						|
                  TF_VAR_image=/tmp/${name}.qcow2 \
 | 
						|
                  tf_env=${tf-env} bash ./deployment/run/tf-proxmox/run.sh
 | 
						|
                '';
 | 
						|
            # # don't really wanna deal with having to do versioned updates for now
 | 
						|
            # qemu-img convert -f raw -O qcow2 -C "${machine.config.system.build.image}/${name}.raw" /tmp/${name}.qcow2
 | 
						|
          };
 | 
						|
        };
 | 
						|
      });
 | 
						|
    };
 | 
						|
  };
 | 
						|
in
 | 
						|
{
 | 
						|
  options = {
 | 
						|
    resources = mkOption {
 | 
						|
      description = "Collection of deployment resources that can be required by applications and policed by hosting providers";
 | 
						|
      type = attrsOf (
 | 
						|
        submodule (
 | 
						|
          { ... }:
 | 
						|
          {
 | 
						|
            _class = "fediversity-resource";
 | 
						|
            options = {
 | 
						|
              description = mkOption {
 | 
						|
                description = "Description of the resource to help application module authors and hosting providers to work with it";
 | 
						|
                type = types.str;
 | 
						|
              };
 | 
						|
              request = mkOption {
 | 
						|
                description = "Options for declaring resource requirements by an application, a description of how the resource is consumed or accessed";
 | 
						|
                type = deferredModuleWith { staticModules = [ { _class = "fediversity-resource-request"; } ]; };
 | 
						|
              };
 | 
						|
              policy = mkOption {
 | 
						|
                description = "Options for configuring the resource policy for the hosting provider, a description of how the resource is made available";
 | 
						|
                type = deferredModuleWith {
 | 
						|
                  staticModules = [
 | 
						|
                    (policy: {
 | 
						|
                      _class = "fediversity-resource-policy";
 | 
						|
                      options.resource-type = mkOption {
 | 
						|
                        description = "The type of resource this policy configures";
 | 
						|
                        type = types.optionType;
 | 
						|
                      };
 | 
						|
                      # TODO(@fricklerhandwerk): we may want to make the function type explicit here: `application-resources -> resource-type`
 | 
						|
                      # and then also rename this to be consistent with the application's resource mapping
 | 
						|
                      options.apply = mkOption {
 | 
						|
                        description = "Apply the policy to a request";
 | 
						|
                        type = functionTo policy.config.resource-type;
 | 
						|
                      };
 | 
						|
                    })
 | 
						|
                  ];
 | 
						|
                };
 | 
						|
              };
 | 
						|
            };
 | 
						|
          }
 | 
						|
        )
 | 
						|
      );
 | 
						|
    };
 | 
						|
    applications = mkOption {
 | 
						|
      description = "Collection of Fediversity applications";
 | 
						|
      type = attrsOf (
 | 
						|
        submodule (application: {
 | 
						|
          _class = "fediversity-application";
 | 
						|
          options = {
 | 
						|
            description = mkOption {
 | 
						|
              description = "Description to be shown in the application overview";
 | 
						|
              type = types.str;
 | 
						|
            };
 | 
						|
            module = mkOption {
 | 
						|
              description = "Operator-facing configuration options for the application";
 | 
						|
              type = deferredModuleWith { staticModules = [ { _class = "fediversity-application-config"; } ]; };
 | 
						|
            };
 | 
						|
            implementation = mkOption {
 | 
						|
              description = "Mapping of application configuration to deployment resources, a description of what an application needs to run";
 | 
						|
              type = application.config.config-mapping.function-type;
 | 
						|
            };
 | 
						|
            resources = mkOption {
 | 
						|
              description = "Compute resources required by an application";
 | 
						|
              type = application.config.config-mapping.function-type;
 | 
						|
              readOnly = true;
 | 
						|
              default = application.config.config-mapping.apply;
 | 
						|
            };
 | 
						|
            # TODO(@fricklerhandwerk): this needs a better name
 | 
						|
            config-mapping = mkOption {
 | 
						|
              description = "Function type for the mapping from application configuration to required resources";
 | 
						|
              type = functionType;
 | 
						|
              readOnly = true;
 | 
						|
              default = {
 | 
						|
                input-type = submodule application.config.module;
 | 
						|
                output-type = application-resources;
 | 
						|
                implementation = application.config.implementation;
 | 
						|
              };
 | 
						|
            };
 | 
						|
          };
 | 
						|
        })
 | 
						|
      );
 | 
						|
    };
 | 
						|
    environments = mkOption {
 | 
						|
      description = "Run-time environments for Fediversity applications to be deployed to";
 | 
						|
      type = attrsOf (
 | 
						|
        submodule (environment: {
 | 
						|
          _class = "fediversity-environment";
 | 
						|
          options = {
 | 
						|
            resources = mkOption {
 | 
						|
              description = ''
 | 
						|
                Resources made available by the hosting provider, and their policies.
 | 
						|
 | 
						|
                Setting this is optional, but provides a place to declare that information for programmatic use in the resource mapping.
 | 
						|
              '';
 | 
						|
              # TODO: maybe transpose, and group the resources by type instead
 | 
						|
              type = attrsOf (
 | 
						|
                attrTag (
 | 
						|
                  lib.mapAttrs (_name: resource: mkOption { type = submodule resource.policy; }) config.resources
 | 
						|
                )
 | 
						|
              );
 | 
						|
            };
 | 
						|
            implementation = mkOption {
 | 
						|
              description = "Mapping of resources required by applications to available resources; the result can be deployed";
 | 
						|
              type = environment.config.resource-mapping.function-type;
 | 
						|
            };
 | 
						|
            resource-mapping = mkOption {
 | 
						|
              description = "Function type for the mapping from resources to a deployment";
 | 
						|
              type = functionType;
 | 
						|
              readOnly = true;
 | 
						|
              default = {
 | 
						|
                input-type = submodule {
 | 
						|
                  options = {
 | 
						|
                    deployment-name = mkOption {
 | 
						|
                      type = types.str;
 | 
						|
                    };
 | 
						|
                    required-resources = mkOption {
 | 
						|
                      type = attrsOf application-resources;
 | 
						|
                    };
 | 
						|
                  };
 | 
						|
                };
 | 
						|
                output-type = deployment-type;
 | 
						|
                implementation = environment.config.implementation;
 | 
						|
              };
 | 
						|
            };
 | 
						|
            config-mapping = mkOption {
 | 
						|
              description = "Mapping from a configuration to a deployment";
 | 
						|
              type = functionType;
 | 
						|
              readOnly = true;
 | 
						|
              default = {
 | 
						|
                input-type = submodule {
 | 
						|
                  options = {
 | 
						|
                    deployment-name = mkOption {
 | 
						|
                      type = types.str;
 | 
						|
                    };
 | 
						|
                    configuration = mkOption {
 | 
						|
                      type = config.configuration;
 | 
						|
                    };
 | 
						|
                  };
 | 
						|
                };
 | 
						|
                output-type = deployment-type;
 | 
						|
                implementation =
 | 
						|
                  {
 | 
						|
                    deployment-name,
 | 
						|
                    configuration,
 | 
						|
                  }:
 | 
						|
                  # TODO: check cfg.enable.true
 | 
						|
                  let
 | 
						|
                    required-resources = lib.mapAttrs (
 | 
						|
                      name: application-settings: config.applications.${name}.resources application-settings
 | 
						|
                    ) configuration.applications;
 | 
						|
                  in
 | 
						|
                  environment.config.resource-mapping.apply { inherit required-resources deployment-name; };
 | 
						|
              };
 | 
						|
            };
 | 
						|
            # TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`,
 | 
						|
            # which makes explicit which applications and environments are available.
 | 
						|
            # then the deployments can simply be the result of the function application baked into this module.
 | 
						|
            deployment = mkOption {
 | 
						|
              description = "Generate a deployment from a configuration, by applying an environment's resource policies to the applications' resource mappings";
 | 
						|
              type = environment.config.config-mapping.function-type;
 | 
						|
              readOnly = true;
 | 
						|
              default = environment.config.config-mapping.apply;
 | 
						|
            };
 | 
						|
          };
 | 
						|
        })
 | 
						|
      );
 | 
						|
    };
 | 
						|
    configuration = mkOption {
 | 
						|
      description = "Configuration type declaring options to be set by operators";
 | 
						|
      type = optionType;
 | 
						|
      readOnly = true;
 | 
						|
      default = submodule {
 | 
						|
        options = {
 | 
						|
          enable = lib.mkEnableOption "your Fediversity configuration";
 | 
						|
          applications = lib.mapAttrs (
 | 
						|
            _name: application:
 | 
						|
            mkOption {
 | 
						|
              description = application.description;
 | 
						|
              type = submodule application.module;
 | 
						|
              default = { };
 | 
						|
            }
 | 
						|
          ) config.applications;
 | 
						|
        };
 | 
						|
      };
 | 
						|
    };
 | 
						|
  };
 | 
						|
}
 |