Skip to content

Commit e3fdae9

Browse files
committed
Ignore ancient builds with higher safe levels
When searching for the latest build on a branch for a particular safe level, it is possible to encounter very old builds. This happens if a build used to have more validation, and it since has dropped running that validation. To find the combination of "latest" and "most safe", the fetcher will now discard builds from consideration if they are more than a week older than the latest build at any safe level. Bug: b/524908123 Test: Use build example build from b/524908123#comment19
1 parent 3fe63ea commit e3fdae9

2 files changed

Lines changed: 45 additions & 3 deletions

File tree

base/cvd/cuttlefish/host/libs/web/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ cf_cc_library(
4343
"//cuttlefish/result",
4444
"//libbase",
4545
"@abseil-cpp//absl/log",
46+
"@abseil-cpp//absl/log:check",
4647
"@jsoncpp",
4748
],
4849
)

base/cvd/cuttlefish/host/libs/web/android_build_api.cpp

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616
#include "cuttlefish/host/libs/web/android_build_api.h"
1717

1818
#include <dirent.h>
19+
#include <time.h>
1920
#include <unistd.h>
2021

22+
#include <algorithm>
2123
#include <chrono>
24+
#include <iomanip>
2225
#include <memory>
2326
#include <optional>
27+
#include <ranges>
2428
#include <set>
29+
#include <sstream>
2530
#include <string>
2631
#include <string_view>
2732
#include <thread>
@@ -31,6 +36,7 @@
3136
#include <variant>
3237
#include <vector>
3338

39+
#include "absl/log/check.h"
3440
#include "absl/log/log.h"
3541
#include "android-base/file.h"
3642
#include "json/value.h"
@@ -84,6 +90,15 @@ Result<Json::Value> GetResponseJson(const HttpResponse<Json::Value>& response,
8490
return response.data;
8591
}
8692

93+
Result<std::chrono::system_clock::time_point> ParseTime(std::string_view str) {
94+
std::stringstream stream = std::stringstream(std::string(str));
95+
tm time_tm;
96+
stream >> std::get_time(&time_tm, "%Y-%m-%dT%H:%M:%S.");
97+
CF_EXPECTF(!!stream, "Failed to parse time '{}'", str);
98+
99+
return std::chrono::system_clock::from_time_t(mktime(&time_tm));
100+
}
101+
87102
} // namespace
88103

89104
AndroidBuildApi::AndroidBuildApi(HttpClient& http_client,
@@ -263,6 +278,12 @@ Result<std::vector<std::string>> AndroidBuildApi::Headers() {
263278

264279
Result<std::optional<std::string>> AndroidBuildApi::LatestBuildId(
265280
const std::string& branch, const std::string& target) {
281+
struct CandidateBuild {
282+
std::string build_id;
283+
std::chrono::system_clock::time_point creation_time;
284+
};
285+
// Find the latest build at every safe level
286+
std::vector<CandidateBuild> candidates;
266287
for (const SafeLevel safe_level : kAllSafeLevels) {
267288
VLOG(0) << "Attempting to download build at safe level '" << safe_level
268289
<< "' for branch '" << branch << "' and target '" << target << "'";
@@ -276,7 +297,7 @@ Result<std::optional<std::string>> AndroidBuildApi::LatestBuildId(
276297
if (!json_res.ok()) {
277298
continue;
278299
}
279-
const Json::Value json = *json_res;
300+
const Json::Value& json = *json_res;
280301

281302
if (!json.isMember("builds")) {
282303
continue;
@@ -287,9 +308,29 @@ Result<std::optional<std::string>> AndroidBuildApi::LatestBuildId(
287308
"target \"{}\" in the response array, "
288309
"but found {}",
289310
branch, target, json["builds"].size());
290-
return CF_EXPECT(GetValue<std::string>(json["builds"][0], {"buildId"}));
311+
312+
const Json::Value& build = json["builds"][0];
313+
const std::string& completion = build["completionTimestamp"].asString();
314+
candidates.emplace_back(CandidateBuild{
315+
.build_id = build["buildId"].asString(),
316+
.creation_time = CF_EXPECT(ParseTime(completion)),
317+
});
318+
}
319+
if (candidates.empty()) {
320+
return std::nullopt;
291321
}
292-
return std::nullopt;
322+
// Drop candidate builds 1 week older than the latest
323+
auto creation = [](const auto& cd) { return cd.creation_time; };
324+
std::chrono::system_clock::time_point latest =
325+
std::ranges::max(std::views::transform(candidates, creation));
326+
auto recent = [latest](const auto& cd) {
327+
// NOLINTNEXTLINE(misc-include-cleaner): <chrono>
328+
return latest - cd.creation_time < std::chrono::weeks(1);
329+
};
330+
// Return the first valid build (which will have the highest safe level)
331+
auto it = std::find_if(candidates.begin(), candidates.end(), recent);
332+
CHECK(it != candidates.end());
333+
return it->build_id;
293334
}
294335

295336
Result<std::unordered_set<std::string>> AndroidBuildApi::Artifacts(

0 commit comments

Comments
 (0)