mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 18:26:15 +00:00
Bug 1506175 - Fetch recipes from Remote Settings r=mythmon,Gijs
Instead of obtaining the recipes from the Normandy server, obtain them from RemoteSettings Differential Revision: https://phabricator.services.mozilla.com/D11490 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
c46d35ae2f
commit
2bc67b2afd
@ -1742,6 +1742,7 @@ pref("app.normandy.first_run", true);
|
||||
pref("app.normandy.logging.level", 50); // Warn
|
||||
pref("app.normandy.run_interval_seconds", 21600); // 6 hours
|
||||
pref("app.normandy.shieldLearnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/shield");
|
||||
pref("app.normandy.remotesettings.enabled", false);
|
||||
#ifdef MOZ_DATA_REPORTING
|
||||
pref("app.shield.optoutstudies.enabled", true);
|
||||
#else
|
||||
|
@ -13,6 +13,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "timerManager",
|
||||
"nsIUpdateTimerManager");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
RemoteSettings: "resource://services-settings/remote-settings.js",
|
||||
Storage: "resource://normandy/lib/Storage.jsm",
|
||||
FilterExpressions: "resource://gre/modules/components-utils/FilterExpressions.jsm",
|
||||
NormandyApi: "resource://normandy/lib/NormandyApi.jsm",
|
||||
@ -26,6 +27,7 @@ var EXPORTED_SYMBOLS = ["RecipeRunner"];
|
||||
|
||||
const log = LogManager.getLogger("recipe-runner");
|
||||
const TIMER_NAME = "recipe-client-addon-run";
|
||||
const REMOTE_SETTINGS_COLLECTION = "normandy-recipes";
|
||||
const PREF_CHANGED_TOPIC = "nsPref:changed";
|
||||
|
||||
const TELEMETRY_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
|
||||
@ -37,6 +39,7 @@ const SHIELD_ENABLED_PREF = `${PREF_PREFIX}.enabled`;
|
||||
const DEV_MODE_PREF = `${PREF_PREFIX}.dev_mode`;
|
||||
const API_URL_PREF = `${PREF_PREFIX}.api_url`;
|
||||
const LAZY_CLASSIFY_PREF = `${PREF_PREFIX}.experiments.lazy_classify`;
|
||||
const REMOTE_SETTINGS_ENABLED_PREF = `${PREF_PREFIX}.remotesettings.enabled`;
|
||||
|
||||
const PREFS_TO_WATCH = [
|
||||
RUN_INTERVAL_PREF,
|
||||
@ -45,6 +48,12 @@ const PREFS_TO_WATCH = [
|
||||
API_URL_PREF,
|
||||
];
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gRemoteSettingsClient", () => {
|
||||
return RemoteSettings(REMOTE_SETTINGS_COLLECTION, {
|
||||
filterFunc: async recipe => RecipeRunner.checkFilter(recipe) ? recipe : null,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* cacheProxy returns an object Proxy that will memoize properties of the target.
|
||||
*/
|
||||
@ -202,25 +211,11 @@ var RecipeRunner = {
|
||||
}
|
||||
|
||||
// Fetch recipes before execution in case we fail and exit early.
|
||||
let recipes;
|
||||
let recipesToRun;
|
||||
try {
|
||||
recipes = await NormandyApi.fetchRecipes({enabled: true});
|
||||
log.debug(
|
||||
`Fetched ${recipes.length} recipes from the server: ` +
|
||||
recipes.map(r => r.name).join(", ")
|
||||
);
|
||||
|
||||
recipesToRun = await this.loadRecipes();
|
||||
} catch (e) {
|
||||
const apiUrl = Services.prefs.getCharPref(API_URL_PREF);
|
||||
log.error(`Could not fetch recipes from ${apiUrl}: "${e}"`);
|
||||
|
||||
let status = Uptake.RUNNER_SERVER_ERROR;
|
||||
if (/NetworkError/.test(e)) {
|
||||
status = Uptake.RUNNER_NETWORK_ERROR;
|
||||
} else if (e instanceof NormandyApi.InvalidSignatureError) {
|
||||
status = Uptake.RUNNER_INVALID_SIGNATURE;
|
||||
}
|
||||
Uptake.reportRunner(status);
|
||||
// The legacy call to `Normandy.fetchRecipes()` can throw.
|
||||
return;
|
||||
}
|
||||
|
||||
@ -228,14 +223,6 @@ var RecipeRunner = {
|
||||
await actions.fetchRemoteActions();
|
||||
await actions.preExecution();
|
||||
|
||||
// Evaluate recipe filters
|
||||
const recipesToRun = [];
|
||||
for (const recipe of recipes) {
|
||||
if (await this.checkFilter(recipe)) {
|
||||
recipesToRun.push(recipe);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute recipes, if we have any.
|
||||
if (recipesToRun.length === 0) {
|
||||
log.debug("No recipes to execute");
|
||||
@ -250,6 +237,46 @@ var RecipeRunner = {
|
||||
Uptake.reportRunner(Uptake.RUNNER_SUCCESS);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the list of recipes to run, filtered for the current environment.
|
||||
*/
|
||||
async loadRecipes() {
|
||||
// If RemoteSettings is enabled, we read the list of recipes from there.
|
||||
// The JEXL filtering is done via the provided callback.
|
||||
if (Services.prefs.getBoolPref(REMOTE_SETTINGS_ENABLED_PREF, false)) {
|
||||
return gRemoteSettingsClient.get();
|
||||
}
|
||||
// Obtain the recipes from the Normandy server (legacy).
|
||||
let recipes;
|
||||
try {
|
||||
recipes = await NormandyApi.fetchRecipes({enabled: true});
|
||||
log.debug(
|
||||
`Fetched ${recipes.length} recipes from the server: ` +
|
||||
recipes.map(r => r.name).join(", ")
|
||||
);
|
||||
} catch (e) {
|
||||
const apiUrl = Services.prefs.getCharPref(API_URL_PREF);
|
||||
log.error(`Could not fetch recipes from ${apiUrl}: "${e}"`);
|
||||
|
||||
let status = Uptake.RUNNER_SERVER_ERROR;
|
||||
if (/NetworkError/.test(e)) {
|
||||
status = Uptake.RUNNER_NETWORK_ERROR;
|
||||
} else if (e instanceof NormandyApi.InvalidSignatureError) {
|
||||
status = Uptake.RUNNER_INVALID_SIGNATURE;
|
||||
}
|
||||
Uptake.reportRunner(status);
|
||||
throw e;
|
||||
}
|
||||
// Evaluate recipe filters
|
||||
const recipesToRun = [];
|
||||
for (const recipe of recipes) {
|
||||
if (await this.checkFilter(recipe)) {
|
||||
recipesToRun.push(recipe);
|
||||
}
|
||||
}
|
||||
return recipesToRun;
|
||||
},
|
||||
|
||||
getFilterContext(recipe) {
|
||||
const environment = cacheProxy(ClientEnvironment);
|
||||
environment.recipe = {
|
||||
|
@ -10,6 +10,8 @@ ChromeUtils.import("resource://normandy/lib/ActionsManager.jsm", this);
|
||||
ChromeUtils.import("resource://normandy/lib/AddonStudies.jsm", this);
|
||||
ChromeUtils.import("resource://normandy/lib/Uptake.jsm", this);
|
||||
|
||||
const { RemoteSettings } = ChromeUtils.import("resource://services-settings/remote-settings.js", {});
|
||||
|
||||
add_task(async function getFilterContext() {
|
||||
const recipe = {id: 17, arguments: {foo: "bar"}, unrelated: false};
|
||||
const context = RecipeRunner.getFilterContext(recipe);
|
||||
@ -171,6 +173,41 @@ decorate_task(
|
||||
}
|
||||
);
|
||||
|
||||
decorate_task(
|
||||
withPrefEnv({
|
||||
set: [
|
||||
["app.normandy.remotesettings.enabled", true],
|
||||
],
|
||||
}),
|
||||
withStub(ActionsManager.prototype, "runRecipe"),
|
||||
withStub(ActionsManager.prototype, "fetchRemoteActions"),
|
||||
withStub(ActionsManager.prototype, "finalize"),
|
||||
async function testReadFromRemoteSettings(
|
||||
runRecipeStub,
|
||||
fetchRemoteActionsStub,
|
||||
finalizeStub,
|
||||
) {
|
||||
const matchRecipe = { id: "match", action: "matchAction", filter_expression: "true", _status: "synced", enabled: true };
|
||||
const noMatchRecipe = { id: "noMatch", action: "noMatchAction", filter_expression: "false", _status: "synced", enabled: true };
|
||||
const missingRecipe = { id: "missing", action: "missingAction", filter_expression: "true", _status: "synced", enabled: true };
|
||||
|
||||
const rsCollection = await RemoteSettings("normandy-recipes").openCollection();
|
||||
await rsCollection.create(matchRecipe, { synced: true });
|
||||
await rsCollection.create(noMatchRecipe, { synced: true });
|
||||
await rsCollection.create(missingRecipe, { synced: true });
|
||||
await rsCollection.db.saveLastModified(42);
|
||||
rsCollection.db.close();
|
||||
|
||||
await RecipeRunner.run();
|
||||
|
||||
Assert.deepEqual(
|
||||
runRecipeStub.args,
|
||||
[[matchRecipe], [missingRecipe]],
|
||||
"recipe with matching filters should be executed",
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
decorate_task(
|
||||
withMockNormandyApi,
|
||||
async function testRunFetchFail(mockApi) {
|
||||
|
Loading…
Reference in New Issue
Block a user