Skip to content

Commit

Permalink
Sets the minimum number of Fly machines to 1 (#1535)
Browse files Browse the repository at this point in the history
  • Loading branch information
infomiho authored Nov 6, 2023
1 parent 2b47abd commit 2752f88
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 30 deletions.
4 changes: 4 additions & 0 deletions waspc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ app todoApp {
}
```

### 🐞 Bug fixes / 🔧 small improvements
- Changed the minimum number of machines that a server app is using when deployed to Fly.io from 0 to 1. This prevents the server app from shutting down when there are no requests to it. There might be some other work that the server is doing e.g. running periodic Jobs or sending e-mails, so we want to make sure that the server is always running.


## 0.11.7

### 🐞 Bug fixes / 🔧 small improvements
Expand Down
22 changes: 22 additions & 0 deletions waspc/packages/deploy/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions waspc/packages/deploy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"eslint": "^8.31.0",
"prettier": "^2.8.8",
"typescript": "^4.9.4"
}
}
28 changes: 22 additions & 6 deletions waspc/packages/deploy/src/providers/fly/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ export function isYes(str: string): boolean {
}

export function ensureWaspDirLooksRight(thisCommand: Command): void {
const dirContainsWasproot = fs.existsSync(path.join(thisCommand.opts().waspProjectDir, '.wasproot'));
const dirContainsWasproot = fs.existsSync(
path.join(thisCommand.opts().waspProjectDir, '.wasproot'),
);
if (dirContainsWasproot) {
return;
}

waspSays('The supplied Wasp directory does not appear to be a valid Wasp project.');
waspSays(
'The supplied Wasp directory does not appear to be a valid Wasp project.',
);
waspSays('Please double check your path.');
exit(1);
}
Expand All @@ -38,8 +42,11 @@ function getWaspBuildDir(waspProjectDir: string) {
return path.join(waspProjectDir, '.wasp', 'build');
}

export function ensureDirsInCmdAreAbsoluteAndPresent(thisCommand: Command): void {
const waspProjectDirPath: string | undefined = thisCommand.opts().waspProjectDir;
export function ensureDirsInCmdAreAbsoluteAndPresent(
thisCommand: Command,
): void {
const waspProjectDirPath: string | undefined =
thisCommand.opts().waspProjectDir;
if (waspProjectDirPath) {
if (!path.isAbsolute(waspProjectDirPath)) {
waspSays('The Wasp dir path must be absolute.');
Expand Down Expand Up @@ -88,6 +95,10 @@ export function waspSays(str: string): void {
console.log('🚀 \x1b[33m ' + str + ' \x1b[0m');
}

export function boldText(str: string): string {
return '\x1b[1m' + str + '\x1b[0m';
}

export function displayWaspRocketImage(): void {
// Escaping backslashes makes it look weird here, but it works in console.
const asciiArt = `
Expand All @@ -109,7 +120,10 @@ export function getCommandHelp(command: Command): string {
}

function trimUsage(usage: string): string {
return usage.split(/[\r\n]+/)[0].replace('Usage: ', '').replace(' [options]', '');
return usage
.split(/[\r\n]+/)[0]
.replace('Usage: ', '')
.replace(' [options]', '');
}

// There is a theoretical race condition here since we are modifying a global `$`
Expand All @@ -118,7 +132,9 @@ function trimUsage(usage: string): string {
// times concurrently could change the setting incorrectly.
// However, our pattern of awaiting for both `$` and `silence` calls without any random
// callbacks using either means this interleaving should not ever happen.
export async function silence(cmd: ($hh: Shell) => Promise<ProcessOutput>): Promise<ProcessOutput> {
export async function silence(
cmd: ($hh: Shell) => Promise<ProcessOutput>,
): Promise<ProcessOutput> {
const verboseSetting = $.verbose;
$.verbose = false;
const proc = await cmd($);
Expand Down
22 changes: 17 additions & 5 deletions waspc/packages/deploy/src/providers/fly/helpers/tomlFileHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import path from 'node:path';
import { CommonOptions } from '../CommonOptions.js';

export interface TomlFilePaths {
serverTomlPath: string;
clientTomlPath: string;
serverTomlPath: string;
clientTomlPath: string;
}

export function getTomlFilePaths(options: CommonOptions): TomlFilePaths {
Expand Down Expand Up @@ -56,18 +56,30 @@ export function getAppNameFromToml(path: string): string {
return data.app;
}

export function getInferredBasenameFromServerToml(paths: TomlFilePaths): string {
export function getInferredBasenameFromServerToml(
paths: TomlFilePaths,
): string {
const serverName = getAppNameFromToml(paths.serverTomlPath);
return serverName.replace('-server', '');
}

export function getInferredBasenameFromClientToml(paths: TomlFilePaths): string {
export function getInferredBasenameFromClientToml(
paths: TomlFilePaths,
): string {
const clientName = getAppNameFromToml(paths.clientTomlPath);
return clientName.replace('-client', '');
}

export function replaceLineInLocalToml(searchValue: string | RegExp, replaceValue: string): void {
export function replaceLineInLocalToml(
searchValue: string | RegExp,
replaceValue: string,
): void {
const content = fs.readFileSync('fly.toml', 'utf8');
const updatedContent = content.replace(searchValue, replaceValue);
fs.writeFileSync('fly.toml', updatedContent);
}

export function doesLocalTomlContainLine(searchValue: string | RegExp): boolean {
const content = fs.readFileSync('fly.toml', 'utf8');
return content.search(searchValue) !== -1;
}
93 changes: 80 additions & 13 deletions waspc/packages/deploy/src/providers/fly/setup/setup.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import { $, cd } from 'zx';
import { $, cd, question } from 'zx';
import crypto from 'crypto';
import {
clientTomlExistsInProject,
copyLocalClientTomlToProject,
copyLocalServerTomlToProject,
deleteLocalToml,
doesLocalTomlContainLine,
getTomlFilePaths,
replaceLineInLocalToml,
serverTomlExistsInProject,
} from '../helpers/tomlFileHelpers.js';
import { createDeploymentInfo, DeploymentInfo } from '../DeploymentInfo.js';
import { SetupOptions } from './SetupOptions.js';
import { cdToClientBuildDir, cdToServerBuildDir, makeIdempotent, getCommandHelp, waspSays } from '../helpers/helpers.js';
import {
cdToClientBuildDir,
cdToServerBuildDir,
makeIdempotent,
getCommandHelp,
waspSays,
boldText,
} from '../helpers/helpers.js';
import { createFlyDbCommand } from '../index.js';

export async function setup(baseName: string, region: string, options: SetupOptions): Promise<void> {
export async function setup(
baseName: string,
region: string,
options: SetupOptions,
): Promise<void> {
waspSays('Setting up your Wasp app with Fly.io!');

const buildWasp = makeIdempotent(async () => {
Expand All @@ -24,7 +36,12 @@ export async function setup(baseName: string, region: string, options: SetupOpti
});

const tomlFilePaths = getTomlFilePaths(options);
const deploymentInfo = createDeploymentInfo(baseName, region, options, tomlFilePaths);
const deploymentInfo = createDeploymentInfo(
baseName,
region,
options,
tomlFilePaths,
);

if (serverTomlExistsInProject(tomlFilePaths)) {
waspSays(`${tomlFilePaths.serverTomlPath} exists. Skipping server setup.`);
Expand All @@ -40,7 +57,11 @@ export async function setup(baseName: string, region: string, options: SetupOpti
await setupClient(deploymentInfo);
}

waspSays(`Don't forget to create your database by running "${getCommandHelp(createFlyDbCommand)}".`);
waspSays(
`Don't forget to create your database by running "${getCommandHelp(
createFlyDbCommand,
)}".`,
);
}

async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
Expand All @@ -50,8 +71,10 @@ async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
deleteLocalToml();

const launchArgs = [
'--name', deploymentInfo.serverName,
'--region', deploymentInfo.region,
'--name',
deploymentInfo.serverName,
'--region',
deploymentInfo.region,
];

if (deploymentInfo.options.org) {
Expand All @@ -61,6 +84,31 @@ async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
// This creates the fly.toml file, but does not attempt to deploy.
await $`flyctl launch --no-deploy ${launchArgs}`;

const minMachinesOptionRegex = /min_machines_running = 0/g;

if (!doesLocalTomlContainLine(minMachinesOptionRegex)) {
await question(`\n⚠️ There was a possible issue setting up your server app.
We tried modifying your server fly.toml to set ${boldText(
'min_machines_running = 1',
)}, but couldn't find the option ${boldText(
'min_machines_running',
)} in the fly.toml.
We advise that you additionaly check what is the value for "minimal number of machines running" on Fly
for this server app and confirm that it is set to the value you are OK with.
Be aware that if it is set to 0, your server will shut down when there are no requests from the client,
which might be an issue for you if you have recurring Jobs or some other processes that need to keep
running on the server even without external input, in which case we advise keeping "minimal number
of machines running" setting at a number larger than zero.
Contact the Wasp Team at our Discord server if you need help with this: https://discord.gg/rzdnErX
Press any key to continue or Ctrl+C to cancel.`);
} else {
replaceLineInLocalToml(minMachinesOptionRegex, 'min_machines_running = 1');
}

copyLocalServerTomlToProject(deploymentInfo.tomlFilePaths);

const randomString = crypto.randomBytes(32).toString('hex');
Expand All @@ -74,7 +122,7 @@ async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
];

if (deploymentInfo.options.serverSecret.length > 0) {
deploymentInfo.options.serverSecret.forEach(secret => {
deploymentInfo.options.serverSecret.forEach((secret) => {
secretsArgs.push(secret);
});
}
Expand All @@ -92,8 +140,10 @@ async function setupClient(deploymentInfo: DeploymentInfo<SetupOptions>) {
deleteLocalToml();

const launchArgs = [
'--name', deploymentInfo.clientName,
'--region', deploymentInfo.region,
'--name',
deploymentInfo.clientName,
'--region',
deploymentInfo.region,
];

if (deploymentInfo.options.org) {
Expand All @@ -103,9 +153,26 @@ async function setupClient(deploymentInfo: DeploymentInfo<SetupOptions>) {
// This creates the fly.toml file, but does not attempt to deploy.
await $`flyctl launch --no-deploy ${launchArgs}`;

// goStatic listens on port 8043 by default, but the default fly.toml
// assumes port 8080 (or 3000, depending on flyctl version).
replaceLineInLocalToml(/internal_port = \d+/g, 'internal_port = 8043');
const internalPortOptionRegex = /internal_port = \d+/g;

if (!doesLocalTomlContainLine(internalPortOptionRegex)) {
await question(`\n⚠️ There was an issue setting up your client app.
We tried modifying your client fly.toml to set ${boldText(
'internal_port = 8043',
)}, but couldn't find the option ${boldText(
'internal_port',
)} in the fly.toml.
This means your client app might not be accessible.
Contact the Wasp Team at our Discord server if you need help with this: https://discord.gg/rzdnErX
Press any key to continue or Ctrl+C to cancel.`);
} else {
// goStatic listens on port 8043 by default, but the default fly.toml
// assumes port 8080 (or 3000, depending on flyctl version).
replaceLineInLocalToml(internalPortOptionRegex, 'internal_port = 8043');
}

copyLocalClientTomlToProject(deploymentInfo.tomlFilePaths);

Expand Down
1 change: 0 additions & 1 deletion waspc/waspc.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ data-files:
packages/studio/dist/**/*.js
packages/studio/dist/**/*.html
packages/studio/dist/**/*.css
packages/studio/dist/**/*.png
packages/studio/package.json
packages/studio/package-lock.json
data-dir: data/
Expand Down
16 changes: 11 additions & 5 deletions web/docs/advanced/deployment/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,23 @@ wasp deploy fly create-db mia
wasp deploy fly deploy
```

The commands above use the app basename `my-wasp-app` and deploy it to the _Miami, Florida (US) region_ (called `mia`).
The commands above use the app basename `my-wasp-app` and deploy it to the _Miami, Florida (US) region_ (called `mia`). Read more about Fly.io regions [here](#flyio-regions).

:::caution Unique Name
Your app name must be unique across all of Fly or deployment will fail.
:::

The basename is used to create all three app tiers, resulting in three separate apps in your Fly dashboard:

- `my-wasp-app-client`
- `my-wasp-app-server`
- `my-wasp-app-db`

:::caution Unique Name
Your app name must be unique across all of Fly or deployment will fail.
:::
You'll notice that Wasp creates two new files in your project root directory:
- `fly-server.toml`
- `fly-client.toml`

Read more about Fly.io regions [here](#flyio-regions).
You should include these files in your version control so that you can deploy your app with a single command in the future.

### Using a Custom Domain For Your App

Expand Down Expand Up @@ -159,6 +163,8 @@ It accepts the following arguments:
After running `setup`, Wasp creates two new files in your project root directory: `fly-server.toml` and `fly-client.toml`.
You should include these files in your version control.

You **can edit the `fly-server.toml` and `fly-client.toml` files** to further configure your Fly deployments. Wasp will use the TOML files when you run `deploy`.

If you want to maintain multiple apps, you can add the `--fly-toml-dir <abs-path>` option to point to different directories, like "dev" or "staging".

:::caution Execute Only Once
Expand Down

0 comments on commit 2752f88

Please sign in to comment.