Skip to content

Commit

Permalink
WIP: Manage GCB connection resources more carefully.
Browse files Browse the repository at this point in the history
  • Loading branch information
taeold committed Nov 22, 2023
1 parent edbbc62 commit 33f4c48
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 146 deletions.
5 changes: 3 additions & 2 deletions src/gcp/cloudbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,15 @@ interface LinkableRepositories {
export async function createConnection(
projectId: string,
location: string,
connectionId: string
connectionId: string,
githubConfig: GitHubConfig = {}
): Promise<Operation> {
const res = await client.post<
Omit<Omit<Connection, "name">, ConnectionOutputOnlyFields>,
Operation
>(
`projects/${projectId}/locations/${location}/connections`,
{ githubConfig: {} },
{ githubConfig },
{ queryParams: { connectionId } }
);
return res.body;
Expand Down
251 changes: 126 additions & 125 deletions src/init/features/frameworks/index.ts
Original file line number Diff line number Diff line change
@@ -1,170 +1,171 @@
import * as clc from "colorette";
import * as utils from "../../../utils";
import { logger } from "../../../logger";
import { promptOnce } from "../../../prompt";
import { DEFAULT_REGION, ALLOWED_REGIONS } from "./constants";
import * as repo from "./repo";
import { Backend, BackendOutputOnlyFields } from "../../../gcp/frameworks";
import { Repository } from "../../../gcp/cloudbuild";
import * as poller from "../../../operation-poller";
import { frameworksOrigin } from "../../../api";
import * as gcp from "../../../gcp/frameworks";
import { API_VERSION } from "../../../gcp/frameworks";
import { FirebaseError } from "../../../error";
import {frameworksOrigin} from "../../../api";

Check failure on line 6 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Replace `frameworksOrigin` with `·frameworksOrigin·`

Check failure on line 6 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `frameworksOrigin` with `·frameworksOrigin·`

Check failure on line 6 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `frameworksOrigin` with `·frameworksOrigin·`

Check failure on line 6 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `frameworksOrigin` with `·frameworksOrigin·`

Check failure on line 6 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `frameworksOrigin` with `·frameworksOrigin·`
import {Backend, BackendOutputOnlyFields} from "../../../gcp/frameworks";

Check failure on line 7 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Replace `Backend,·BackendOutputOnlyFields` with `·Backend,·BackendOutputOnlyFields·`

Check failure on line 7 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `Backend,·BackendOutputOnlyFields` with `·Backend,·BackendOutputOnlyFields·`

Check failure on line 7 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `Backend,·BackendOutputOnlyFields` with `·Backend,·BackendOutputOnlyFields·`

Check failure on line 7 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `Backend,·BackendOutputOnlyFields` with `·Backend,·BackendOutputOnlyFields·`

Check failure on line 7 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `Backend,·BackendOutputOnlyFields` with `·Backend,·BackendOutputOnlyFields·`
import {Repository} from "../../../gcp/cloudbuild";

Check failure on line 8 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Replace `Repository` with `·Repository·`

Check failure on line 8 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `Repository` with `·Repository·`

Check failure on line 8 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `Repository` with `·Repository·`

Check failure on line 8 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `Repository` with `·Repository·`

Check failure on line 8 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `Repository` with `·Repository·`
import {API_VERSION} from "../../../gcp/frameworks";

Check failure on line 9 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Replace `API_VERSION` with `·API_VERSION·`

Check failure on line 9 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `API_VERSION` with `·API_VERSION·`

Check failure on line 9 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `API_VERSION` with `·API_VERSION·`

Check failure on line 9 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `API_VERSION` with `·API_VERSION·`

Check failure on line 9 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `API_VERSION` with `·API_VERSION·`
import {FirebaseError} from "../../../error";

Check failure on line 10 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Replace `FirebaseError` with `·FirebaseError·`

Check failure on line 10 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `FirebaseError` with `·FirebaseError·`

Check failure on line 10 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `FirebaseError` with `·FirebaseError·`

Check failure on line 10 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `FirebaseError` with `·FirebaseError·`

Check failure on line 10 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `FirebaseError` with `·FirebaseError·`
import {logger} from "../../../logger";

Check failure on line 11 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Replace `logger` with `·logger·`

Check failure on line 11 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `logger` with `·logger·`

Check failure on line 11 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `logger` with `·logger·`

Check failure on line 11 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `logger` with `·logger·`

Check failure on line 11 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `logger` with `·logger·`
import {promptOnce} from "../../../prompt";

Check failure on line 12 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Replace `promptOnce` with `·promptOnce·`

Check failure on line 12 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `promptOnce` with `·promptOnce·`

Check failure on line 12 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `promptOnce` with `·promptOnce·`

Check failure on line 12 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `promptOnce` with `·promptOnce·`

Check failure on line 12 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `promptOnce` with `·promptOnce·`
import {DEFAULT_REGION, ALLOWED_REGIONS} from "./constants";

Check failure on line 13 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Replace `DEFAULT_REGION,·ALLOWED_REGIONS` with `·DEFAULT_REGION,·ALLOWED_REGIONS·`

Check failure on line 13 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `DEFAULT_REGION,·ALLOWED_REGIONS` with `·DEFAULT_REGION,·ALLOWED_REGIONS·`

Check failure on line 13 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `DEFAULT_REGION,·ALLOWED_REGIONS` with `·DEFAULT_REGION,·ALLOWED_REGIONS·`

Check failure on line 13 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Replace `DEFAULT_REGION,·ALLOWED_REGIONS` with `·DEFAULT_REGION,·ALLOWED_REGIONS·`

Check failure on line 13 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Replace `DEFAULT_REGION,·ALLOWED_REGIONS` with `·DEFAULT_REGION,·ALLOWED_REGIONS·`

const frameworksPollerOptions: Omit<poller.OperationPollerOptions, "operationResourceName"> = {
apiOrigin: frameworksOrigin,
apiVersion: API_VERSION,
masterTimeout: 25 * 60 * 1_000,
maxBackoff: 10_000,
apiOrigin: frameworksOrigin,

Check failure on line 16 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Delete `··`

Check failure on line 16 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Delete `··`

Check failure on line 16 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Delete `··`

Check failure on line 16 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Delete `··`

Check failure on line 16 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Delete `··`
apiVersion: API_VERSION,

Check failure on line 17 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Delete `··`

Check failure on line 17 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Delete `··`

Check failure on line 17 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Delete `··`

Check failure on line 17 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (18)

Delete `··`

Check failure on line 17 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / unit (16)

Delete `··`
masterTimeout: 25 * 60 * 1_000,
maxBackoff: 10_000,
};

/**
* Setup new frameworks project.
*/
export async function doSetup(setup: any, projectId: string): Promise<void> {

Check warning on line 25 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unexpected any. Specify a different type
setup.frameworks = {};
setup.frameworks = {};

Check warning on line 26 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe member access .frameworks on an `any` value

utils.logBullet("First we need a few details to create your backend.");
utils.logBullet("First we need a few details to create your backend.");

await promptOnce(
{
name: "serviceName",
type: "input",
default: "acme-inc-web",
message: "Create a name for your backend [1-30 characters]",
},
setup.frameworks
);
await promptOnce(
{
name: "serviceName",
type: "input",
default: "acme-inc-web",
message: "Create a name for your backend [1-30 characters]",
},
setup.frameworks

Check warning on line 37 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe argument of type `any` assigned to a parameter of type `Options | undefined`

Check warning on line 37 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe member access .frameworks on an `any` value
);

await promptOnce(
{
name: "region",
type: "list",
default: DEFAULT_REGION,
message:
"Please select a region " +
`(${clc.yellow("info")}: Your region determines where your backend is located):\n`,
choices: ALLOWED_REGIONS,
},
setup.frameworks
);
await promptOnce(
{
name: "region",
type: "list",
default: DEFAULT_REGION,
message:
"Please select a region " +
`(${clc.yellow("info")}: Your region determines where your backend is located):\n`,
choices: ALLOWED_REGIONS,
},
setup.frameworks

Check warning on line 50 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe argument of type `any` assigned to a parameter of type `Options | undefined`

Check warning on line 50 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe member access .frameworks on an `any` value
);

utils.logSuccess(`Region set to ${setup.frameworks.region}.`);
utils.logSuccess(`Region set to ${setup.frameworks.region}.`);

Check warning on line 53 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Invalid type "any" of template literal expression

Check warning on line 53 in src/init/features/frameworks/index.ts

View workflow job for this annotation

GitHub Actions / lint (18)

Unsafe member access .frameworks on an `any` value

const backend: Backend | undefined = await getOrCreateBackend(projectId, setup);
if (backend) {
logger.info();
utils.logSuccess(`Successfully created backend:\n ${backend.name}`);
logger.info();
utils.logSuccess(`Your site is being deployed at:\n https://${backend.uri}`);
logger.info();
utils.logSuccess(
`View the rollout status by running:\n firebase backends:get --backend=${backend.name}`
);
logger.info();
}
const backend: Backend | undefined = await getOrCreateBackend(projectId, setup);

if (backend) {
logger.info();
utils.logSuccess(`Successfully created backend:\n ${backend.name}`);
logger.info();
utils.logSuccess(`Your site is being deployed at:\n https://${backend.uri}`);
logger.info();
utils.logSuccess(
`View the rollout status by running:\n firebase backends:get --backend=${backend.name}`
);
logger.info();
}
}

function toBackend(cloudBuildConnRepo: Repository): Omit<Backend, BackendOutputOnlyFields> {
return {
codebase: {
repository: `${cloudBuildConnRepo.name}`,
rootDirectory: "/",
},
labels: {},
};
return {
codebase: {
repository: `${cloudBuildConnRepo.name}`,
rootDirectory: "/",
},
labels: {},
};
}

/**
* Creates backend if it doesn't exist.
*/
export async function getOrCreateBackend(
projectId: string,
setup: any
projectId: string,
setup: any
): Promise<Backend | undefined> {
const location: string = setup.frameworks.region;
try {
return await getExistingBackend(projectId, setup, location);
} catch (err: unknown) {
if ((err as FirebaseError).status === 404) {
const cloudBuildConnRepo = await repo.linkGitHubRepository(projectId, location);
logger.info();
await promptOnce(
{
name: "branchName",
type: "input",
default: "main",
message: "Which branch do you want to deploy?",
},
setup.frameworks
);
const backendDetails = toBackend(cloudBuildConnRepo);
logger.info(clc.bold(`\n${clc.white("===")} Creating your backend`));
return await createBackend(projectId, location, backendDetails, setup.frameworks.serviceName);
} else {
throw new FirebaseError(
`Failed to get or create a backend using the given initialization details: ${err}`
);
const location: string = setup.frameworks.region;
try {
return await getExistingBackend(projectId, setup, location);
} catch (err: unknown) {
if ((err as FirebaseError).status === 404) {
const cloudBuildConnRepo = await repo.linkGitHubRepository(projectId, location);
logger.info();
await promptOnce(
{
name: "branchName",
type: "input",
default: "main",
message: "Which branch do you want to deploy?",
},
setup.frameworks
);
const backendDetails = toBackend(cloudBuildConnRepo);
logger.info(clc.bold(`\n${clc.white("===")} Creating your backend`));
return await createBackend(projectId, location, backendDetails, setup.frameworks.serviceName);
} else {
throw new FirebaseError(
`Failed to get or create a backend using the given initialization details: ${err}`
);
}
}
}

return undefined;
return undefined;
}

async function getExistingBackend(
projectId: string,
setup: any,
location: string
projectId: string,
setup: any,
location: string
): Promise<Backend> {
let backend = await gcp.getBackend(projectId, location, setup.frameworks.serviceName);
while (backend) {
setup.frameworks.serviceName = undefined;
await promptOnce(
{
name: "existingBackend",
type: "confirm",
default: true,
message:
"A backend already exists for the given serviceName, do you want to use existing backend? (yes/no)",
},
setup.frameworks
);
if (setup.frameworks.existingBackend) {
logger.info("Using the existing backend.");
return backend;
let backend = await gcp.getBackend(projectId, location, setup.frameworks.serviceName);
while (backend) {
setup.frameworks.serviceName = undefined;
await promptOnce(
{
name: "existingBackend",
type: "confirm",
default: true,
message:
"A backend already exists for the given serviceName, do you want to use existing backend? (yes/no)",
},
setup.frameworks
);
if (setup.frameworks.existingBackend) {
logger.info("Using the existing backend.");
return backend;
}
await promptOnce(
{
name: "serviceName",
type: "input",
default: "acme-inc-web",
message: "Please enter a new service name [1-30 characters]",
},
setup.frameworks
);
backend = await gcp.getBackend(projectId, location, setup.frameworks.serviceName);
setup.frameworks.existingBackend = undefined;
}
await promptOnce(
{
name: "serviceName",
type: "input",
default: "acme-inc-web",
message: "Please enter a new service name [1-30 characters]",
},
setup.frameworks
);
backend = await gcp.getBackend(projectId, location, setup.frameworks.serviceName);
setup.frameworks.existingBackend = undefined;
}

return backend;
return backend;
}

/**
* Creates backend object from long running operations.
*/
export async function createBackend(
projectId: string,
location: string,
backendReqBoby: Omit<Backend, BackendOutputOnlyFields>,
backendId: string
projectId: string,
location: string,
backendReqBoby: Omit<Backend, BackendOutputOnlyFields>,
backendId: string
): Promise<Backend> {
const op = await gcp.createBackend(projectId, location, backendReqBoby, backendId);
const backend = await poller.pollOperation<Backend>({
...frameworksPollerOptions,
pollerName: `create-${projectId}-${location}-${backendId}`,
operationResourceName: op.name,
});
const op = await gcp.createBackend(projectId, location, backendReqBoby, backendId);
const backend = await poller.pollOperation<Backend>({
...frameworksPollerOptions,
pollerName: `create-${projectId}-${location}-${backendId}`,
operationResourceName: op.name,
});

return backend;
return backend;
}
Loading

0 comments on commit 33f4c48

Please sign in to comment.