Refactor and add deployment support
This commit is contained in:
parent
ae2e359225
commit
6cc0596b72
|
@ -1,5 +0,0 @@
|
||||||
local credentials = import "../credentials.libsonnet";
|
|
||||||
local servers = import "servers.libsonnet";
|
|
||||||
local networking = import "networking.libsonnet";
|
|
||||||
|
|
||||||
credentials + servers + networking
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
infraDomain: "m5w.de",
|
||||||
|
# needs to be /96
|
||||||
|
ipSubnet: "fdc2:d459:3f8a:84a3:coffe:coffe",
|
||||||
|
defaultTTL: 3600,
|
||||||
|
defaultZoneTTL: 86400,
|
||||||
|
defaultSshKeys: {
|
||||||
|
martin: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzXsN8jgzF51mQS5gfo4H7QKNhDKDEyXZSGen83MYw9GyIMi+AdH1fuhnYBlN2fTlHjs88otZkBMhVzE5lbkutz07j+ZpF6AdUvxqesqkXa2hdXFBRRwnG7u0Pxbi7vhr7uUWMa1WzJYynwmYBLL0yNEK6dI1qJcpwaK6v8UOZymiSJh04Sqd1LfEKd7R3BdzRCqkeKab1351OmJSswN+HRsAsDbdOIDBXpUMomvYAxJud4Wv90NcXfYikI7lhaAILBPTSUQqgTFFHhjfw9pe6Uhxb5URVS5ENjYVDyD2Lo1daZwy+sSYvA1LKZLQVEBKyx1o6SLLsuYqOuOIxiy8UEQ9vLHBdYQ+Ca0m2TruPtxEIu67WQFMBjMXcja4p516UkiuFqr0sQftI0HvVIZHS95DTK2BygkOy9Aok/fQ4IBeraN9EjIRkAB5Hn0z8vxBQMf9ZKUisMbN8nk22YpGte1RD9BFS9Swm7IE1c55QD30S6tD5z0lMUcU+ol3rOIh/013hNj9ZLsYxOtGJtIX3Xc+tIbUgXKou1sjPGQx4M2t9RRZTJ8L4l2DYw4joNoFXGiwFW586DBMw6wb9YeikA+Nuy0RFY8ytgBD5Qdh7IbF7+aA8f0ZkGHkmf/VLM1UkO5XXh3bNlz03IPcav091mAAlu/OHCdOhN54V9vE1FQ== cardno:4268913'
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
[
|
||||||
|
'example.com'
|
||||||
|
]
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
infraDomain: "m5w.de",
|
|
||||||
# needs to be /96
|
|
||||||
ipSubnet: "fdc2:d459:3f8a:84a3:coffe:coffe",
|
|
||||||
defaultTTL: 3600,
|
|
||||||
defaultZoneTTL: 86400,
|
|
||||||
}
|
|
|
@ -1,24 +1,16 @@
|
||||||
|
local deployments = import "../lib/deployments.libsonnet";
|
||||||
local terraform = import "../lib/terraform.libsonnet";
|
local terraform = import "../lib/terraform.libsonnet";
|
||||||
local networking = import "networking.libsonnet";
|
local server = import "../lib/servers.libsonnet";
|
||||||
|
|
||||||
local hashIp(name) = std.substr(std.md5(name), 0, 4) + ":" + std.substr(std.md5(name), 4, 4);
|
server.meta(
|
||||||
local serverMeta(name, instance) = {
|
"dust2",
|
||||||
[name]: {
|
terraform.HcloudInstance {
|
||||||
name: name,
|
|
||||||
publicSubdomain: name + ".infra",
|
|
||||||
internalSubdomain: name + ".i.infra",
|
|
||||||
publicDomain: self.publicSubdomain + "." + networking.infraDomain,
|
|
||||||
internalDomain: self.internalSubdomain + "." + networking.infraDomain,
|
|
||||||
wireguardIp: networking.ipSubnet + ":" + hashIp(name),
|
|
||||||
instance: instance + { name: name }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
servers: serverMeta("dust2", terraform.HcloudInstance {
|
|
||||||
server_type: "cx11"
|
server_type: "cx11"
|
||||||
}),
|
|
||||||
sshKeys: {
|
|
||||||
martin: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzXsN8jgzF51mQS5gfo4H7QKNhDKDEyXZSGen83MYw9GyIMi+AdH1fuhnYBlN2fTlHjs88otZkBMhVzE5lbkutz07j+ZpF6AdUvxqesqkXa2hdXFBRRwnG7u0Pxbi7vhr7uUWMa1WzJYynwmYBLL0yNEK6dI1qJcpwaK6v8UOZymiSJh04Sqd1LfEKd7R3BdzRCqkeKab1351OmJSswN+HRsAsDbdOIDBXpUMomvYAxJud4Wv90NcXfYikI7lhaAILBPTSUQqgTFFHhjfw9pe6Uhxb5URVS5ENjYVDyD2Lo1daZwy+sSYvA1LKZLQVEBKyx1o6SLLsuYqOuOIxiy8UEQ9vLHBdYQ+Ca0m2TruPtxEIu67WQFMBjMXcja4p516UkiuFqr0sQftI0HvVIZHS95DTK2BygkOy9Aok/fQ4IBeraN9EjIRkAB5Hn0z8vxBQMf9ZKUisMbN8nk22YpGte1RD9BFS9Swm7IE1c55QD30S6tD5z0lMUcU+ol3rOIh/013hNj9ZLsYxOtGJtIX3Xc+tIbUgXKou1sjPGQx4M2t9RRZTJ8L4l2DYw4joNoFXGiwFW586DBMw6wb9YeikA+Nuy0RFY8ytgBD5Qdh7IbF7+aA8f0ZkGHkmf/VLM1UkO5XXh3bNlz03IPcav091mAAlu/OHCdOhN54V9vE1FQ== cardno:4268913'
|
|
||||||
},
|
},
|
||||||
}
|
[
|
||||||
|
deployments.dockerComposeApp('promstack'),
|
||||||
|
deployments.dockerComposeApp('mailcow', 'mail.example.com'),
|
||||||
|
deployments.laravelApp('test', 'www.example.com'),
|
||||||
|
deployments.laravelApp('app', 'example.com'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
Deployment:: {
|
||||||
|
roles: error "At least one role is required",
|
||||||
|
variables: error "Deployment variables missing"
|
||||||
|
},
|
||||||
|
|
||||||
|
dockerComposeApp: function (name, domain = null) self.Deployment {
|
||||||
|
roles: ['reverse-proxy', 'docker', 'docker-compose-app'],
|
||||||
|
variables: {
|
||||||
|
docker_compose_app: [ name ],
|
||||||
|
domains: [ domain ]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
laravelApp: function (name, domain) self.Deployment {
|
||||||
|
roles: ['reverse-proxy', 'laravel-app'],
|
||||||
|
variables: {
|
||||||
|
laravel_apps: [ { name: name, domain: domain } ] ,
|
||||||
|
domains: [ domain ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
local defaults = import "../config/defaults.libsonnet";
|
||||||
|
local utils = import "../lib/utils.libsonnet";
|
||||||
|
|
||||||
|
{
|
||||||
|
hashIp: function (name) std.substr(std.md5(name), 0, 4) + ":" + std.substr(std.md5(name), 4, 4),
|
||||||
|
meta: function (name, instance, deployments) {
|
||||||
|
[name]: {
|
||||||
|
name: name,
|
||||||
|
networking: {
|
||||||
|
publicSubdomain: name + ".infra",
|
||||||
|
internalSubdomain: name + ".i.infra",
|
||||||
|
publicDomain: self.publicSubdomain + "." + defaults.infraDomain,
|
||||||
|
internalDomain: self.internalSubdomain + "." + defaults.infraDomain,
|
||||||
|
wireguardIp: defaults.ipSubnet + ":" + $.hashIp(name),
|
||||||
|
},
|
||||||
|
instance: instance + { name: name },
|
||||||
|
roles: std.uniq(std.sort(std.foldl(function (roles, deployment) roles + deployment.roles, deployments, []))),
|
||||||
|
deployment_vars: std.foldl(function (vars, deployment) utils.merge(vars, deployment.variables), deployments, {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
local config = import "../config/config.libsonnet";
|
local defaults = import "../config/defaults.libsonnet";
|
||||||
|
|
||||||
{
|
{
|
||||||
local terraform = self,
|
|
||||||
local rname (server, suffix) = "host_" + server.name + "_" + suffix,
|
local rname (server, suffix) = "host_" + server.name + "_" + suffix,
|
||||||
|
|
||||||
HcloudInstance:: {
|
HcloudInstance:: {
|
||||||
|
@ -13,8 +12,7 @@ local config = import "../config/config.libsonnet";
|
||||||
|
|
||||||
HcloudSSHKey:: {
|
HcloudSSHKey:: {
|
||||||
name: error "Key must have field: name",
|
name: error "Key must have field: name",
|
||||||
public_key: error "Key must have field: public_key",
|
public_key: error "Key must have field: public_key"
|
||||||
labels: { source: "terraform" }
|
|
||||||
},
|
},
|
||||||
|
|
||||||
HdnsRecord:: {
|
HdnsRecord:: {
|
||||||
|
@ -22,14 +20,14 @@ local config = import "../config/config.libsonnet";
|
||||||
name: error "Record must have field: name",
|
name: error "Record must have field: name",
|
||||||
value: error "Record must have field: value",
|
value: error "Record must have field: value",
|
||||||
type: error "Record must have field: type",
|
type: error "Record must have field: type",
|
||||||
ttl: config.defaultTTL
|
ttl: defaults.defaultTTL
|
||||||
},
|
},
|
||||||
|
|
||||||
serverDnsRecords: function (s) {
|
serverDnsRecords: function (s) {
|
||||||
local attr (s, n) = "${hcloud_server." + s.name + "." + n + "}",
|
local attr (s, n) = "${hcloud_server." + s.name + "." + n + "}",
|
||||||
|
|
||||||
[rname(s, "A")]: terraform.HdnsRecord{ name: s.publicSubdomain, value: attr(s, "ipv4_address"), type: "A" },
|
[rname(s, "A")]: $.HdnsRecord{ name: s.networking.publicSubdomain, value: attr(s, "ipv4_address"), type: "A" },
|
||||||
[rname(s, "AAAA")]: terraform.HdnsRecord{ name: s.publicSubdomain, value: attr(s, "ipv6_address"), type: "AAAA" },
|
[rname(s, "AAAA")]: $.HdnsRecord{ name: s.networking.publicSubdomain, value: attr(s, "ipv6_address"), type: "AAAA" },
|
||||||
[rname(s, "VPN")]: terraform.HdnsRecord{ name: s.internalSubdomain, value: s.wireguardIp, type: "AAAA" },
|
[rname(s, "VPN")]: $.HdnsRecord{ name: s.networking.internalSubdomain, value: s.networking.wireguardIp, type: "AAAA" },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
# adopted from stdlib source to handle array merges
|
||||||
|
# https://github.com/google/jsonnet/blob/4e67da2c015bb316158d3e52a47376b38a29a4ef/stdlib/std.jsonnet#L1473
|
||||||
|
merge (target, patch)::
|
||||||
|
if std.isObject(patch) then
|
||||||
|
local target_object =
|
||||||
|
if std.isObject(target) then target else {};
|
||||||
|
|
||||||
|
local target_fields =
|
||||||
|
if std.isObject(target_object) then std.objectFields(target_object) else [];
|
||||||
|
|
||||||
|
local null_fields = [k for k in std.objectFields(patch) if patch[k] == null];
|
||||||
|
local both_fields = std.setUnion(target_fields, std.objectFields(patch));
|
||||||
|
|
||||||
|
{
|
||||||
|
[k]:
|
||||||
|
if !std.objectHas(patch, k) then
|
||||||
|
target_object[k]
|
||||||
|
else if !std.objectHas(target_object, k) then
|
||||||
|
$.merge(null, patch[k])
|
||||||
|
else
|
||||||
|
$.merge(target_object[k], patch[k])
|
||||||
|
for k in std.setDiff(both_fields, null_fields)
|
||||||
|
}
|
||||||
|
else if std.isArray(patch) then
|
||||||
|
if std.isArray(target) && target != [null] then
|
||||||
|
target + patch
|
||||||
|
else
|
||||||
|
patch
|
||||||
|
else
|
||||||
|
patch
|
||||||
|
}
|
|
@ -1,68 +1,5 @@
|
||||||
local terraform = import "lib/terraform.libsonnet";
|
|
||||||
local config = import "config/config.libsonnet";
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"terraform/terraform.tf.json": std.manifestJson({
|
"terraform/terraform.tf.json": std.manifestJson(import "services/terraform.libsonnet"),
|
||||||
terraform: {
|
"ansible/inventory.yaml": std.manifestYamlDoc(import "services/inventory.libsonnet"),
|
||||||
required_providers: {
|
"ansible/site.yaml": std.manifestYamlDoc(import "services/playbook.libsonnet")
|
||||||
hcloud: {
|
|
||||||
source: "hetznercloud/hcloud",
|
|
||||||
version: "1.30.0"
|
|
||||||
},
|
|
||||||
hetznerdns: {
|
|
||||||
source: "timohirt/hetznerdns",
|
|
||||||
version: "1.1.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
provider: {
|
|
||||||
hcloud: {
|
|
||||||
token: config.hcloudToken
|
|
||||||
},
|
|
||||||
hetznerdns: {
|
|
||||||
apitoken: config.hdnsToken
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
resource: {
|
|
||||||
hcloud_ssh_key: {
|
|
||||||
[k]: terraform.HcloudSSHKey { name: k, public_key: config.sshKeys[k] }
|
|
||||||
for k in std.objectFields(config.sshKeys)
|
|
||||||
},
|
|
||||||
hcloud_server: {
|
|
||||||
[s]: config.servers[s].instance
|
|
||||||
for s in std.objectFields(config.servers)
|
|
||||||
},
|
|
||||||
hetznerdns_zone: {
|
|
||||||
infra: { name: config.infraDomain, ttl: config.defaultZoneTTL },
|
|
||||||
},
|
|
||||||
hetznerdns_record: std.foldl(function (a, b) a + b, [
|
|
||||||
terraform.serverDnsRecords(config.servers[s])
|
|
||||||
for s in std.objectFields(config.servers)
|
|
||||||
], {})
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"ansible/inventory.yaml": std.manifestYamlDoc({
|
|
||||||
all: {
|
|
||||||
hosts: {
|
|
||||||
[s]: config.servers[s] + {
|
|
||||||
ansible_host: config.servers[s].publicDomain,
|
|
||||||
ansible_user: "root"
|
|
||||||
}
|
|
||||||
for s in std.objectFields(config.servers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"ansible/site.yaml": std.manifestYamlDoc([
|
|
||||||
{
|
|
||||||
name: "Test command",
|
|
||||||
hosts: "all",
|
|
||||||
tasks: [
|
|
||||||
{
|
|
||||||
"ansible.builtin.command": "ls"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
local servers = import "../config/servers.libsonnet";
|
||||||
|
|
||||||
|
{
|
||||||
|
all: {
|
||||||
|
hosts: {
|
||||||
|
[s]: servers[s] + {
|
||||||
|
ansible_host: servers[s].networking.publicDomain,
|
||||||
|
ansible_user: "root"
|
||||||
|
}
|
||||||
|
for s in std.objectFields(servers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "Test command",
|
||||||
|
hosts: "all",
|
||||||
|
tasks: [
|
||||||
|
{
|
||||||
|
"ansible.builtin.command": "ls"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,85 @@
|
||||||
|
local domains = import "../config/domains.libsonnet";
|
||||||
|
local servers = import "../config/servers.libsonnet";
|
||||||
|
local credentials = import "../credentials.libsonnet";
|
||||||
|
local terraform = import "../lib/terraform.libsonnet";
|
||||||
|
local defaults = import "../config/defaults.libsonnet";
|
||||||
|
|
||||||
|
local domainToName (domain) = std.strReplace(domain, '.', '_');
|
||||||
|
local splitDomainName (domain, subdomaine = []) =
|
||||||
|
local found = std.find(domain, domains);
|
||||||
|
local split = std.split(domain, '.');
|
||||||
|
if std.length(found) > 0
|
||||||
|
then { subdomain: std.join('.', subdomaine), zone: domainToName(domains[found[0]]) }
|
||||||
|
else splitDomainName(std.join('.', split[1:]), subdomaine + [split[0]]);
|
||||||
|
|
||||||
|
local domainEntries(domain, server) =
|
||||||
|
local split = splitDomainName(domain);
|
||||||
|
local record = terraform.HdnsRecord { zone_id: "${hetznerdns_zone." + split.zone + ".id}", name: "" };
|
||||||
|
local hostAttr (attr) = "${hcloud_server." + server.name + "." + attr + "}";
|
||||||
|
if split.subdomain == ""
|
||||||
|
then {
|
||||||
|
["deployment_" + domainToName(domain) + "_A"]: record { value: hostAttr('ipv4_address'), type: "A" },
|
||||||
|
["deployment_" + domainToName(domain) + "_AAAA"]: record { value: hostAttr('ipv6_address'), type: "AAAA" }
|
||||||
|
}
|
||||||
|
else { ["deployment_" + domainToName(domain) + "_CNAME"]: record { name: split.subdomain, value: server.networking.publicDomain, type: 'CNAME' } };
|
||||||
|
|
||||||
|
{
|
||||||
|
terraform: {
|
||||||
|
required_providers: {
|
||||||
|
hcloud: {
|
||||||
|
source: "hetznercloud/hcloud",
|
||||||
|
version: "1.30.0"
|
||||||
|
},
|
||||||
|
hetznerdns: {
|
||||||
|
source: "timohirt/hetznerdns",
|
||||||
|
version: "1.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
provider: {
|
||||||
|
hcloud: {
|
||||||
|
token: credentials.hcloudToken
|
||||||
|
},
|
||||||
|
hetznerdns: {
|
||||||
|
apitoken: credentials.hdnsToken
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
resource: {
|
||||||
|
hcloud_ssh_key: {
|
||||||
|
[k]: terraform.HcloudSSHKey { name: k, public_key: defaults.defaultSshKeys[k] }
|
||||||
|
for k in std.objectFields(defaults.defaultSshKeys)
|
||||||
|
},
|
||||||
|
|
||||||
|
hcloud_server: {
|
||||||
|
[s]: servers[s].instance
|
||||||
|
for s in std.objectFields(servers)
|
||||||
|
},
|
||||||
|
|
||||||
|
hetznerdns_zone: {
|
||||||
|
infra: { name: defaults.infraDomain, ttl: defaults.defaultZoneTTL },
|
||||||
|
} + {
|
||||||
|
[domainToName(domain)]: { name: domain, ttl: defaults.defaultZoneTTL }
|
||||||
|
for domain in domains
|
||||||
|
},
|
||||||
|
|
||||||
|
# Default records for every host (v4, v6, VPN)
|
||||||
|
local hostRecords = std.foldl(function (a, b) a + b, [
|
||||||
|
terraform.serverDnsRecords(servers[s])
|
||||||
|
for s in std.objectFields(servers)
|
||||||
|
], {}),
|
||||||
|
|
||||||
|
# DNS records for deployed apps
|
||||||
|
local appRecords = std.foldl(
|
||||||
|
function (records, server)
|
||||||
|
records + std.foldl(
|
||||||
|
function (entries, domain) entries + domainEntries(domain, servers[server]),
|
||||||
|
servers[server].deployment_vars.domains, {}
|
||||||
|
),
|
||||||
|
std.objectFields(servers), {}
|
||||||
|
),
|
||||||
|
|
||||||
|
hetznerdns_record: hostRecords + appRecords
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue