1import * as GCP from "@/GCP";
2import { GcpEnvironment } from "@/GCP/Environment";
3import * as Test from "@/Test/Alchemy";
4import * as Core from "@/Test/Core";
5import * as resourcemanager from "@distilled.cloud/gcp/cloudresourcemanager_v3";
6import * as rma from "@distilled.cloud/gcp/rapidmigrationassessment_v1";
7import { describe, expect } from "alchemy-test";
8import * as Effect from "effect/Effect";
9import { dockerAvailable, expectProbe } from "../bindingHost.ts";
10import RmaBindingsHost, { OnPrem } from "./fixtures/bindings-host.ts";
11
12const testOptions = { providers: GCP.providers() };
13const { test, beforeAll, afterAll } = Test.make(testOptions);
14const sharedStack = Core.scratchStack(
15 testOptions,
16 "RapidMigrationAssessmentBindings",
17);
18
19// Needs the Rapid Migration Assessment API (disabled on the testing
20// project). Set GCP_TEST_RMA=1 on a project with the API enabled.
21const runLifecycle = !!process.env.GCP_TEST_RMA && !process.env.FAST;
22
23let baseUrl: string;
24let member: string;
25let collectorName: string;
26
27/** The operation a probe started exists and targets the bound collector. */
28const expectOperationOnCollector = (operation: rma.Operation) =>
29 Effect.gen(function* () {
30 expect(operation.name).toContain("/operations/");
31 const live = yield* rma.getProjectsLocationsOperations({
32 name: operation.name!,
33 });
34 expect(live.name).toEqual(operation.name);
35 expect(JSON.stringify(live.metadata ?? {})).toContain(collectorName);
36 });
37
38/** `[role, condition]` the host holds on the project. */
39const hostProjectGrants = Effect.gen(function* () {
40 const { project } = yield* GcpEnvironment.current;
41 const policy = yield* resourcemanager.getIamPolicyProjects({
42 resource: `projects/${project}`,
43 body: { options: { requestedPolicyVersion: 3 } },
44 });
45 return (policy.bindings ?? [])
46 .filter((binding) => (binding.members ?? []).includes(member))
47 .map((binding) => [binding.role, binding.condition]);
48});
49
50describe.skipIf(!dockerAvailable || !runLifecycle)(
51 "RapidMigrationAssessment Bindings",
52 {
53 tags: [
54 "provider:gcp",
55 "provider:gcp:rapidmigrationassessment",
56 "provider:gcp:run",
57 "live",
58 ],
59 },
60 () => {
61 beforeAll(
62 Effect.gen(function* () {
63 yield* sharedStack.destroy();
64 const out = yield* sharedStack.deploy(
65 Effect.gen(function* () {
66 const host = yield* RmaBindingsHost;
67 return {
68 uri: host.uri,
69 serviceAccount: host.serviceAccount,
70 collector: (yield* OnPrem).name,
71 };
72 }),
73 );
74 baseUrl = out.uri!;
75 member = `serviceAccount:${out.serviceAccount!}`;
76 collectorName = out.collector;
77 }),
78 { timeout: 900_000 },
79 );
80
81 afterAll(sharedStack.destroy(), { timeout: 600_000 });
82
83 // RMA has no resource-level IAM; rma.runner is granted on the project.
84 const runnerOnProject = [["roles/rma.runner", undefined]];
85
86 describe("PauseCollector", () => {
87 test.provider(
88 "starts a pause operation on the collector",
89 (_stack) =>
90 Effect.gen(function* () {
91 const operation = yield* expectProbe<rma.Operation>(
92 baseUrl,
93 "pause",
94 );
95 yield* expectOperationOnCollector(operation);
96 expect(yield* hostProjectGrants).toEqual(runnerOnProject);
97 }),
98 {
99 tags: [
100 "provider:gcp",
101 "provider:gcp:rapidmigrationassessment",
102 "live",
103 ],
104 timeout: 600_000,
105 },
106 );
107 });
108
109 describe("ResumeCollector", () => {
110 test.provider(
111 "starts a resume operation on the collector",
112 (_stack) =>
113 Effect.gen(function* () {
114 const operation = yield* expectProbe<rma.Operation>(
115 baseUrl,
116 "resume",
117 );
118 yield* expectOperationOnCollector(operation);
119 expect(yield* hostProjectGrants).toEqual(runnerOnProject);
120 }),
121 {
122 tags: [
123 "provider:gcp",
124 "provider:gcp:rapidmigrationassessment",
125 "live",
126 ],
127 timeout: 600_000,
128 },
129 );
130 });
131
132 describe("RegisterCollector", () => {
133 test.provider(
134 "starts a register operation on the collector",
135 (_stack) =>
136 Effect.gen(function* () {
137 const operation = yield* expectProbe<rma.Operation>(
138 baseUrl,
139 "register",
140 );
141 yield* expectOperationOnCollector(operation);
142 expect(yield* hostProjectGrants).toEqual(runnerOnProject);
143 }),
144 {
145 tags: [
146 "provider:gcp",
147 "provider:gcp:rapidmigrationassessment",
148 "live",
149 ],
150 timeout: 600_000,
151 },
152 );
153 });
154 },
155);