console/deployment/index.ts

337 lines
8.9 KiB
TypeScript
Raw Normal View History

2022-05-18 07:26:57 +00:00
import * as pulumi from '@pulumi/pulumi';
2022-12-28 19:22:54 +00:00
import { deployApp } from './services/app';
import { deployStripeBilling } from './services/billing';
import { deployCFBroker } from './services/cf-broker';
import { deployCFCDN } from './services/cf-cdn';
import { deployClickhouse } from './services/clickhouse';
import { deployCloudFlareSecurityTransform } from './services/cloudflare-security';
import { deployDatabaseCleanupJob } from './services/database-cleanup';
2022-05-18 07:26:57 +00:00
import { deployDbMigrations } from './services/db-migrations';
2024-03-04 12:56:12 +00:00
import { configureDocker } from './services/docker';
import { deployEmails } from './services/emails';
2024-03-04 12:56:12 +00:00
import { prepareEnvironment } from './services/environment';
import { configureGithubApp } from './services/github';
2022-05-18 07:26:57 +00:00
import { deployGraphQL } from './services/graphql';
import { deployKafka } from './services/kafka';
2024-04-07 08:57:03 +00:00
import { deployObservability } from './services/observability';
import { deploySchemaPolicy } from './services/policy';
2024-03-04 12:56:12 +00:00
import { deployPostgres } from './services/postgres';
2022-05-18 07:26:57 +00:00
import { deployProxy } from './services/proxy';
2022-12-28 19:22:54 +00:00
import { deployRateLimit } from './services/rate-limit';
import { deployRedis } from './services/redis';
import { deployS3, deployS3Mirror } from './services/s3';
2022-12-28 19:22:54 +00:00
import { deploySchema } from './services/schema';
2024-03-04 12:56:12 +00:00
import { configureSentry } from './services/sentry';
import { deploySentryEventsMonitor } from './services/sentry-events';
2024-03-04 12:56:12 +00:00
import { configureSlackApp } from './services/slack-app';
feat: replace auth0 with supertokens (#303) * add supertoken container to docker-compose file * yeah I am sorry this one big commit and I am ashamed of it * use logOut function * feat: show header on 404 page * feat: better handling for organization cookie when not authenticated * wrap it * check session within server side props * add is_admin flag user migration * simplify and annotate the config * fix: handle status codes + fix email/password sign up with import from auth0 * no hardcoded env pls * decode process.env * secure update user id mapping via a key * fix: login form * lol we don't need to hit the API * fix: do graphql api authorization via authorization header instead of cookie * implement isAdmin flag * fix: types :) * skipit * yo we can run this * set env variables * disable because it annoys the hell out of me * use the right host * add not about token length * refactor: decode environment variables * feat: store external user id from guthub/google provider in the database * workaround supertokens omitting null values from the token * re-enable check * i have no time for this shit * add missing env variable * fix: email test; missing domain extension * configure pulumi deployment Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Dotan Simha <dotansimha@gmail.com> * configure pulumi deployment Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Dotan Simha <dotansimha@gmail.com> * configure pulumi deployment Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Dotan Simha <dotansimha@gmail.com> * configure pulumi deployment Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> * fix: env names * fix: link google account to the correct db record * feat: email confirmation emails * ? * bump ts-node * fix types * omit package form the bundle * remove it from dependencies... * add emails apckage to dev deps * resolve eslint issues * remove comments * update dev info + change env variable (no need to expose it on the frontend) * use correct user id lol Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Dotan Simha <dotansimha@gmail.com>
2022-09-06 07:38:31 +00:00
import { deploySuperTokens } from './services/supertokens';
2022-12-28 19:22:54 +00:00
import { deployTokens } from './services/tokens';
import { deployUsage } from './services/usage';
import { deployUsageEstimation } from './services/usage-estimation';
import { deployUsageIngestor } from './services/usage-ingestor';
import { deployWebhooks } from './services/webhooks';
2024-03-04 12:56:12 +00:00
import { configureZendesk } from './services/zendesk';
2022-05-18 07:26:57 +00:00
import { optimizeAzureCluster } from './utils/azure-helpers';
2024-03-04 12:56:12 +00:00
import { isDefined } from './utils/helpers';
import { publishAppDeployment } from './utils/publish-app-deployment';
import { publishGraphQLSchema } from './utils/publish-graphql-schema';
// eslint-disable-next-line no-process-env
const imagesTag = process.env.DOCKER_IMAGE_TAG as string;
if (!imagesTag) {
throw new Error(`DOCKER_IMAGE_TAG env variable is not set.`);
}
// eslint-disable-next-line no-process-env
const graphqlSchemaAbsolutePath: string | undefined = process.env.GRAPHQL_SCHEMA_ABSOLUTE_PATH;
if (!graphqlSchemaAbsolutePath) {
throw new Error(`GRAPHQL_SCHEMA_ABSOLUTE_PATH env variable is not set.`);
}
// eslint-disable-next-line no-process-env
let hiveAppPersistedDocumentsAbsolutePath: string | undefined =
process.env.HIVE_APP_PERSISTED_DOCUMENTS_ABSOLUTE_PATH;
if (!hiveAppPersistedDocumentsAbsolutePath) {
console.warn(
'HIVE_APP_PERSISTED_DOCUMENTS_ABSOLUTE_PATH env variable is not set, defaulting to "0" (disabled).',
);
hiveAppPersistedDocumentsAbsolutePath = undefined;
}
2024-03-04 12:56:12 +00:00
optimizeAzureCluster();
2022-05-18 07:26:57 +00:00
2024-03-04 12:56:12 +00:00
const docker = configureDocker();
const envName = pulumi.getStack();
const heartbeatsConfig = new pulumi.Config('heartbeats');
2024-03-04 12:56:12 +00:00
const sentry = configureSentry();
const environment = prepareEnvironment({
release: imagesTag,
environment: envName,
rootDns: new pulumi.Config('common').require('dnsZone'),
});
deploySentryEventsMonitor({ docker, environment, sentry });
const observability = deployObservability({
envName,
tableSuffix: envName === 'prod' ? 'production' : envName,
});
2024-03-04 12:56:12 +00:00
const clickhouse = deployClickhouse();
const postgres = deployPostgres();
const redis = deployRedis({ environment });
const kafka = deployKafka();
const s3 = deployS3();
const s3Mirror = deployS3Mirror();
2022-11-07 13:27:19 +00:00
const cdn = deployCFCDN({
2024-03-04 12:56:12 +00:00
s3,
s3Mirror,
2024-03-04 12:56:12 +00:00
sentry,
environment,
2022-11-07 13:27:19 +00:00
});
2024-03-04 12:56:12 +00:00
const broker = deployCFBroker({
environment,
sentry,
2022-05-18 07:26:57 +00:00
});
// eslint-disable-next-line no-process-env
const shouldCleanDatabase = process.env.CLEAN_DATABASE === 'true';
2024-03-04 12:56:12 +00:00
const databaseCleanupJob = shouldCleanDatabase ? deployDatabaseCleanupJob({ environment }) : null;
// eslint-disable-next-line no-process-env
const forceRunDbMigrations = process.env.FORCE_DB_MIGRATIONS === 'true';
2022-05-18 07:26:57 +00:00
const dbMigrations = deployDbMigrations({
2024-03-04 12:56:12 +00:00
clickhouse,
docker,
postgres,
s3,
cdn,
environment,
image: docker.factory.getImageId('storage', imagesTag),
force: forceRunDbMigrations,
dependencies: [databaseCleanupJob].filter(isDefined),
2022-05-18 07:26:57 +00:00
});
2024-03-04 12:56:12 +00:00
const tokens = deployTokens({
image: docker.factory.getImageId('tokens', imagesTag),
environment,
2022-05-18 07:26:57 +00:00
dbMigrations,
2024-03-04 12:56:12 +00:00
docker,
postgres,
redis,
heartbeat: heartbeatsConfig.get('tokens'),
2024-03-04 12:56:12 +00:00
sentry,
2024-04-07 08:57:03 +00:00
observability,
2022-05-18 07:26:57 +00:00
});
2024-03-04 12:56:12 +00:00
const webhooks = deployWebhooks({
image: docker.factory.getImageId('webhooks', imagesTag),
environment,
heartbeat: heartbeatsConfig.get('webhooks'),
2024-03-04 12:56:12 +00:00
broker,
docker,
redis,
sentry,
2024-04-07 08:57:03 +00:00
observability,
2022-05-18 07:26:57 +00:00
});
2024-03-04 12:56:12 +00:00
const emails = deployEmails({
image: docker.factory.getImageId('emails', imagesTag),
docker,
environment,
redis,
sentry,
2024-04-07 08:57:03 +00:00
observability,
});
2024-03-04 12:56:12 +00:00
const usageEstimator = deployUsageEstimation({
image: docker.factory.getImageId('usage-estimator', imagesTag),
docker,
environment,
clickhouse,
2022-05-18 07:26:57 +00:00
dbMigrations,
2024-03-04 12:56:12 +00:00
sentry,
2024-04-07 08:57:03 +00:00
observability,
2022-05-18 07:26:57 +00:00
});
2024-03-04 12:56:12 +00:00
const billing = deployStripeBilling({
image: docker.factory.getImageId('stripe-billing', imagesTag),
docker,
postgres,
environment,
2022-05-18 07:26:57 +00:00
dbMigrations,
2024-03-04 12:56:12 +00:00
usageEstimator,
sentry,
2024-04-07 08:57:03 +00:00
observability,
2022-05-18 07:26:57 +00:00
});
2024-03-04 12:56:12 +00:00
const rateLimit = deployRateLimit({
image: docker.factory.getImageId('rate-limit', imagesTag),
docker,
environment,
2022-05-18 07:26:57 +00:00
dbMigrations,
2024-03-04 12:56:12 +00:00
usageEstimator,
emails,
postgres,
sentry,
2024-04-07 08:57:03 +00:00
observability,
2022-05-18 07:26:57 +00:00
});
2024-03-04 12:56:12 +00:00
const usage = deployUsage({
image: docker.factory.getImageId('usage', imagesTag),
docker,
environment,
tokens,
kafka,
2022-05-18 07:26:57 +00:00
dbMigrations,
2024-03-04 12:56:12 +00:00
rateLimit,
sentry,
2022-05-18 07:26:57 +00:00
});
2024-03-04 12:56:12 +00:00
const usageIngestor = deployUsageIngestor({
image: docker.factory.getImageId('usage-ingestor', imagesTag),
docker,
clickhouse,
kafka,
environment,
2022-05-18 07:26:57 +00:00
dbMigrations,
heartbeat: heartbeatsConfig.get('usageIngestor'),
2024-03-04 12:56:12 +00:00
sentry,
2022-05-18 07:26:57 +00:00
});
2024-03-04 12:56:12 +00:00
const schema = deploySchema({
image: docker.factory.getImageId('schema', imagesTag),
docker,
environment,
redis,
broker,
sentry,
2024-04-07 08:57:03 +00:00
observability,
});
2024-03-04 12:56:12 +00:00
const schemaPolicy = deploySchemaPolicy({
image: docker.factory.getImageId('policy', imagesTag),
docker,
environment,
sentry,
2024-04-07 08:57:03 +00:00
observability,
});
feat: replace auth0 with supertokens (#303) * add supertoken container to docker-compose file * yeah I am sorry this one big commit and I am ashamed of it * use logOut function * feat: show header on 404 page * feat: better handling for organization cookie when not authenticated * wrap it * check session within server side props * add is_admin flag user migration * simplify and annotate the config * fix: handle status codes + fix email/password sign up with import from auth0 * no hardcoded env pls * decode process.env * secure update user id mapping via a key * fix: login form * lol we don't need to hit the API * fix: do graphql api authorization via authorization header instead of cookie * implement isAdmin flag * fix: types :) * skipit * yo we can run this * set env variables * disable because it annoys the hell out of me * use the right host * add not about token length * refactor: decode environment variables * feat: store external user id from guthub/google provider in the database * workaround supertokens omitting null values from the token * re-enable check * i have no time for this shit * add missing env variable * fix: email test; missing domain extension * configure pulumi deployment Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Dotan Simha <dotansimha@gmail.com> * configure pulumi deployment Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Dotan Simha <dotansimha@gmail.com> * configure pulumi deployment Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Dotan Simha <dotansimha@gmail.com> * configure pulumi deployment Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> * fix: env names * fix: link google account to the correct db record * feat: email confirmation emails * ? * bump ts-node * fix types * omit package form the bundle * remove it from dependencies... * add emails apckage to dev deps * resolve eslint issues * remove comments * update dev info + change env variable (no need to expose it on the frontend) * use correct user id lol Co-authored-by: Kamil Kisiela <kamil.kisiela@gmail.com> Co-authored-by: Dotan Simha <dotansimha@gmail.com>
2022-09-06 07:38:31 +00:00
2024-03-04 12:56:12 +00:00
const supertokens = deploySuperTokens(postgres, { dependencies: [dbMigrations] }, environment);
const zendesk = configureZendesk({ environment });
const githubApp = configureGithubApp();
const slackApp = configureSlackApp();
const graphql = deployGraphQL({
postgres,
environment,
clickhouse,
image: docker.factory.getImageId('server', imagesTag),
docker,
tokens,
webhooks,
schema,
schemaPolicy,
2022-05-18 07:26:57 +00:00
dbMigrations,
2024-03-04 12:56:12 +00:00
redis,
usage,
2022-11-07 13:27:19 +00:00
cdn,
2024-03-04 12:56:12 +00:00
usageEstimator,
rateLimit,
billing,
emails,
supertokens,
s3,
s3Mirror,
2024-03-04 12:56:12 +00:00
zendesk,
githubApp,
sentry,
2024-04-07 08:57:03 +00:00
observability,
2022-05-18 07:26:57 +00:00
});
const apiConfig = new pulumi.Config('api');
const apiEnv = apiConfig.requireObject<Record<string, string>>('env');
const publishGraphQLSchemaCommand = publishGraphQLSchema({
graphql,
registry: {
endpoint: `https://${environment.appDns}/registry`,
accessToken: apiEnv.HIVE_API_TOKEN,
},
version: {
commit: imagesTag,
},
schemaPath: graphqlSchemaAbsolutePath,
});
let publishAppDeploymentCommand: pulumi.Resource | undefined;
if (hiveAppPersistedDocumentsAbsolutePath) {
publishAppDeploymentCommand = publishAppDeployment({
appName: 'hive-app',
registry: {
endpoint: `https://${environment.appDns}/registry`,
accessToken: apiEnv.HIVE_API_TOKEN,
},
version: {
commit: imagesTag,
},
persistedDocumentsPath: hiveAppPersistedDocumentsAbsolutePath,
// We need to wait until the new GraphQL schema is published before we can publish the app deployment.
dependsOn: [publishGraphQLSchemaCommand],
});
}
2022-05-18 07:26:57 +00:00
const app = deployApp({
2024-03-04 12:56:12 +00:00
environment,
graphql,
publishAppDeploymentCommand,
2022-05-18 07:26:57 +00:00
dbMigrations,
2024-03-04 12:56:12 +00:00
image: docker.factory.getImageId('app', imagesTag),
docker,
zendesk,
billing,
github: githubApp,
slackApp,
sentry,
2022-05-18 07:26:57 +00:00
});
const proxy = deployProxy({
2024-04-07 08:57:03 +00:00
observability,
2022-05-18 07:26:57 +00:00
app,
2024-03-04 12:56:12 +00:00
graphql,
usage,
environment,
2022-05-18 07:26:57 +00:00
});
deployCloudFlareSecurityTransform({
envName,
// Paths used by 3rd-party software.
// The CF Page Rules should not affect them and do not apply any special security headers.
ignoredPaths: [
'/api/auth',
'/api/health',
'/usage',
'/graphql',
'/registry',
'/server',
'/api/github',
'/api/slack',
'/api/lab',
],
ignoredHosts: ['cdn.graphql-hive.com', 'cdn.staging.graphql-hive.com'],
});
2024-03-04 12:56:12 +00:00
export const graphqlApiServiceId = graphql.service.id;
export const usageApiServiceId = usage.service.id;
export const usageIngestorApiServiceId = usageIngestor.service.id;
export const tokensApiServiceId = tokens.service.id;
export const schemaApiServiceId = schema.service.id;
export const webhooksApiServiceId = webhooks.service.id;
2022-05-18 07:26:57 +00:00
export const appId = app.deployment.id;
export const publicIp = proxy!.status.loadBalancer.ingress[0].ip;