Skip to content

Commit

Permalink
Merge pull request mozilla#633 from andymikulski/433-addon-storage-du…
Browse files Browse the repository at this point in the history
…rability

addon: Add Storage durability checks (mozilla#433)
  • Loading branch information
mythmon committed Apr 3, 2017
2 parents 606a355 + ced6ba3 commit deff7bd
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 3 deletions.
2 changes: 1 addition & 1 deletion recipe-client-addon/bin/make-xpi.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ rm "${DEST}/install.rdf.in"
cp "${BASE_DIR}/install.rdf" "${DEST}"
rm "${DEST}/jar.mn"
cp "${BASE_DIR}/chrome.manifest" "${DEST}"
rm "${DEST}/test" -r
rm -r "${DEST}/test"

pushd $DEST
zip -r shield-recipe-client.xpi *
Expand Down
7 changes: 6 additions & 1 deletion recipe-client-addon/lib/NormandyDriver.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const log = LogManager.getLogger("normandy-driver");
const actionLog = LogManager.getLogger("normandy-driver.actions");

this.NormandyDriver = function(sandboxManager) {

if (!sandboxManager) {
throw new Error("sandboxManager is required");
}
Expand Down Expand Up @@ -118,9 +119,13 @@ this.NormandyDriver = function(sandboxManager) {
return ret;
},

createStorage(keyPrefix) {
async createStorage(keyPrefix, skipDurabilityCheck) {
let storage;
try {
if (!skipDurabilityCheck) {
await Storage.checkDurability(sandbox);
}

storage = Storage.makeStorage(keyPrefix, sandbox);
} catch (e) {
log.error(e.stack);
Expand Down
4 changes: 4 additions & 0 deletions recipe-client-addon/lib/RecipeRunner.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Cu.import("resource://shield-recipe-client/lib/NormandyDriver.jsm");
Cu.import("resource://shield-recipe-client/lib/FilterExpressions.jsm");
Cu.import("resource://shield-recipe-client/lib/NormandyApi.jsm");
Cu.import("resource://shield-recipe-client/lib/SandboxManager.jsm");
Cu.import("resource://shield-recipe-client/lib/Storage.jsm");
Cu.import("resource://shield-recipe-client/lib/ClientEnvironment.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.importGlobalProperties(["fetch"]); /* globals fetch */
Expand All @@ -30,6 +31,9 @@ this.RecipeRunner = {
return;
}

const durabilityManager = new SandboxManager();
Storage.seedDurability(durabilityManager.sandbox);

let delay;
if (prefs.getBoolPref("dev_mode")) {
delay = 0;
Expand Down
32 changes: 32 additions & 0 deletions recipe-client-addon/lib/Storage.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm
this.EXPORTED_SYMBOLS = ["Storage"];

const log = LogManager.getLogger("storage");

let storePromise;

function loadStorage() {
Expand All @@ -29,6 +30,37 @@ function loadStorage() {
}

this.Storage = {
DURABILITY_NAMESPACE: "normandy_storageDurability",
DURABILITY_KEY: "durable",
isDurabilityInvalid(value) {
return typeof value === "undefined" || isNaN(value);
},
async seedDurability(sandbox) {
let globalDurabilityStore = this.makeStorage(this.DURABILITY_NAMESPACE, sandbox);
globalDurabilityStore = Cu.waiveXrays(globalDurabilityStore);

let durability = await globalDurabilityStore.getItem(this.DURABILITY_KEY);

if (this.isDurabilityInvalid(durability)) {
durability = 0;
}

globalDurabilityStore.setItem(this.DURABILITY_KEY, durability + 1);
},

async checkDurability(sandbox) {
let globalDurabilityStore = this.makeStorage(this.DURABILITY_NAMESPACE, sandbox);
globalDurabilityStore = Cu.waiveXrays(globalDurabilityStore);

const durability = await globalDurabilityStore.getItem(this.DURABILITY_KEY)
const isDurabilityInvalid = this.isDurabilityInvalid(durability) || durability < 2;

if(isDurabilityInvalid) {
throw new Error('Storage durability unconfirmed');
}
},


makeStorage(prefix, sandbox) {
if (!sandbox) {
throw new Error("No sandbox passed");
Expand Down
34 changes: 34 additions & 0 deletions recipe-client-addon/test/browser/browser_NormandyDriver.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
"use strict";

Cu.import("resource://shield-recipe-client/lib/NormandyDriver.jsm", this);
Cu.import("resource://shield-recipe-client/lib/Storage.jsm", this);


// Storage durability checks occur when drivers create new Storage instances.
// This test determines if `skipDurabilityCheck` correctly ignores the
// store durability.
add_task(async function(){
const fakeSandbox = { Promise, Error };
const driver = new NormandyDriver({ sandbox: fakeSandbox });

const store = Storage.makeStorage(Storage.DURABILITY_NAMESPACE, fakeSandbox);
await store.setItem(Storage.DURABILITY_KEY, -1);
let hadError = false;

try {
// Create storage with the 'skipDurabilityCheck' flag set to true
await driver.createStorage('test', true);
} catch(e) {
hadError = true;
}
Assert.equal(hadError, false, 'skipDurabilityCheck should ignore bad storage durability');


await store.setItem(Storage.DURABILITY_KEY, -1);
try {
// Create storage with 'skipDurabilityCheck' disabled
await driver.createStorage('test', false);
} catch(e) {
Assert.equal(e.message, 'Storage durability unconfirmed',
'skipDurabilityCheck should throw an error for invalid storage durability');
}
});

add_task(withDriver(Assert, async function uuids(driver) {
// Test that it is a UUID
const uuid1 = driver.uuid();
Expand Down
35 changes: 34 additions & 1 deletion recipe-client-addon/test/browser/browser_Storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Cu.import("resource://shield-recipe-client/lib/Storage.jsm", this);

const fakeSandbox = {Promise};
const fakeSandbox = { Promise, Error };
const store1 = Storage.makeStorage("prefix1", fakeSandbox);
const store2 = Storage.makeStorage("prefix2", fakeSandbox);

Expand Down Expand Up @@ -44,3 +44,36 @@ add_task(async function() {
Assert.equal(await store1.getItem("removeTest"), null);
Assert.equal(await store2.getItem("removeTest"), null);
});

// Tests fail if durability is not seeded properly
add_task(async function() {
const store = Storage.makeStorage(Storage.DURABILITY_NAMESPACE, fakeSandbox);
await Storage.seedDurability(fakeSandbox);

// Properly seeded store should have a starting value of 1
Assert.equal(await store.getItem(Storage.DURABILITY_KEY), 1, "Storage durability is set to 1 on start");
});

// Tests fails if storage is does not appear to be durable.
add_task(async function() {
// Manually edit the storage durability values to be invalid
const store = Storage.makeStorage(Storage.DURABILITY_NAMESPACE, fakeSandbox);
await store.setItem(Storage.DURABILITY_KEY, -1);

// Ensure invalid values fail durability checks
try {
await Storage.checkDurability(fakeSandbox);
throw new Error('Did not throw error');
} catch (err) {
Assert.equal(err.message, 'Storage durability unconfirmed', "Storage durability fails for invalid values");
}

// Ensure NaN's fail durability checks
await store.setItem(Storage.DURABILITY_KEY, 'not a number');
try {
await Storage.checkDurability(fakeSandbox);
throw new Error('Did not throw error');
} catch (err) {
Assert.equal(err.message, 'Storage durability unconfirmed', "Storage durability fails for NaN values");
}
});

0 comments on commit deff7bd

Please sign in to comment.