diff --git a/.gitignore b/.gitignore index 5cd68ee..30f247f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ .envrc.local .direnv/ +# Terranix symlink +config.tf.json + # Local .terraform directories **/.terraform/* diff --git a/flake.lock b/flake.lock index f176ba7..6b5ccb3 100644 --- a/flake.lock +++ b/flake.lock @@ -131,6 +131,23 @@ "type": "github" } }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1727826117, + "narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", + "type": "github" + }, + "original": { + "id": "flake-parts", + "type": "indirect" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -267,6 +284,18 @@ "type": "github" } }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1727825735, + "narHash": "sha256-0xHYkMkeLVQAMa7gvkddbPqpxph+hDzdu1XdGPJR+Os=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" + } + }, "refraction": { "inputs": { "nixpkgs": [ @@ -299,6 +328,7 @@ "nixpkgs": "nixpkgs", "refraction": "refraction", "srvos": "srvos", + "terranix": "terranix", "treefmt-nix": "treefmt-nix" } }, @@ -337,6 +367,34 @@ "type": "github" } }, + "terranix": { + "inputs": { + "bats-assert": [], + "bats-support": [], + "flake-parts": "flake-parts_2", + "nixpkgs": [ + "nixpkgs" + ], + "systems": [ + "flake-utils", + "systems" + ], + "terranix-examples": [] + }, + "locked": { + "lastModified": 1728959489, + "narHash": "sha256-1Pu2j5xsBTuoyga08ZVf+rKp3FOMmJh/0fXen/idOrA=", + "owner": "terranix", + "repo": "terranix", + "rev": "7734e2ee6a1472807a33ce1e7da794bed2aaf91c", + "type": "github" + }, + "original": { + "owner": "terranix", + "repo": "terranix", + "type": "github" + } + }, "treefmt-nix": { "inputs": { "nixpkgs": [ diff --git a/flake.nix b/flake.nix index b93a383..fcd5ac3 100644 --- a/flake.nix +++ b/flake.nix @@ -27,6 +27,16 @@ url = "github:nlewo/comin"; inputs.nixpkgs.follows = "nixpkgs"; }; + terranix = { + url = "github:terranix/terranix"; + inputs = { + nixpkgs.follows = "nixpkgs"; + systems.follows = "flake-utils/systems"; + terranix-examples.follows = ""; + bats-support.follows = ""; + bats-assert.follows = ""; + }; + }; blockgame-meta = { url = "github:PrismLauncher/meta"; inputs.nixpkgs.follows = "nixpkgs"; @@ -42,6 +52,7 @@ flake-utils.lib.meld inputs [ ./machines/andesite ./modules + ./tf ./development.nix ]; } diff --git a/main.tf b/main.tf deleted file mode 100644 index a910c46..0000000 --- a/main.tf +++ /dev/null @@ -1,41 +0,0 @@ -resource "hcloud_server" "andesite" { - name = "andesite" - image = "ubuntu-22.04" - server_type = "cax11" - datacenter = "fsn1-dc14" - public_net { - ipv4_enabled = true - ipv6_enabled = true - } -} - -resource "netlify_dns_zone" "prismlauncher" { - name = "prismlauncher.org" - lifecycle { - prevent_destroy = true - } -} - -resource "netlify_dns_record" "andesite4" { - type = "A" - zone_id = netlify_dns_zone.prismlauncher.id - hostname = "andesite.prismlauncher.org" - value = hcloud_server.andesite.ipv4_address -} - -resource "netlify_dns_record" "andesite6" { - type = "AAAA" - zone_id = netlify_dns_zone.prismlauncher.id - hostname = "andesite.prismlauncher.org" - value = hcloud_server.andesite.ipv6_address -} - -resource "local_file" "andesite-facts" { - content = jsonencode({ - "hostname" = hcloud_server.andesite.name - "domain" = netlify_dns_zone.prismlauncher.name - "ipv4_address" = hcloud_server.andesite.ipv4_address - "ipv6_address" = hcloud_server.andesite.ipv6_address - }) - filename = "${path.root}/machines/andesite/facts.json" -} diff --git a/provider.tf b/provider.tf deleted file mode 100644 index a959708..0000000 --- a/provider.tf +++ /dev/null @@ -1,38 +0,0 @@ -terraform { - required_providers { - hcloud = { - source = "hetznercloud/hcloud" - version = "~> 1.45" - } - netlify = { - source = "netlify/netlify" - version = "~> 0.2" - } - } - - cloud { - hostname = "app.terraform.io" - organization = "prismlauncher" - - workspaces { - name = "infrastructure" - } - } -} - -variable "hcloud_token" { - sensitive = true -} - -provider "hcloud" { - token = var.hcloud_token -} - -variable "netlify_token" { - type = string -} - -provider "netlify" { - token = var.netlify_token - default_team_slug = "prismlauncher" -} diff --git a/tf/default.nix b/tf/default.nix new file mode 100644 index 0000000..6d44efc --- /dev/null +++ b/tf/default.nix @@ -0,0 +1,55 @@ +{ + flake-utils, + nixpkgs, + terranix, + ... +}: + +flake-utils.lib.eachDefaultSystem ( + system: + + let + pkgs = nixpkgs.legacyPackages.${system}; + + terranixConfiguration = terranix.lib.terranixConfiguration { + inherit system; + modules = [ + ./modules + ./main.nix + ./provider.nix + ]; + }; + + opentofu = pkgs.opentofu.withPlugins (plugins: [ + plugins.hcloud + + # netlify/netlify + (plugins.mkProvider { + owner = "netlify"; + repo = "terraform-provider-netlify"; + rev = "v0.2.0"; + hash = "sha256-QfthllLxJvlXKYvsq54+ETY15cwY7QXFpCTdW3PZnsE="; + + vendorHash = "sha256-R+10PeyU6attKT+Y5TbRBXLYS6Xdx8RHJUzEAxatf10="; + + homepage = "https://registry.terraform.io/providers/netlify/netlify"; + spdx = "MIT"; + }) + ]); + in + + { + apps.tf = flake-utils.lib.mkApp { + drv = pkgs.writeShellApplication { + name = "tf"; + + runtimeInputs = [ opentofu ]; + + text = '' + ln -sf ${terranixConfiguration} config.tf.json + exec tofu "$@" + ''; + }; + }; + } +) diff --git a/tf/main.nix b/tf/main.nix new file mode 100644 index 0000000..4bc48bd --- /dev/null +++ b/tf/main.nix @@ -0,0 +1,56 @@ +{ config, lib, ... }: + +{ + resource = { + hcloud_server = { + andesite = { + name = "andesite"; + image = "ubuntu-22.04"; + server_type = "cax11"; + datacenter = "fsn1-dc14"; + public_net = { + ipv4_enabled = true; + ipv6_enabled = true; + }; + }; + }; + + netlify_dns_zone = { + "prismlauncher" = { + name = "prismlauncher.org"; + lifecycle = { + prevent_destroy = true; + }; + }; + }; + + netlify_dns_record = { + "andesite4" = { + type = "A"; + zone_id = lib.tfRef "netlify_dns_zone.prismlauncher.id"; + hostname = "andesite.prismlauncher.org"; + value = lib.tfRef "hcloud_server.andesite.ipv4_address"; + }; + + "andesite6" = { + type = "AAAA"; + zone_id = lib.tfRef "netlify_dns_zone.prismlauncher.id"; + hostname = "andesite.prismlauncher.org"; + value = lib.tfRef "hcloud_server.andesite.ipv6_address"; + }; + }; + + local_file = { + andesite-facts = { + content = lib.generators.toJSON { } { + hostname = config.resource.hcloud_server.andesite.name; + domain = config.resource.netlify_dns_zone.prismlauncher.name; + ipv4_address = lib.tfRef "resource.hcloud_server.andesite.ipv4_address"; + ipv6_address = lib.tfRef "hcloud_server.andesite.ipv6_address"; + }; + + filename = toString ../machines/andesite/facts.json; + }; + }; + }; +} diff --git a/tf/modules/default.nix b/tf/modules/default.nix new file mode 100644 index 0000000..178d148 --- /dev/null +++ b/tf/modules/default.nix @@ -0,0 +1,5 @@ +{ + imports = [ + ./providers.nix + ]; +} diff --git a/tf/modules/providers.nix b/tf/modules/providers.nix new file mode 100644 index 0000000..dc3450c --- /dev/null +++ b/tf/modules/providers.nix @@ -0,0 +1,83 @@ +{ config, lib, ... }: + +let + cfg = config.infra.providers; + + providerSettingsSubmodule = { + freeformType = lib.types.attrsOf lib.types.anything; + }; + + providerSubmodule = + { config, name, ... }: + { + options = { + name = lib.mkOption { + type = lib.types.str; + default = name; + defaultText = lib.literalExpression ""; + description = "Name of the provider."; + example = "tailscale"; + }; + + registry = lib.mkOption { + type = lib.types.str; + default = cfg.defaultRegistry; + defaultText = lib.literalExpression "config.infra.providers.registry"; + description = "URL of Terraform provider registry."; + example = "registry.mydomain.org"; + }; + + source = lib.mkOption { + type = lib.types.str; + default = "${config.name}/${config.name}"; + defaultText = lib.literalExpression "\${name}/\${name}"; + apply = source: cfg.required.${name}.registry + "/${source}"; + description = '' + Source of the provider in `/` format. + + NOTE: The registry URL is prepended to this value. + ''; + example = "tailscale/tailscale"; + }; + + settings = lib.mkOption { + type = lib.types.submodule providerSettingsSubmodule; + default = { }; + description = "Settings for this provider."; + }; + }; + }; +in + +{ + options.infra.providers = { + defaultRegistry = lib.mkOption { + type = lib.types.str; + default = "registry.terraform.io"; + description = "URL of Terraform provider registry."; + example = "registry.mydomain.org"; + }; + + required = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule providerSubmodule); + default = { }; + description = '' + Attribute set declaring required Terraform providers. + + Definitions with no explicit declarations are used to configure the + provider -- i.e., defining `tailscale.tailnet = "mydomain.org"` would + evaluate to `provider.tailscale.tailnet = "mydomain.org"`. + ''; + }; + }; + + config = lib.mkIf (cfg.required != { }) { + terraform.required_providers = lib.mapAttrs (lib.const (cfg': { + inherit (cfg') source; + })) cfg.required; + + provider = lib.mapAttrs' (lib.const ( + cfg': lib.nameValuePair cfg'.name (lib.mkIf (cfg'.settings != { }) cfg'.settings) + )) cfg.required; + }; +} diff --git a/tf/provider.nix b/tf/provider.nix new file mode 100644 index 0000000..7f8cd08 --- /dev/null +++ b/tf/provider.nix @@ -0,0 +1,39 @@ +{ lib, ... }: + +{ + infra.providers.required = { + hcloud = { + source = "hetznercloud/hcloud"; + + settings = { + token = lib.tfRef "var.hcloud_token"; + }; + }; + + netlify = { + settings = { + token = lib.tfRef "var.netlify_token"; + default_team_slug = "prismlauncher"; + }; + }; + }; + + terraform.cloud = { + hostname = "app.terraform.io"; + organization = "prismlauncher"; + + workspaces = { + name = "infrastructure"; + }; + }; + + variable = { + hcloud_token = { + sensitive = true; + }; + + netlify_token = { + type = "string"; + }; + }; +} diff --git a/treefmt.nix b/treefmt.nix index 648d4d6..847c955 100644 --- a/treefmt.nix +++ b/treefmt.nix @@ -3,12 +3,10 @@ projectRootFile = "flake.nix"; programs.actionlint.enable = true; - programs.hclfmt.enable = true; programs.just.enable = true; programs.mdformat.enable = true; programs.nixfmt.enable = true; programs.shfmt.enable = true; - programs.terraform.enable = true; programs.yamlfmt.enable = true; settings.global.excludes = [