about summary refs log blame commit diff
path: root/users/wpcarro/terraform/default.nix
blob: b3c16144a2096b9bddc36ce9dcba31cc8c054195 (plain) (tree)
























































































































































































                                                                                                      
{ depot, pkgs, lib, ... }:

let
  inherit (builtins) concatLists concatStringsSep toJSON unsafeDiscardStringContext;
  inherit (depot.users) wpcarro;
  inherit (pkgs) writeText;

  images = import "${pkgs.path}/nixos/modules/virtualisation/gce-images.nix";
  nixosImage = images."20.09";
in {
  googleCloudVM = {
    project,
    name,
    region,
    zone,
    configuration,
    extraConfig ? {},
  }: let
    inherit (configuration.users.users) root;
    inherit (configuration.networking) firewall;

    # Convert NixOS-style port numbers to Terraform-style.
    asStrings = xs: map toString xs;
    asRanges = xs: map (x: "${toString x.from}-${toString x.to}") xs;

    sshKeys = concatStringsSep "\n"
      (map (key: "root:${key}") root.openssh.authorizedKeys.keys);

    os = depot.ops.nixos.nixosFor (_: {
      imports = [
        "${pkgs.path}/nixos/modules/virtualisation/google-compute-image.nix"
        configuration
      ];

      networking.hostName = name;

      fileSystems."/nix" = {
        device = "/dev/disk/by-label/google-${name}-disk";
        fsType = "ext4";
      };
    });

    osRoot = os.config.system.build.toplevel;
    osPath = unsafeDiscardStringContext (toString osRoot.outPath);
    drvPath = unsafeDiscardStringContext (toString osRoot.drvPath);
  in writeText "terraform.tf.json" (toJSON (lib.recursiveUpdate extraConfig {
    provider.google = {
      inherit project region zone;
    };

    resource.google_compute_instance."${name}" = {
      inherit name zone;
      machine_type = "e2-standard-2";
  
      tags = [
        "http-server"
        "https-server"
        "${name}-firewall"
      ];
  
      boot_disk = {
        device_name = "boot";
        initialize_params = {
          size = 10;
          image = "projects/nixos-cloud/global/images/${nixosImage.name}";
        };
      };
  
      attached_disk = {
        source = "\${google_compute_disk.${name}.id}";
        device_name = "${name}-disk";
      };
  
      network_interface = {
        network = "default";
        subnetwork = "default";
        access_config = {};
      };
  
      # Copy root's SSH keys from the NixOS configuration and expose them to the
      # metadata server.
      metadata = {
        inherit sshKeys;
        ssh-keys = sshKeys;

        # NixOS's fetch-instance-ssh-keys.bash relies on these fields being
        # available on the metadata server.
        ssh_host_ed25519_key = "\${tls_private_key.${name}.private_key_pem}";
        ssh_host_ed25519_key_pub = "\${tls_private_key.${name}.public_key_pem}";

        # Even though we have SSH access, having oslogin can still be useful for
        # troubleshooting in the browser if for some reason SSH isn't working as
        # expected.
        enable-oslogin = "TRUE";
      };
  
      service_account.scopes = ["cloud-platform"];
    };

    resource.tls_private_key."${name}" = {
      algorithm = "ECDSA";
      ecdsa_curve = "P384";
    };

    resource.google_compute_firewall."${name}" = {
      name = "${name}-firewall";
      network = "default";

      # Read the firewall configuration from the NixOS configuration.
      allow = [
        {
          protocol = "tcp";
          ports = concatLists [
            (asStrings (firewall.allowedTCPPorts or []))
            (asRanges (firewall.allowedTCPPortRanges or []))
          ];
        }
        {
          protocol = "udp";
          ports = concatLists [
            (asStrings (firewall.allowedUDPPorts or []))
            (asRanges (firewall.allowedUDPPortRanges or []))
          ];
        }
      ];
      source_tags = ["${name}-firewall"];
    };
  
    resource.google_compute_disk."${name}" = {
      inherit zone;
      name = "${name}-disk";
      size = 100;
    };

    resource.null_resource.deploy_nixos = {
      triggers = {
        # Redeploy when the NixOS configuration changes.
        os = "${osPath}";
        # Redeploy when a new machine is provisioned.
        machine_id = "\${google_compute_instance.${name}.id}";
      };

      connection = {
        host = "\${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip}";
      };

      provisioner = [
        { remote-exec.inline = ["true"]; }
        {
          local-exec.command = ''
            export PATH="${pkgs.openssh}/bin:$PATH"

            scratch="$(mktemp -d)"
            function cleanup() {
              rm -rf $scratch
            }
            trap cleanup EXIT

            # write out ssh key
            echo -n "''${tls_private_key.${name}.private_key_pem}" > $scratch/id_rsa.pem
            chmod 0600 $scratch/id_rsa.pem

            export NIX_SSHOPTS="\
              -o StrictHostKeyChecking=no\
              -o UserKnownHostsFile=/dev/null\
              -o GlobalKnownHostsFile=/dev/null\
              -o IdentityFile=$scratch/id_rsa.pem
            "

            nix-build ${drvPath}
            nix-copy-closure --to \
              root@''${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip} \
              ${osPath} --gzip --use-substitutes
          '';
        }
        {
          remote-exec.inline = [
            "nix-env --profile /nix/var/nix/profiles/system --set ${osPath}"
            "${osPath}/bin/switch-to-configuration switch"
          ];
        }
      ];
    };
  }));
}