@homeflare/alchemy 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -55
- package/dist/caddy/admin-calls.d.ts +48 -0
- package/dist/caddy/admin-calls.d.ts.map +1 -0
- package/dist/caddy/admin-guard.d.ts +40 -0
- package/dist/caddy/admin-guard.d.ts.map +1 -0
- package/dist/caddy/admin.d.ts +73 -0
- package/dist/caddy/admin.d.ts.map +1 -0
- package/dist/caddy/caddyfile-tripwire.d.ts +5 -0
- package/dist/caddy/caddyfile-tripwire.d.ts.map +1 -0
- package/dist/caddy/config-form.d.ts +27 -0
- package/dist/caddy/config-form.d.ts.map +1 -0
- package/dist/caddy/config-lifecycle.d.ts +45 -0
- package/dist/caddy/config-lifecycle.d.ts.map +1 -0
- package/dist/caddy/config.d.ts +30 -0
- package/dist/caddy/config.d.ts.map +1 -0
- package/dist/caddy/digest.d.ts +7 -0
- package/dist/caddy/digest.d.ts.map +1 -0
- package/dist/caddy/fake-caddy.d.ts +43 -0
- package/dist/caddy/fake-caddy.d.ts.map +1 -0
- package/dist/caddy/index.d.ts +21 -0
- package/dist/caddy/index.d.ts.map +1 -0
- package/dist/caddy/index.js +912 -0
- package/dist/caddy/index.js.map +28 -0
- package/dist/caddy/local-admin.d.ts +45 -0
- package/dist/caddy/local-admin.d.ts.map +1 -0
- package/dist/caddy/providers.d.ts +14 -0
- package/dist/caddy/providers.d.ts.map +1 -0
- package/dist/caddy/with-file.d.ts +47 -0
- package/dist/caddy/with-file.d.ts.map +1 -0
- package/dist/cloudflare/fake-mesh.d.ts +58 -0
- package/dist/cloudflare/fake-mesh.d.ts.map +1 -0
- package/dist/cloudflare/index.d.ts +9 -3
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +218 -7
- package/dist/cloudflare/index.js.map +9 -4
- package/dist/cloudflare/mesh-node-api.d.ts +75 -0
- package/dist/cloudflare/mesh-node-api.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node-form.d.ts +82 -0
- package/dist/cloudflare/mesh-node-form.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node-harness.d.ts +46 -0
- package/dist/cloudflare/mesh-node-harness.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node-lifecycle.d.ts +22 -0
- package/dist/cloudflare/mesh-node-lifecycle.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node-token.d.ts +23 -0
- package/dist/cloudflare/mesh-node-token.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node.d.ts +48 -0
- package/dist/cloudflare/mesh-node.d.ts.map +1 -0
- package/dist/cloudflare/providers.d.ts +1 -12
- package/dist/cloudflare/providers.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/launchd/fake-runner.d.ts +36 -0
- package/dist/launchd/fake-runner.d.ts.map +1 -0
- package/dist/launchd/host-effect.d.ts +19 -0
- package/dist/launchd/host-effect.d.ts.map +1 -0
- package/dist/launchd/host-file-form.d.ts +24 -0
- package/dist/launchd/host-file-form.d.ts.map +1 -0
- package/dist/launchd/host-file-lifecycle.d.ts +29 -0
- package/dist/launchd/host-file-lifecycle.d.ts.map +1 -0
- package/dist/launchd/host-file.d.ts +25 -0
- package/dist/launchd/host-file.d.ts.map +1 -0
- package/dist/launchd/host-lookup.d.ts +27 -0
- package/dist/launchd/host-lookup.d.ts.map +1 -0
- package/dist/launchd/index.d.ts +21 -0
- package/dist/launchd/index.d.ts.map +1 -0
- package/dist/launchd/index.js +969 -0
- package/dist/launchd/index.js.map +25 -0
- package/dist/launchd/job-form.d.ts +90 -0
- package/dist/launchd/job-form.d.ts.map +1 -0
- package/dist/launchd/job-lifecycle.d.ts +34 -0
- package/dist/launchd/job-lifecycle.d.ts.map +1 -0
- package/dist/launchd/job-preflight.d.ts +50 -0
- package/dist/launchd/job-preflight.d.ts.map +1 -0
- package/dist/launchd/job-validate.d.ts +25 -0
- package/dist/launchd/job-validate.d.ts.map +1 -0
- package/dist/launchd/job.d.ts +34 -0
- package/dist/launchd/job.d.ts.map +1 -0
- package/dist/launchd/launchctl.d.ts +62 -0
- package/dist/launchd/launchctl.d.ts.map +1 -0
- package/dist/launchd/local-runner.d.ts +7 -0
- package/dist/launchd/local-runner.d.ts.map +1 -0
- package/dist/launchd/plist.d.ts +41 -0
- package/dist/launchd/plist.d.ts.map +1 -0
- package/dist/launchd/providers.d.ts +15 -0
- package/dist/launchd/providers.d.ts.map +1 -0
- package/dist/launchd/runner.d.ts +91 -0
- package/dist/launchd/runner.d.ts.map +1 -0
- package/dist/launchd/secret-tripwire.d.ts +24 -0
- package/dist/launchd/secret-tripwire.d.ts.map +1 -0
- package/dist/openbao/auth-method-form.d.ts +7 -0
- package/dist/openbao/auth-method-form.d.ts.map +1 -1
- package/dist/openbao/auth-method-reconcile.d.ts +15 -0
- package/dist/openbao/auth-method-reconcile.d.ts.map +1 -0
- package/dist/openbao/auth-method.d.ts +6 -0
- package/dist/openbao/auth-method.d.ts.map +1 -1
- package/dist/openbao/auth-role-form.d.ts +9 -0
- package/dist/openbao/auth-role-form.d.ts.map +1 -1
- package/dist/openbao/auth-role.d.ts.map +1 -1
- package/dist/openbao/auth-token-form.d.ts +48 -0
- package/dist/openbao/auth-token-form.d.ts.map +1 -0
- package/dist/openbao/bao-identity.d.ts +81 -0
- package/dist/openbao/bao-identity.d.ts.map +1 -0
- package/dist/openbao/bao-namespace.d.ts +20 -0
- package/dist/openbao/bao-namespace.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role.d.ts.map +1 -1
- package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -1
- package/dist/openbao/fake-engines.d.ts +30 -0
- package/dist/openbao/fake-engines.d.ts.map +1 -0
- package/dist/openbao/fake-stack.d.ts +29 -0
- package/dist/openbao/fake-stack.d.ts.map +1 -0
- package/dist/openbao/host-approles.d.ts +62 -0
- package/dist/openbao/host-approles.d.ts.map +1 -0
- package/dist/openbao/index.d.ts +19 -0
- package/dist/openbao/index.d.ts.map +1 -1
- package/dist/openbao/index.js +1518 -310
- package/dist/openbao/index.js.map +40 -17
- package/dist/openbao/jwt-config-form.d.ts +51 -0
- package/dist/openbao/jwt-config-form.d.ts.map +1 -0
- package/dist/openbao/jwt-config.d.ts +25 -0
- package/dist/openbao/jwt-config.d.ts.map +1 -0
- package/dist/openbao/jwt-role-form.d.ts +91 -0
- package/dist/openbao/jwt-role-form.d.ts.map +1 -0
- package/dist/openbao/jwt-role.d.ts +31 -0
- package/dist/openbao/jwt-role.d.ts.map +1 -0
- package/dist/openbao/kubernetes-role-form.d.ts +68 -0
- package/dist/openbao/kubernetes-role-form.d.ts.map +1 -0
- package/dist/openbao/kubernetes-role.d.ts +25 -0
- package/dist/openbao/kubernetes-role.d.ts.map +1 -0
- package/dist/openbao/mfa-enforcement-form.d.ts +46 -0
- package/dist/openbao/mfa-enforcement-form.d.ts.map +1 -0
- package/dist/openbao/mfa-enforcement.d.ts +41 -0
- package/dist/openbao/mfa-enforcement.d.ts.map +1 -0
- package/dist/openbao/mfa-totp-form.d.ts +52 -0
- package/dist/openbao/mfa-totp-form.d.ts.map +1 -0
- package/dist/openbao/mfa-totp.d.ts +33 -0
- package/dist/openbao/mfa-totp.d.ts.map +1 -0
- package/dist/openbao/mfa-wire.d.ts +63 -0
- package/dist/openbao/mfa-wire.d.ts.map +1 -0
- package/dist/openbao/mount-form.d.ts +7 -0
- package/dist/openbao/mount-form.d.ts.map +1 -1
- package/dist/openbao/mount-move.d.ts +49 -0
- package/dist/openbao/mount-move.d.ts.map +1 -0
- package/dist/openbao/mount-path.d.ts +8 -4
- package/dist/openbao/mount-path.d.ts.map +1 -1
- package/dist/openbao/mount-reconcile.d.ts +16 -0
- package/dist/openbao/mount-reconcile.d.ts.map +1 -0
- package/dist/openbao/mount.d.ts +9 -0
- package/dist/openbao/mount.d.ts.map +1 -1
- package/dist/openbao/pki-role-canonical.d.ts +46 -0
- package/dist/openbao/pki-role-canonical.d.ts.map +1 -0
- package/dist/openbao/pki-role-form.d.ts +38 -30
- package/dist/openbao/pki-role-form.d.ts.map +1 -1
- package/dist/openbao/pki-role.d.ts.map +1 -1
- package/dist/openbao/policy.d.ts +2 -0
- package/dist/openbao/policy.d.ts.map +1 -1
- package/dist/openbao/proxmox-role-form.d.ts +18 -8
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
- package/dist/openbao/proxmox-role-wire.d.ts +18 -0
- package/dist/openbao/proxmox-role-wire.d.ts.map +1 -0
- package/dist/openbao/proxmox-role.d.ts.map +1 -1
- package/dist/openbao/remount-wire.d.ts +44 -0
- package/dist/openbao/remount-wire.d.ts.map +1 -0
- package/dist/openbao/rename.d.ts +106 -0
- package/dist/openbao/rename.d.ts.map +1 -0
- package/dist/openbao/role-reconcile.d.ts +40 -0
- package/dist/openbao/role-reconcile.d.ts.map +1 -0
- package/docs/caddy.md +180 -0
- package/docs/launchd.md +167 -0
- package/docs/mesh-node.md +188 -0
- package/package.json +12 -2
- package/src/caddy/admin-calls.ts +128 -0
- package/src/caddy/admin-guard.ts +170 -0
- package/src/caddy/admin.ts +74 -0
- package/src/caddy/caddyfile-tripwire.ts +133 -0
- package/src/caddy/config-form.ts +64 -0
- package/src/caddy/config-lifecycle.test.ts +218 -0
- package/src/caddy/config-lifecycle.ts +148 -0
- package/src/caddy/config.ts +116 -0
- package/src/caddy/digest.ts +44 -0
- package/src/caddy/fake-caddy.ts +160 -0
- package/src/caddy/guards.test.ts +156 -0
- package/src/caddy/index.ts +25 -0
- package/src/caddy/local-admin.test.ts +160 -0
- package/src/caddy/local-admin.ts +189 -0
- package/src/caddy/providers.test.ts +244 -0
- package/src/caddy/providers.ts +17 -0
- package/src/caddy/with-file.ts +62 -0
- package/src/cloudflare/fake-mesh.ts +181 -0
- package/src/cloudflare/index.ts +9 -3
- package/src/cloudflare/mesh-node-api.ts +127 -0
- package/src/cloudflare/mesh-node-edges.test.ts +103 -0
- package/src/cloudflare/mesh-node-form.test.ts +80 -0
- package/src/cloudflare/mesh-node-form.ts +115 -0
- package/src/cloudflare/mesh-node-harness.ts +124 -0
- package/src/cloudflare/mesh-node-lifecycle.ts +187 -0
- package/src/cloudflare/mesh-node-policy.test.ts +151 -0
- package/src/cloudflare/mesh-node-token.test.ts +117 -0
- package/src/cloudflare/mesh-node-token.ts +125 -0
- package/src/cloudflare/mesh-node.test.ts +172 -0
- package/src/cloudflare/mesh-node.ts +120 -0
- package/src/cloudflare/providers.ts +20 -2
- package/src/index.ts +2 -0
- package/src/launchd/fake-runner.ts +176 -0
- package/src/launchd/host-effect.ts +28 -0
- package/src/launchd/host-file-form.ts +71 -0
- package/src/launchd/host-file-lifecycle.test.ts +198 -0
- package/src/launchd/host-file-lifecycle.ts +164 -0
- package/src/launchd/host-file.ts +64 -0
- package/src/launchd/host-lookup.test.ts +92 -0
- package/src/launchd/host-lookup.ts +94 -0
- package/src/launchd/index.ts +26 -0
- package/src/launchd/job-form.test.ts +171 -0
- package/src/launchd/job-form.ts +180 -0
- package/src/launchd/job-identity.test.ts +147 -0
- package/src/launchd/job-lifecycle.test.ts +220 -0
- package/src/launchd/job-lifecycle.ts +194 -0
- package/src/launchd/job-preflight.ts +152 -0
- package/src/launchd/job-validate.ts +134 -0
- package/src/launchd/job.ts +96 -0
- package/src/launchd/launchctl.test.ts +181 -0
- package/src/launchd/launchctl.ts +188 -0
- package/src/launchd/local-runner.test.ts +125 -0
- package/src/launchd/local-runner.ts +143 -0
- package/src/launchd/plist.test.ts +186 -0
- package/src/launchd/plist.ts +156 -0
- package/src/launchd/providers.test.ts +222 -0
- package/src/launchd/providers.ts +21 -0
- package/src/launchd/runner.ts +95 -0
- package/src/launchd/secret-tripwire.test.ts +110 -0
- package/src/launchd/secret-tripwire.ts +81 -0
- package/src/openbao/README.md +181 -0
- package/src/openbao/REPLACE.md +163 -0
- package/src/openbao/auth-method-form.ts +7 -0
- package/src/openbao/auth-method-reconcile.ts +74 -0
- package/src/openbao/auth-method.ts +20 -41
- package/src/openbao/auth-role-form.test.ts +16 -1
- package/src/openbao/auth-role-form.ts +11 -0
- package/src/openbao/auth-role.ts +12 -0
- package/src/openbao/auth-token-form.ts +161 -0
- package/src/openbao/bao-identity.test.ts +131 -0
- package/src/openbao/bao-identity.ts +188 -0
- package/src/openbao/bao-namespace.ts +32 -0
- package/src/openbao/cloudflare-role-wire.test.ts +8 -34
- package/src/openbao/cloudflare-role.ts +30 -8
- package/src/openbao/cloudflare-roles-expand.ts +2 -5
- package/src/openbao/fake-engines.ts +119 -0
- package/src/openbao/fake-stack.ts +112 -0
- package/src/openbao/host-approles.test.ts +121 -0
- package/src/openbao/host-approles.ts +126 -0
- package/src/openbao/index.ts +19 -0
- package/src/openbao/jwt-config-form.ts +161 -0
- package/src/openbao/jwt-config.ts +85 -0
- package/src/openbao/jwt-role-form.test.ts +130 -0
- package/src/openbao/jwt-role-form.ts +231 -0
- package/src/openbao/jwt-role.ts +102 -0
- package/src/openbao/kubernetes-role-form.test.ts +82 -0
- package/src/openbao/kubernetes-role-form.ts +149 -0
- package/src/openbao/kubernetes-role.ts +94 -0
- package/src/openbao/mfa-enforcement-form.ts +147 -0
- package/src/openbao/mfa-enforcement.ts +143 -0
- package/src/openbao/mfa-totp-form.ts +155 -0
- package/src/openbao/mfa-totp.ts +118 -0
- package/src/openbao/mfa-wire.ts +117 -0
- package/src/openbao/mfa.test.ts +200 -0
- package/src/openbao/mount-form.ts +9 -1
- package/src/openbao/mount-move.test.ts +164 -0
- package/src/openbao/mount-move.ts +113 -0
- package/src/openbao/mount-path.test.ts +11 -1
- package/src/openbao/mount-path.ts +17 -11
- package/src/openbao/mount-reconcile.ts +75 -0
- package/src/openbao/mount.ts +26 -43
- package/src/openbao/pki-role-canonical.ts +163 -0
- package/src/openbao/pki-role-form.test.ts +112 -0
- package/src/openbao/pki-role-form.ts +76 -110
- package/src/openbao/pki-role.ts +12 -27
- package/src/openbao/policy-rename.test.ts +112 -0
- package/src/openbao/policy-wire.test.ts +7 -26
- package/src/openbao/policy.ts +27 -4
- package/src/openbao/proxmox-role-form.ts +27 -8
- package/src/openbao/proxmox-role-wire.ts +24 -0
- package/src/openbao/proxmox-role.ts +62 -37
- package/src/openbao/remount-wire.test.ts +87 -0
- package/src/openbao/remount-wire.ts +112 -0
- package/src/openbao/rename-occupied.test.ts +166 -0
- package/src/openbao/rename.ts +162 -0
- package/src/openbao/role-reconcile.test.ts +104 -0
- package/src/openbao/role-reconcile.ts +84 -0
- package/src/openbao/role-rename.test.ts +205 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moving a Bao.CloudflareRole or a Bao.ProxmoxRole to a new `mount` or `name`, through Alchemy's own
|
|
3
|
+
* plan and apply (fake-stack.ts) against fakes of each engine's role store (fake-engines.ts). Before
|
|
4
|
+
* 2026-09-21 every move below planned `update` and left the old role minting.
|
|
5
|
+
*/
|
|
6
|
+
import assert from 'node:assert/strict';
|
|
7
|
+
import { describe, it } from 'node:test';
|
|
8
|
+
import * as Output from 'alchemy/Output';
|
|
9
|
+
import * as RemovalPolicy from 'alchemy/RemovalPolicy';
|
|
10
|
+
import * as Effect from 'effect/Effect';
|
|
11
|
+
import * as Layer from 'effect/Layer';
|
|
12
|
+
import * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';
|
|
13
|
+
import { groupKey } from './cloudflare-group-scope.ts';
|
|
14
|
+
import { CloudflarePermissionGroups } from './cloudflare-permission-groups.ts';
|
|
15
|
+
import { BaoCloudflareRole, BaoCloudflareRoleProvider } from './cloudflare-role.ts';
|
|
16
|
+
import type { BaoCloudflareRoleProps } from './cloudflare-role-form.ts';
|
|
17
|
+
import type { Seen } from './fake-bao.ts';
|
|
18
|
+
import { type Store, cloudflareRoles, proxmoxRoles } from './fake-engines.ts';
|
|
19
|
+
import { type StackBody, withFakeStack, writesOf as writes } from './fake-stack.ts';
|
|
20
|
+
import { BaoProxmoxRole, BaoProxmoxRoleProvider } from './proxmox-role.ts';
|
|
21
|
+
import type { BaoProxmoxRoleProps } from './proxmox-role-form.ts';
|
|
22
|
+
|
|
23
|
+
/** Every group resolves, to an id named after it — this file is about paths, not groups. */
|
|
24
|
+
const groups = Layer.succeed(CloudflarePermissionGroups, {
|
|
25
|
+
resolve: (_mount, refs) =>
|
|
26
|
+
Effect.succeed(new Map(refs.map((ref) => [groupKey(ref.name, ref.scope), `id-${ref.name}`]))),
|
|
27
|
+
});
|
|
28
|
+
const providers = Layer.mergeAll(
|
|
29
|
+
BaoCloudflareRoleProvider().pipe(Layer.provide(groups)),
|
|
30
|
+
BaoProxmoxRoleProvider(),
|
|
31
|
+
FetchHttpClient.layer,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
type Removal = typeof RemovalPolicy.destroy;
|
|
35
|
+
|
|
36
|
+
const withStack = (
|
|
37
|
+
store: Store,
|
|
38
|
+
body: (deploy: (body: StackBody) => Promise<unknown>, seen: Seen[]) => Promise<void>,
|
|
39
|
+
) => withFakeStack(providers, store, (stack, bao) => body(stack.deploy, bao.seen));
|
|
40
|
+
|
|
41
|
+
const livePaths = (store: Store) => [...store.live.keys()].sort();
|
|
42
|
+
|
|
43
|
+
const CF: BaoCloudflareRoleProps = {
|
|
44
|
+
description: 'Read DNS records on one zone. (zone: example.com)',
|
|
45
|
+
maxTtl: '1h',
|
|
46
|
+
mount: 'cloudflare-acme-dns',
|
|
47
|
+
name: 'example-com-dns-read',
|
|
48
|
+
policies: [
|
|
49
|
+
{
|
|
50
|
+
effect: 'allow',
|
|
51
|
+
groupOrder: 'declared',
|
|
52
|
+
groups: ['DNS Read'],
|
|
53
|
+
resource: 'com.cloudflare.api.account.zone.z1',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
ttl: '5m',
|
|
57
|
+
};
|
|
58
|
+
const cf = (props: Partial<BaoCloudflareRoleProps>, removal: Removal = RemovalPolicy.retain) =>
|
|
59
|
+
Effect.asVoid(BaoCloudflareRole('Role', { ...CF, ...props }).pipe(removal()));
|
|
60
|
+
|
|
61
|
+
describe('Bao.CloudflareRole move, through the engine', () => {
|
|
62
|
+
it('a new name plans replace and deletes the OLD path under destroy', async () => {
|
|
63
|
+
const store = cloudflareRoles();
|
|
64
|
+
await withStack(store, async (deploy, seen) => {
|
|
65
|
+
await deploy(cf({}, RemovalPolicy.destroy));
|
|
66
|
+
seen.length = 0;
|
|
67
|
+
assert.deepEqual(await deploy(cf({ name: 'example-com-dns-edit' }, RemovalPolicy.destroy)), {
|
|
68
|
+
Role: 'replace',
|
|
69
|
+
});
|
|
70
|
+
assert.deepEqual(writes(seen), [
|
|
71
|
+
'PUT /v1/cloudflare-acme-dns/roles/example-com-dns-edit',
|
|
72
|
+
'DELETE /v1/cloudflare-acme-dns/roles/example-com-dns-read',
|
|
73
|
+
]);
|
|
74
|
+
assert.deepEqual(livePaths(store), ['cloudflare-acme-dns/roles/example-com-dns-edit']);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('a new mount plans replace, and the default retain keeps the old role live', async () => {
|
|
79
|
+
const store = cloudflareRoles();
|
|
80
|
+
await withStack(store, async (deploy, seen) => {
|
|
81
|
+
await deploy(cf({}));
|
|
82
|
+
assert.deepEqual(await deploy(cf({ mount: 'cloudflare-other-dns' })), { Role: 'replace' });
|
|
83
|
+
assert.ok(!writes(seen).some((each) => each.startsWith('DELETE')));
|
|
84
|
+
assert.deepEqual(livePaths(store), [
|
|
85
|
+
'cloudflare-acme-dns/roles/example-com-dns-read',
|
|
86
|
+
'cloudflare-other-dns/roles/example-com-dns-read',
|
|
87
|
+
]);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('a trailing slash on the mount is the same path: noop, nothing deleted', async () => {
|
|
92
|
+
const store = cloudflareRoles();
|
|
93
|
+
await withStack(store, async (deploy, seen) => {
|
|
94
|
+
await deploy(cf({}, RemovalPolicy.destroy));
|
|
95
|
+
seen.length = 0;
|
|
96
|
+
const planned = await deploy(cf({ mount: 'cloudflare-acme-dns/' }, RemovalPolicy.destroy));
|
|
97
|
+
assert.deepEqual(planned, { Role: 'noop' });
|
|
98
|
+
assert.deepEqual(writes(seen), []);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const PVE: BaoProxmoxRoleProps = {
|
|
104
|
+
maxTtl: '6h',
|
|
105
|
+
mintUser: 'reader@pve',
|
|
106
|
+
mount: 'proxmox-lab',
|
|
107
|
+
name: 'read',
|
|
108
|
+
ttl: '1h',
|
|
109
|
+
};
|
|
110
|
+
const pve = (props: Partial<BaoProxmoxRoleProps>, removal: Removal = RemovalPolicy.retain) =>
|
|
111
|
+
Effect.asVoid(BaoProxmoxRole('Role', { ...PVE, ...props }).pipe(removal()));
|
|
112
|
+
|
|
113
|
+
describe('Bao.ProxmoxRole move, through the engine', () => {
|
|
114
|
+
it('a new name plans replace and deletes the OLD path under destroy', async () => {
|
|
115
|
+
const store = proxmoxRoles();
|
|
116
|
+
await withStack(store, async (deploy, seen) => {
|
|
117
|
+
await deploy(pve({}, RemovalPolicy.destroy));
|
|
118
|
+
seen.length = 0;
|
|
119
|
+
assert.deepEqual(await deploy(pve({ name: 'reader' }, RemovalPolicy.destroy)), {
|
|
120
|
+
Role: 'replace',
|
|
121
|
+
});
|
|
122
|
+
assert.deepEqual(writes(seen), [
|
|
123
|
+
'PUT /v1/proxmox-lab/roles/reader',
|
|
124
|
+
'DELETE /v1/proxmox-lab/roles/read',
|
|
125
|
+
]);
|
|
126
|
+
assert.deepEqual(livePaths(store), ['proxmox-lab/roles/reader']);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('refuses a rename that also re-scopes mint_user, at plan, before any write', async () => {
|
|
131
|
+
const store = proxmoxRoles();
|
|
132
|
+
await withStack(store, async (deploy, seen) => {
|
|
133
|
+
await deploy(pve({}));
|
|
134
|
+
seen.length = 0;
|
|
135
|
+
const moved = pve({ mintUser: 'provisioner@pve', name: 'provision' });
|
|
136
|
+
await assert.rejects(deploy(moved), /allowMintUserChange: 'reader@pve'/);
|
|
137
|
+
assert.deepEqual(writes(seen), []);
|
|
138
|
+
assert.deepEqual(livePaths(store), ['proxmox-lab/roles/read']);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('lets the re-scoping rename through when it names the mint user it replaces', async () => {
|
|
143
|
+
const store = proxmoxRoles();
|
|
144
|
+
await withStack(store, async (deploy) => {
|
|
145
|
+
await deploy(pve({}));
|
|
146
|
+
const allowed = pve({
|
|
147
|
+
allowMintUserChange: 'reader@pve',
|
|
148
|
+
mintUser: 'provisioner@pve',
|
|
149
|
+
name: 'provision',
|
|
150
|
+
});
|
|
151
|
+
assert.deepEqual(await deploy(allowed), { Role: 'replace' });
|
|
152
|
+
assert.equal(store.live.get('proxmox-lab/roles/provision')?.['mint_user'], 'provisioner@pve');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('keeps a same-path mint_user change an update, refused at apply as before', async () => {
|
|
157
|
+
const store = proxmoxRoles();
|
|
158
|
+
await withStack(store, async (deploy, seen) => {
|
|
159
|
+
await deploy(pve({}));
|
|
160
|
+
seen.length = 0;
|
|
161
|
+
await assert.rejects(deploy(pve({ mintUser: 'provisioner@pve' })), /re-scopes mint_user/);
|
|
162
|
+
assert.deepEqual(writes(seen), []);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* ★ The name is an Output of an upstream role whose `ttl` is changing, so no diff can see it at plan.
|
|
169
|
+
* Reconcile refuses the `update`; the next deploy, with the value settled, plans `replace`.
|
|
170
|
+
*/
|
|
171
|
+
describe('an update across a move the diff could not see', () => {
|
|
172
|
+
it('Bao.CloudflareRole: refused before any write, then replaced', async () => {
|
|
173
|
+
const store = cloudflareRoles();
|
|
174
|
+
const body = (ttl: string): StackBody =>
|
|
175
|
+
Effect.gen(function* () {
|
|
176
|
+
const up = yield* BaoCloudflareRole('Upstream', { ...CF, name: 'upstream', ttl });
|
|
177
|
+
const name = Output.interpolate`role-${up.ttl}`;
|
|
178
|
+
yield* BaoCloudflareRole('Role', { ...CF, name }).pipe(RemovalPolicy.destroy());
|
|
179
|
+
});
|
|
180
|
+
await withStack(store, async (deploy) => {
|
|
181
|
+
await deploy(body('5m'));
|
|
182
|
+
await assert.rejects(deploy(body('10m')), /identity moved from .*role-300 to .*role-600/);
|
|
183
|
+
assert.ok(!store.live.has('cloudflare-acme-dns/roles/role-600'));
|
|
184
|
+
assert.deepEqual(await deploy(body('10m')), { Role: 'replace', Upstream: 'noop' });
|
|
185
|
+
assert.ok(!store.live.has('cloudflare-acme-dns/roles/role-300'));
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('Bao.ProxmoxRole: refused before any write, then replaced', async () => {
|
|
190
|
+
const store = proxmoxRoles();
|
|
191
|
+
const body = (ttl: string): StackBody =>
|
|
192
|
+
Effect.gen(function* () {
|
|
193
|
+
const up = yield* BaoProxmoxRole('Upstream', { ...PVE, name: 'upstream', ttl });
|
|
194
|
+
const name = Output.interpolate`r-${up.ttl}`;
|
|
195
|
+
yield* BaoProxmoxRole('Role', { ...PVE, name }).pipe(RemovalPolicy.destroy());
|
|
196
|
+
});
|
|
197
|
+
await withStack(store, async (deploy) => {
|
|
198
|
+
await deploy(body('1h'));
|
|
199
|
+
await assert.rejects(deploy(body('2h')), /identity moved from .*r-1h to .*r-2h/);
|
|
200
|
+
assert.ok(!store.live.has('proxmox-lab/roles/r-2h'));
|
|
201
|
+
assert.deepEqual(await deploy(body('2h')), { Role: 'replace', Upstream: 'noop' });
|
|
202
|
+
assert.deepEqual(livePaths(store), ['proxmox-lab/roles/r-2h', 'proxmox-lab/roles/upstream']);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|