Skip to content

Commit f8bc023

Browse files
feat: support TLS (#7)
* docs: document `indexStats` (#3) * feat: support TLS (#6) * docs: improve docs * feat: support TLS connections * docs: document TLS options * ci: trigger ci * ci: trigger ci
1 parent bcc8fae commit f8bc023

File tree

9 files changed

+151
-22
lines changed

9 files changed

+151
-22
lines changed

README.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Usage
44

5+
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`.
6+
57
```shell
68
docker run quay.io/codefresh/index-alignment:<version> --help
79
```
@@ -19,7 +21,21 @@ Commands:
1921
help [command] display help for command
2022
```
2123

24+
### TLS
25+
26+
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.
2227

28+
```shell
29+
docker run \
30+
--volume </host/path/to/cert.pem>:/tmp/cert.pem \
31+
--volume </host/path/to/ca.pem>:/tmp/ca.pem \
32+
quay.io/codefresh/index-alignment:<version> \
33+
stats \
34+
--tls \
35+
--tlsCertificateKeyFile="/tmp/cert.pem" \
36+
--tlsCAFile="/tmp/ca.pem" \
37+
--uri=<mongo-uri>
38+
```
2339

2440
### Commands
2541

@@ -38,6 +54,15 @@ We recommend redirecting the output of `compare` command to JSON file.
3854
Options:
3955
-p, --product <product> Codefresh product: classic | gitops
4056
-u, --uri <uri> MongoDB URI
57+
--tls Use TLS for the connection. If you are using a self-signed certificate,
58+
you may also need to specify "--tlsCAFile" and/or
59+
"--tlsCertificateKeyFile" (default: false)
60+
--tlsInsecure Allow insecure TLS connections (do not validate CA) (default: false)
61+
--tlsCAFile <path> Specifies the location of a local .pem file that contains the root
62+
certificate chain from the Certificate Authority. This file is used to
63+
validate the certificate presented by the mongod/mongos instance
64+
--tlsCertificateKeyFile <path> Specifies the location of a local .pem file that contains either the
65+
client's TLS/SSL certificate and key
4166
-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"])
4267
-h, --help display help for command
4368
```
@@ -73,8 +98,17 @@ We recommend redirecting the output of `stats` command to JSON file.
7398

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

80114
Example:
@@ -97,6 +131,15 @@ Sync indexes from a recommended dump with a target MongoDB instance. The command
97131
Options:
98132
-p, --product <product> Codefresh product: classic | gitops
99133
-u, --uri <uri> MongoDB URI
134+
--tls Use TLS for the connection. If you are using a self-signed certificate,
135+
you may also need to specify "--tlsCAFile" and/or
136+
"--tlsCertificateKeyFile" (default: false)
137+
--tlsInsecure Allow insecure TLS connections (do not validate CA) (default: false)
138+
--tlsCAFile <path> Specifies the location of a local .pem file that contains the root
139+
certificate chain from the Certificate Authority. This file is used to
140+
validate the certificate presented by the mongod/mongos instance
141+
--tlsCertificateKeyFile <path> Specifies the location of a local .pem file that contains either the
142+
client's TLS/SSL certificate and key
100143
-f --force Create indexes even on heavily populated collections, which may take a while
101144
-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"])
102145
-h, --help display help for command

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "index-alignment",
3-
"version": "2.8-0.0.2",
3+
"version": "0.1.0-2.8",
44
"main": "dist/index.js",
55
"private": true,
66
"type": "module",

src/commands/dump-all-indexes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import { MongoClient } from 'mongodb';
21
import { mkdir, writeFile } from 'node:fs/promises';
32
import { isAbsolute, resolve } from 'node:path';
43
import { cwd } from 'node:process';
54
import { getAllIndexes } from '../get-indexes.js';
5+
import { getMongoClient } from '../get-mongo-client.js';
66
import { logger } from '../logger.js';
77
import type { DumpOptions } from '../types.js';
88

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

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

src/commands/stats.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Collection, Db, Document, MongoClient } from 'mongodb';
1+
import type { Collection, Db, Document, MongoClient } from 'mongodb';
2+
import { getMongoClient } from '../get-mongo-client.js';
23
import { logger } from '../logger.js';
34
import type { CollectionStats, DatabaseStats, StatsOptions } from '../types.js';
45

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

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

80-
const client = new MongoClient(uri);
80+
const client = getMongoClient(options);
8181
await client.connect();
8282
logger.stderr('Connected to MongoDB');
8383

src/commands/sync.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { Collection, Db, MongoClient } from 'mongodb';
1+
import type { Collection, Db } from 'mongodb';
22
import { compareDump } from '../compare-dump.js';
33
import { heavyCollections, indexLimitPerCollection } from '../config.js';
44
import { getCollectionIndexes } from '../get-indexes.js';
5+
import { getMongoClient } from '../get-mongo-client.js';
56
import { logger } from '../logger.js';
67
import type { CollectionDiff, DatabaseDiff, Index, SyncOptions } from '../types.js';
78
import { getTargetToDumpDb } from '../utils.js';
@@ -134,7 +135,7 @@ const syncDatabase = async (db: Db, diff: DatabaseDiff, options: SyncOptions): P
134135
export const sync = async (options: SyncOptions): Promise<void> => {
135136
logger.stderr(`Syncing indexes for "${options.product}"`);
136137
const diff = await compareDump(options);
137-
const client = new MongoClient(options.uri);
138+
const client = getMongoClient(options);
138139
await client.connect();
139140
for (const [databaseName, databaseDiff] of Object.entries(diff.databases)) {
140141
const db = client.db(databaseName);

src/compare-dump.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { MongoClient } from 'mongodb';
21
import { getAllIndexes } from './get-indexes.js';
2+
import { getMongoClient } from './get-mongo-client.js';
33
import { isIndexEqual } from './is-index-equal.js';
44
import { shouldIgnoreIndexInDump } from './overrides/should-ignore-index-in-dump.js';
5-
5+
import { shouldIgnoreIndexInTarget } from './overrides/should-ignore-index-in-target.js';
66
import { readDump } from './read-dump.js';
77
import type { CollectionDiff, CollectionIndexes, CompareOptions, DatabaseDiff, DatabaseIndexes, DbMapRaw, FullDiff } from './types.js';
8-
import { shouldIgnoreIndexInTarget } from './overrides/should-ignore-index-in-target.js';
98
import { getTargetToDumpDb } from './utils.js';
109

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

50-
export const compareDump = async ({ product, uri, dbMap }: CompareOptions): Promise<FullDiff> => {
49+
export const compareDump = async (options: CompareOptions): Promise<FullDiff> => {
50+
const { product, dbMap } = options;
5151
const desired = await readDump(product, dbMap);
52-
const client = new MongoClient(uri);
52+
const client = getMongoClient(options);
5353
await client.connect();
5454
const actual = await getAllIndexes(client);
5555

src/get-mongo-client.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { MongoClient, MongoClientOptions } from 'mongodb';
2+
import { logger } from './logger.js';
3+
4+
export const getMongoClient = (options: Partial<MongoClientOptions> & { uri: string }) => {
5+
const clientOptions = {
6+
tls: options.tls,
7+
tlsInsecure: options.tlsInsecure,
8+
tlsCAFile: options.tlsCAFile,
9+
tlsCertificateKeyFile: options.tlsCertificateKeyFile,
10+
} satisfies MongoClientOptions;
11+
logger.stderr(`The following options will be used for the MongoDB connection: ${JSON.stringify(clientOptions, null, 2)}`);
12+
return new MongoClient(options.uri, clientOptions);
13+
};

src/index.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,73 @@ program
1010
.description('[Internal] Dump all indexes from a MongoDB instance')
1111
.requiredOption('-p, --path <path>', 'Path to index dump')
1212
.requiredOption('-u, --uri <uri>', 'MongoDB URI')
13+
.option(
14+
'--tls',
15+
`Use TLS for the connection. If you are using a self-signed certificate, you may also need to specify "--tlsCAFile" and/or "--tlsCertificateKeyFile"`,
16+
false,
17+
)
18+
.option(
19+
'--tlsInsecure',
20+
'Allow insecure TLS connections (do not validate CA)',
21+
false,
22+
)
23+
.option(
24+
'--tlsCAFile <path>',
25+
'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',
26+
)
27+
.option(
28+
'--tlsCertificateKeyFile <path>',
29+
`Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key`,
30+
)
1331
.action(dumpAllIndexes);
1432

1533
program
1634
.command('stats')
1735
.description('Get stats for all collections from a MongoDB instance')
1836
.requiredOption('-u, --uri <uri>', 'MongoDB URI')
37+
.option(
38+
'--tls',
39+
`Use TLS for the connection. If you are using a self-signed certificate, you may also need to specify "--tlsCAFile" and/or "--tlsCertificateKeyFile"`,
40+
false,
41+
)
42+
.option(
43+
'--tlsInsecure',
44+
'Allow insecure TLS connections (do not validate CA)',
45+
false,
46+
)
47+
.option(
48+
'--tlsCAFile <path>',
49+
'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',
50+
)
51+
.option(
52+
'--tlsCertificateKeyFile <path>',
53+
`Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key`,
54+
)
1955
.action(stats);
2056

2157
program
2258
.command('compare')
2359
.description('Compare indexes from a target MongoDB instance with a recommended dump')
2460
.requiredOption('-p, --product <product>', 'Codefresh product: classic | gitops')
2561
.requiredOption('-u, --uri <uri>', 'MongoDB URI')
62+
.option(
63+
'--tls',
64+
`Use TLS for the connection. If you are using a self-signed certificate, you may also need to specify "--tlsCAFile" and/or "--tlsCertificateKeyFile"`,
65+
false,
66+
)
67+
.option(
68+
'--tlsInsecure',
69+
'Allow insecure TLS connections (do not validate CA)',
70+
false,
71+
)
72+
.option(
73+
'--tlsCAFile <path>',
74+
'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',
75+
)
76+
.option(
77+
'--tlsCertificateKeyFile <path>',
78+
`Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key`,
79+
)
2680
.option(
2781
'-m --db-map [dump-db-name=target-db-name...]',
2882
'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',
@@ -32,9 +86,27 @@ program
3286

3387
program
3488
.command('sync')
35-
.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')
89+
.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')
3690
.requiredOption('-p, --product <product>', 'Codefresh product: classic | gitops')
3791
.requiredOption('-u, --uri <uri>', 'MongoDB URI')
92+
.option(
93+
'--tls',
94+
`Use TLS for the connection. If you are using a self-signed certificate, you may also need to specify "--tlsCAFile" and/or "--tlsCertificateKeyFile"`,
95+
false,
96+
)
97+
.option(
98+
'--tlsInsecure',
99+
'Allow insecure TLS connections (do not validate CA)',
100+
false,
101+
)
102+
.option(
103+
'--tlsCAFile <path>',
104+
'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',
105+
)
106+
.option(
107+
'--tlsCertificateKeyFile <path>',
108+
`Specifies the location of a local .pem file that contains either the client's TLS/SSL certificate and key`,
109+
)
38110
.option('-f --force', 'Create indexes even on heavily populated collections, which may take a while')
39111
.option(
40112
'-m --db-map [dump-db-name=target-db-name...]',

src/types.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Document, IndexDescription } from 'mongodb';
1+
import type { Document, IndexDescription, MongoClientOptions } from 'mongodb';
22

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

@@ -61,24 +61,24 @@ export interface DatabaseStats {
6161
}
6262

6363
// Options
64-
export interface SyncOptions {
64+
export interface SyncOptions extends Partial<MongoClientOptions> {
6565
uri: string;
6666
product: Product;
6767
dbMap?: DbMapRaw;
6868
force?: boolean;
6969
}
7070

71-
export interface CompareOptions {
71+
export interface CompareOptions extends Partial<MongoClientOptions> {
7272
uri: string;
7373
product: Product;
7474
dbMap?: DbMapRaw;
7575
}
7676

77-
export interface StatsOptions {
77+
export interface StatsOptions extends Partial<MongoClientOptions> {
7878
uri: string;
7979
}
8080

81-
export interface DumpOptions {
81+
export interface DumpOptions extends Partial<MongoClientOptions> {
8282
uri: string;
8383
path: string;
8484
}

0 commit comments

Comments
 (0)