Scatter downloads to reduce bandwidth spikes.
Support in update_engine to honor the enterprise policy to scatter the
downloading of ChromeOS automatic updates so that we reduce bandwidth
spikes caused due to simultaneous downloads of updates by a large number
of enterprise devices.
This has no effect on consumer devices.
BUG=chromeos-29615: Implement scattering of downloads in UpdateEngine
TEST=Manually tested all scenarios, Unit tests added for all new code.
CQ-DEPEND=I1f56b5516970d5988eebb2cf8f93f6905823801d
Change-Id: I4a8f4974467a064d723ab13cbd78b1ca3ceff420
Reviewed-on: https://gerrit.chromium.org/gerrit/21574
Commit-Ready: Jay Srinivasan <jaysri@chromium.org>
Reviewed-by: Jay Srinivasan <jaysri@chromium.org>
Tested-by: Jay Srinivasan <jaysri@chromium.org>
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index e4ab729..d8bef5e 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -16,8 +16,10 @@
#include "update_engine/omaha_hash_calculator.h"
#include "update_engine/omaha_request_action.h"
#include "update_engine/omaha_request_params.h"
+#include "update_engine/prefs.h"
#include "update_engine/prefs_mock.h"
#include "update_engine/test_utils.h"
+#include "update_engine/utils.h"
using base::Time;
using base::TimeDelta;
@@ -36,7 +38,8 @@
class OmahaRequestActionTest : public ::testing::Test { };
namespace {
-const OmahaRequestParams kDefaultTestParams(
+
+OmahaRequestParams kDefaultTestParams(
OmahaRequestParams::kOsPlatform,
OmahaRequestParams::kOsVersion,
"service_pack",
@@ -49,7 +52,7 @@
false, // delta okay
"http://url",
false, // update_disabled
- ""); // target_version_prefix
+ ""); // target_version_prefix);
string GetNoUpdateResponse(const string& app_id) {
return string(
@@ -59,6 +62,37 @@
"status=\"ok\"/><updatecheck status=\"noupdate\"/></app></gupdate>";
}
+string GetUpdateResponse2(const string& app_id,
+ const string& display_version,
+ const string& more_info_url,
+ const string& prompt,
+ const string& codebase,
+ const string& hash,
+ const string& needsadmin,
+ const string& size,
+ const string& deadline,
+ Time published_on,
+ const string& max_days_to_scatter) {
+ return string(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
+ "xmlns=\"http://www.google.com/update2/response\" "
+ "protocol=\"2.0\"><app "
+ "appid=\"") + app_id + "\" status=\"ok\"><ping "
+ "status=\"ok\"/><updatecheck DisplayVersion=\"" + display_version + "\" "
+ "ChromeOSVersion=\"" + display_version + "\" "
+ "MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt + "\" "
+ "IsDelta=\"true\" "
+ "UpdatePublishedOn=\"" + utils::ToString(published_on) + "\" "
+ "MaxDaysToScatter=\"" + max_days_to_scatter + "\" "
+ "codebase=\"" + codebase + "\" "
+ "hash=\"not-applicable\" "
+ "sha256=\"" + hash + "\" "
+ "needsadmin=\"" + needsadmin + "\" "
+ "size=\"" + size + "\" " +
+ (deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
+ "status=\"ok\"/></app></gupdate>";
+}
+
string GetUpdateResponse(const string& app_id,
const string& display_version,
const string& more_info_url,
@@ -68,19 +102,17 @@
const string& needsadmin,
const string& size,
const string& deadline) {
- return string(
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" "
- "protocol=\"2.0\"><app "
- "appid=\"") + app_id + "\" status=\"ok\"><ping "
- "status=\"ok\"/><updatecheck DisplayVersion=\"" + display_version + "\" "
- "ChromeOSVersion=\"" + display_version + "\" "
- "MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt + "\" "
- "IsDelta=\"true\" "
- "codebase=\"" + codebase + "\" hash=\"not-applicable\" "
- "sha256=\"" + hash + "\" needsadmin=\"" + needsadmin + "\" "
- "size=\"" + size + "\" deadline=\"" + deadline +
- "\" status=\"ok\"/></app></gupdate>";
+ return GetUpdateResponse2(app_id,
+ display_version,
+ more_info_url,
+ prompt,
+ codebase,
+ hash,
+ needsadmin,
+ size,
+ deadline,
+ Time::Now(),
+ "7");
}
class OmahaRequestActionTestProcessorDelegate : public ActionProcessorDelegate {
@@ -157,7 +189,7 @@
// OmahaRequestAction constructor. out_post_data may be null; if non-null, the
// post-data received by the mock HttpFetcher is returned.
bool TestUpdateCheck(PrefsInterface* prefs,
- const OmahaRequestParams& params,
+ OmahaRequestParams params,
const string& http_response,
int fail_http_response_code,
bool ping_only,
@@ -173,7 +205,7 @@
}
NiceMock<PrefsMock> local_prefs;
OmahaRequestAction action(prefs ? prefs : &local_prefs,
- params,
+ ¶ms,
NULL,
fetcher,
ping_only);
@@ -202,7 +234,7 @@
// Tests Event requests -- they should always succeed. |out_post_data|
// may be null; if non-null, the post-data received by the mock
// HttpFetcher is returned.
-void TestEvent(const OmahaRequestParams& params,
+void TestEvent(OmahaRequestParams params,
OmahaEvent* event,
const string& http_response,
vector<char>* out_post_data) {
@@ -211,7 +243,7 @@
http_response.size(),
NULL);
NiceMock<PrefsMock> prefs;
- OmahaRequestAction action(&prefs, params, event, fetcher, false);
+ OmahaRequestAction action(&prefs, ¶ms, event, fetcher, false);
OmahaRequestActionTestProcessorDelegate delegate;
delegate.loop_ = loop;
ActionProcessor processor;
@@ -284,7 +316,7 @@
"HASH1234=", // checksum
"false", // needs admin
"123", // size
- "20101020"), // deadline
+ ""), // deadline
-1,
false, // ping_only
kActionCodeOmahaUpdateIgnoredPerPolicy,
@@ -293,7 +325,6 @@
EXPECT_FALSE(response.update_exists);
}
-
TEST(OmahaRequestActionTest, NoUpdatesSentWhenBlockedByPolicyTest) {
OmahaResponse response;
OmahaRequestParams params = kDefaultTestParams;
@@ -310,6 +341,265 @@
EXPECT_FALSE(response.update_exists);
}
+TEST(OmahaRequestActionTest, WallClockBasedWaitAloneCausesScattering) {
+ OmahaResponse response;
+ OmahaRequestParams params = kDefaultTestParams;
+ params.wall_clock_based_wait_enabled = true;
+ params.update_check_count_wait_enabled = false;
+ params.waiting_period = TimeDelta::FromDays(2);
+
+ string prefs_dir;
+ EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
+ &prefs_dir));
+ ScopedDirRemover temp_dir_remover(prefs_dir);
+
+ Prefs prefs;
+ LOG_IF(ERROR, !prefs.Init(FilePath(prefs_dir)))
+ << "Failed to initialize preferences.";
+
+ ASSERT_FALSE(
+ TestUpdateCheck(&prefs, // prefs
+ params,
+ GetUpdateResponse2(OmahaRequestParams::kAppId,
+ "1.2.3.4", // version
+ "http://more/info",
+ "true", // prompt
+ "http://code/base", // dl url
+ "HASH1234=", // checksum
+ "false", // needs admin
+ "123", // size
+ "", // deadline
+ Time::Now(), // published on
+ "7"), // max days to scatter
+ -1,
+ false, // ping_only
+ kActionCodeOmahaUpdateDeferredPerPolicy,
+ &response,
+ NULL));
+ EXPECT_FALSE(response.update_exists);
+}
+
+TEST(OmahaRequestActionTest, NoWallClockBasedWaitCausesNoScattering) {
+ OmahaResponse response;
+ OmahaRequestParams params = kDefaultTestParams;
+ params.wall_clock_based_wait_enabled = false;
+ params.waiting_period = TimeDelta::FromDays(2);
+
+ params.update_check_count_wait_enabled = true;
+ params.min_update_checks_needed = 1;
+ params.max_update_checks_allowed = 8;
+
+ string prefs_dir;
+ EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
+ &prefs_dir));
+ ScopedDirRemover temp_dir_remover(prefs_dir);
+
+ Prefs prefs;
+ LOG_IF(ERROR, !prefs.Init(FilePath(prefs_dir)))
+ << "Failed to initialize preferences.";
+
+ ASSERT_TRUE(
+ TestUpdateCheck(&prefs, // prefs
+ params,
+ GetUpdateResponse2(OmahaRequestParams::kAppId,
+ "1.2.3.4", // version
+ "http://more/info",
+ "true", // prompt
+ "http://code/base", // dl url
+ "HASH1234=", // checksum
+ "false", // needs admin
+ "123", // size
+ "", // deadline
+ Time::Now(), // published on
+ "7"), // max days to scatter
+ -1,
+ false, // ping_only
+ kActionCodeSuccess,
+ &response,
+ NULL));
+ EXPECT_TRUE(response.update_exists);
+}
+
+TEST(OmahaRequestActionTest, ZeroMaxDaysToScatterCausesNoScattering) {
+ OmahaResponse response;
+ OmahaRequestParams params = kDefaultTestParams;
+ params.wall_clock_based_wait_enabled = true;
+ params.waiting_period = TimeDelta::FromDays(2);
+
+ params.update_check_count_wait_enabled = true;
+ params.min_update_checks_needed = 1;
+ params.max_update_checks_allowed = 8;
+
+ string prefs_dir;
+ EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
+ &prefs_dir));
+ ScopedDirRemover temp_dir_remover(prefs_dir);
+
+ Prefs prefs;
+ LOG_IF(ERROR, !prefs.Init(FilePath(prefs_dir)))
+ << "Failed to initialize preferences.";
+
+ ASSERT_TRUE(
+ TestUpdateCheck(&prefs, // prefs
+ params,
+ GetUpdateResponse2(OmahaRequestParams::kAppId,
+ "1.2.3.4", // version
+ "http://more/info",
+ "true", // prompt
+ "http://code/base", // dl url
+ "HASH1234=", // checksum
+ "false", // needs admin
+ "123", // size
+ "", // deadline
+ Time::Now(), // published on
+ "0"), // max days to scatter
+ -1,
+ false, // ping_only
+ kActionCodeSuccess,
+ &response,
+ NULL));
+ EXPECT_TRUE(response.update_exists);
+}
+
+
+TEST(OmahaRequestActionTest, ZeroUpdateCheckCountCausesNoScattering) {
+ OmahaResponse response;
+ OmahaRequestParams params = kDefaultTestParams;
+ params.wall_clock_based_wait_enabled = true;
+ params.waiting_period = TimeDelta();
+
+ params.update_check_count_wait_enabled = true;
+ params.min_update_checks_needed = 0;
+ params.max_update_checks_allowed = 0;
+
+ string prefs_dir;
+ EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
+ &prefs_dir));
+ ScopedDirRemover temp_dir_remover(prefs_dir);
+
+ Prefs prefs;
+ LOG_IF(ERROR, !prefs.Init(FilePath(prefs_dir)))
+ << "Failed to initialize preferences.";
+
+ ASSERT_TRUE(TestUpdateCheck(
+ &prefs, // prefs
+ params,
+ GetUpdateResponse2(OmahaRequestParams::kAppId,
+ "1.2.3.4", // version
+ "http://more/info",
+ "true", // prompt
+ "http://code/base", // dl url
+ "HASH1234=", // checksum
+ "false", // needs admin
+ "123", // size
+ "", // deadline
+ Time::Now(), // published on
+ "7"), // max days to scatter
+ -1,
+ false, // ping_only
+ kActionCodeSuccess,
+ &response,
+ NULL));
+
+ int64 count;
+ ASSERT_TRUE(prefs.GetInt64(kPrefsUpdateCheckCount, &count));
+ ASSERT_TRUE(count == 0);
+ EXPECT_TRUE(response.update_exists);
+}
+
+TEST(OmahaRequestActionTest, NonZeroUpdateCheckCountCausesScattering) {
+ OmahaResponse response;
+ OmahaRequestParams params = kDefaultTestParams;
+ params.wall_clock_based_wait_enabled = true;
+ params.waiting_period = TimeDelta();
+
+ params.update_check_count_wait_enabled = true;
+ params.min_update_checks_needed = 1;
+ params.max_update_checks_allowed = 8;
+
+ string prefs_dir;
+ EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
+ &prefs_dir));
+ ScopedDirRemover temp_dir_remover(prefs_dir);
+
+ Prefs prefs;
+ LOG_IF(ERROR, !prefs.Init(FilePath(prefs_dir)))
+ << "Failed to initialize preferences.";
+
+ ASSERT_FALSE(TestUpdateCheck(
+ &prefs, // prefs
+ params,
+ GetUpdateResponse2(OmahaRequestParams::kAppId,
+ "1.2.3.4", // version
+ "http://more/info",
+ "true", // prompt
+ "http://code/base", // dl url
+ "HASH1234=", // checksum
+ "false", // needs admin
+ "123", // size
+ "", // deadline
+ Time::Now(), // published on
+ "7"), // max days to scatter
+ -1,
+ false, // ping_only
+ kActionCodeOmahaUpdateDeferredPerPolicy,
+ &response,
+ NULL));
+
+ int64 count;
+ ASSERT_TRUE(prefs.GetInt64(kPrefsUpdateCheckCount, &count));
+ ASSERT_TRUE(count > 0);
+ EXPECT_FALSE(response.update_exists);
+}
+
+TEST(OmahaRequestActionTest, ExistingUpdateCheckCountCausesScattering) {
+ OmahaResponse response;
+ OmahaRequestParams params = kDefaultTestParams;
+ params.wall_clock_based_wait_enabled = true;
+ params.waiting_period = TimeDelta();
+
+ params.update_check_count_wait_enabled = true;
+ params.min_update_checks_needed = 1;
+ params.max_update_checks_allowed = 8;
+
+ string prefs_dir;
+ EXPECT_TRUE(utils::MakeTempDirectory("/tmp/ue_ut_prefs.XXXXXX",
+ &prefs_dir));
+ ScopedDirRemover temp_dir_remover(prefs_dir);
+
+ Prefs prefs;
+ LOG_IF(ERROR, !prefs.Init(FilePath(prefs_dir)))
+ << "Failed to initialize preferences.";
+
+ ASSERT_TRUE(prefs.SetInt64(kPrefsUpdateCheckCount, 5));
+
+ ASSERT_FALSE(TestUpdateCheck(
+ &prefs, // prefs
+ params,
+ GetUpdateResponse2(OmahaRequestParams::kAppId,
+ "1.2.3.4", // version
+ "http://more/info",
+ "true", // prompt
+ "http://code/base", // dl url
+ "HASH1234=", // checksum
+ "false", // needs admin
+ "123", // size
+ "", // deadline
+ Time::Now(), // published on
+ "7"), // max days to scatter
+ -1,
+ false, // ping_only
+ kActionCodeOmahaUpdateDeferredPerPolicy,
+ &response,
+ NULL));
+
+ int64 count;
+ ASSERT_TRUE(prefs.GetInt64(kPrefsUpdateCheckCount, &count));
+ // count remains the same, as the decrementing happens in update_attempter
+ // which this test doesn't exercise.
+ ASSERT_TRUE(count == 5);
+ EXPECT_FALSE(response.update_exists);
+}
TEST(OmahaRequestActionTest, NoOutputPipeTest) {
const string http_response(GetNoUpdateResponse(OmahaRequestParams::kAppId));
@@ -317,7 +607,8 @@
GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
NiceMock<PrefsMock> prefs;
- OmahaRequestAction action(&prefs, kDefaultTestParams, NULL,
+ OmahaRequestParams params = kDefaultTestParams;
+ OmahaRequestAction action(&prefs, ¶ms, NULL,
new MockHttpFetcher(http_response.data(),
http_response.size(),
NULL),
@@ -473,7 +764,8 @@
GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
NiceMock<PrefsMock> prefs;
- OmahaRequestAction action(&prefs, kDefaultTestParams, NULL,
+ OmahaRequestParams params = kDefaultTestParams;
+ OmahaRequestAction action(&prefs, ¶ms, NULL,
new MockHttpFetcher(http_response.data(),
http_response.size(),
NULL),
@@ -511,7 +803,7 @@
false, // delta okay
"http://url",
false, // update_disabled
- ""); // target_version_prefix
+ ""); // target_version_prefix
OmahaResponse response;
ASSERT_FALSE(
TestUpdateCheck(NULL, // prefs
@@ -680,9 +972,10 @@
TEST(OmahaRequestActionTest, IsEventTest) {
string http_response("doesn't matter");
NiceMock<PrefsMock> prefs;
+ OmahaRequestParams params = kDefaultTestParams;
OmahaRequestAction update_check_action(
&prefs,
- kDefaultTestParams,
+ ¶ms,
NULL,
new MockHttpFetcher(http_response.data(),
http_response.size(),
@@ -690,9 +983,10 @@
false);
EXPECT_FALSE(update_check_action.IsEvent());
+ params = kDefaultTestParams;
OmahaRequestAction event_action(
&prefs,
- kDefaultTestParams,
+ ¶ms,
new OmahaEvent(OmahaEvent::kTypeUpdateComplete),
new MockHttpFetcher(http_response.data(),
http_response.size(),