257 lines
6.6 KiB
TypeScript
257 lines
6.6 KiB
TypeScript
import * as pulumi from "@pulumi/pulumi";
|
|
import * as proxmox from "@muhlba91/pulumi-proxmoxve";
|
|
|
|
const config = new pulumi.Config();
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Providers — one per standalone Proxmox machine
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const pveProvider = new proxmox.Provider("pve", {
|
|
endpoint: config.requireSecret("pve1Endpoint"),
|
|
apiToken: config.requireSecret("pve1ApiToken"),
|
|
insecure: true,
|
|
});
|
|
|
|
const pveBckpProvider = new proxmox.Provider("pve-bckp", {
|
|
endpoint: config.requireSecret("pve2Endpoint"),
|
|
apiToken: config.requireSecret("pve2ApiToken"),
|
|
insecure: true,
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Download Ubuntu Noble cloud image to each node's ISO storage
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const ubuntuNobleUrl =
|
|
"https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img";
|
|
|
|
const ubuntuImagePve = new proxmox.download.File(
|
|
"ubuntu-noble-pve",
|
|
{
|
|
nodeName: "pve",
|
|
datastoreId: "pve-local-ext1",
|
|
contentType: "import",
|
|
fileName: "noble-server-cloudimg-amd64.qcow2",
|
|
url: ubuntuNobleUrl,
|
|
overwrite: true,
|
|
overwriteUnmanaged: true,
|
|
},
|
|
{ provider: pveProvider },
|
|
);
|
|
|
|
const ubuntuImagePveBckp = new proxmox.download.File(
|
|
"ubuntu-noble-pve-bckp",
|
|
{
|
|
nodeName: "pve-bckp",
|
|
datastoreId: "local",
|
|
contentType: "import",
|
|
fileName: "noble-server-cloudimg-amd64.qcow2",
|
|
url: ubuntuNobleUrl,
|
|
overwrite: true,
|
|
overwriteUnmanaged: true,
|
|
},
|
|
{ provider: pveBckpProvider },
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// VM templates — one per node, cloned from the downloaded cloud image.
|
|
// Templates are not started and have no cloud-init config; that is applied
|
|
// per-clone so each node gets its own hostname (derived from VM name).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const templateSettings = {
|
|
template: true,
|
|
started: false,
|
|
stopOnDestroy: true,
|
|
scsiHardware: "virtio-scsi-pci",
|
|
cpu: {
|
|
cores: 2,
|
|
sockets: 1,
|
|
type: "host",
|
|
numa: true,
|
|
},
|
|
memory: {
|
|
dedicated: 2048,
|
|
floating: 0,
|
|
},
|
|
networkDevices: [{ bridge: "vmbr0", model: "virtio" }],
|
|
serialDevices: [{}],
|
|
vga: { type: "serial0" },
|
|
agent: { enabled: true },
|
|
};
|
|
|
|
const pveTemplate = new proxmox.VmLegacy(
|
|
"k3s-template-pve",
|
|
{
|
|
...templateSettings,
|
|
nodeName: "pve",
|
|
name: "k3s-ubuntu-noble-template",
|
|
disks: [
|
|
{
|
|
interface: "scsi0",
|
|
datastoreId: "local-lvm",
|
|
importFrom: pulumi.interpolate`${ubuntuImagePve.datastoreId}:import/${ubuntuImagePve.fileName}`,
|
|
size: 10,
|
|
ssd: true,
|
|
discard: "on",
|
|
},
|
|
],
|
|
},
|
|
{ provider: pveProvider },
|
|
);
|
|
|
|
const pveBckpTemplate = new proxmox.VmLegacy(
|
|
"k3s-template-pve-bckp",
|
|
{
|
|
...templateSettings,
|
|
nodeName: "pve-bckp",
|
|
name: "k3s-ubuntu-noble-template",
|
|
disks: [
|
|
{
|
|
interface: "scsi0",
|
|
datastoreId: "local",
|
|
importFrom: pulumi.interpolate`${ubuntuImagePveBckp.datastoreId}:import/${ubuntuImagePveBckp.fileName}`,
|
|
size: 10,
|
|
ssd: true,
|
|
discard: "on",
|
|
},
|
|
],
|
|
},
|
|
{ provider: pveBckpProvider },
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// k3s nodes — full clones of their respective templates
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const k3sVmPassword = config.requireSecret("k3sVmPassword");
|
|
const sshPvePublicKey = config.requireSecret("sshPvePublicKey");
|
|
|
|
interface NodeConfig {
|
|
name: string;
|
|
role: "master" | "worker";
|
|
nodeName: string;
|
|
provider: proxmox.Provider;
|
|
template: proxmox.VmLegacy;
|
|
diskDatastore: string;
|
|
}
|
|
|
|
const nodeConfigs: NodeConfig[] = [
|
|
{
|
|
name: "k3s-master-1",
|
|
role: "master",
|
|
nodeName: "pve",
|
|
provider: pveProvider,
|
|
template: pveTemplate,
|
|
diskDatastore: "local-lvm",
|
|
},
|
|
{
|
|
name: "k3s-master-2",
|
|
role: "master",
|
|
nodeName: "pve",
|
|
provider: pveProvider,
|
|
template: pveTemplate,
|
|
diskDatastore: "local-lvm",
|
|
},
|
|
{
|
|
name: "k3s-worker-1",
|
|
role: "worker",
|
|
nodeName: "pve",
|
|
provider: pveProvider,
|
|
template: pveTemplate,
|
|
diskDatastore: "local-lvm",
|
|
},
|
|
{
|
|
name: "k3s-master-3",
|
|
role: "master",
|
|
nodeName: "pve-bckp",
|
|
provider: pveBckpProvider,
|
|
template: pveBckpTemplate,
|
|
diskDatastore: "local",
|
|
},
|
|
{
|
|
name: "k3s-worker-2",
|
|
role: "worker",
|
|
nodeName: "pve-bckp",
|
|
provider: pveBckpProvider,
|
|
template: pveBckpTemplate,
|
|
diskDatastore: "local",
|
|
},
|
|
];
|
|
|
|
const k3sVms = nodeConfigs.map(
|
|
(node) =>
|
|
new proxmox.VmLegacy(
|
|
node.name,
|
|
{
|
|
nodeName: node.nodeName,
|
|
name: node.name,
|
|
description: "k3s " + node.role + " node — managed by Pulumi",
|
|
tags: ["k3s", node.role],
|
|
clone: {
|
|
vmId: node.template.vmId,
|
|
full: true,
|
|
datastoreId: node.diskDatastore,
|
|
},
|
|
cpu: {
|
|
cores: 2,
|
|
sockets: 1,
|
|
type: "host",
|
|
numa: true,
|
|
},
|
|
memory: {
|
|
dedicated: 2048,
|
|
floating: 0,
|
|
},
|
|
disks: [
|
|
{
|
|
interface: "scsi0",
|
|
datastoreId: node.diskDatastore,
|
|
size: 10,
|
|
ssd: true,
|
|
discard: "on",
|
|
},
|
|
],
|
|
initialization: {
|
|
datastoreId: node.diskDatastore,
|
|
ipConfigs: [{ ipv4: { address: "dhcp" } }],
|
|
userAccount: {
|
|
username: "ubuntu",
|
|
password: k3sVmPassword,
|
|
keys: [sshPvePublicKey.apply((k) => k.trim())],
|
|
},
|
|
},
|
|
networkDevices: [{ bridge: "vmbr0", model: "virtio" }],
|
|
scsiHardware: "virtio-scsi-pci",
|
|
serialDevices: [{}],
|
|
vga: { type: "serial0" },
|
|
agent: { enabled: true },
|
|
started: false,
|
|
stopOnDestroy: true,
|
|
},
|
|
{
|
|
provider: node.provider,
|
|
retainOnDelete: true,
|
|
ignoreChanges: ["clone"],
|
|
},
|
|
),
|
|
);
|
|
|
|
export const clusterInfo = k3sVms.map((vm, index) => ({
|
|
nodeName: vm.nodeName,
|
|
vmId: vm.vmId,
|
|
name: nodeConfigs[index].name,
|
|
role: nodeConfigs[index].role,
|
|
}));
|
|
|
|
// Individual vmId exports — used by k8s-bootstrap to start VMs and run guest exec.
|
|
// Order matches nodeConfigs: master-1, master-2, worker-1, master-3, worker-2.
|
|
export const vmIds = {
|
|
master1: k3sVms[0].vmId,
|
|
master2: k3sVms[1].vmId,
|
|
worker1: k3sVms[2].vmId,
|
|
master3: k3sVms[3].vmId,
|
|
worker2: k3sVms[4].vmId,
|
|
};
|