UNPKG

pmcf

Version:

Poor mans configuration management

227 lines (197 loc) 6.55 kB
import { join } from "node:path"; import { FileContentProvider } from "npm-pkgbuild"; import { Owner } from "./owner.mjs"; import { Host } from "./host.mjs"; import { addType } from "./types.mjs"; import { writeLines } from "./utils.mjs"; const ClusterTypeDefinition = { name: "cluster", owners: [Owner.typeDefinition, "network", "location", "root"], priority: 0.7, extends: Host.typeDefinition, properties: { routerId: { type: "number", collection: false, writeable: true }, masters: { type: "network_interface", collection: true, writeable: true }, backups: { type: "network_interface", collection: true, writeable: true }, members: { type: "network_interface", collection: true, writeable: false }, checkInterval: { type: "number", collection: false, writeable: true } } }; export class Cluster extends Host { _masters = new Set(); _backups = new Set(); routerId = 100; checkInterval = 60; static { addType(this); } static get typeDefinition() { return ClusterTypeDefinition; } constructor(owner, data) { super(owner, data); this.read(data, ClusterTypeDefinition); } set masters(value) { this._masters.add(value); value.cluster = this; } get masters() { return this._masters; } set backups(value) { this._backups.add(value); value.cluster = this; } get backups() { return this._backups; } get members() { return this.masters.union(this.backups); } async *preparePackages(stagingDir) { for (const ni of [...this.owner.clusters()].reduce( (all, cluster) => all.union(cluster.members), new Set() )) { const host = ni.host; const name = `keepalived-${host.location.name}-${host.name}`; const packageStagingDir = join(stagingDir, name); const result = { sources: [ new FileContentProvider(packageStagingDir + "/")[ Symbol.asyncIterator ]() ], outputs: host.outputs, properties: { name, description: `${this.typeName} definitions for ${this.fullName}`, access: "private", dependencies: ["keepalived>=2.3.3"], replaces: [`keepalived-${host.name}`] } }; const cfg = [ "global_defs {", " notification_email {", " " + this.administratorEmail, " }", ` smtp_server ${this.smtp.address}`, ` notification_email_from keepalived@${host.domainName}`, " enable_script_security", " script_user root", " max_auto_priority 20", "}", "" ]; for (const cluster of [...this.owner.clusters()].sort((a, b) => a.name.localeCompare(b.name) )) { cfg.push(`vrrp_instance ${cluster.name} {`); cfg.push(` state ${cluster.masters.has(ni) ? "MASTER" : "BACKUP"}`); cfg.push(` interface ${ni.name}`); cfg.push(" virtual_ipaddress {"); for (const na of cluster.networkAddresses( na => na.networkInterface.kind !== "loopback" )) { cfg.push( ` ${na.cidrAddress} dev ${ni.name} label ${cluster.name}` ); } cfg.push(" }"); cfg.push(` virtual_router_id ${cluster.routerId}`); cfg.push( ` priority ${host.priority - (cluster.masters.has(ni) ? 0 : 5)}` ); cfg.push(" smtp_alert"); cfg.push(" advert_int 5"); cfg.push(" authentication {"); cfg.push(" auth_type PASS"); cfg.push(" auth_pass pass1234"); cfg.push(" }"); cfg.push( ` notify_master "/usr/bin/systemctl start ${cluster.name}-master.target"` ); cfg.push( ` notify_backup "/usr/bin/systemctl start ${cluster.name}-backup.target"` ); cfg.push( ` notify_fault "/usr/bin/systemctl start ${cluster.name}-fault.target"` ); cfg.push("}"); cfg.push(""); for (const service of cluster.findServices({ type: "http" })) { cfg.push(`virtual_server ${cluster.address} ${service.port} {`); cfg.push(` delay_loop ${cluster.checkInterval}`); cfg.push(" lb_algo wlc"); cfg.push(" persistence_timeout 600"); cfg.push(` protocol ${service.protocol.toUpperCase()}`); for (const member of this.members) { const memberService = member.findService({ type: service.type }); cfg.push(` real_server ${member.address} ${memberService.port} {`); cfg.push(` weight ${memberService.weight}`); switch (service.type) { case "dns": cfg.push(` DNS_CHECK {`); cfg.push(" type A"); cfg.push(" name google.com"); cfg.push(" }"); break; case "smtp": cfg.push(` SMTP_CHECK {`); cfg.push(" }"); break; default: switch (service.protocol) { case "tcp": cfg.push(` TCP_CHECK {`); cfg.push(" connect_timeout 10"); cfg.push(" }"); break; } } cfg.push(" }"); } cfg.push("}"); cfg.push(""); } } await writeLines( join(packageStagingDir, "etc/keepalived"), "keepalived.conf", cfg ); await writeLines( join(packageStagingDir, "/usr/lib/systemd/system"), `${this.name}-master.target`, [ "[Unit]", `Description=master state of cluster ${this.name}`, "PartOf=keepalived.service", `Conflicts=${this.name}-fault.target` ] ); await writeLines( join(packageStagingDir, "/usr/lib/systemd/system"), `${this.name}-backup.target`, [ "[Unit]", `Description=backup state of cluster ${this.name}`, "PartOf=keepalived.service", `Conflicts=${this.name}-fault.target` ] ); await writeLines( join(packageStagingDir, "/usr/lib/systemd/system"), `${this.name}-fault.target`, [ "[Unit]", `Description=fault state of cluster ${this.name}`, `Conflicts=${this.name}-master.target ${this.name}-backup.target` ] ); yield result; } } }