diff --git a/runner/package.json b/runner/package.json index 87b21b967..3cc4f15bc 100644 --- a/runner/package.json +++ b/runner/package.json @@ -14,7 +14,8 @@ "start:dev": "tsc -p ./tsconfig.build.json && node ./dist", "start:docker": "node dist/index.js", "test": "npm run codegen && node --experimental-vm-modules ./node_modules/.bin/jest --silent", - "lint": "eslint -c .eslintrc.js" + "lint": "eslint -c .eslintrc.js", + "script:suspend-indexer": "tsc -p ./tsconfig.json && node ./dist/scripts/suspend-indexer.js" }, "keywords": [], "author": "", diff --git a/runner/scripts/suspend-indexer.ts b/runner/scripts/suspend-indexer.ts new file mode 100644 index 000000000..060339a2e --- /dev/null +++ b/runner/scripts/suspend-indexer.ts @@ -0,0 +1,100 @@ +/* + * This script is used to suspend an indexer for a given account. It will: + * 1. Call Coordinator to disable the indexer + * 2. Write to the Indexers logs table to notify of suspension + * + * Note that as Coordinator is in a private network, you must tunnel to the machine to expose the gRPC server. + * This can be achieved via running the following in a separate terminal: + * ```sh + * gcloud compute ssh ubuntu@queryapi-coordinator-mainnet -- -L 9003:0.0.0.0:9003 + * ``` + * + * The following environment variables are required: + * - `HASURA_ADMIN_SECRET` + * - `HASURA_ENDPOINT` + * - `PGPORT` + * - `PGHOST` + * + * All of which can be found in the Runner compute instance metadata: + * ```sh + * gcloud compute instances describe queryapi-runner-mainnet + * ``` + * + * + * Usage: npm run script:suspend-indexer -- +*/ + +import assert from 'assert' +import * as fs from 'fs' + +import * as grpc from '@grpc/grpc-js' +import * as protoLoader from '@grpc/proto-loader' + +import Provisioner from '../src/provisioner' +import IndexerConfig from '../src/indexer-config' +import IndexerMeta, { LogEntry } from '../src/indexer-meta'; + +const COORDINATOR_PROTO_PATH = '../coordinator/proto/indexer_manager.proto'; + +assert(exists(COORDINATOR_PROTO_PATH), 'Coordinator proto file not found. Make sure you run this script from the root directory.'); +assert(process.argv.length === 4, 'Usage: npm run script:suspend-indexer -- '); +assert(process.env.COORDINATOR_PORT, 'COORDINATOR_PORT env var is required'); +assert(process.env.HASURA_ADMIN_SECRET, 'HASURA_ADMIN_SECRET env var is required'); +assert(process.env.HASURA_ENDPOINT, 'HASURA_ENDPOINT env var is required'); +assert(process.env.PGPORT, 'PGPORT env var is required'); +assert(process.env.PGHOST, 'PGHOST env var is required'); + +const [_binary, _file, accountId, functionName] = process.argv; +const { COORDINATOR_PORT = 9003 } = process.env; + +main(); + +async function main() { + await suspendIndexer(); + await logSuspension(); + + console.log('Done') +} + +async function logSuspension() { + console.log('Logging suspension notification'); + + const config = new IndexerConfig('not needed', accountId, functionName, 0, 'not needed', 'not needed', 2); + + const pgCredentials = await new Provisioner().getPostgresConnectionParameters(config.userName()); + + await new IndexerMeta(config, pgCredentials).writeLogs([ + LogEntry.systemInfo('The indexer is suspended due to inactivity.'), + ]); +} + +async function suspendIndexer() { + console.log(`Suspending indexer: ${accountId}/${functionName}`); + + const indexerManager = createIndexerManagerClient(); + + return new Promise((resolve, reject) => { + indexerManager.disable({ accountId, functionName }, (err: any, response: any) => { + if (err) { + reject(err); + } else { + resolve(response); + } + }); + }) +} + +function exists(path: string): boolean { + try { + fs.statSync(path); + return true; + } catch (err) { + return false; + } +} + +function createIndexerManagerClient() { + const packageDefinition = protoLoader.loadSync(COORDINATOR_PROTO_PATH); + const protoDescriptor: any = grpc.loadPackageDefinition(packageDefinition); + return new protoDescriptor.indexer.IndexerManager(`localhost:${COORDINATOR_PORT}`, grpc.credentials.createInsecure()); +} diff --git a/runner/src/indexer-meta/index.ts b/runner/src/indexer-meta/index.ts index a6bf324cc..05406387c 100644 --- a/runner/src/indexer-meta/index.ts +++ b/runner/src/indexer-meta/index.ts @@ -1,2 +1,3 @@ export { default } from './indexer-meta'; export { IndexerStatus, METADATA_TABLE_UPSERT, MetadataFields } from './indexer-meta'; +export { default as LogEntry } from './log-entry'; diff --git a/runner/tsconfig.build.json b/runner/tsconfig.build.json index d37e1cd77..dfbf07a6b 100644 --- a/runner/tsconfig.build.json +++ b/runner/tsconfig.build.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", "include": ["./src"], - "exclude": ["node_modules", "dist", "**/*.test.*"] + "exclude": ["node_modules", "dist", "**/*.test.*", "scripts"] } diff --git a/runner/tsconfig.json b/runner/tsconfig.json index 4e64096ab..d4949c0e4 100644 --- a/runner/tsconfig.json +++ b/runner/tsconfig.json @@ -3,7 +3,7 @@ "target": "es2018", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "lib": ["es2021"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ "module": "commonjs", /* Specify what module code is generated. */ - "rootDirs": ["./src", "./tests"], + "rootDirs": ["./src", "./tests", "./scripts"], "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ "resolveJsonModule": true, /* Enable importing .json files. */ "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ @@ -20,6 +20,6 @@ "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "include": ["./src", "./tests"], + "include": ["./src", "./tests", "./scripts"], "exclude": ["node_modules", "dist"] }