diff options
Diffstat (limited to 'jstests/free_mon')
22 files changed, 1577 insertions, 0 deletions
diff --git a/jstests/free_mon/free_mon_announce.js b/jstests/free_mon/free_mon_announce.js new file mode 100644 index 00000000000..d78d0b58608 --- /dev/null +++ b/jstests/free_mon/free_mon_announce.js @@ -0,0 +1,35 @@ +// Validate connect message display. +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +const mock_web = new FreeMonWebServer(); +mock_web.start(); + +const mongod = MongoRunner.runMongod({ + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), +}); +assert.neq(mongod, null, 'mongod not running'); +const admin = mongod.getDB('admin'); + +function getConnectAnnounce() { + // Capture message as it'd be presented to a user. + clearRawMongoProgramOutput(); + const exitCode = runMongoProgram( + 'mongo', '--port', mongod.port, '--eval', "shellHelper( 'show', 'freeMonitoring' );"); + assert.eq(exitCode, 0); + return rawMongoProgramOutput(); +} + +// state === 'enabled'. +admin.enableFreeMonitoring(); +WaitForRegistration(mongod); +const reminder = "To see your monitoring data"; +assert.neq(getConnectAnnounce().search(reminder), -1, 'userReminder not found'); + +// Cleanup. +MongoRunner.stopMongod(mongod); +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_disable.js b/jstests/free_mon/free_mon_disable.js new file mode 100644 index 00000000000..4bbc2236407 --- /dev/null +++ b/jstests/free_mon/free_mon_disable.js @@ -0,0 +1,35 @@ +// Validate disable works +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + freeMonitoringTag: "foo", + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); + +assert.commandWorked(conn.adminCommand({setFreeMonitoring: 1, action: "disable"})); + +const stats = mock_web.queryStats(); +print(tojson(stats)); + +assert.eq(stats.registers, 0); + +assert.eq(FreeMonGetStatus(conn).state, "disabled"); + +assert.eq(FreeMonGetServerStatus(conn).state, "disabled"); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_http_down.js b/jstests/free_mon/free_mon_http_down.js new file mode 100644 index 00000000000..6e7226e8732 --- /dev/null +++ b/jstests/free_mon/free_mon_http_down.js @@ -0,0 +1,32 @@ +// Validate registration retries if the web server is down. +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(FAULT_FAIL_REGISTER); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); +const admin = conn.getDB('admin'); + +mock_web.waitRegisters(3); + +assert.soon(function() { + const freeMonStats = FreeMonGetServerStatus(conn); + return freeMonStats.registerErrors >= 3; +}, "Failed to wait for 3 register errors: " + FreeMonGetServerStatus(conn), 20 * 1000); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_http_validate.js b/jstests/free_mon/free_mon_http_validate.js new file mode 100644 index 00000000000..30f521903cf --- /dev/null +++ b/jstests/free_mon/free_mon_http_validate.js @@ -0,0 +1,35 @@ +// Ensure free monitoring gives up if registration fails +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(FAULT_INVALID_REGISTER); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); + +mock_web.waitRegisters(1); + +// Sleep for some more time in case free monitoring would still try to register +sleep(20 * 1000); + +// Ensure it only tried to register once since we gave it a bad response. +const stats = mock_web.queryStats(); +print(tojson(stats)); + +assert.eq(stats.registers, 1); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_metrics_halt.js b/jstests/free_mon/free_mon_metrics_halt.js new file mode 100644 index 00000000000..ec1c1248801 --- /dev/null +++ b/jstests/free_mon/free_mon_metrics_halt.js @@ -0,0 +1,29 @@ +// Ensure free monitoring gives up if metrics returns halt +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(FAULT_HALT_METRICS_5); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); + +mock_web.waitMetrics(6); + +// It gets marked as disabled on halt +WaitForUnRegistration(conn); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_metrics_perm_del.js b/jstests/free_mon/free_mon_metrics_perm_del.js new file mode 100644 index 00000000000..b10d8d97f25 --- /dev/null +++ b/jstests/free_mon/free_mon_metrics_perm_del.js @@ -0,0 +1,35 @@ +// Ensure free monitoring gives up if metrics returns permanently delete +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(FAULT_PERMANENTLY_DELETE_AFTER_3); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); + +mock_web.waitMetrics(4); + +// Make sure the registration document gets removed +assert.soon( + function() { + const reg = FreeMonGetRegistration(conn); + return reg === undefined; + }, + "Failed to wait for free mon document to be removed: " + FreeMonGetRegistration(conn), + 20 * 1000); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_register.js b/jstests/free_mon/free_mon_register.js new file mode 100644 index 00000000000..19bb2e59244 --- /dev/null +++ b/jstests/free_mon/free_mon_register.js @@ -0,0 +1,53 @@ +// Validate registration works +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +const localTime = Date.now(); + +let mock_web = new FreeMonWebServer(); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + freeMonitoringTag: "foo", + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); + +WaitForRegistration(conn); + +const stats = mock_web.queryStats(); +print(tojson(stats)); + +assert.eq(stats.registers, 1); + +const last_register = mock_web.query("last_register"); +print(tojson(last_register)); + +assert.eq(last_register.version, 2); +assert.gt(new Date().setTime(last_register.localTime["$date"]), localTime); +assert.eq(last_register.payload.buildInfo.bits, 64); +assert.eq(last_register.payload.buildInfo.ok, 1); +assert.eq(last_register.payload.storageEngine.readOnly, false); +assert.eq(last_register.payload.isMaster.ok, 1); +assert.eq(last_register.tags, ["foo"]); + +mock_web.waitMetrics(2); + +const last_metrics = mock_web.query("last_metrics"); +print(tojson(last_metrics)); + +assert.eq(last_metrics.version, 2); +assert.gt(new Date().setTime(last_metrics.localTime["$date"]), localTime); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_register_cmd.js b/jstests/free_mon/free_mon_register_cmd.js new file mode 100644 index 00000000000..295908663a5 --- /dev/null +++ b/jstests/free_mon/free_mon_register_cmd.js @@ -0,0 +1,74 @@ +// Validate registration works via a command. Validate it can be registered and unregistered +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); + +// Wait an arbitrary amount of time to allow the processor loop to start. +sleep(10 * 1000); + +// Then verify that no registrations happened since we haven't runtime enabled yed. +assert.eq('undecided', + conn.getDB('admin').getFreeMonitoringStatus().state, + "Initial state should be 'undecided'"); +assert.eq(0, mock_web.queryStats().registers, "mongod registered without enabling free_mod"); + +assert.commandWorked(conn.adminCommand({setFreeMonitoring: 1, action: "enable"})); + +WaitForFreeMonServerStatusState(conn, 'enabled'); + +// The command should either timeout or suceed after registration is complete +const retStatus1 = conn.adminCommand({getFreeMonitoringStatus: 1}); +assert.commandWorked(retStatus1); +assert.eq(retStatus1.state, "enabled", tojson(retStatus1)); + +const stats = mock_web.queryStats(); +print(tojson(stats)); + +assert.eq(stats.registers, 1); + +const last_register = mock_web.query("last_register"); +print(tojson(last_register)); + +assert.eq(last_register.version, 2); +assert.eq(last_register.payload.buildInfo.bits, 64); +assert.eq(last_register.payload.buildInfo.ok, 1); +assert.eq(last_register.payload.storageEngine.readOnly, false); +assert.eq(last_register.payload.isMaster.ok, 1); + +mock_web.waitMetrics(2); + +const last_metrics = mock_web.query("last_metrics"); +print(tojson(last_metrics)); + +assert.eq(last_metrics.version, 2); + +assert.commandWorked(conn.adminCommand({setFreeMonitoring: 1, action: "disable"})); + +// Wait for unregistration to occur +assert.soon(function() { + const regDoc = FreeMonGetRegistration(conn); + return regDoc.state == "disabled"; +}, "Failed to unregister", 60 * 1000); + +const retStatus2 = conn.adminCommand({getFreeMonitoringStatus: 1}); +assert.commandWorked(retStatus2); +assert.eq(retStatus2.state, "disabled", tojson(retStatus1)); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_register_off.js b/jstests/free_mon/free_mon_register_off.js new file mode 100644 index 00000000000..591f95f0a09 --- /dev/null +++ b/jstests/free_mon/free_mon_register_off.js @@ -0,0 +1,40 @@ +// Validate registration does work if free monitoring is disabled. +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "off", + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); + +assert.commandFailed(conn.adminCommand({setFreeMonitoring: 1, action: "enable"})); + +// If it some time in case it actually started to process something. +sleep(10 * 1000); + +const retStatus1 = conn.adminCommand({getFreeMonitoringStatus: 1}); +assert.commandWorked(retStatus1); +assert.eq(retStatus1.state, "disabled", tojson(retStatus1)); + +const stats = mock_web.queryStats(); +print(tojson(stats)); + +assert.eq(stats.registers, 0); + +assert.commandFailed(conn.adminCommand({setFreeMonitoring: 1, action: "disable"})); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_register_resend.js b/jstests/free_mon/free_mon_register_resend.js new file mode 100644 index 00000000000..ab9af561a4d --- /dev/null +++ b/jstests/free_mon/free_mon_register_resend.js @@ -0,0 +1,28 @@ +// Validate resend registration works in a replica set +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(FAULT_RESEND_REGISTRATION_ONCE); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up'); + +WaitForRegistration(conn); + +mock_web.waitRegisters(2); + +MongoRunner.stopMongod(conn); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_rs_corrupt.js b/jstests/free_mon/free_mon_rs_corrupt.js new file mode 100644 index 00000000000..c8e3099447c --- /dev/null +++ b/jstests/free_mon/free_mon_rs_corrupt.js @@ -0,0 +1,37 @@ +// Validate a user manipulating system.version for free monitoring does +// not crash mongod +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const rst = new ReplSetTest({nodes: 2, nodeOptions: options}); +rst.startSet(); +rst.initiate(); +rst.awaitReplication(); + +WaitForRegistration(rst.getPrimary()); + +mock_web.waitRegisters(2); + +// For kicks, corrupt the free monitoring storage state to knock free mon offline +// and make sure the node does not crash +rst.getPrimary().getDB("admin").system.version.update({_id: "free_monitoring"}, + {$set: {version: 2}}); + +sleep(20 * 1000); + +rst.stopSet(); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_rs_delete.js b/jstests/free_mon/free_mon_rs_delete.js new file mode 100644 index 00000000000..0d043c394c1 --- /dev/null +++ b/jstests/free_mon/free_mon_rs_delete.js @@ -0,0 +1,66 @@ +// Validate a user deleting free monitoring in system.version does +// not crash mongod +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const rst = new ReplSetTest({nodes: 2, nodeOptions: options}); +rst.startSet(); +rst.initiate(); +rst.awaitReplication(); + +WaitForRegistration(rst.getPrimary()); + +mock_web.waitRegisters(2); + +WaitForRegistration(rst.getPrimary()); +WaitForRegistration(rst.getSecondary()); + +const qs1 = mock_web.queryStats(); + +jsTestLog("Breaking Free Monitoring"); + +// For kicks, delete the free monitoring storage state to knock free mon offline +// and make sure the node does not crash +rst.getPrimary().getDB("admin").system.version.remove({_id: "free_monitoring"}); + +jsTestLog("Sleeping for 20s"); +sleep(20 * 1000); + +const qs2 = mock_web.queryStats(); + +// Make sure we are back to the initial state. +WaitForFreeMonServerStatusState(rst.getPrimary(), 'undecided'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'undecided'); + +// Verify free monitoring stops but tolerate one additional collection +assert.gte(qs1.metrics + 2, qs2.metrics); +// Tolerate an additional registration on the secondary side. We may delete the record on the +// primary before the secondary processes all pending registrations requests. +assert.lte(qs1.registers, qs2.registers); + +// Enable it again to be sure we can resume +assert.commandWorked(rst.getPrimary().adminCommand({setFreeMonitoring: 1, action: "enable"})); +WaitForRegistration(rst.getPrimary()); +WaitForRegistration(rst.getSecondary()); + +sleep(20 * 1000); + +WaitForRegistration(rst.getPrimary()); +WaitForRegistration(rst.getSecondary()); + +rst.stopSet(); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_rs_halt.js b/jstests/free_mon/free_mon_rs_halt.js new file mode 100644 index 00000000000..e89a855a424 --- /dev/null +++ b/jstests/free_mon/free_mon_rs_halt.js @@ -0,0 +1,62 @@ +// Validate that if the endpoint returns halt = true that free monitoring halts +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(FAULT_HALT_METRICS_5, true); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const rst = new ReplSetTest({nodes: 2, nodeOptions: options}); +rst.startSet(); +rst.initiate(); +rst.awaitReplication(); + +WaitForRegistration(rst.getPrimary()); + +mock_web.waitRegisters(2); + +WaitForFreeMonServerStatusState(rst.getPrimary(), 'enabled'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'enabled'); + +mock_web.enableFaults(); +mock_web.waitFaults(1); + +const qs1 = mock_web.queryStats(); + +sleep(20 * 1000); + +const qs2 = mock_web.queryStats(); + +// Verify free monitoring stops but tolerate one additional collection +assert.gte(qs1.metrics + 1, qs2.metrics); +assert.eq(qs1.registers, qs2.registers); + +// Halt causes us to disable free monitoring, not return it to initial state. +WaitForFreeMonServerStatusState(rst.getPrimary(), 'disabled'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'disabled'); + +// Disable the fault so we can re-enable again +mock_web.disableFaults(); + +// Enable it again to be sure we can resume +assert.commandWorked(rst.getPrimary().adminCommand({setFreeMonitoring: 1, action: "enable"})); +WaitForRegistration(rst.getPrimary()); +WaitForRegistration(rst.getSecondary()); + +sleep(20 * 1000); + +WaitForFreeMonServerStatusState(rst.getPrimary(), 'enabled'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'enabled'); + +rst.stopSet(); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_rs_off.js b/jstests/free_mon/free_mon_rs_off.js new file mode 100644 index 00000000000..2b4d34fc87e --- /dev/null +++ b/jstests/free_mon/free_mon_rs_off.js @@ -0,0 +1,38 @@ +// Validate replica set starts up with free monitoring disabled +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "off", + verbose: 1, +}; + +const rst = new ReplSetTest({nodes: 2, nodeOptions: options}); + +rst.startSet(); +rst.initiate(); +rst.awaitReplication(); + +const retStatus1 = rst.getPrimary().adminCommand({getFreeMonitoringStatus: 1}); +assert.commandWorked(retStatus1); +assert.eq(retStatus1.state, "disabled", tojson(retStatus1)); + +const stats = mock_web.queryStats(); +print(tojson(stats)); + +assert.eq(stats.registers, 0); + +assert.commandFailed(rst.getPrimary().adminCommand({setFreeMonitoring: 1, action: "disable"})); + +rst.stopSet(); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_rs_perm_del.js b/jstests/free_mon/free_mon_rs_perm_del.js new file mode 100644 index 00000000000..182880ff921 --- /dev/null +++ b/jstests/free_mon/free_mon_rs_perm_del.js @@ -0,0 +1,55 @@ +// Validate that if the endpoint says permanently delete that the state +// document is deleted and replicated properly +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(FAULT_PERMANENTLY_DELETE_AFTER_3, true); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + enableFreeMonitoring: "on", + verbose: 1, +}; + +const rst = new ReplSetTest({nodes: 2, nodeOptions: options}); +rst.startSet(); +rst.initiate(); +rst.awaitReplication(); + +WaitForRegistration(rst.getPrimary()); + +mock_web.waitRegisters(2); + +WaitForRegistration(rst.getPrimary()); +WaitForRegistration(rst.getSecondary()); + +mock_web.enableFaults(); +mock_web.waitFaults(1); + +sleep(20 * 1000); + +// Make sure we are back to the initial state. +WaitForFreeMonServerStatusState(rst.getPrimary(), 'undecided'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'undecided'); + +// Disable the fault so we can re-enable again +mock_web.disableFaults(); + +// Enable it again to be sure we can resume +assert.commandWorked(rst.getPrimary().adminCommand({setFreeMonitoring: 1, action: "enable"})); +WaitForRegistration(rst.getPrimary()); +WaitForRegistration(rst.getSecondary()); + +sleep(20 * 1000); + +WaitForFreeMonServerStatusState(rst.getPrimary(), 'enabled'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'enabled'); + +rst.stopSet(); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_rs_register.js b/jstests/free_mon/free_mon_rs_register.js new file mode 100644 index 00000000000..1c312d5fb4c --- /dev/null +++ b/jstests/free_mon/free_mon_rs_register.js @@ -0,0 +1,95 @@ +// Validate registration works in a replica set +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(); + +mock_web.start(); + +let options = { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + verbose: 1, +}; + +const rst = new ReplSetTest({nodes: 2, nodeOptions: options}); + +rst.startSet(); +rst.initiate(); +rst.awaitReplication(); + +sleep(10 * 1000); +assert.eq(0, mock_web.queryStats().registers, "mongod registered without enabling free_mod"); + +assert.commandWorked(rst.getPrimary().adminCommand({setFreeMonitoring: 1, action: "enable"})); +WaitForRegistration(rst.getPrimary()); + +mock_web.waitRegisters(2); + +WaitForRegistration(rst.getPrimary()); +WaitForRegistration(rst.getSecondary()); +ValidateFreeMonReplicaSet(rst); + +const last_register = mock_web.query("last_register"); +print(tojson(last_register)); + +assert.eq(last_register.version, 2); +assert.eq(last_register.payload.buildInfo.bits, 64); +assert.eq(last_register.payload.buildInfo.ok, 1); +assert.eq(last_register.payload.storageEngine.readOnly, false); +assert.eq(last_register.payload.isMaster.ok, 1); +assert.gte(last_register.payload.replSetGetConfig.config.version, 2); + +function isUUID(val) { + // Mock webserver gives us back unpacked BinData/UUID in the form: + // { '$uuid': '0123456789abcdef0123456789abcdef' }. + if ((typeof val) !== 'object') { + return false; + } + const uuid = val['$uuid']; + if ((typeof uuid) !== 'string') { + return false; + } + return uuid.match(/^[0-9a-fA-F]{32}$/) !== null; +} +assert.eq(isUUID(last_register.payload.uuid['local.oplog.rs']), true); + +// Restart the secondary +var s1 = rst.getSecondary(); +var s1Id = rst.getNodeId(s1); + +rst.stop(s1Id); +rst.waitForState(s1, ReplSetTest.State.DOWN); + +rst.restart(s1Id); + +mock_web.waitRegisters(3); + +// Now disable it +assert.commandWorked(rst.getPrimary().adminCommand({setFreeMonitoring: 1, action: "disable"})); + +WaitForUnRegistration(rst.getPrimary()); +WaitForUnRegistration(rst.getSecondary()); + +WaitForFreeMonServerStatusState(rst.getPrimary(), 'disabled'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'disabled'); + +// Restart the secondary with it disabled +var s1 = rst.getSecondary(); +var s1Id = rst.getNodeId(s1); + +rst.stop(s1Id); +rst.waitForState(s1, ReplSetTest.State.DOWN); + +rst.restart(s1Id); + +// Make sure it is disabled +WaitForFreeMonServerStatusState(rst.getPrimary(), 'disabled'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'disabled'); + +rst.stopSet(); + +mock_web.stop(); +})(); diff --git a/jstests/free_mon/free_mon_rs_resend.js b/jstests/free_mon/free_mon_rs_resend.js new file mode 100644 index 00000000000..bdf7602c871 --- /dev/null +++ b/jstests/free_mon/free_mon_rs_resend.js @@ -0,0 +1,66 @@ +// Validate resend registration works in a replica set +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +let mock_web = new FreeMonWebServer(FAULT_RESEND_REGISTRATION_AT_3); +let mock_web_sec = new FreeMonWebServer(FAULT_RESEND_REGISTRATION_ONCE, true); + +mock_web.start(); +mock_web_sec.start(); + +const rst = new ReplSetTest({ + name: "free_mon_rs_register", + nodes: [ + { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), + verbose: 1, + }, + { + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web_sec.getURL(), + verbose: 1, + } + ] +}); + +rst.startSet(); +rst.initiate(); +rst.awaitReplication(); + +sleep(10 * 1000); +assert.eq(0, mock_web.queryStats().registers, "mongod registered without enabling free_mod"); +assert.eq(0, mock_web_sec.queryStats().registers, "mongod registered without enabling free_mod"); + +assert.commandWorked(rst.getPrimary().adminCommand({setFreeMonitoring: 1, action: "enable"})); +WaitForRegistration(rst.getPrimary()); + +mock_web.waitRegisters(1); +mock_web_sec.waitRegisters(1); + +WaitForFreeMonServerStatusState(rst.getPrimary(), 'enabled'); +WaitForFreeMonServerStatusState(rst.getSecondary(), 'enabled'); +ValidateFreeMonReplicaSet(rst); + +mock_web.waitRegisters(2); +mock_web_sec.waitRegisters(2); +mock_web_sec.disableFaults(); + +// Trigger resend on the secondary only +mock_web_sec.enableFaults(); +mock_web_sec.waitFaults(1); +mock_web_sec.waitRegisters(3); + +// Double check registers were as expected +const stats = mock_web.queryStats(); +assert.eq(stats.registers, 2); + +const stats_sec = mock_web_sec.queryStats(); +assert.gte(stats_sec.registers, 3); + +rst.stopSet(); + +mock_web.stop(); +mock_web_sec.stop(); +})(); diff --git a/jstests/free_mon/free_mon_server_status.js b/jstests/free_mon/free_mon_server_status.js new file mode 100644 index 00000000000..5d9848affb8 --- /dev/null +++ b/jstests/free_mon/free_mon_server_status.js @@ -0,0 +1,49 @@ +// Validate serverStatus output. +// +load("jstests/free_mon/libs/free_mon.js"); + +(function() { +'use strict'; + +const mock_web = new FreeMonWebServer(); +mock_web.start(); + +const mongod = MongoRunner.runMongod({ + setParameter: "cloudFreeMonitoringEndpointURL=" + mock_web.getURL(), +}); +assert.neq(mongod, null, 'mongod not running'); +const admin = mongod.getDB('admin'); + +const kRetryIntervalSecs = 1; +function freeMonStats() { + return assert.commandWorked(admin.runCommand({serverStatus: 1})).freeMonitoring; +} + +// Initial state. +assert.eq(freeMonStats().state, 'undecided'); + +admin.enableFreeMonitoring(); +WaitForRegistration(mongod); + +// Enabled. +const enabled = freeMonStats(); +assert.eq(enabled.state, 'enabled'); +assert.eq(enabled.retryIntervalSecs, kRetryIntervalSecs); +assert.eq(enabled.registerErrors, 0); +assert.eq(enabled.metricsErrors, 0); + +// Explicitly disabled. +admin.disableFreeMonitoring(); + +WaitForFreeMonServerStatusState(mongod, "disabled"); + +const disabled = freeMonStats(); +assert.eq(disabled.state, 'disabled'); +assert.eq(disabled.retryIntervalSecs, kRetryIntervalSecs); +assert.eq(disabled.registerErrors, 0); +assert.eq(disabled.metricsErrors, 0); + +// Cleanup. +MongoRunner.stopMongod(mongod); +mock_web.stop(); +})(); diff --git a/jstests/free_mon/libs/free_mon.js b/jstests/free_mon/libs/free_mon.js new file mode 100644 index 00000000000..269f1e65306 --- /dev/null +++ b/jstests/free_mon/libs/free_mon.js @@ -0,0 +1,331 @@ + +/** + * Control the Free Monitoring Mock Webserver. + */ + +// These faults must match the list of faults in mock_http_server.py, see the +// SUPPORTED_FAULT_TYPES list in mock_http_server.py +const FAULT_FAIL_REGISTER = "fail_register"; +const FAULT_INVALID_REGISTER = "invalid_register"; +const FAULT_HALT_METRICS_5 = "halt_metrics_5"; +const FAULT_PERMANENTLY_DELETE_AFTER_3 = "permanently_delete_after_3"; +const FAULT_RESEND_REGISTRATION_AT_3 = "resend_registration_at_3"; +const FAULT_RESEND_REGISTRATION_ONCE = "resend_registration_once"; + +const DISABLE_FAULTS = "disable_faults"; +const ENABLE_FAULTS = "enable_faults"; + +class FreeMonWebServer { + /** + * Create a new webserver. + * + * @param {string} fault_type + * @param {bool} disableFaultsOnStartup optionally disable fault on startup + */ + constructor(fault_type, disableFaultsOnStartup) { + this.python = "python3"; + this.disableFaultsOnStartup = disableFaultsOnStartup || false; + this.fault_type = fault_type; + + if (_isWindows()) { + this.python = "python.exe"; + } + + print("Using python interpreter: " + this.python); + this.web_server_py = "jstests/free_mon/libs/mock_http_server.py"; + this.control_py = "jstests/free_mon/libs/mock_http_control.py"; + + this.pid = undefined; + this.port = -1; + } + + /** + * Get the Port. + * + * @return {number} port number of http server + */ + getPort() { + return port; + } + + /** + * Get the URL. + * + * @return {string} url of http server + */ + getURL() { + return "http://localhost:" + this.port; + } + + /** + * Start the Mock HTTP Server. + */ + start() { + this.port = allocatePort(); + print("Mock Web server is listening on port: " + this.port); + + let args = [this.python, "-u", this.web_server_py, "--port=" + this.port]; + if (this.fault_type) { + args.push("--fault=" + this.fault_type); + if (this.disableFaultsOnStartup) { + args.push("--disable-faults"); + } + } + + clearRawMongoProgramOutput(); + + this.pid = _startMongoProgram({args: args}); + + assert(checkProgram(this.pid)); + + // Wait for the web server to start + assert.soon(function() { + return rawMongoProgramOutput().search("Mock Web Server Listening") !== -1; + }); + + print("Mock HTTP Server sucessfully started."); + } + + /** + * Stop the Mock HTTP Server. + */ + stop() { + stopMongoProgramByPid(this.pid); + } + + /** + * Query the HTTP server. + * + * @param {string} query type + * + * @return {object} Object representation of JSON from the server. + */ + query(query) { + const out_file = "out_" + this.port + ".txt"; + const python_command = this.python + " -u " + this.control_py + " --port=" + this.port + + " --query=" + query + " > " + out_file; + + let ret = 0; + if (_isWindows()) { + ret = runProgram('cmd.exe', '/c', python_command); + } else { + ret = runProgram('/bin/sh', '-c', python_command); + } + + assert.eq(ret, 0); + + const result = cat(out_file); + + try { + return JSON.parse(result); + } catch (e) { + jsTestLog("Failed to parse: " + result + "\n" + result); + throw e; + } + } + + /** + * Control the HTTP server. + * + * @param {string} query type + */ + control(query) { + const out_file = "out_" + this.port + ".txt"; + const python_command = this.python + " -u " + this.control_py + " --port=" + this.port + + " --query=" + query + " > " + out_file; + + let ret = 0; + if (_isWindows()) { + ret = runProgram('cmd.exe', '/c', python_command); + } else { + ret = runProgram('/bin/sh', '-c', python_command); + } + + assert.eq(ret, 0); + } + + /** + * Disable Faults + */ + disableFaults() { + this.control(DISABLE_FAULTS); + } + + /** + * Enable Faults + */ + enableFaults() { + this.control(ENABLE_FAULTS); + } + + /** + * Query the stats page for the HTTP server. + * + * @return {object} Object representation of JSON from the server. + */ + queryStats() { + return this.query("stats"); + } + + /** + * Wait for N register calls to be received by web server. + * + * @throws assert.soon() exception + */ + waitRegisters(count) { + const qs = this.queryStats.bind(this); + const port = this.port; + // Wait for registration to occur + assert.soon(function() { + const stats = qs(); + print("w" + port + "| waiting for registers >= (" + count + ") QS : " + tojson(stats)); + return stats.registers >= count; + }, "Failed to web server register", 60 * 1000); + } + + /** + * Wait for N metrics calls to be received by web server. + * + * @throws assert.soon() exception + */ + waitMetrics(count) { + const qs = this.queryStats.bind(this); + const port = this.port; + // Wait for metrics uploads to occur + assert.soon(function() { + const stats = qs(); + print("w" + port + "| waiting for metrics >= (" + count + ") QS : " + tojson(stats)); + return stats.metrics >= count; + }, "Failed to web server metrics", 60 * 1000); + } + + /** + * Wait for N fault calls to e received by web server. + * + * @throws assert.soon() exception + */ + waitFaults(count) { + const qs = this.queryStats.bind(this); + const port = this.port; + // Wait for faults to be triggered + assert.soon(function() { + const stats = qs(); + print("w" + port + "| waiting for faults >= (" + count + ") QS : " + tojson(stats)); + return stats.faults >= count; + }, "Failed to web server faults", 60 * 1000); + } +} + +/** + * Wait for registration information to be populated in the database. + * + * @param {object} conn + * @param {string} state + */ +function WaitForDiskState(conn, state) { + 'use strict'; + + const admin = conn.getDB("admin"); + + // Wait for registration to occur + assert.soon(function() { + const docs = admin.system.version.find({_id: "free_monitoring"}); + const da = docs.toArray(); + return da.length === 1 && da[0].state === state; + }, "Failed to disk state", 60 * 1000); +} + +/** + * Wait for registration information to be populated in the database. + * + * @param {object} conn + */ +function WaitForRegistration(conn) { + WaitForDiskState(conn, 'enabled'); +} + +/** + * Wait for unregistration information to be populated in the database. + * + * @param {object} conn + */ +function WaitForUnRegistration(conn) { + WaitForDiskState(conn, 'disabled'); +} + +/** + * Get registration document. + * + * @param {object} registration document + */ +function FreeMonGetRegistration(conn) { + 'use strict'; + + const admin = conn.getDB("admin"); + const docs = admin.system.version.find({_id: "free_monitoring"}); + const da = docs.toArray(); + return da[0]; +} + +/** + * Get current Free Monitoring Status via serverStatus. + * + * @param {object} serverStatus.freeMonitoring section + */ +function FreeMonGetServerStatus(conn) { + 'use strict'; + + const admin = conn.getDB("admin"); + return assert.commandWorked(admin.runCommand({serverStatus: 1})).freeMonitoring; +} + +/** + * Get current Free Monitoring Status via getFreeMonitoringStatus. + * + * @param {object} getFreeMonitoringStatus document + */ +function FreeMonGetStatus(conn) { + 'use strict'; + + const admin = conn.getDB("admin"); + return assert.commandWorked(admin.runCommand({getFreeMonitoringStatus: 1})); +} + +/** + * Wait for server status state + * + * @param {object} conn + * @param {string} state + */ +function WaitForFreeMonServerStatusState(conn, state) { + 'use strict'; + + // Wait for registration to occur + assert.soon( + function() { + let status = FreeMonGetServerStatus(conn).state; + return status === state; + }, + "Failed to find expected server status state: expected: '" + state + + "', actual: " + tojson(FreeMonGetServerStatus(conn)), + 20 * 1000); +} + +/** + * Validate Free Monitoring Replica Set consistency + * WARNING: Not valid if secondary is started with enableFreeMonitoring since it registers before it + * joins the replica set. + * + * @param {object} rst + */ +function ValidateFreeMonReplicaSet(rst) { + 'use strict'; + + const primary_status = FreeMonGetStatus(rst.getPrimary()); + const primary_url = primary_status.url; + const secondary_status = FreeMonGetStatus(rst.getSecondary()); + const secondary_url = secondary_status.url; + assert.eq(primary_url, + secondary_url, + `DUMP ${tojson(primary_status)} == ${tojson(secondary_status)}`); +} diff --git a/jstests/free_mon/libs/mock_http_common.py b/jstests/free_mon/libs/mock_http_common.py new file mode 100644 index 00000000000..14cc6ba5a21 --- /dev/null +++ b/jstests/free_mon/libs/mock_http_common.py @@ -0,0 +1,24 @@ +"""Common code for mock free monitoring http endpoint.""" +import json + +URL_PATH_STATS = "/stats" +URL_PATH_LAST_REGISTER = "/last_register" +URL_PATH_LAST_METRICS = "/last_metrics" +URL_DISABLE_FAULTS = "/disable_faults" +URL_ENABLE_FAULTS = "/enable_faults" + + +class Stats: + """Stats class shared between client and server.""" + + def __init__(self): + self.register_calls = 0 + self.metrics_calls = 0 + self.fault_calls = 0 + + def __repr__(self): + return json.dumps({ + 'metrics': self.metrics_calls, + 'registers': self.register_calls, + 'faults': self.fault_calls, + }) diff --git a/jstests/free_mon/libs/mock_http_control.py b/jstests/free_mon/libs/mock_http_control.py new file mode 100644 index 00000000000..778450dd374 --- /dev/null +++ b/jstests/free_mon/libs/mock_http_control.py @@ -0,0 +1,52 @@ +#! /usr/bin/env python3 +""" +Python script to interact with mock free monitoring HTTP server. +""" + +import argparse +import json +import logging +import sys +import urllib.request + +import mock_http_common + + +def main(): + """Main entry point.""" + parser = argparse.ArgumentParser(description='MongoDB Mock Free Monitoring Endpoint.') + + parser.add_argument('-p', '--port', type=int, default=8000, help="Port to listen on") + + parser.add_argument('-v', '--verbose', action='count', help="Enable verbose tracing") + + parser.add_argument('--query', type=str, help="Query endpoint <name>") + + args = parser.parse_args() + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + + url_str = "http://localhost:" + str(args.port) + if args.query == "stats": + url_str += mock_http_common.URL_PATH_STATS + elif args.query == "last_register": + url_str += mock_http_common.URL_PATH_LAST_REGISTER + elif args.query == "last_metrics": + url_str += mock_http_common.URL_PATH_LAST_METRICS + elif args.query == "disable_faults": + url_str += mock_http_common.URL_DISABLE_FAULTS + elif args.query == "enable_faults": + url_str += mock_http_common.URL_ENABLE_FAULTS + else: + print("Unknown query type") + sys.exit(1) + + with urllib.request.urlopen(url_str) as f: + print(f.read().decode('utf-8')) + + sys.exit(0) + + +if __name__ == '__main__': + + main() diff --git a/jstests/free_mon/libs/mock_http_server.py b/jstests/free_mon/libs/mock_http_server.py new file mode 100644 index 00000000000..7bfd8974c59 --- /dev/null +++ b/jstests/free_mon/libs/mock_http_server.py @@ -0,0 +1,306 @@ +#! /usr/bin/env python3 +"""Mock Free Monitoring Endpoint.""" + +import argparse +import collections +import http.server +import json +import logging +import socketserver +import sys +import urllib.parse + +import bson +from bson.codec_options import CodecOptions +from bson.json_util import dumps +import mock_http_common + +# Pass this data out of band instead of storing it in FreeMonHandler since the +# BaseHTTPRequestHandler does not call the methods as object methods but as class methods. This +# means there is not self. +stats = mock_http_common.Stats() +last_metrics = None +last_register = None +disable_faults = False +fault_type = None +"""Fault which causes the server to return an HTTP failure on register.""" +FAULT_FAIL_REGISTER = "fail_register" +"""Fault which causes the server to return a response with a document with a bad version.""" +FAULT_INVALID_REGISTER = "invalid_register" +"""Fault which causes metrics to return halt after 5 metric uploads have occurred.""" +FAULT_HALT_METRICS_5 = "halt_metrics_5" +"""Fault which causes metrics to return permanentlyDelete = true after 3 uploads.""" +FAULT_PERMANENTLY_DELETE_AFTER_3 = "permanently_delete_after_3" +"""Fault which causes metrics to trigger resentRegistration at 3 uploads.""" +FAULT_RESEND_REGISTRATION_AT_3 = "resend_registration_at_3" +"""Fault which causes metrics to trigger resentRegistration once.""" +FAULT_RESEND_REGISTRATION_ONCE = "resend_registration_once" + +# List of supported fault types +SUPPORTED_FAULT_TYPES = [ + FAULT_FAIL_REGISTER, + FAULT_INVALID_REGISTER, + FAULT_HALT_METRICS_5, + FAULT_PERMANENTLY_DELETE_AFTER_3, + FAULT_RESEND_REGISTRATION_AT_3, + FAULT_RESEND_REGISTRATION_ONCE, +] + +# Supported POST URL types +URL_POST_REGISTER = '/register' +URL_POST_METRICS = '/metrics' + + +class FreeMonHandler(http.server.BaseHTTPRequestHandler): + """ + Handle requests from Free Monitoring and test commands + """ + + def do_GET(self): + """Serve a Test GET request.""" + parts = urllib.parse.urlsplit(self.path) + path = parts[2] + + if path == mock_http_common.URL_PATH_STATS: + self._do_stats() + elif path == mock_http_common.URL_PATH_LAST_REGISTER: + self._do_last_register() + elif path == mock_http_common.URL_PATH_LAST_METRICS: + self._do_last_metrics() + elif path == mock_http_common.URL_DISABLE_FAULTS: + self._do_disable_faults() + elif path == mock_http_common.URL_ENABLE_FAULTS: + self._do_enable_faults() + else: + self.send_response(http.HTTPStatus.NOT_FOUND) + self.end_headers() + self.wfile.write("Unknown URL".encode()) + + def do_POST(self): + """Serve a Free Monitoring POST request.""" + parts = urllib.parse.urlsplit(self.path) + path = parts[2] + + if path == URL_POST_REGISTER: + self._do_registration() + elif path == URL_POST_METRICS: + self._do_metrics() + else: + self.send_response(http.HTTPStatus.NOT_FOUND) + self.end_headers() + self.wfile.write("Unknown URL".encode()) + + def _send_header(self): + self.send_response(http.HTTPStatus.OK) + self.send_header("content-type", "application/octet-stream") + self.end_headers() + + def _do_registration(self): + global stats + global last_register + clen = int(self.headers.get('content-length')) + + stats.register_calls += 1 + + raw_input = self.rfile.read(clen) + decoded_doc = bson.BSON.decode(raw_input) + last_register = dumps(decoded_doc) + + if not disable_faults and fault_type == FAULT_FAIL_REGISTER: + stats.fault_calls += 1 + self.send_response(http.HTTPStatus.INTERNAL_SERVER_ERROR) + self.send_header("content-type", "application/octet-stream") + self.end_headers() + self.wfile.write("Internal Error of some sort.".encode()) + return + + if not disable_faults and fault_type == FAULT_INVALID_REGISTER: + stats.fault_calls += 1 + data = bson.BSON.encode({ + 'version': bson.int64.Int64(42), + 'haltMetricsUploading': False, + 'id': '', + 'informationalURL': 'http://www.example.com/123', + 'message': 'Welcome to the Mock Free Monitoring Endpoint', + 'reportingInterval': bson.int64.Int64(1), + }) + else: + reg_id = 'mock123_' + str(stats.register_calls) + if 'id' in decoded_doc: + reg_id = decoded_doc['id'] + + data = bson.BSON.encode({ + 'version': + bson.int64.Int64(1), + 'haltMetricsUploading': + False, + 'id': + reg_id, + 'informationalURL': + 'http://www.example.com/' + reg_id, + 'message': + 'Welcome to the Mock Free Monitoring Endpoint', + 'reportingInterval': + bson.int64.Int64(1), + 'userReminder': + """To see your monitoring data, navigate to the unique URL below. +Anyone you share the URL with will also be able to view this page. + +https://localhost:8080/someUUID6v5jLKTIZZklDvN5L8sZ + +You can disable monitoring at any time by running db.disableFreeMonitoring().""", + }) + + self._send_header() + + self.wfile.write(data) + + def _do_metrics(self): + global stats + global last_metrics + clen = int(self.headers.get('content-length')) + + stats.metrics_calls += 1 + + raw_input = self.rfile.read(clen) + decoded_doc = bson.BSON.decode(raw_input) + last_metrics = dumps(decoded_doc) + + if not disable_faults and \ + stats.metrics_calls > 5 and \ + fault_type == FAULT_HALT_METRICS_5: + stats.fault_calls += 1 + data = bson.BSON.encode({ + 'version': bson.int64.Int64(1), + 'haltMetricsUploading': True, + 'permanentlyDelete': False, + 'id': 'mock123', + 'reportingInterval': bson.int64.Int64(1), + 'message': 'Thanks for all the metrics', + }) + elif not disable_faults and \ + stats.metrics_calls > 3 and fault_type == FAULT_PERMANENTLY_DELETE_AFTER_3: + stats.fault_calls += 1 + data = bson.BSON.encode({ + 'version': bson.int64.Int64(1), + 'haltMetricsUploading': False, + 'permanentlyDelete': True, + 'id': 'mock123', + 'reportingInterval': bson.int64.Int64(1), + 'message': 'Thanks for all the metrics', + }) + elif not disable_faults and \ + stats.metrics_calls > 3 and \ + stats.fault_calls < 1 and fault_type == FAULT_RESEND_REGISTRATION_ONCE: + stats.fault_calls += 1 + data = bson.BSON.encode({ + 'version': bson.int64.Int64(2), + 'haltMetricsUploading': False, + 'permanentlyDelete': False, + 'id': 'mock123', + 'reportingInterval': bson.int64.Int64(1), + 'message': 'Thanks for all the metrics', + 'resendRegistration': True, + }) + elif not disable_faults and \ + stats.metrics_calls == 3 and fault_type == FAULT_RESEND_REGISTRATION_AT_3: + stats.fault_calls += 1 + data = bson.BSON.encode({ + 'version': bson.int64.Int64(2), + 'haltMetricsUploading': False, + 'permanentlyDelete': False, + 'id': 'mock123', + 'reportingInterval': bson.int64.Int64(1), + 'message': 'Thanks for all the metrics', + 'resendRegistration': True, + }) + else: + data = bson.BSON.encode({ + 'version': bson.int64.Int64(1), + 'haltMetricsUploading': False, + 'permanentlyDelete': False, + 'id': decoded_doc['id'], + 'reportingInterval': bson.int64.Int64(1), + 'message': 'Thanks for all the metrics', + }) + + # TODO: test what if header is sent first? + self._send_header() + + self.wfile.write(data) + + def _do_stats(self): + self._send_header() + + self.wfile.write(str(stats).encode('utf-8')) + + def _do_last_register(self): + self._send_header() + + self.wfile.write(str(last_register).encode('utf-8')) + + def _do_last_metrics(self): + self._send_header() + + self.wfile.write(str(last_metrics).encode('utf-8')) + + def _do_disable_faults(self): + global disable_faults + disable_faults = True + self._send_header() + + def _do_enable_faults(self): + global disable_faults + disable_faults = False + self._send_header() + + +def run(port, server_class=http.server.HTTPServer, handler_class=FreeMonHandler): + """Run web server.""" + server_address = ('', port) + + http.server.HTTPServer.protocol_version = "HTTP/1.1" + + httpd = server_class(server_address, handler_class) + + print("Mock Web Server Listening on %s" % (str(server_address))) + + httpd.serve_forever() + + +def main(): + """Main Method.""" + global fault_type + global disable_faults + + parser = argparse.ArgumentParser(description='MongoDB Mock Free Monitoring Endpoint.') + + parser.add_argument('-p', '--port', type=int, default=8000, help="Port to listen on") + + parser.add_argument('-v', '--verbose', action='count', help="Enable verbose tracing") + + parser.add_argument('--fault', type=str, help="Type of fault to inject") + + parser.add_argument('--disable-faults', action='store_true', help="Disable faults on startup") + + args = parser.parse_args() + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + + if args.fault: + if args.fault not in SUPPORTED_FAULT_TYPES: + print("Unsupported fault type %s, supports types are %s" % (args.fault, + SUPPORTED_FAULT_TYPES)) + sys.exit(1) + + fault_type = args.fault + + if args.disable_faults: + disable_faults = True + + run(args.port) + + +if __name__ == '__main__': + + main() |
