npm.io
0.3.0 • Published yesterday

@celilo/terraform-fake

Licence
MIT
Version
0.3.0
Deps
3
Size
43 kB
Vulns
0
Weekly
0

@celilo/terraform-fake

A fake Proxmox API you can point a real Terraform provider at.

Not a mock that returns 200 OK. It holds state, so a terraform apply creates something, a second apply reports no changes, and a destroy removes it — which is the part that actually catches bugs, because a fake that always says yes lets a broken plan look like a working one.

bun add -d @celilo/terraform-fake     # or npm / pnpm

Why this exists

Testing Terraform against a real hypervisor is slow, expensive, and hard to get into a known state. Testing it against nothing at all is the usual alternative, and it means the provider interaction — the part most likely to break — is the one part never exercised.

The endpoint list here is not a reading of the Proxmox API docs. It is what telmate/proxmox and one production consumer were observed to call across a full create → re-apply → destroy cycle, captured by pointing the real provider at a logging server and implementing whatever it asked for next.

Usage

import { createProxmoxFake } from '@celilo/terraform-fake';

const fake = createProxmoxFake({
  tls: { key: myKeyPem, cert: myCertPem },   // self-signed is fine
  nodes: [{ name: 'pve1', cores: 8, memoryBytes: 16 * 1024 ** 3, diskBytes: 500 * 1024 ** 3 }],
});

const port = await fake.listen(8006);
// ... run terraform against https://127.0.0.1:8006/api2/json ...
await fake.close();

Point Terraform at it:

provider "proxmox" {
  pm_api_url          = "https://127.0.0.1:8006/api2/json"
  pm_api_token_id     = "you@pve!testing"
  pm_api_token_secret = "any-value"
  pm_tls_insecure     = true          # the cert is self-signed
}

The token's user does not need to exist in any config you write — the fake reports whoever your token says you are.

Making the guests real

By default nothing is provisioned: the fake is bookkeeping, which is all you need to exercise a provider. If you want a create to produce an actual machine — a container, a VM, a process — supply a provisioner:

const fake = createProxmoxFake({
  tls,
  provisioner: {
    async createGuest(guest) {
      // guest.vmid, guest.hostname, guest.config (the raw create parameters,
      // including net0 with the address Terraform asked for)
      await startSomethingReal(guest);
    },
    async destroyGuest(guest) {
      await removeIt(guest.vmid);
    },
  },
});

This seam is deliberate. Everything environment-specific — Docker, networks, SSH keys, naming conventions — belongs to you, not to this package.

Asserting on what happened

fake.state is the state store, so a test can check what a plan actually did:

const guest = fake.state.findGuest(203);
expect(guest?.config.net0).toContain('ip=10.0.20.13/24');

Things that will bite you if you build this yourself

Each of these was found by a provider crash, and each is handled here.

The config read-back must be re-typed. A create arrives form-encoded, so every value is a string. Echo it back and the provider panics — it type-asserts without the comma-ok form, so a mismatch takes down the plugin rather than producing an error:

panic: interface conversion: interface {} is string, not float64

cpulimit is the trap. The obvious repair — coerce anything numeric-looking — panics the other way, because cpulimit is semantically a number and read as a string. The exact field list lives in config-types.ts.

/cluster/resources must honour ?type=. The provider fetches ?type=vm and then reads vmid as a float on every row returned. A node row has no vmid, so answering an unfiltered list crashes it.

State must outlive a single apply. Restart the fake between applies and its guests vanish, so the provider concludes the resource was deleted and recreates it. Correct provider behaviour; a badly misleading test.

The first call is /access/users. Not /version. A fake that omits it fails before doing anything, with an error naming neither.

Scope

Supported: the proxmox_lxc lifecycle (create, read, update, delete, start, stop), cluster resources, nodes, storage, tasks, access and pools.

Not yet: proxmox_vm_qemu. Its config parser has its own type map and, in all likelihood, its own cpulimit-shaped surprise; it should be extracted from the provider source the same way rather than guessed.

Verified against telmate/proxmox@3.0.2-rc07 and Terraform 1.7.1. Newer provider versions may assert different types — re-extract rather than assume.

License

MIT

Keywords