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 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);
|
||||
local serverMeta(name, instance) = {
|
||||
[name]: {
|
||||
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.meta(
|
||||
"dust2",
|
||||
terraform.HcloudInstance {
|
||||
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,
|
||||
|
||||
HcloudInstance:: {
|
||||
|
@ -13,8 +12,7 @@ local config = import "../config/config.libsonnet";
|
|||
|
||||
HcloudSSHKey:: {
|
||||
name: error "Key must have field: name",
|
||||
public_key: error "Key must have field: public_key",
|
||||
labels: { source: "terraform" }
|
||||
public_key: error "Key must have field: public_key"
|
||||
},
|
||||
|
||||
HdnsRecord:: {
|
||||
|
@ -22,14 +20,14 @@ local config = import "../config/config.libsonnet";
|
|||
name: error "Record must have field: name",
|
||||
value: error "Record must have field: value",
|
||||
type: error "Record must have field: type",
|
||||
ttl: config.defaultTTL
|
||||
ttl: defaults.defaultTTL
|
||||
},
|
||||
|
||||
serverDnsRecords: function (s) {
|
||||
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, "AAAA")]: terraform.HdnsRecord{ name: s.publicSubdomain, value: attr(s, "ipv6_address"), type: "AAAA" },
|
||||
[rname(s, "VPN")]: terraform.HdnsRecord{ name: s.internalSubdomain, value: s.wireguardIp, type: "AAAA" },
|
||||
[rname(s, "A")]: $.HdnsRecord{ name: s.networking.publicSubdomain, value: attr(s, "ipv4_address"), type: "A" },
|
||||
[rname(s, "AAAA")]: $.HdnsRecord{ name: s.networking.publicSubdomain, value: attr(s, "ipv6_address"), 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: {
|
||||
required_providers: {
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
"terraform/terraform.tf.json": std.manifestJson(import "services/terraform.libsonnet"),
|
||||
"ansible/inventory.yaml": std.manifestYamlDoc(import "services/inventory.libsonnet"),
|
||||
"ansible/site.yaml": std.manifestYamlDoc(import "services/playbook.libsonnet")
|
||||
}
|
||||
|
|
|
@ -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