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
5 changes: 5 additions & 0 deletions packages/i18n/src/locales/en_US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,11 @@ const translations: Catalog = {
description: 'Calls isMaster',
example: 'rs.isMaster()',
},
hello: {
link: 'https://docs.mongodb.com/manual/reference/method/rs.hello',
description: 'Calls hello',
example: 'rs.hello()',
},
printSecondaryReplicationInfo: {
link: 'https://docs.mongodb.com/manual/reference/method/rs.printSecondaryReplicationInfo',
description: 'Calls db.printSecondaryReplicationInfo',
Expand Down
40 changes: 20 additions & 20 deletions packages/shell-api/src/database.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1988,16 +1988,16 @@ describe('Database', () => {

describe('enableFreeMonitoring', () => {
it('throws if serviceProvider isMaster is false', async() => {
serviceProvider.runCommand.resolves({ ismaster: false });
serviceProvider.runCommandWithCheck.resolves({ isWritablePrimary: false });
const catchedError = await database.enableFreeMonitoring()
.catch(e => e);
expect(catchedError).to.be.instanceOf(MongoshInvalidInputError);
expect(catchedError.code).to.equal(CommonErrors.InvalidOperation);
});

it('calls serviceProvider.runCommand on the database', async() => {
serviceProvider.runCommand.onCall(0).resolves({ ismaster: true });
serviceProvider.runCommand.onCall(1).resolves({ ok: 1 });
serviceProvider.runCommandWithCheck.resolves({ isWritablePrimary: true });
serviceProvider.runCommand.onCall(0).resolves({ ok: 1 });
await database.enableFreeMonitoring();

expect(serviceProvider.runCommand).to.have.been.calledWith(
Expand All @@ -2012,44 +2012,44 @@ describe('Database', () => {
it('returns whatever serviceProvider.runCommand returns if enabled', async() => {
const expectedFMState = { ok: 1, state: 'enabled' };

serviceProvider.runCommand.onCall(0).resolves({ ismaster: true });
serviceProvider.runCommand.onCall(1).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(2).resolves(expectedFMState);
serviceProvider.runCommandWithCheck.resolves({ isWritablePrimary: true });
serviceProvider.runCommand.onCall(0).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(1).resolves(expectedFMState);
const result = await database.enableFreeMonitoring();
expect(result).to.deep.equal(expectedFMState);
});
it('returns warning if not enabled', async() => {
serviceProvider.runCommand.onCall(0).resolves({ ismaster: true });
serviceProvider.runCommand.onCall(1).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(2).resolves({ ok: 1, enabled: false });
serviceProvider.runCommand.onCall(3).resolves({ cloudFreeMonitoringEndpointURL: 'URL' });
serviceProvider.runCommandWithCheck.resolves({ isWritablePrimary: true });
serviceProvider.runCommand.onCall(0).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(1).resolves({ ok: 1, enabled: false });
serviceProvider.runCommand.onCall(2).resolves({ cloudFreeMonitoringEndpointURL: 'URL' });
const result = await database.enableFreeMonitoring();
expect(result).to.include('URL');
});

it('returns warning if returns ok: 0 with auth error', async() => {
serviceProvider.runCommand.onCall(0).resolves({ ismaster: true });
serviceProvider.runCommand.onCall(1).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(2).resolves({ ok: 0, codeName: 'Unauthorized' });
serviceProvider.runCommandWithCheck.resolves({ isWritablePrimary: true });
serviceProvider.runCommand.onCall(0).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(1).resolves({ ok: 0, codeName: 'Unauthorized' });
const result = await database.enableFreeMonitoring();
expect(result).to.be.a('string');
expect(result).to.include('privilege');
});
it('returns warning if throws with auth error', async() => {
const expectedError = new Error();
(expectedError as any).codeName = 'Unauthorized';
serviceProvider.runCommand.onCall(0).resolves({ ismaster: true });
serviceProvider.runCommand.onCall(1).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(2).rejects(expectedError);
serviceProvider.runCommandWithCheck.resolves({ isWritablePrimary: true });
serviceProvider.runCommand.onCall(0).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(1).rejects(expectedError);
const result = await database.enableFreeMonitoring();
expect(result).to.be.a('string');
expect(result).to.include('privilege');
});

it('throws if throws with non-auth error', async() => {
serviceProvider.runCommand.onCall(0).resolves({ ismaster: true });
serviceProvider.runCommand.onCall(1).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(2).rejects(new Error());
serviceProvider.runCommandWithCheck.resolves({ isWritablePrimary: true });
serviceProvider.runCommand.onCall(0).resolves({ ok: 1 });
serviceProvider.runCommand.onCall(1).rejects(new Error());

const error = await database.enableFreeMonitoring().catch(e => e);
expect(error).to.be.instanceOf(MongoshRuntimeError);
Expand All @@ -2058,7 +2058,7 @@ describe('Database', () => {

it('throws if serviceProvider.runCommand rejects without auth error', async() => {
const expectedError = new Error();
serviceProvider.runCommand.rejects(expectedError);
serviceProvider.runCommandWithCheck.rejects(expectedError);
const catchedError = await database.enableFreeMonitoring()
.catch(e => e);
expect(catchedError).to.equal(expectedError);
Expand Down
10 changes: 5 additions & 5 deletions packages/shell-api/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,8 @@ export default class Database extends ShellApiWithMongoClass {
@returnsPromise
async enableFreeMonitoring(): Promise<Document | string> {
this._emitDatabaseApiCall('enableFreeMonitoring', {});
const isMaster = await this._mongo._serviceProvider.runCommand(this._name, { isMaster: 1 }, this._baseOptions);
if (!isMaster.ismaster) {
const helloResult = await this.hello();
if (!helloResult.isWritablePrimary) {
throw new MongoshInvalidInputError(
'db.enableFreeMonitoring() may only be run on a primary',
CommonErrors.InvalidOperation
Expand Down Expand Up @@ -1312,10 +1312,10 @@ export default class Database extends ShellApiWithMongoClass {
try {
replInfo = await this.getReplicationInfo();
} catch (error) {
const isMaster = await this._runCommand({ isMaster: 1 });
if (isMaster.arbiterOnly) {
const helloResult = await this.hello();
if (helloResult.arbiterOnly) {
return new CommandResult('StatsResult', { message: 'cannot provide replication status from an arbiter' });
} else if (!isMaster.ismaster) {
} else if (!helloResult.isWritablePrimary) {
const secondaryInfo = await this.printSecondaryReplicationInfo();
return new CommandResult('StatsResult', {
message: 'this is a secondary, printing secondary replication info.',
Expand Down
2 changes: 1 addition & 1 deletion packages/shell-api/src/helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ describe('getPrintableShardStatus', () => {

const origRunCommandWithCheck = serviceProvider.runCommandWithCheck;
serviceProvider.runCommandWithCheck = async(db, cmd) => {
if (db === 'admin' && cmd.isMaster) {
if (cmd.hello) {
return { ok: 1, msg: 'isdbgrid' };
}
if (db === 'admin' && cmd.balancerStatus) {
Expand Down
4 changes: 2 additions & 2 deletions packages/shell-api/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,8 @@ export async function getPrintableShardStatus(db: Database, verbose: boolean): P
}

export async function getConfigDB(db: Database): Promise<Database> {
const isM = await db._runAdminCommand({ isMaster: 1 });
if (isM.msg !== 'isdbgrid') {
const helloResult = await db.hello();
if (helloResult.msg !== 'isdbgrid') {
throw new MongoshInvalidInputError('Not connected to a mongos', ShellApiErrors.NotConnectedToMongos);
}
return db.getSiblingDB('config');
Expand Down
27 changes: 27 additions & 0 deletions packages/shell-api/src/replica-set.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,33 @@ describe('ReplicaSet', () => {
expect(catchedError).to.equal(expectedError);
});
});
describe('hello', () => {
it('calls serviceProvider.runCommandWithCheck', async() => {
await rs.hello();

expect(serviceProvider.runCommandWithCheck).to.have.been.calledWith(
ADMIN_DB,
{
hello: 1
}
);
});

it('returns whatever serviceProvider.runCommandWithCheck returns', async() => {
const expectedResult = { ok: 1 };
serviceProvider.runCommandWithCheck.resolves(expectedResult);
const result = await rs.hello();
expect(result).to.deep.equal(expectedResult);
});

it('throws if serviceProvider.runCommandWithCheck rejects', async() => {
const expectedError = new Error();
serviceProvider.runCommandWithCheck.rejects(expectedError);
const catchedError = await rs.hello()
.catch(e => e);
expect(catchedError).to.equal(expectedError);
});
});
describe('add', () => {
it('calls serviceProvider.runCommandWithCheck with no arb and string hostport', async() => {
const configDoc = { version: 1, members: [{ _id: 0 }, { _id: 1 }] };
Expand Down
12 changes: 7 additions & 5 deletions packages/shell-api/src/replica-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,13 @@ export default class ReplicaSet extends ShellApiWithMongoClass {
@returnsPromise
async isMaster(): Promise<Document> {
this._emitReplicaSetApiCall('isMaster', {});
return this._database._runAdminCommand(
{
isMaster: 1,
}
);
return this._database.getSiblingDB('admin').isMaster();
}

@returnsPromise
async hello(): Promise<Document> {
this._emitReplicaSetApiCall('hello', {});
return this._database.getSiblingDB('admin').hello();
}

@returnsPromise
Expand Down
25 changes: 25 additions & 0 deletions packages/shell-api/src/shard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,31 @@ describe('Shard', () => {
Object.keys(result.value).includes('active mongoses') ||
Object.keys(result.value).includes('most recently active mongoses')).to.be.true;
});
context('with 5.0+ server', () => {
skipIfServerVersion(mongos, '<= 4.4');
let apiStrictServiceProvider;

before(async() => {
apiStrictServiceProvider = await CliServiceProvider.connect(await mongos.connectionString(), {
serverApi: { version: '1', strict: true }
});
});

after(async() => {
await apiStrictServiceProvider.close(true);
});

it('returns the status when used with apiStrict', async() => {
const internalState = new ShellInternalState(apiStrictServiceProvider);
const sh = new Shard(internalState.currentDb);

const result = await sh.status();
expect(result.type).to.equal('StatsResult');
expect(Object.keys(result.value)).to.include.members([
'shardingVersion', 'shards', 'autosplit', 'balancer', 'databases'
]);
});
});
});
describe('turn on sharding', () => {
it('enableSharding for a db', async() => {
Expand Down