Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Usage

Please check [Releases](https://github.com/codefresh-io/index-alignment/releases) for the relevant version. Version suffix match the corresponding on-premise version, ie this tool `v1.0.2-2.8` is for Codefresh On-prem `v2.8`.

```shell
docker run quay.io/codefresh/index-alignment:<version> --help
```
Expand All @@ -19,7 +21,21 @@ Commands:
help [command] display help for command
```

### TLS

To use, TLS certificates must be mounted into the container. Please read the help for the corresponding command to learn more about the available TLS flags.

```shell
docker run \
--volume </host/path/to/cert.pem>:/tmp/cert.pem \
--volume </host/path/to/ca.pem>:/tmp/ca.pem \
quay.io/codefresh/index-alignment:<version> \
stats \
--tls \
--tlsCertificateKeyFile="/tmp/cert.pem" \
--tlsCAFile="/tmp/ca.pem" \
--uri=<mongo-uri>
```

### Commands

Expand All @@ -38,6 +54,15 @@ We recommend redirecting the output of `compare` command to JSON file.
Options:
-p, --product <product> Codefresh product: classic | gitops
-u, --uri <uri> MongoDB URI
--tls Use TLS for the connection. If you are using a self-signed certificate,
you may also need to specify "--tlsCAFile" and/or
"--tlsCertificateKeyFile" (default: false)
--tlsInsecure Allow insecure TLS connections (do not validate CA) (default: false)
--tlsCAFile <path> Specifies the location of a local .pem file that contains the root
certificate chain from the Certificate Authority. This file is used to
validate the certificate presented by the mongod/mongos instance
--tlsCertificateKeyFile <path> Specifies the location of a local .pem file that contains either the
client's TLS/SSL certificate and key
-m --db-map [dump-db-name=target-db-name...] Map the databases in the dump with the target databases. We have our own naming convention for the production databases, but it is up to the customers to name their databases (default: ["google_production=codefresh","chart-manager=charts-manager","kubernetes-monitor=k8s-monitor"])
-h, --help display help for command
```
Expand Down Expand Up @@ -73,8 +98,17 @@ We recommend redirecting the output of `stats` command to JSON file.

```
Options:
-u, --uri <uri> MongoDB URI
-h, --help display help for command
-u, --uri <uri> MongoDB URI
--tls Use TLS for the connection. If you are using a self-signed certificate,
you may also need to specify "--tlsCAFile" and/or
"--tlsCertificateKeyFile" (default: false)
--tlsInsecure Allow insecure TLS connections (do not validate CA) (default: false)
--tlsCAFile <path> Specifies the location of a local .pem file that contains the root
certificate chain from the Certificate Authority. This file is used to
validate the certificate presented by the mongod/mongos instance
--tlsCertificateKeyFile <path> Specifies the location of a local .pem file that contains either the
client's TLS/SSL certificate and key
-h, --help display help for command
```

Example:
Expand All @@ -97,6 +131,15 @@ Sync indexes from a recommended dump with a target MongoDB instance. The command
Options:
-p, --product <product> Codefresh product: classic | gitops
-u, --uri <uri> MongoDB URI
--tls Use TLS for the connection. If you are using a self-signed certificate,
you may also need to specify "--tlsCAFile" and/or
"--tlsCertificateKeyFile" (default: false)
--tlsInsecure Allow insecure TLS connections (do not validate CA) (default: false)
--tlsCAFile <path> Specifies the location of a local .pem file that contains the root
certificate chain from the Certificate Authority. This file is used to
validate the certificate presented by the mongod/mongos instance
--tlsCertificateKeyFile <path> Specifies the location of a local .pem file that contains either the
client's TLS/SSL certificate and key
-f --force Create indexes even on heavily populated collections, which may take a while
-m --db-map [dump-db-name=target-db-name...] Map the databases in the dump with the target databases. We have our own naming convention for the production databases, but it is up to the customers to name their databases (default: ["google_production=codefresh","chart-manager=charts-manager","kubernetes-monitor=k8s-monitor"])
-h, --help display help for command
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "index-alignment",
"version": "2.8-0.0.2",
"version": "0.1.0-2.8",
"main": "dist/index.js",
"private": true,
"type": "module",
Expand Down
6 changes: 3 additions & 3 deletions src/commands/dump-all-indexes.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { MongoClient } from 'mongodb';
import { mkdir, writeFile } from 'node:fs/promises';
import { isAbsolute, resolve } from 'node:path';
import { cwd } from 'node:process';
import { getAllIndexes } from '../get-indexes.js';
import { getMongoClient } from '../get-mongo-client.js';
import { logger } from '../logger.js';
import type { DumpOptions } from '../types.js';

export const dumpAllIndexes = async (options: DumpOptions): Promise<void> => {
const { uri, path } = options;
const { path } = options;
const dumpDirPath = isAbsolute(path) ? path : resolve(cwd(), path);
logger.stderr(`Dumping all indexes to "${dumpDirPath}". Hidden indexes will be ignored. Only databases and collections with authorized access will be dumped.`);

await mkdir(dumpDirPath, { recursive: true });
const client = new MongoClient(uri);
const client = getMongoClient(options);
await client.connect();
logger.stderr('Connected to MongoDB');

Expand Down
6 changes: 3 additions & 3 deletions src/commands/stats.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Collection, Db, Document, MongoClient } from 'mongodb';
import type { Collection, Db, Document, MongoClient } from 'mongodb';
import { getMongoClient } from '../get-mongo-client.js';
import { logger } from '../logger.js';
import type { CollectionStats, DatabaseStats, StatsOptions } from '../types.js';

Expand Down Expand Up @@ -74,10 +75,9 @@ export const getAllStats = async (client: MongoClient): Promise<unknown> => {
};

export const stats = async (options: StatsOptions): Promise<void> => {
const { uri } = options;
logger.stderr('Reading MongoDB stats. Only databases and collections with authorized access will be covered');

const client = new MongoClient(uri);
const client = getMongoClient(options);
await client.connect();
logger.stderr('Connected to MongoDB');

Expand Down
5 changes: 3 additions & 2 deletions src/commands/sync.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Collection, Db, MongoClient } from 'mongodb';
import type { Collection, Db } from 'mongodb';
import { compareDump } from '../compare-dump.js';
import { heavyCollections, indexLimitPerCollection } from '../config.js';
import { getCollectionIndexes } from '../get-indexes.js';
import { getMongoClient } from '../get-mongo-client.js';
import { logger } from '../logger.js';
import type { CollectionDiff, DatabaseDiff, Index, SyncOptions } from '../types.js';
import { getTargetToDumpDb } from '../utils.js';
Expand Down Expand Up @@ -134,7 +135,7 @@ const syncDatabase = async (db: Db, diff: DatabaseDiff, options: SyncOptions): P
export const sync = async (options: SyncOptions): Promise<void> => {
logger.stderr(`Syncing indexes for "${options.product}"`);
const diff = await compareDump(options);
const client = new MongoClient(options.uri);
const client = getMongoClient(options);
await client.connect();
for (const [databaseName, databaseDiff] of Object.entries(diff.databases)) {
const db = client.db(databaseName);
Expand Down
10 changes: 5 additions & 5 deletions src/compare-dump.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { MongoClient } from 'mongodb';
import { getAllIndexes } from './get-indexes.js';
import { getMongoClient } from './get-mongo-client.js';
import { isIndexEqual } from './is-index-equal.js';
import { shouldIgnoreIndexInDump } from './overrides/should-ignore-index-in-dump.js';

import { shouldIgnoreIndexInTarget } from './overrides/should-ignore-index-in-target.js';
import { readDump } from './read-dump.js';
import type { CollectionDiff, CollectionIndexes, CompareOptions, DatabaseDiff, DatabaseIndexes, DbMapRaw, FullDiff } from './types.js';
import { shouldIgnoreIndexInTarget } from './overrides/should-ignore-index-in-target.js';
import { getTargetToDumpDb } from './utils.js';

const compareCollections = (desired: CollectionIndexes, actual?: CollectionIndexes, dbMap?: DbMapRaw): CollectionDiff => {
Expand Down Expand Up @@ -47,9 +46,10 @@ const compareDatabases = (desired: DatabaseIndexes, actual?: DatabaseIndexes, db
return dbDiff;
};

export const compareDump = async ({ product, uri, dbMap }: CompareOptions): Promise<FullDiff> => {
export const compareDump = async (options: CompareOptions): Promise<FullDiff> => {
const { product, dbMap } = options;
const desired = await readDump(product, dbMap);
const client = new MongoClient(uri);
const client = getMongoClient(options);
await client.connect();
const actual = await getAllIndexes(client);

Expand Down
13 changes: 13 additions & 0 deletions src/get-mongo-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { MongoClient, MongoClientOptions } from 'mongodb';
import { logger } from './logger.js';

export const getMongoClient = (options: Partial<MongoClientOptions> & { uri: string }) => {
const clientOptions = {
tls: options.tls,
tlsInsecure: options.tlsInsecure,
tlsCAFile: options.tlsCAFile,
tlsCertificateKeyFile: options.tlsCertificateKeyFile,
} satisfies MongoClientOptions;
logger.stderr(`The following options will be used for the MongoDB connection: ${JSON.stringify(clientOptions, null, 2)}`);
return new MongoClient(options.uri, clientOptions);
};
74 changes: 73 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,73 @@ program
.description('[Internal] Dump all indexes from a MongoDB instance')
.requiredOption('-p, --path <path>', 'Path to index dump')
.requiredOption('-u, --uri <uri>', 'MongoDB URI')
.option(
'--tls',
`Use TLS for the connection. If you are using a self-signed certificate, you may also need to specify "--tlsCAFile" and/or "--tlsCertificateKeyFile"`,
false,
)
.option(
'--tlsInsecure',
'Allow insecure TLS connections (do not validate CA)',
false,
)
.option(
'--tlsCAFile <path>',
'Specifies the location of a local .pem file that contains the root certificate chain from the Certificate Authority. This file is used to validate the certificate presented by the mongod/mongos instance',
)
.option(
'--tlsCertificateKeyFile <path>',
`Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key`,
)
.action(dumpAllIndexes);

program
.command('stats')
.description('Get stats for all collections from a MongoDB instance')
.requiredOption('-u, --uri <uri>', 'MongoDB URI')
.option(
'--tls',
`Use TLS for the connection. If you are using a self-signed certificate, you may also need to specify "--tlsCAFile" and/or "--tlsCertificateKeyFile"`,
false,
)
.option(
'--tlsInsecure',
'Allow insecure TLS connections (do not validate CA)',
false,
)
.option(
'--tlsCAFile <path>',
'Specifies the location of a local .pem file that contains the root certificate chain from the Certificate Authority. This file is used to validate the certificate presented by the mongod/mongos instance',
)
.option(
'--tlsCertificateKeyFile <path>',
`Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key`,
)
.action(stats);

program
.command('compare')
.description('Compare indexes from a target MongoDB instance with a recommended dump')
.requiredOption('-p, --product <product>', 'Codefresh product: classic | gitops')
.requiredOption('-u, --uri <uri>', 'MongoDB URI')
.option(
'--tls',
`Use TLS for the connection. If you are using a self-signed certificate, you may also need to specify "--tlsCAFile" and/or "--tlsCertificateKeyFile"`,
false,
)
.option(
'--tlsInsecure',
'Allow insecure TLS connections (do not validate CA)',
false,
)
.option(
'--tlsCAFile <path>',
'Specifies the location of a local .pem file that contains the root certificate chain from the Certificate Authority. This file is used to validate the certificate presented by the mongod/mongos instance',
)
.option(
'--tlsCertificateKeyFile <path>',
`Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key`,
)
.option(
'-m --db-map [dump-db-name=target-db-name...]',
'Map the databases in the dump with the target databases. We have our own naming convention for the production databases, but it is up to the customers to name their databases',
Expand All @@ -32,9 +86,27 @@ program

program
.command('sync')
.description('Sync indexes from a recommended dump with a target MongoDB instance. The command will fail if it is required to create indexes on heavily populated collections and the `--force` flag has not been specified')
.description('[ ⚠️ Warning! Do not run this command against production. ] Sync indexes from a recommended dump with a target MongoDB instance. The command will fail if it is required to create indexes on heavily populated collections and the `--force` flag has not been specified')
.requiredOption('-p, --product <product>', 'Codefresh product: classic | gitops')
.requiredOption('-u, --uri <uri>', 'MongoDB URI')
.option(
'--tls',
`Use TLS for the connection. If you are using a self-signed certificate, you may also need to specify "--tlsCAFile" and/or "--tlsCertificateKeyFile"`,
false,
)
.option(
'--tlsInsecure',
'Allow insecure TLS connections (do not validate CA)',
false,
)
.option(
'--tlsCAFile <path>',
'Specifies the location of a local .pem file that contains the root certificate chain from the Certificate Authority. This file is used to validate the certificate presented by the mongod/mongos instance',
)
.option(
'--tlsCertificateKeyFile <path>',
`Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key`,
)
.option('-f --force', 'Create indexes even on heavily populated collections, which may take a while')
.option(
'-m --db-map [dump-db-name=target-db-name...]',
Expand Down
10 changes: 5 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Document, IndexDescription } from 'mongodb';
import type { Document, IndexDescription, MongoClientOptions } from 'mongodb';

export type Product = 'classic' | 'gitops';

Expand Down Expand Up @@ -61,24 +61,24 @@ export interface DatabaseStats {
}

// Options
export interface SyncOptions {
export interface SyncOptions extends Partial<MongoClientOptions> {
uri: string;
product: Product;
dbMap?: DbMapRaw;
force?: boolean;
}

export interface CompareOptions {
export interface CompareOptions extends Partial<MongoClientOptions> {
uri: string;
product: Product;
dbMap?: DbMapRaw;
}

export interface StatsOptions {
export interface StatsOptions extends Partial<MongoClientOptions> {
uri: string;
}

export interface DumpOptions {
export interface DumpOptions extends Partial<MongoClientOptions> {
uri: string;
path: string;
}
Expand Down