From c2510eb3461c34334d880a89fb17e1dd3800d825 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Fri, 1 Aug 2025 16:20:22 +0200 Subject: [PATCH] WIP: proxmox deployment Signed-off-by: Kiara Grouwstra --- .forgejo/workflows/ci.yaml | 6 + .../check/common/data-model-options.nix | 13 + deployment/check/common/data-model.nix | 45 ++- deployment/check/data-model-ssh/nixosTest.nix | 1 + .../check/data-model-tf-proxmox/constants.nix | 11 + .../check/data-model-tf-proxmox/default.nix | 48 +++ .../check/data-model-tf-proxmox/nixosTest.nix | 277 ++++++++++++++++++ deployment/data-model.nix | 104 +++++++ deployment/flake-part.nix | 7 +- deployment/run/tf-proxmox/main.tf | 175 +++++++++++ deployment/run/tf-proxmox/run.sh | 10 + deployment/run/tf-proxmox/setup.nix | 16 + deployment/run/tf-proxmox/tf-env.nix | 33 +++ deployment/run/tf-proxmox/tf.nix | 26 ++ deployment/run/tf-proxmox/variables.tf | 114 +++++++ npins/sources.json | 13 + 16 files changed, 897 insertions(+), 2 deletions(-) create mode 100644 deployment/check/data-model-tf-proxmox/constants.nix create mode 100644 deployment/check/data-model-tf-proxmox/default.nix create mode 100644 deployment/check/data-model-tf-proxmox/nixosTest.nix create mode 100644 deployment/run/tf-proxmox/main.tf create mode 100644 deployment/run/tf-proxmox/run.sh create mode 100644 deployment/run/tf-proxmox/setup.nix create mode 100644 deployment/run/tf-proxmox/tf-env.nix create mode 100644 deployment/run/tf-proxmox/tf.nix create mode 100644 deployment/run/tf-proxmox/variables.tf diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml index 4c7effbc..5b03d844 100644 --- a/.forgejo/workflows/ci.yaml +++ b/.forgejo/workflows/ci.yaml @@ -88,6 +88,12 @@ jobs: - uses: actions/checkout@v4 - run: nix build .#checks.x86_64-linux.deployment-model-tf -L + check-deployment-model-tf-proxmox: + runs-on: native + steps: + - uses: actions/checkout@v4 + - run: nix build .#checks.x86_64-linux.deployment-model-tf-proxmox -L + ## NOTE: NixOps4 does not provide a good “dry run” mode, so we instead check ## proxies for resources, namely whether their `.#vmOptions.` and ## `.#nixosConfigurations.` outputs evaluate and build correctly, and diff --git a/deployment/check/common/data-model-options.nix b/deployment/check/common/data-model-options.nix index 57dc88c5..a36b3107 100644 --- a/deployment/check/common/data-model-options.nix +++ b/deployment/check/common/data-model-options.nix @@ -21,5 +21,18 @@ in default = [ ]; example = "ConnectTimeout=60"; }; + proxmox-user = mkOption { + description = "The ProxmoX user to use."; + type = types.str; + default = "root@pam"; + }; + 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; + }; }; } diff --git a/deployment/check/common/data-model.nix b/deployment/check/common/data-model.nix index 9c5e942e..98e9511d 100644 --- a/deployment/check/common/data-model.nix +++ b/deployment/check/common/data-model.nix @@ -23,13 +23,16 @@ let pathToRoot targetSystem sshOpts + proxmox-user + proxmox-password + node-name ; inherit (lib) mkOption types; eval = module: (lib.evalModules { specialArgs = { - inherit pkgs inputs; + inherit pkgs inputs sources; }; modules = [ module @@ -206,6 +209,35 @@ let }; }; }; + single-nixos-vm-tf-proxmox = environment: { + resources."operator-environment".login-shell.username = "operator"; + implementation = + { + required-resources, + deployment-name, + }: + { + tf-proxmox-host = { + nixos-configuration = mkNixosConfiguration environment required-resources; + system = targetSystem; + ssh = { + username = "root"; + host = nodeName; + key-file = null; + inherit sshOpts; + }; + module = self; + inherit + args + deployment-name + proxmox-user + proxmox-password + node-name + ; + root-path = pathToRoot; + }; + }; + }; }; }; options = { @@ -249,6 +281,17 @@ let configuration = config."example-configuration"; }; }; + "tf-proxmox-deployment" = + let + env = config.environments."single-nixos-vm-tf-proxmox"; + in + mkOption { + type = env.resource-mapping.output-type; + default = env.deployment { + deployment-name = "tf-proxmox-deployment"; + configuration = config."example-configuration"; + }; + }; }; } ); diff --git a/deployment/check/data-model-ssh/nixosTest.nix b/deployment/check/data-model-ssh/nixosTest.nix index 56710c4e..8f4aa83a 100644 --- a/deployment/check/data-model-ssh/nixosTest.nix +++ b/deployment/check/data-model-ssh/nixosTest.nix @@ -1,5 +1,6 @@ { lib, + config, pkgs, ... }: diff --git a/deployment/check/data-model-tf-proxmox/constants.nix b/deployment/check/data-model-tf-proxmox/constants.nix new file mode 100644 index 00000000..921d03cc --- /dev/null +++ b/deployment/check/data-model-tf-proxmox/constants.nix @@ -0,0 +1,11 @@ +{ + targetMachines = [ + "mypve" + ]; + pathToRoot = builtins.path { + path = ../../..; + name = "root"; + }; + pathFromRoot = "/deployment/check/data-model-tf-proxmox"; + enableAcme = true; +} diff --git a/deployment/check/data-model-tf-proxmox/default.nix b/deployment/check/data-model-tf-proxmox/default.nix new file mode 100644 index 00000000..e6f7b08f --- /dev/null +++ b/deployment/check/data-model-tf-proxmox/default.nix @@ -0,0 +1,48 @@ +{ + runNixOSTest, + inputs, + sources, + system, +}: + +let + pkgs = import sources.nixpkgs-stable { + inherit system; + overlays = [ overlay ]; + }; + overlay = _: _: { + inherit + (import "${sources.proxmox-nixos}/pkgs" { + craneLib = pkgs.callPackage "${sources.crane}/lib" { }; + # breaks from https://github.com/NixOS/nixpkgs/commit/06b354eb2dc535c57e9b4caaa16d79168f117a26, + # which updates libvncserver to 0.9.15, which was not yet patched at https://git.proxmox.com/?p=vncterm.git. + inherit pkgs; + # not so picky about version for our purposes + pkgs-unstable = pkgs; + }) + proxmox-ve + pve-ha-manager + ; + }; +in +runNixOSTest { + node.specialArgs = { + inherit + sources + pkgs + ; + }; + imports = [ + ../../data-model.nix + ../../function.nix + ../common/nixosTest.nix + ./nixosTest.nix + ]; + _module.args = { inherit inputs sources; }; + inherit (import ./constants.nix) + targetMachines + pathToRoot + pathFromRoot + enableAcme + ; +} diff --git a/deployment/check/data-model-tf-proxmox/nixosTest.nix b/deployment/check/data-model-tf-proxmox/nixosTest.nix new file mode 100644 index 00000000..3302805e --- /dev/null +++ b/deployment/check/data-model-tf-proxmox/nixosTest.nix @@ -0,0 +1,277 @@ +{ + lib, + pkgs, + sources, + ... +}: +let + inherit (import ./constants.nix) pathToRoot pathFromRoot; + inherit (pkgs) system; + deployment-config = { + inherit pathToRoot pathFromRoot; + nodeName = "mypve"; + targetSystem = system; + sshOpts = [ ]; + }; + deployment = + (import ../common/data-model.nix { + inherit system; + config = deployment-config; + proxmox-username = "root@pam"; + proxmox-password = "mytestpw"; + node-name = ""; + # opt not to pass `inputs`, as we could only pass serializable arguments through to its self-call + })."tf-proxmox-deployment".tf-proxmox-host; + # tracking non-tarball downloads seems unsupported still in npins: + # https://github.com/andir/npins/issues/163 + minimalIso = pkgs.fetchurl { + url = "https://releases.nixos.org/nixos/24.05/nixos-24.05.7139.bcba2fbf6963/nixos-minimal-24.05.7139.bcba2fbf6963-x86_64-linux.iso"; + hash = "sha256-plre/mIHdIgU4xWU+9xErP+L4i460ZbcKq8iy2n4HT8="; + }; + machine = + (import "${pkgs.nixos-generators}/share/nixos-generator/nixos-generate.nix" { + inherit system; + inherit (sources) nixpkgs; + formatConfig = "${pkgs.nixos-generators}/share/nixos-generator/formats/proxmox.nix"; + configuration = deployment.nixos-configuration; + }).config; +in +{ + _class = "nixosTest"; + imports = [ + ../common/data-model-options.nix + ]; + + name = "deployment-model"; + sourceFileset = lib.fileset.unions [ + ../../run/tf-proxmox/run.sh + ]; + + nodes.mypve = + { sources, ... }: + { + imports = [ + "${sources.proxmox-nixos}/modules/proxmox-ve" + ]; + users.users.root = { + password = "mytestpw"; + hashedPasswordFile = lib.mkForce null; + }; + services.proxmox-ve = { + enable = true; + ipAddress = "192.168.1.1"; + vms = { + myvm1 = { + vmid = 100; + memory = 1024; + cores = 1; + sockets = 1; + kvm = true; + scsi = [ { file = "local:16"; } ]; + cdrom = "local:iso/minimal.iso"; + }; + }; + }; + virtualisation = { + additionalPaths = [ minimalIso ]; + diskSize = 4096; + memorySize = 2048; + }; + }; + + nodes.deployer = + { ... }: + { + nix.nixPath = [ + (lib.concatStringsSep ":" (lib.mapAttrsToList (k: v: k + "=" + v) sources)) + ]; + + environment.systemPackages = [ + deployment.run + ]; + + # needed only when building from deployer + system.extraDependenciesFromModule = + { pkgs, ... }: + { + environment.systemPackages = with pkgs; [ + hello + ]; + }; + system.extraDependencies = + # (lib.lists.map lib.traceVal) + ( + (lib.lists.concatMap ( + pkg: + ( + if + pkg ? inputDerivation + # error: output '/nix/store/dki9d3vldafg9ydrfm7x0g0rr0qljk98-sudo-1.9.16p2' is not allowed to refer to the following paths: + # /nix/store/2xdmps65ryklmbf025bm4pxv16gb8ajv-sudo-1.9.16p2.tar.gz + # /nix/store/58br4vk3q5akf4g8lx0pqzfhn47k3j8d-bash-5.2p37 + # /nix/store/8v6k283dpbc0qkdq81nb6mrxrgcb10i1-gcc-wrapper-14-20241116 + # /nix/store/9r1nl9ksiyszy4qzzg6y2gcdkca0xmhy-stdenv-linux + # /nix/store/a4rmp6in7igbl1wbz9pli5nq0wiclq0y-groff-1.23.0 + # /nix/store/dki9d3vldafg9ydrfm7x0g0rr0qljk98-sudo-1.9.16p2 + # /nix/store/f5y58qz2fzpzgkhp0nizixi10x04ppyy-linux-pam-1.6.1 + # /nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh + # /nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh + # /nix/store/yh6qg1nsi5h2xblcr67030pz58fsaxx3-coreutils-9.6 + && !(lib.strings.hasInfix "sudo" (lib.traceVal (builtins.toString pkg))) + then + lib.trace "yes" [ + # lib.traceVal pkg.inputDerivation # not of type `path in the Nix store' + ( + ( + x: builtins.trace "${builtins.toString pkg}: ${builtins.toString (lib.isPath x.inputDerivation)}" x + ) + pkg + ).inputDerivation + ] + else + lib.trace "no" [ ] + ) + ) machine.environment.systemPackages) + ++ [ + ( + ( + x: + builtins.trace "machine.system.build.toplevel.inputDerivation: ${builtins.toString (lib.isPath x)}" x + ) + machine.system.build.toplevel.inputDerivation + ) + ( + ( + x: builtins.trace "machine.system.build.etc.inputDerivation: ${builtins.toString (lib.isPath x)}" x + ) + machine.system.build.etc.inputDerivation + ) + ( + ( + x: + builtins.trace "machine.system.build.etcBasedir.inputDerivation: ${builtins.toString (lib.isPath x)}" x + ) + machine.system.build.etcBasedir.inputDerivation + ) + ( + ( + x: + builtins.trace "machine.system.build.etcMetadataImage.inputDerivation: ${builtins.toString (lib.isPath x)}" x + ) + machine.system.build.etcMetadataImage.inputDerivation + ) + ( + ( + x: + builtins.trace "machine.system.build.extraUtils.inputDerivation: ${builtins.toString (lib.isPath x)}" x + ) + machine.system.build.extraUtils.inputDerivation + ) + ((x: builtins.trace "machine.system.path.inputDerivation: ${builtins.toString (lib.isPath x)}" x) + machine.system.path.inputDerivation + ) + ( + ( + x: + builtins.trace "machine.system.build.setEnvironment.inputDerivation: ${builtins.toString (lib.isPath x)}" x + ) + machine.system.build.setEnvironment.inputDerivation + ) + ( + (x: builtins.trace "machine.system.build.vm.inputDerivation: ${builtins.toString (lib.isPath x)}" x) + machine.system.build.vm.inputDerivation + ) + ( + ( + x: + builtins.trace "machine.system.build.bootStage1.inputDerivation: ${builtins.toString (lib.isPath x)}" x + ) + machine.system.build.bootStage1.inputDerivation + ) + ( + ( + x: + builtins.trace "machine.system.build.bootStage2.inputDerivation: ${builtins.toString (lib.isPath x)}" x + ) + machine.system.build.bootStage2.inputDerivation + ) + pkgs.gnu-config + # pkgs.gnu-config.inputDerivation + pkgs.byacc + # pkgs.byacc.inputDerivation + pkgs.stdenv + pkgs.stdenvNoCC + sources.nixpkgs + pkgs.vte + + ( + ## We build a whole NixOS system that contains the module + ## `system.extraDependenciesFromModule`, only to grab its + ## configuration and the store paths needed to build it and + ## dump them in `system.extraDependencies`. + # see: https://git.fediversity.eu/Fediversity/Fediversity/pulls/338/files + + pkgs.closureInfo { + rootPaths = map (drv: drv.drvPath) ( + [ + machine.system.build.toplevel.inputDerivation + machine.system.build.etc.inputDerivation + machine.system.build.etcBasedir.inputDerivation + machine.system.build.etcMetadataImage.inputDerivation + machine.system.build.extraUtils.inputDerivation + machine.system.path.inputDerivation + machine.system.build.setEnvironment.inputDerivation + machine.system.build.vm.inputDerivation + machine.system.build.bootStage1.inputDerivation + machine.system.build.bootStage2.inputDerivation + ] + ++ lib.concatMap (x: if x ? source.inputDerivation then [ x.source.inputDerivation ] else [ ]) ( + lib.attrValues machine.environment.etc + ) + ++ machine.environment.systemPackages + ); + } + ) + + ] + ++ lib.concatLists ( + lib.mapAttrsToList ( + _k: v: + if v ? source.inputDerivation then + [ + # v.source.inputDerivation + ( + ( + x: + builtins.trace "${builtins.toString (lib.attrNames v)}: ${builtins.toString (lib.isPath x.source.inputDerivation)}" x + ) + v + ).source.inputDerivation + ] + else + [ ] + ) machine.environment.etc + ) + ); + }; + + extraTestScript = '' + mypve.wait_for_unit("pveproxy.service") + assert "running" in mypve.succeed("pveproxy status") + mypve.succeed("mkdir -p /run/pve") + assert "Proxmox" in mypve.succeed("curl -s -i -k https://localhost:8006") + + # mypve.succeed("pvesh set /access/password --userid root@pam --password mypwdlol --confirmation-password mytestpw 1>&2") + # mypve.succeed("curl -s -i -k -d '{\"userid\":\"root@pam\",\"password\":\"mypwdhaha\",\"confirmation-password\":\"mypwdlol\"}' -X PUT https://localhost:8006/api2/json/access/password 1>&2") + # on mistake: 401 No ticket + # mypve.succeed("haha") + + with subtest("Run the deployment"): + # target.fail("hello 1>&2") + deployer.succeed(""" + ${lib.getExe deployment.run} + """) + # target.wait_for_unit("multi-user.target") + # target.succeed("su - operator -c hello 1>&2") + ''; +} diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 86eac2d7..8989d02c 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -253,6 +253,110 @@ let }; }); }; + 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; + }; + 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 + root-path + proxmox-user + proxmox-password + node-name + ; + inherit (ssh) + host + username + key-file + sshOpts + ; + environment = { + key_file = key-file; + deployment_name = deployment-name; + root_path = root-path; + ssh_opts = sshOpts; + inherit + system + host + module + args + ; + proxmox_user = proxmox-user; + proxmox_password = proxmox-password; + deployment_type = "tf-proxmox-host"; + ssh_user = username; + node_name = node-name; + }; + tf-env = pkgs.callPackage ./run/tf-proxmox/tf-env.nix { }; + in + pkgs.writers.writeBashBin "deploy-tf-proxmox.sh" + { + makeWrapperArgs = [ + "--prefix" + "PATH" + ":" + "${lib.makeBinPath [ + pkgs.jq + pkgs.nixos-generators + (pkgs.callPackage ./run/tf-proxmox/tf.nix { inherit sources; }) + ]}" + ]; + } + '' + env ${toString (lib.mapAttrsToList (k: v: "TF_VAR_${k}=\"${toBash v}\"") environment)} \ + tf_env=${tf-env} bash ./deployment/run/tf-proxmox/run.sh + ''; + }; + }; + }); + }; }; in { diff --git a/deployment/flake-part.nix b/deployment/flake-part.nix index c11512e6..2555fcb3 100644 --- a/deployment/flake-part.nix +++ b/deployment/flake-part.nix @@ -38,9 +38,14 @@ }; deployment-model-tf = import ./check/data-model-tf { - inherit (pkgs.testers) runNixOSTest; + inherit (pkgs.testers) runNixOSTest; inherit inputs sources; }; + + deployment-model-tf-proxmox = import ./check/data-model-tf-proxmox { + inherit (pkgs.testers) runNixOSTest; + inherit inputs sources system; + }; }; }; } diff --git a/deployment/run/tf-proxmox/main.tf b/deployment/run/tf-proxmox/main.tf new file mode 100644 index 00000000..875d2c81 --- /dev/null +++ b/deployment/run/tf-proxmox/main.tf @@ -0,0 +1,175 @@ +terraform { + required_providers { + proxmox = { + source = "bpg/proxmox" + version = "= 0.76.1" + } + } +} + +locals { + dump_name = "vzdump-qemu-nixos-fediversity-${var.category}.vma.zst" +} + +provider "proxmox" { + endpoint = "https://${var.host}:8006/" + insecure = true + + ssh { + agent = true + } + + # # Choose one authentication method: + # api_token = var.virtual_environment_api_token + # # OR + username = var.proxmox_user + password = var.proxmox_password + # # OR + # auth_ticket = var.virtual_environment_auth_ticket + # csrf_prevention_token = var.virtual_environment_csrf_prevention_token +} + +# FIXME move to host +# FIXME add proxmox +data "external" "base-hash" { + program = ["sh", "-c", "echo \"{\\\"hash\\\":\\\"$(nix-hash ${path.module}/../common/nixos/base.nix)\\\"}\""] +} + +# hash of our code directory, used to trigger re-deploy +# FIXME calculate separately to reduce false positives +data "external" "hash" { + program = ["sh", "-c", "echo \"{\\\"hash\\\":\\\"$(nix-hash ..)\\\"}\""] +} + +# FIXME move to host +resource "terraform_data" "template" { + # triggers_replace = [ + # data.external.base-hash.result, + # ] + + provisioner "local-exec" { + working_dir = path.root + # FIXME configure to use actual base image + command = <<-EOF + set -xeuo pipefail + + # XXX nixos-generate needs NIX_PATH to have `nixpkgs` set! + nixos-generate -f proxmox -o /tmp/nixos-image + # the above makes /tmp/nixos-image read-only, so our stable file name needs a different directory + mkdir -p /tmp/proxmox-image + ln -sf /tmp/nixos-image/vzdump-qemu-nixos-*.vma.zst /tmp/proxmox-image/${local.dump_name} + EOF + } +} + +# FIXME move to host +resource "proxmox_virtual_environment_file" "upload" { + lifecycle { + replace_triggered_by = [ + terraform_data.template, + ] + } + + content_type = "images" + datastore_id = "local" + node_name = var.node_name + overwrite = true + + source_file { + path = "/tmp/proxmox-image/${local.dump_name}" + file_name = local.dump_name + } +} + +# FIXME distinguish var.category +data "proxmox_virtual_environment_vms" "nixos_base" { + node_name = var.node_name + filter { + name = "template" + values = [true] + } + # filter { + # name = "node_name" + # values = ["nixos-base"] + # } +} + +# resource "proxmox_virtual_environment_vm" "nix_vm" { +# lifecycle { +# replace_triggered_by = [ +# proxmox_virtual_environment_file.upload, +# ] +# } + +# node_name = var.node_name +# pool_id = var.pool_id +# description = var.description +# started = true + +# agent { +# enabled = true +# } + +# cpu { +# type = "x86-64-v2-AES" +# cores = var.cores +# sockets = var.sockets +# numa = true +# } + +# memory { +# dedicated = var.memory +# } + +# efi_disk { +# datastore_id = "linstor_storage" +# type = "4m" +# } + +# disk { +# datastore_id = "linstor_storage" +# interface = "scsi0" +# discard = "on" +# iothread = true +# size = var.disk_size +# ssd = true +# } + +# clone { +# datastore_id = "local" +# node_name = data.proxmox_virtual_environment_vms.nixos_base.vms[0].node_name # invalid index: empty list +# vm_id = data.proxmox_virtual_environment_vms.nixos_base.vms[0].vm_id +# full = true +# } + +# network_device { +# model = "virtio" +# bridge = "vnet1306" +# } + +# operating_system { +# type = "l26" +# } + +# scsi_hardware = "virtio-scsi-single" +# bios = "ovmf" +# } + +# module "nixos-rebuild" { +# depends_on = [ +# proxmox_virtual_environment_vm.nix_vm +# ] + +# source = "../tf-single-host" + +# system = var.system +# username = var.ssh_user +# host = proxmox_virtual_environment_vm.nix_vm.ipv4_addresses[0] # needs guest agent installed +# module = var.module +# args = var.args +# key_file = var.key_file +# deployment_name = var.deployment_name +# root_path = var.root_path +# ssh_opts = var.ssh_opts +# deployment_type = var.deployment_type +# } diff --git a/deployment/run/tf-proxmox/run.sh b/deployment/run/tf-proxmox/run.sh new file mode 100644 index 00000000..eba2a2c4 --- /dev/null +++ b/deployment/run/tf-proxmox/run.sh @@ -0,0 +1,10 @@ +#! /usr/bin/env bash +set -xeuo pipefail +declare tf_env + +export TF_LOG=info +# export TF_LOG=debug + +cd "${tf_env}/deployment/run/tf-proxmox" +# parallelism=1: limit OOM risk +tofu apply --auto-approve -lock=false -input=false -parallelism=1 diff --git a/deployment/run/tf-proxmox/setup.nix b/deployment/run/tf-proxmox/setup.nix new file mode 100644 index 00000000..4166812e --- /dev/null +++ b/deployment/run/tf-proxmox/setup.nix @@ -0,0 +1,16 @@ +{ + pkgs, + lib, + sources, +}: +pkgs.writeScriptBin "setup" '' + set -xe + # calculated pins + echo '${lib.strings.toJSON sources}' > ./.npins.json + # generate TF lock for nix's TF providers + rm -rf .terraform/ + rm -f .terraform.lock.hcl + # suppress warning on architecture-specific generated lock file: + # `Warning: Incomplete lock file information for providers`. + tofu init -input=false 1>/dev/null +'' diff --git a/deployment/run/tf-proxmox/tf-env.nix b/deployment/run/tf-proxmox/tf-env.nix new file mode 100644 index 00000000..54c7fcc8 --- /dev/null +++ b/deployment/run/tf-proxmox/tf-env.nix @@ -0,0 +1,33 @@ +{ + lib, + pkgs, + sources ? import ../../../npins, +}: +pkgs.stdenv.mkDerivation { + name = "tf-repo"; + src = + with lib.fileset; + toSource { + root = ../../../.; + # don't copy ignored files + fileset = intersection (gitTracked ../../../.) ../../../.; + }; + buildInputs = [ + (pkgs.callPackage ./tf.nix { inherit sources; }) + (pkgs.callPackage ./setup.nix { inherit sources; }) + ]; + buildPhase = '' + runHook preBuild + for category in deployment/run/tf-single-host deployment/run/tf-proxmox; do + pushd "$category" + source setup + popd + done + runHook postBuild + ''; + installPhase = '' + runHook preInstall + cp -r . $out + runHook postInstall + ''; +} diff --git a/deployment/run/tf-proxmox/tf.nix b/deployment/run/tf-proxmox/tf.nix new file mode 100644 index 00000000..bea786d5 --- /dev/null +++ b/deployment/run/tf-proxmox/tf.nix @@ -0,0 +1,26 @@ +# FIXME: use overlays so this gets imported just once? +{ + pkgs, + sources ? import ../../../npins, + ... +}: +let + mkProvider = + args: + pkgs.terraform-providers.mkProvider ( + { mkProviderFetcher = { repo, ... }: sources.${repo}; } // args + ); +in +pkgs.opentofu.withPlugins (p: [ + p.external + (mkProvider { + owner = "bpg"; + repo = "terraform-provider-proxmox"; + rev = "v0.76.1"; + spdx = "MPL-2.0"; + hash = null; + vendorHash = "sha256-3KJ7gi3UEZu31LhEtcRssRUlfsi4mIx6FGTKi1TDRdg="; + homepage = "https://registry.terraform.io/providers/bpg/proxmox"; + provider-source-address = "registry.opentofu.org/bpg/proxmox"; + }) +]) diff --git a/deployment/run/tf-proxmox/variables.tf b/deployment/run/tf-proxmox/variables.tf new file mode 100644 index 00000000..88506a4f --- /dev/null +++ b/deployment/run/tf-proxmox/variables.tf @@ -0,0 +1,114 @@ +variable "system" { + description = "The architecture of the system to deploy to." + type = string + default = "x86_64-linux" +} + +variable "ssh_user" { + description = "the SSH user to use" + type = string + default = "root" +} + +variable "proxmox_user" { + description = "the ProxmoX user to use" + type = string + default = "root@pam" +} + +variable "proxmox_password" { + description = "the ProxmoX password to use" + type = string + sensitive = true +} + +variable "host" { + description = "the host of the ProxmoX Virtual Environment." + type = string +} + +variable "node_name" { + description = "the name of the ProxmoX node to use." + type = string +} + +variable "module" { + description = "The module to call to obtain the NixOS configuration from." + type = string +} + +variable "args" { + description = "The arguments with which to call the module to obtain the NixOS configuration." + type = string + default = "{}" +} + +variable "key_file" { + description = "path to the user's SSH private key" + type = string +} + +variable "deployment_name" { + description = "The name of the deployment for which to obtain the NixOS configuration." + type = string +} + +variable "root_path" { + description = "The path to the root of the repository." + type = string +} + +variable "ssh_opts" { + description = "Extra SSH options (`-o`) to use." + type = string + default = "[]" +} + +variable "deployment_type" { + description = "A `deployment-type` from the Fediversity data model, for grabbing the desired NixOS configuration." + type = string + default = "tf-proxmox-host" +} + +######################################### + +variable "category" { + type = string + description = "Category to be used in naming the base image." + default = "test" +} + +variable "description" { + type = string + default = "" +} + +variable "sockets" { + type = number + description = "The number of sockets of the VM." + default = 1 +} + +variable "cores" { + type = number + description = "The number of cores of the VM." + default = 1 +} + +variable "memory" { + type = number + description = "The amount of memory of the VM in MiB." + default = 2048 +} + +variable "disk_size" { + type = number + description = "The amount of disk of the VM in GiB." + default = 32 +} + +variable "pool_id" { + type = string + description = "The identifier for a pool to assign the virtual machine to." + default = "Fediversity" +} diff --git a/npins/sources.json b/npins/sources.json index 352ccbb5..85e6ea4b 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -192,6 +192,19 @@ "revision": "48f39fbe2e8f90f9ac160dd4b6929f3ac06d8223", "url": "https://github.com/SaumonNet/proxmox-nixos/archive/48f39fbe2e8f90f9ac160dd4b6929f3ac06d8223.tar.gz", "hash": "0606qcs8x1jwckd1ivf52rqdmi3lkn66iiqh6ghd4kqx0g2bw3nv" + }, + "terraform-provider-proxmox": { + "type": "Git", + "repository": { + "type": "GitHub", + "owner": "kiaragrouwstra", + "repo": "terraform-provider-proxmox" + }, + "branch": "content-type-images", + "submodules": false, + "revision": "d465b71e2c112903b9cf235e3a2b4f7997272ab9", + "url": "https://github.com/kiaragrouwstra/terraform-provider-proxmox/archive/d465b71e2c112903b9cf235e3a2b4f7997272ab9.tar.gz", + "hash": "05l45w40708sx1hyli10ncr0hsjsf0djkc7x9xkdl4gw96m1578n" } }, "version": 5