about summary refs log tree commit diff
path: root/ops/terraform/deploy-nixos/main.tf
diff options
context:
space:
mode:
Diffstat (limited to 'ops/terraform/deploy-nixos/main.tf')
-rw-r--r--ops/terraform/deploy-nixos/main.tf113
1 files changed, 113 insertions, 0 deletions
diff --git a/ops/terraform/deploy-nixos/main.tf b/ops/terraform/deploy-nixos/main.tf
new file mode 100644
index 0000000000..50278b248e
--- /dev/null
+++ b/ops/terraform/deploy-nixos/main.tf
@@ -0,0 +1,113 @@
+# SPDX-FileCopyrightText: 2023 The TVL Authors
+#
+# SPDX-License-Identifier: MIT
+
+# This module deploys a NixOS host by building a system closure
+# located at the specified attribute in the current repository.
+#
+# The closure's derivation path is persisted in the Terraform state to
+# determine after Nix evaluation whether the system closure has
+# changed and needs to be built/deployed.
+#
+# The system configuration is then built (or substituted) on the
+# machine that runs `terraform apply`, then copied and activated on
+# the target machine using `nix-copy-closure`.
+
+variable "attrpath" {
+  description = "attribute set path pointing to the NixOS system closure"
+  type        = string
+}
+
+variable "target_host" {
+  description = "address (IP or hostname) at which the target is reachable"
+  type        = string
+}
+
+variable "entrypoint" {
+  description = <<EOT
+    Path to a .nix file (or directory containing `default.nix` file)
+    that provides the attrset specified in `closure`.
+    If unset, asks git for the root of the repository.
+  EOT
+  type        = string
+  default     = ""
+}
+
+variable "target_user" {
+  description = "username on the target machine"
+  type        = string
+}
+
+variable "target_user_ssh_key" {
+  description = "SSH key to use for connecting to the target"
+  type        = string
+  default     = ""
+  sensitive   = true
+}
+
+variable "triggers" {
+  type        = map(string)
+  description = "Triggers for deploy"
+  default     = {}
+}
+
+# Fetch the derivation hash for the NixOS system.
+data "external" "nixos_system" {
+  program = ["${path.module}/nix-eval.sh"]
+
+  query = {
+    attrpath   = var.attrpath
+    entrypoint = var.entrypoint
+  }
+}
+
+# Deploy the NixOS configuration if anything changed.
+resource "null_resource" "nixos_deploy" {
+  connection {
+    type        = "ssh"
+    host        = var.target_host
+    user        = var.target_user
+    private_key = var.target_user_ssh_key
+  }
+
+  # 1. Wait for SSH to become available.
+  provisioner "remote-exec" {
+    inline = ["true"]
+  }
+
+  # 2. Build NixOS system.
+  provisioner "local-exec" {
+    command = "nix-build ${data.external.nixos_system.result.drv} --no-out-link"
+  }
+
+  # 3. Copy closure to the target.
+  provisioner "local-exec" {
+    command = "${path.module}/nixos-copy.sh"
+
+    environment = {
+      SYSTEM_DRV  = data.external.nixos_system.result.drv
+      TARGET_HOST = var.target_host
+      DEPLOY_KEY  = var.target_user_ssh_key
+      TARGET_USER = var.target_user
+    }
+  }
+
+  # 4. Activate closure on the target.
+  provisioner "remote-exec" {
+    inline = [
+      "set -eu",
+      "SYSTEM=$(nix-build ${data.external.nixos_system.result.drv} --no-out-link)",
+      "sudo nix-env --profile /nix/var/nix/profiles/system --set $SYSTEM",
+      "sudo $SYSTEM/bin/switch-to-configuration switch",
+    ]
+  }
+
+  triggers = merge({
+    nixos_drv   = data.external.nixos_system.result.drv
+    target_host = var.target_host
+  }, var.triggers)
+}
+
+output "nixos_drv" {
+  value = data.external.nixos_system.result
+}