diff --git a/packages/shell-api/src/helpers.ts b/packages/shell-api/src/helpers.ts index d4834c8777..071d871af0 100644 --- a/packages/shell-api/src/helpers.ts +++ b/packages/shell-api/src/helpers.ts @@ -406,16 +406,14 @@ export async function getPrintableShardStatus(mongo: Mongo, verbose: boolean): P (await chunksColl.find({ 'ns': coll._id }) .sort({ min: 1 }).toArray()) .forEach((chunk) => { - const c = [ - JSON.stringify(chunk.min), - '-->>', - JSON.stringify(chunk.max), - 'on', - JSON.stringify(chunk.shard), - JSON.stringify(chunk.lastmod) - ]; - if (chunk.jumbo) c.push('jumbo'); - chunksRes.push(c.join(' ')); + const c = { + min: chunk.min, + max: chunk.max, + 'on shard': chunk.shard, + 'last modified': chunk.lastmod + } as any; + if (chunk.jumbo) c.jumbo = 'yes'; + chunksRes.push(c); }); } else { chunksRes.push('too many chunks to print, use verbose if you want to force print'); @@ -427,13 +425,11 @@ export async function getPrintableShardStatus(mongo: Mongo, verbose: boolean): P .sort({ min: 1 }) .toArray()) .forEach((tag) => { - const t = [ - tag.tag, - tag.min, - ' -->> ', - tag.max - ]; - tagsRes.push(t.join(' ')); + tagsRes.push({ + tag: tag.tag, + min: tag.min, + max: tag.max + }); }); collRes.chunks = chunksRes; collRes.tags = tagsRes; diff --git a/packages/shell-api/src/shard.spec.ts b/packages/shell-api/src/shard.spec.ts index 145fbef3b3..73e044aef1 100644 --- a/packages/shell-api/src/shard.spec.ts +++ b/packages/shell-api/src/shard.spec.ts @@ -8,6 +8,7 @@ import { bson, ServiceProvider, Cursor as ServiceProviderCursor } from '@mongosh import { EventEmitter } from 'events'; import ShellInternalState from './shell-internal-state'; import { UpdateResult } from './result'; +import { CliServiceProvider } from '../../service-provider-server'; describe('Shard', () => { describe('help', () => { @@ -51,7 +52,7 @@ describe('Shard', () => { }); }); }); - describe('commands', () => { + describe('unit', () => { let mongo: Mongo; let serviceProvider: StubbedInstance; let shard: Shard; @@ -1082,4 +1083,131 @@ describe('Shard', () => { }); }); }); + + xdescribe('integration', () => { + let serviceProvider: CliServiceProvider; + let internalState; + let mongo; + let sh; + const dbName = 'test'; + const ns = `${dbName}.coll`; + const mongosPort = 27017; + const host = 'localhost'; + const shardId = 'rs-shard0'; + const shardPorts = ['47017', '47020']; + const connectionString = `mongodb://${host}:${mongosPort}`; // startTestServer(); + + before(async() => { + serviceProvider = await CliServiceProvider.connect(connectionString); + internalState = new ShellInternalState(serviceProvider); + mongo = internalState.currentDb.getMongo(); + sh = new Shard(mongo); + + // check replset uninitialized + let members = await sh._mongo.getDB('config').getCollection('shards').find().sort({ _id: 1 }).toArray(); + if (members.length === 0) { + // add new shards + expect((await sh.addShard(`${shardId}-0/${host}:${shardPorts[0]}`)).shardAdded).to.equal(`${shardId}-0`); + expect((await sh.addShard(`${shardId}-1/${host}:${shardPorts[1]}`)).shardAdded).to.equal(`${shardId}-1`); + members = await sh._mongo.getDB('config').getCollection('shards').find().sort({ _id: 1 }).toArray(); + } else { + console.log('WARN: shards already added'); + } + expect(members.length).to.equal(2); + await sh._mongo.getDB(dbName).dropDatabase(); + }); + + after(() => { + return serviceProvider.close(true); + }); + + describe('sharding info', () => { + it('returns the status', async() => { + const result = await sh.status(); + expect(result.type).to.equal('StatsResult'); + expect(Object.keys(result.value)).to.include.members([ + 'shardingVersion', 'shards', 'active mongoses', 'autosplit', 'balancer', 'databases' + ]); + }); + }); + describe('turn on sharding', () => { + it('enableSharding for a db', async() => { + expect((await sh.status()).value.databases.length).to.equal(1); + expect((await sh.enableSharding(dbName)).ok).to.equal(1); + expect((await sh.status()).value.databases.length).to.equal(2); + }); + it('enableSharding for a collection', async() => { + expect(Object.keys((await sh.status()).value.databases[0].collections).length).to.equal(0); + expect((await sh.shardCollection(ns, { key: 1 })).collectionsharded).to.equal(ns); + expect((await sh.status()).value.databases[0].collections[ns].shardKey).to.deep.equal({ key: 1 }); + }); + }); + describe('autosplit', () => { + it('disables correctly', async() => { + expect((await sh.disableAutoSplit()).acknowledged).to.equal(1); + expect((await sh.status()).value.autosplit['Currently enabled']).to.equal('no'); + }); + it('enables correctly', async() => { + expect((await sh.enableAutoSplit()).acknowledged).to.equal(1); + expect((await sh.status()).value.autosplit['Currently enabled']).to.equal('yes'); + }); + }); + describe('tags', () => { + it('creates a zone', async() => { + expect((await sh.addShardTag(`${shardId}-1`, 'zone1')).ok).to.equal(1); + expect((await sh.status()).value.shards[1].tags).to.deep.equal(['zone1']); + expect((await sh.addShardToZone(`${shardId}-0`, 'zone0')).ok).to.equal(1); + expect((await sh.status()).value.shards[0].tags).to.deep.equal(['zone0']); + }); + it('sets a zone key range', async() => { + expect((await sh.updateZoneKeyRange(ns, { key: 0 }, { key: 20 }, 'zone1')).ok).to.equal(1); + expect((await sh.status()).value.databases[0].collections[ns].tags[0]).to.deep.equal({ + tag: 'zone1', min: { key: 0 }, max: { key: 20 } + }); + expect((await sh.addTagRange(ns, { key: 21 }, { key: 40 }, 'zone0')).ok).to.equal(1); + expect((await sh.status()).value.databases[0].collections[ns].tags[1]).to.deep.equal({ + tag: 'zone0', min: { key: 21 }, max: { key: 40 } + }); + }); + it('removes a key range', async() => { + expect((await sh.status()).value.databases[0].collections[ns].tags.length).to.equal(2); + expect((await sh.removeRangeFromZone(ns, { key: 0 }, { key: 20 })).ok).to.equal(1); + expect((await sh.status()).value.databases[0].collections[ns].tags.length).to.equal(1); + expect((await sh.removeTagRange(ns, { key: 21 }, { key: 40 })).ok).to.equal(1); + expect((await sh.status()).value.databases[0].collections[ns].tags.length).to.equal(0); + }); + it('removes zones', async() => { + expect((await sh.removeShardFromZone(`${shardId}-1`, 'zone1')).ok).to.equal(1); + expect((await sh.status()).value.shards[1].tags).to.deep.equal([]); + expect((await sh.removeShardTag(`${shardId}-0`, 'zone0')).ok).to.equal(1); + expect((await sh.status()).value.shards[0].tags).to.deep.equal([]); + }); + }); + describe('balancer', () => { + it('reports balancer state', async() => { + expect(Object.keys(await sh.isBalancerRunning())).to.include.members([ + 'mode', 'inBalancerRound', 'numBalancerRounds' + ]); + }); + it('stops balancer', async() => { + expect((await sh.stopBalancer()).ok).to.equal(1); + expect((await sh.isBalancerRunning()).mode).to.equal('off'); + }); + it('starts balancer', async() => { + expect((await sh.startBalancer()).ok).to.equal(1); + expect((await sh.isBalancerRunning()).mode).to.equal('full'); + }); + it('reports state for collection', async() => { + expect(Object.keys(await sh.balancerCollectionStatus(ns))).to.include('balancerCompliant'); + }); + it('disables balancing', async() => { + expect((await sh.disableBalancing(ns)).acknowledged).to.equal(1); + expect((await sh._mongo.getDB('config').getCollection('collections').findOne({ _id: ns })).noBalance).to.equal(true); + }); + it('enables balancing', async() => { + expect((await sh.enableBalancing(ns)).acknowledged).to.equal(1); + expect((await sh._mongo.getDB('config').getCollection('collections').findOne({ _id: ns })).noBalance).to.equal(false); + }); + }); + }); }); diff --git a/packages/shell-api/src/shard.ts b/packages/shell-api/src/shard.ts index 452597e82d..b2170af9f4 100644 --- a/packages/shell-api/src/shard.ts +++ b/packages/shell-api/src/shard.ts @@ -223,7 +223,6 @@ export default class Shard extends ShellApiClass { @returnsPromise @serverVersions(['3.4.0', ServerVersions.latest]) async enableAutoSplit(): Promise { - assertArgsDefined(); this._emitShardApiCall('enableAutoSplit', {}); const config = await getConfigDB(this._mongo); return await config.getCollection('settings').updateOne( @@ -236,7 +235,6 @@ export default class Shard extends ShellApiClass { @returnsPromise @serverVersions(['3.4.0', ServerVersions.latest]) async disableAutoSplit(): Promise { - assertArgsDefined(); this._emitShardApiCall('disableAutoSplit', {}); const config = await getConfigDB(this._mongo); return await config.getCollection('settings').updateOne(