Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
Kiara Grouwstra | 1177ad0bfa | |
Kiara Grouwstra | 2bfdb8af80 | |
Kiara Grouwstra | 9e62215c2a | |
Kiara Grouwstra | 96a4ccf181 | |
Kiara Grouwstra | bf99a6b76c | |
Kiara Grouwstra | 95c29fdf92 | |
Kiara Grouwstra | f9d069beb4 | |
Kiara Grouwstra | bf07fc3d1c | |
Kiara Grouwstra | 2c3a7ee24c | |
Kiara Grouwstra | 33dd0a9ea8 | |
Kiara Grouwstra | 684fe1eea3 | |
Kiara Grouwstra | 57beb5595f |
|
@ -0,0 +1,22 @@
|
|||
tf_cloud_token: ENC[AES256_GCM,data:XkG5QZ6Ul4tIZELgDE3gBwa+iwyUY175L+szDpfz2u/n345LjDwWAqcbwsSm/c6YRUlxdH1WDvNKUWwnlSyD56ksrfO5t0vqPbIQIeHAD/XlCM21lkiPp1hc,iv:2gTTM5jgDZ39LNjlGgv18RVSAEzBOT0soQ6ip5RFbk4=,tag:JPPyvWxXnV0s9Avp5y9iNA==,type:str]
|
||||
hcloud_api_token: ENC[AES256_GCM,data:sJW3nPzVKAn2tLJGKcGi7iuA6WfB3B/06R0E5gsOMDLBYMznRnHyP0BtRyLRxfk4RpMAgWI8QWxn7VQsAXjNzw==,iv:RL5JTCy+oU7B9feRZFnKHCFmaEW7O7PB5xzFUpjDhM8=,tag:3S9rJPj5C0DhgCmn2Um/uA==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1d53yeje0ggysc93uptlpufyhpchyyfs006368j8mw9r20uyeeydse3n7aw
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4RTU4Z3hja24wM1JCU2sz
|
||||
UFdRQ2loZ25RdmNQN3ZZZXZOTTFoUk1VVVg4ClBybkJoeW5kTDY1TndsYUFQREVr
|
||||
TENza0M1TDRLTUtMcTJPS3c4K0R5UnMKLS0tIGRHYzBOQ1JHYnhQN1RDWXFMMVQ3
|
||||
RzNnVTJKSDd0aE41YjRvSmJFVHZDQzQKIvfq+ccMlZ1ypgxrBMOm+On0Nl3GVhKW
|
||||
Y8b//WmiuhmYZueq7ZMp0gT40144cE1HneDyuVRZ1az5XnFv5OGXag==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2024-01-18T17:54:28Z"
|
||||
mac: ENC[AES256_GCM,data:nEwl132d1V7+gzQGP/qTl0/VpX7psgdNzmfLLOhefJZGqoC5INCgt5DVGE4fiHv1yM8Q+3irDuQPvx4N0dt1y7lY+UdhqbAhANtI+OPdbuC5xpLplgTUiaLuiPGLbeDpNZxpgxqXK1oVCJF356rEmLevYmytKO8HUFMt+qZjjqE=,iv:40JxgMB/+CxNvOTSwLpZCijni24stfF+Qg8sps3xTCE=,tag:S/7fsmwfJvuW+hexuA4p2g==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.8.1
|
|
@ -1,8 +1,5 @@
|
|||
*.bck
|
||||
|
||||
# sops
|
||||
/secrets.yaml
|
||||
|
||||
# age
|
||||
/keys.txt
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ keys:
|
|||
|
||||
# actual sops config
|
||||
creation_rules:
|
||||
- path_regex: secrets(\.enc)?\.yaml$
|
||||
- path_regex: \.auto\.tfvars(\.enc)?\.(yaml|json)$
|
||||
key_groups:
|
||||
- age:
|
||||
- *automation
|
||||
|
|
|
@ -1,20 +1,6 @@
|
|||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/carlpett/sops" {
|
||||
version = "1.0.0"
|
||||
hashes = [
|
||||
"h1:tnN2Mgl0NUF3cg7a0HtGmtOhHcG+tkaT6ncOPRuA9l8=",
|
||||
"zh:064e63ea800cd1a8e575064097bc7de6fd5faa8ad50dbb3f2f9d8a3ebc9d7b97",
|
||||
"zh:0663900085949d2faf24c170c7cdfbf76e545797915cc331da8304144c02bf27",
|
||||
"zh:2ff26c7e5ee356c30791a12dd8e114c6237bd873d09e52805cb30dd5d758ed23",
|
||||
"zh:44211fa474112ad0c9fcdae03f13ec7c75cdefd3ab29979b99cb834208055593",
|
||||
"zh:6c3ab441c12b9679ad1dcac580d1ee7782f0d94efe6da6e983435ed39335cd3f",
|
||||
"zh:8924cc939b52382ef042dc38bde93cdf438ff0aeab5e1801fbd198f05b80cd47",
|
||||
"zh:ebc189ce22c23b903399f71e33d465001a79d7de7f7bf115c7763fcf794f4b58",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/local" {
|
||||
version = "2.4.1"
|
||||
hashes = [
|
||||
|
@ -52,3 +38,22 @@ provider "registry.opentofu.org/hetznercloud/hcloud" {
|
|||
"zh:fb0e083d2925f289999dc561ef1c2f84a9e0ab11388c40162ca8b470f50f71f5",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/nomad" {
|
||||
version = "2.1.0"
|
||||
hashes = [
|
||||
"h1:ek0L7fA+4R1/BXhbutSRqlQPzSZ5aY/I2YfVehuYeEU=",
|
||||
"zh:39ba4d4fc9557d4d2c1e4bf866cf63973359b73e908cce237c54384512bdb454",
|
||||
"zh:40d2b66e3f3675e6b88000c145977c1d5288510c76b702c6c131d9168546c605",
|
||||
"zh:40fbe575d85a083f96d4703c6b7334e9fc3e08e4f1d441de2b9513215184ebcc",
|
||||
"zh:42ce6db79e2f94557fae516ee3f22e5271f0b556638eb45d5fbad02c99fc7af3",
|
||||
"zh:4acf63dfb92f879b3767529e75764fef68886521b7effa13dd0323c38133ce88",
|
||||
"zh:72cf35a13c2fb542cd3c8528826e2390db9b8f6f79ccb41532e009ad140a3269",
|
||||
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||
"zh:8b8bcc136c05916234cb0c3bcc3d48fda7ca551a091ad8461ea4ab16fb6960a3",
|
||||
"zh:8e1c2f924eae88afe7ac83775f000ae8fd71a04e06228edf7eddce4df2421169",
|
||||
"zh:abc6e725531fc06a8e02e84946aaabc3453ecafbc1b7a442ea175db14fd9c86a",
|
||||
"zh:b735fcd1fb20971df3e92f81bb6d73eef845dcc9d3d98e908faa3f40013f0f69",
|
||||
"zh:ce59797282505d872903789db8f092861036da6ec3e73f6507dac725458a5ec9",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# sops
|
||||
/secrets.yaml
|
||||
|
||||
# age
|
||||
/keys.txt
|
||||
|
||||
|
|
88
README.md
88
README.md
|
@ -11,97 +11,43 @@ Contains [OpenTofu](https://opentofu.org/) code used to manage our infrastructur
|
|||
|
||||
## Usage
|
||||
|
||||
- Before issuing any other commands, enter the development environment (if not using [`direnv`](https://zero-to-flakes.com/direnv)):
|
||||
### Development shell
|
||||
|
||||
```sh
|
||||
nix develop -c $SHELL
|
||||
```
|
||||
Before issuing any other commands, enter the development environment (if not using [`direnv`](https://zero-to-flakes.com/direnv)):
|
||||
|
||||
- Applying changes:
|
||||
```sh
|
||||
nix develop -c $SHELL
|
||||
```
|
||||
|
||||
```sh
|
||||
nix run
|
||||
```
|
||||
### Commands
|
||||
|
||||
- Validating logic:
|
||||
```sh
|
||||
just -l
|
||||
```
|
||||
|
||||
```sh
|
||||
nix run .#check
|
||||
```
|
||||
### Handling [credentials](#secrets)
|
||||
|
||||
- Showing the generated plan:
|
||||
|
||||
```sh
|
||||
nix run .#plan
|
||||
```
|
||||
|
||||
- Applying changes, approving automatically:
|
||||
|
||||
```sh
|
||||
nix run .#cd
|
||||
```
|
||||
|
||||
- Removing local state and derived credentials:
|
||||
|
||||
```sh
|
||||
nix run .#destroy
|
||||
```
|
||||
|
||||
- Updating dependencies:
|
||||
|
||||
```sh
|
||||
nix flake update
|
||||
```
|
||||
|
||||
- Simulating a CI test ([substituting](#secrets) `<SOPS_AGE_KEY>`):
|
||||
|
||||
```sh
|
||||
woodpecker-cli exec --env "SOPS_AGE_KEY=<SOPS_AGE_KEY>"
|
||||
```
|
||||
|
||||
### Secrets
|
||||
## Secrets
|
||||
|
||||
- if you want to reset secrets:
|
||||
- generate an [`age`](https://age-encryption.org/) key pair, using [`rage`](https://github.com/str4d/rage) installed as part of the nix shell:
|
||||
|
||||
```sh
|
||||
rage-keygen -o keys.txt
|
||||
```
|
||||
|
||||
- generate keypair: `just keygen`
|
||||
- list it in [`sops`](https://getsops.io/) config file `.sops.yaml`
|
||||
- key setup: set environment variable `SOPS_AGE_KEY_FILE` or `SOPS_AGE_KEY` so `sops` can locate the secret key to an `age` key pair that has its public key listed in `.sops.yaml`
|
||||
- encoding secrets:
|
||||
- key setup: set environment variable `SOPS_AGE_KEY_FILE` or `SOPS_AGE_KEY` so `sops` can locate the secret key to an `age` key pair that has its public key listed in `.sops.yaml`, e.g. (listed in `.envrc`):
|
||||
|
||||
```sh
|
||||
sops -e secrets.yaml > secrets.enc.yaml
|
||||
```
|
||||
|
||||
- decoding secrets:
|
||||
|
||||
```sh
|
||||
sops -d secrets.enc.yaml > secrets.yaml
|
||||
export SOPS_AGE_KEY_FILE=./keys.txt
|
||||
```
|
||||
|
||||
- setting Terraform Cloud credentials, either by:
|
||||
- reusing the shared session:
|
||||
|
||||
```sh
|
||||
source login.sh
|
||||
```
|
||||
|
||||
- log in to the Terraform Cloud backend:
|
||||
|
||||
```sh
|
||||
tofu login app.terraform.io
|
||||
```
|
||||
- decode (as per above) to reuse the shared session
|
||||
- log in to the Terraform Cloud backend: `just login`
|
||||
|
||||
### Configuring
|
||||
|
||||
Create a file `.auto.tfvars` containing override for any OpenTofu variables, e.g.:
|
||||
In `.auto.tfvars.json` override any OpenTofu variables, e.g.:
|
||||
|
||||
```tfvars
|
||||
hcloud_location = "nbg1"
|
||||
```
|
||||
|
||||
## [HCL to Nix](https://gist.github.com/KiaraGrouwstra/249ede6a7dfc00ea44d85bc6bdbcd875)
|
||||
|
||||
|
|
33
config.nix
33
config.nix
|
@ -20,13 +20,10 @@ let
|
|||
# { a = 1; } -> { name = "a"; a = 1; }
|
||||
setNames = lib.mapAttrs (k: v: { name = k; } // v);
|
||||
|
||||
# "foo" -> "\${data.sops_file.secrets.data[\"foo\"]}"
|
||||
secret = str: lib.tfRef "data.sops_file.secrets.data[\"${str}\"]";
|
||||
|
||||
hetzner = let
|
||||
|
||||
# https://docs.hetzner.com/cloud/api/getting-started/generating-api-token
|
||||
token = secret "hcloud_api_token";
|
||||
token = lib.tfRef "var.hcloud_api_token";
|
||||
|
||||
in { inherit token; };
|
||||
|
||||
|
@ -34,12 +31,6 @@ in rec {
|
|||
|
||||
terraform = {
|
||||
|
||||
required_providers = {
|
||||
|
||||
sops.source = "carlpett/sops";
|
||||
|
||||
};
|
||||
|
||||
cloud = {
|
||||
hostname = "app.terraform.io";
|
||||
organization = "bij1";
|
||||
|
@ -52,8 +43,6 @@ in rec {
|
|||
|
||||
provider = {
|
||||
|
||||
sops = {};
|
||||
|
||||
# Configure the Hetzner Cloud Provider
|
||||
hcloud.token = lib.mkForce hetzner.token;
|
||||
|
||||
|
@ -61,7 +50,9 @@ in rec {
|
|||
|
||||
resource = (inNamespace "hcloud" {
|
||||
|
||||
ssh_key = setNames (lib.mapAttrs (_: v: { public_key = v; }) my-lib.ssh-keys);
|
||||
ssh_key = setNames
|
||||
(lib.mapAttrs (_: v: { public_key = v; })
|
||||
(my-lib.dirContents ".pub" ./ssh-keys));
|
||||
|
||||
});
|
||||
|
||||
|
@ -69,6 +60,18 @@ in rec {
|
|||
# or using -var="hcloud_api_token=..." CLI option
|
||||
variable = {
|
||||
|
||||
# suppress warning, otherwise unused here
|
||||
tf_cloud_token = {
|
||||
type = "string";
|
||||
sensitive = true;
|
||||
};
|
||||
|
||||
hcloud_api_token = {
|
||||
type = "string";
|
||||
description = "[Hetzner Cloud API Token](https://docs.hetzner.com/cloud/api/getting-started/generating-api-token)";
|
||||
sensitive = true;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
# https://github.com/terranix/terranix-hcloud/blob/main/options.md
|
||||
|
@ -82,10 +85,6 @@ in rec {
|
|||
|
||||
data = {
|
||||
|
||||
sops_file.secrets = {
|
||||
source_file = "secrets.enc.yaml";
|
||||
};
|
||||
|
||||
hcloud_ssh_keys."all_keys" = {};
|
||||
|
||||
};
|
||||
|
|
107
flake.lock
107
flake.lock
|
@ -32,6 +32,21 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
|
@ -50,6 +65,59 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gomod2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705314449,
|
||||
"narHash": "sha256-yfQQ67dLejP0FLK76LKHbkzcQqNIrux6MFe32MMFGNQ=",
|
||||
"owner": "tweag",
|
||||
"repo": "gomod2nix",
|
||||
"rev": "30e3c3a9ec4ac8453282ca7f67fca9e1da12c3e6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "tweag",
|
||||
"repo": "gomod2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-nomad": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"gomod2nix": [
|
||||
"gomod2nix"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1672531382,
|
||||
"narHash": "sha256-zbvXzPBBbv5mYPwy/XB3NaBAx3yTYQWNYjz/c/ccH3w=",
|
||||
"owner": "tristanpemble",
|
||||
"repo": "nix-nomad",
|
||||
"rev": "ffbb8c97b2b665ec3a0dd393af79c0192a5546db",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "tristanpemble",
|
||||
"repo": "nix-nomad",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1704999660,
|
||||
|
@ -65,10 +133,49 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1671929364,
|
||||
"narHash": "sha256-N9GW06FZTKDpkv9YLMXswUxnX27b9qEtfTg7WsSdXjc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "a909f7a2fb4ec6d14d52b8a727bb9ba465e15766",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-unfree": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701957584,
|
||||
"narHash": "sha256-xEpFaRdrneHl3Xdyzp3emd4QVxML7AR3GC91wuWi0Ok=",
|
||||
"owner": "numtide",
|
||||
"repo": "nixpkgs-unfree",
|
||||
"rev": "127b9b18583de04c6207c2a0e674abf64fc4a3b1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "nixpkgs-unfree",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"gomod2nix": "gomod2nix",
|
||||
"nix-nomad": "nix-nomad",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-unfree": "nixpkgs-unfree",
|
||||
"terranix": "terranix",
|
||||
"terranix-hcloud": "terranix-hcloud"
|
||||
}
|
||||
|
|
94
flake.nix
94
flake.nix
|
@ -1,7 +1,12 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs";
|
||||
nixpkgs-unfree = {
|
||||
url = "github:numtide/nixpkgs-unfree";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
flake-compat.url = "github:edolstra/flake-compat";
|
||||
terranix = {
|
||||
url = "github:terranix/terranix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
@ -13,23 +18,45 @@
|
|||
inputs.flake-utils.follows = "flake-utils";
|
||||
inputs.terranix.follows = "terranix";
|
||||
};
|
||||
nix-nomad = {
|
||||
url = "github:tristanpemble/nix-nomad";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.flake-utils.follows = "flake-utils";
|
||||
inputs.flake-compat.follows = "flake-compat";
|
||||
inputs.gomod2nix.follows = "gomod2nix";
|
||||
};
|
||||
gomod2nix = {
|
||||
url = "github:tweag/gomod2nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.flake-utils.follows = "flake-utils";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, ... }@inputs:
|
||||
outputs = { self, nixpkgs, nix-nomad, ... }@inputs:
|
||||
inputs.flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
tfConfig = inputs.terranix.lib.terranixConfiguration {
|
||||
inherit system;
|
||||
modules = [
|
||||
unfree = inputs.nixpkgs-unfree.legacyPackages.${system}.pkgs;
|
||||
modules = {
|
||||
hcloud = [
|
||||
inputs.terranix-hcloud.terranixModules.hcloud
|
||||
./config.nix
|
||||
];
|
||||
nomad = [
|
||||
"${nix-nomad}/modules"
|
||||
./nomad.nix
|
||||
];
|
||||
};
|
||||
tfConfig = modules: inputs.terranix.lib.terranixConfiguration { inherit system modules; };
|
||||
tfCfg = builtins.mapAttrs (_: tfConfig) {
|
||||
hcloud = modules.hcloud ++ modules.nomad;
|
||||
nomad = modules.nomad;
|
||||
};
|
||||
tf = "${pkgs.opentofu}/bin/tofu";
|
||||
sops = "${pkgs.sops}/bin/sops";
|
||||
in
|
||||
{
|
||||
defaultPackage = tfConfig;
|
||||
defaultPackage = tfCfg.hcloud;
|
||||
|
||||
# Auto formatters. This also adds a flake check to ensure that the
|
||||
# source tree was auto formatted.
|
||||
|
@ -44,51 +71,60 @@
|
|||
devShell = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
treefmt
|
||||
sops
|
||||
just
|
||||
pkgs.sops
|
||||
rage
|
||||
woodpecker-cli
|
||||
jq
|
||||
inputs.terranix.defaultPackage.${system}
|
||||
(opentofu.withPlugins (p: with p; [
|
||||
sops # https://registry.terraform.io/providers/carlpett/sops/latest/docs
|
||||
hcloud # https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs
|
||||
nomad # https://registry.terraform.io/providers/hashicorp/nomad/latest/docs
|
||||
]))
|
||||
unfree.nomad
|
||||
damon
|
||||
levant
|
||||
];
|
||||
};
|
||||
|
||||
apps = let
|
||||
locally = ''
|
||||
# using local state, stash cloud state to prevent error `workspaces not supported`
|
||||
if [[ -e .terraform/terraform.tfstate ]]; then mv .terraform/terraform.tfstate terraform.tfstate.d/$(tofu workspace show)/terraform.tfstate; fi;
|
||||
'';
|
||||
compile = tfModule: ''
|
||||
echo ${tfModule};
|
||||
cp ${tfModule} config.tf.json \
|
||||
&& chmod 0600 config.tf.json;
|
||||
'';
|
||||
tfCommand = cmd: ''
|
||||
if [[ -e config.tf.json ]]; then rm -f config.tf.json; fi;
|
||||
export TF_CLOUD_TOKEN=$(${pkgs.sops}/bin/sops -d --extract '["tf_cloud_token"]' secrets.enc.yaml)
|
||||
export TF_CLI_CONFIG_FILE="ci.tfrc"
|
||||
cat << EOF > "$TF_CLI_CONFIG_FILE"
|
||||
credentials "app.terraform.io" {
|
||||
token = "$TF_CLOUD_TOKEN"
|
||||
}
|
||||
EOF
|
||||
cp ${tfConfig} config.tf.json \
|
||||
&& ${tf} init \
|
||||
&& ${tf} ${cmd}
|
||||
# need cloud token as env var for CLI commands like `workspace`
|
||||
export TF_TOKEN_app_terraform_io="$(${sops} -d --extract '["tf_cloud_token"]' .auto.tfvars.enc.yaml)";
|
||||
'' + compile tfCfg.hcloud + locally + ''
|
||||
# load cloud state to prevent error `Cloud backend initialization required: please run "tofu init"`
|
||||
mv terraform.tfstate.d/hcloud/terraform.tfstate .terraform/terraform.tfstate;
|
||||
${tf} workspace select -or-create hcloud;
|
||||
${tf} init && ${tf} ${cmd};
|
||||
'';
|
||||
in builtins.mapAttrs (name: script: {
|
||||
type = "app";
|
||||
program = toString (pkgs.writers.writeBash name script);
|
||||
}) {
|
||||
# nix run .#check
|
||||
check = tfCommand "validate";
|
||||
# nix run .#apply
|
||||
validate = tfCommand "validate";
|
||||
apply = tfCommand "apply";
|
||||
# nix run .#plan
|
||||
plan = tfCommand "plan";
|
||||
# nix run .#cd
|
||||
cd = tfCommand "apply -auto-approve";
|
||||
# nix run .#destroy
|
||||
local = locally + compile tfCfg.nomad + ''
|
||||
${tf} workspace select -or-create nomad;
|
||||
${tf} init && ${tf} apply -auto-approve;
|
||||
'';
|
||||
destroy = ''
|
||||
${tfCommand "destroy"}
|
||||
rm ${toString ./.}/config.tf.json
|
||||
rm ${toString ./.}/*.tfstate*
|
||||
rm ${toString ./.}/secrets.yaml
|
||||
rm ${toString ./.}/ci.tfrc
|
||||
for f in "config.tf.json *.tfstate* *.tfvars.json ci.tfrc .terraform terraform.tfstate.d"; do
|
||||
echo $f
|
||||
if [[ -e "${toString ./.}/$f" ]]; then
|
||||
rm -rf "${toString ./.}/$f";
|
||||
fi;
|
||||
done
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
# try locally
|
||||
default: local
|
||||
|
||||
# encode secrets
|
||||
encode:
|
||||
sops --output-type yaml -e .auto.tfvars.json > .auto.tfvars.enc.yaml
|
||||
|
||||
# decode secrets
|
||||
decode:
|
||||
sops --output-type json -d .auto.tfvars.enc.yaml > .auto.tfvars.json
|
||||
|
||||
# log in to the Terraform Cloud backend
|
||||
login:
|
||||
tofu login app.terraform.io
|
||||
|
||||
# validate logic
|
||||
validate:
|
||||
nix run .#validate
|
||||
|
||||
# apply changes
|
||||
apply:
|
||||
nix run .#apply
|
||||
|
||||
# show generated plan
|
||||
plan:
|
||||
nix run .#plan
|
||||
|
||||
# run CI test locally
|
||||
ci:
|
||||
woodpecker-cli exec --env "SOPS_AGE_KEY=$SOPS_AGE_KEY"
|
||||
|
||||
# apply changes, approving automatically
|
||||
cd:
|
||||
nix run .#cd
|
||||
|
||||
# run Nomad jobs locally
|
||||
local:
|
||||
nix run .#local
|
||||
|
||||
# generate an [`age`](https://age-encryption.org/) key pair
|
||||
keygen:
|
||||
rage-keygen -o keys.txt
|
||||
|
||||
# remove local state and derived credentials
|
||||
destroy:
|
||||
nix run .#destroy
|
||||
|
||||
# update dependencies
|
||||
update:
|
||||
nix flake update
|
|
@ -1,5 +1,36 @@
|
|||
{ lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
# ".ext" -> ./subdir -> { "foo" = "<CONTENTS OF a/b/foo.ext>"; "bar" = "<CONTENTS OF a/b/bar.ext>"; }
|
||||
dirContents = let
|
||||
|
||||
# ".ext" -> "a/b.ext" -> "b"
|
||||
fileAttrName = suffix: path: let
|
||||
ext = lib.last (lib.splitString "." path);
|
||||
in lib.removeSuffix suffix (builtins.baseNameOf path);
|
||||
|
||||
# maps a file to a path
|
||||
# ".ext" -> "a/b" -> "c/d.ext" -> { name = "d"; value = "a/b/c/d.ext"; }
|
||||
fileAttrInPath = suffix: path: name: {
|
||||
name = fileAttrName suffix name;
|
||||
value = path + "/${name}";
|
||||
};
|
||||
|
||||
# get an object of files in a directory with a given suffix
|
||||
# ".ext" -> "a/b" -> { "foo" = "a/b/foo.ext"; "bar" = "a/b/bar.ext"; }
|
||||
dirAttrs = suffix: path: lib.mapAttrs'
|
||||
(name: _: fileAttrInPath suffix path name)
|
||||
(lib.filterAttrs
|
||||
(name: type: lib.hasSuffix suffix name && type == "regular")
|
||||
(builtins.readDir path));
|
||||
|
||||
in suffix: path: lib.mapAttrs (_: lib.readFile) (dirAttrs suffix path);
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
ssh-keys = import ./ssh-keys.nix { inherit lib; };
|
||||
|
||||
inherit dirContents;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
{ lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
# "a/b.ext" -> "b"
|
||||
fileAttrName = path: let
|
||||
ext = lib.last (lib.splitString "." path);
|
||||
in lib.removeSuffix ".${ext}" (builtins.baseNameOf path);
|
||||
|
||||
# maps a file to a path
|
||||
# "a/b" -> "c/d.ext" -> { name = "d"; value = "a/b/c/d.ext"; }
|
||||
fileAttrInPath = path: name: {
|
||||
name = fileAttrName name;
|
||||
value = path + "/${name}";
|
||||
};
|
||||
|
||||
# get an object of files in a directory with a given suffix
|
||||
# "a/b" -> { "foo" = "a/b/foo.ext"; "bar" = "a/b/bar.ext"; }
|
||||
dirAttrs = suffix: path: lib.mapAttrs'
|
||||
(name: _: fileAttrInPath path name)
|
||||
(lib.filterAttrs
|
||||
(name: type: lib.hasSuffix suffix name && type == "regular")
|
||||
(builtins.readDir path));
|
||||
|
||||
in lib.mapAttrs (_: lib.readFile) (dirAttrs ".pub" ../ssh-keys)
|
|
@ -0,0 +1,60 @@
|
|||
{ config, options, lib, ... }:
|
||||
|
||||
let
|
||||
|
||||
var = options.variable;
|
||||
|
||||
my-lib = import ./lib/default.nix { inherit lib; };
|
||||
|
||||
in
|
||||
{
|
||||
|
||||
terraform.required_providers.nomad.source = "registry.terraform.io/hashicorp/nomad";
|
||||
|
||||
# https://developer.hashicorp.com/nomad/docs/job-specification/hcl2/variables
|
||||
# https://developer.hashicorp.com/nomad/docs/runtime/interpolation
|
||||
variable = {
|
||||
|
||||
nomad_host = {
|
||||
type = "string";
|
||||
description = "host of the nomad instance, defaults to local";
|
||||
default = "http://127.0.0.1";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
provider.nomad.address = "${lib.tfRef "var.nomad_host"}:4646";
|
||||
|
||||
# keys: https://tristanpemble.github.io/nix-nomad/
|
||||
# vals: https://developer.hashicorp.com/nomad/api-docs/json-jobs
|
||||
# https://github.com/hetznercloud/csi-driver/blob/main/docs/nomad/README.md#getting-started
|
||||
job = {
|
||||
bar = {
|
||||
type = "batch";
|
||||
group.bar.task.bar = {
|
||||
driver = "raw_exec";
|
||||
config = {
|
||||
command = "echo";
|
||||
args = ["hello"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# https://registry.terraform.io/providers/hashicorp/nomad/latest/docs/
|
||||
resource = {
|
||||
|
||||
nomad_job =
|
||||
# nix jobs
|
||||
lib.mapAttrs (_: v: {
|
||||
json = true;
|
||||
jobspec = lib.strings.toJSON v;
|
||||
}) config.nomad.build.apiJob
|
||||
# hcl jobs
|
||||
// lib.mapAttrs (_: v: {
|
||||
jobspec = v;
|
||||
}) (my-lib.dirContents ".nomad.hcl" ./jobs);
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
tf_cloud_token: ENC[AES256_GCM,data:3vx1n4s7eQxMR2ntOlmnASUuCMxhMMHKLuhf644mNLWbv99aPLsqoUQ+cP01hW/Ra98v3U0C0uYZWfkFn/X8CaVIeu1QPv12D1+XSJB0SJal8NZHJTNVTgzL,iv:W0H4lftTD96/ENjV8tA2a8QqAGI2z/jRvgMtQmaGeB0=,tag:MuXGtyDTbRlNW1xshtCH0g==,type:str]
|
||||
hcloud_api_token: ENC[AES256_GCM,data:HojFdI9gGnO8IkfOREx4bTqrCNBsCDxnUUOmb+VuLMNIEWEifo9tBhm25I+xAogRd0TuYcY4fkARboGL9qsgrw==,iv:18QLpHdNnG82603FxLL38KJaB9sPJ9gj0vmqQWNb1e0=,tag:/6QPhVZy5P5dvP92HUQR6g==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1d53yeje0ggysc93uptlpufyhpchyyfs006368j8mw9r20uyeeydse3n7aw
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHa2EzMFlLdTQvMUtzL3FL
|
||||
Rk5JZEdoVkpEQkk3eTR6QkFKY01CaGVwUnpFCm00OWg2bmJ3U2xRMExyeFZ6WVRB
|
||||
UWVjVzY5Y01EOUpDNHYrMFYzVE9GUUEKLS0tIE1LYm80b3V3OUkrNWxQVTRaRGhk
|
||||
TkxRZlprc0I3Q3dQRS82bEd4b1VxTUkKvHZc4c7+9Tsny8w5Cm5L6H+enU1R0tY4
|
||||
9OcNPXGv8II5OJp1eT14U/sNecPbiBaQSeK4xHaRDKbGyqx92DtQ8A==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2024-01-16T21:29:11Z"
|
||||
mac: ENC[AES256_GCM,data:eIoSEDuND1O5XPisSs/kq7N1UsiZMer9+Ok43o+8HwfH/HAoElM/0fXNhKQWcQQVUdwLIQnJZzHEXIJ77Uh5sDsWynj3ihJBhruDPu3FxOXTvRHBcdxU31b3iQGliaChRD19L2GDhsNO2Pfvhpoovsy2PHoFtpqtYt4+7UmcOCw=,iv:Zz1czzz+3Tb5f81o6adhO7eJSSr+ksXhMQwendPAhM0=,tag:bjF2pXTkGxef9+1kKw0FlQ==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.8.1
|
Loading…
Reference in New Issue