blob: 8cd504c3531e6bbed54e1cd16949f100ffdf67f8 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/cobalt/bin/app/configuration_data.h"
#include <lib/syslog/cpp/macros.h>
#include "src/lib/files/file.h"
#include "src/lib/files/path.h"
#include "src/lib/fxl/strings/concatenate.h"
#include "src/lib/fxl/strings/trim.h"
#include "src/lib/json_parser/json_parser.h"
#include "third_party/cobalt/src/lib/statusor/status_macros.h"
#include "third_party/cobalt/src/lib/util/file_util.h"
#include "third_party/cobalt/src/public/cobalt_service_interface.h"
namespace cobalt {
using cobalt::lib::statusor::StatusOr;
using cobalt::util::Status;
const char FuchsiaConfigurationData::kDefaultEnvironmentDir[] = "/pkg/data";
const char FuchsiaConfigurationData::kDefaultConfigDir[] = "/config/data";
namespace {
constexpr char kCobaltEnvironmentFile[] = "cobalt_environment";
const config::Environment kDefaultEnvironment = config::Environment::PROD;
constexpr char kConfigFile[] = "config.json";
constexpr char kReleaseStageKey[] = "release_stage";
const cobalt::ReleaseStage kDefaultReleaseStage = cobalt::ReleaseStage::GA;
constexpr char kDefaultDataCollectionPolicyKey[] = "default_data_collection_policy";
// When we start Cobalt, we have no idea what the current state of user consent is. Starting with
// DO_NOT_UPLOAD will allow us to collect metrics while the system is booting, before we get an
// updated policy from the UserConsentWatcher.
//
// If we started with DO_NOT_COLLECT, we could possibly miss early boot metrics entirely, and if
// we started with COLLECT_AND_UPLOAD, we could possibly violate the user's chosen
// DataCollectionPolicy by uploading metrics when they have opted out.
const cobalt::CobaltServiceInterface::DataCollectionPolicy kDefaultDataCollectionPolicy =
cobalt::CobaltServiceInterface::DataCollectionPolicy::DO_NOT_UPLOAD;
constexpr char kWatchForUserConsentKey[] = "watch_for_user_consent";
const bool kDefaultWatchForUserConsent = true;
constexpr char kEnableReplacementMetricsKey[] = "enable_replacement_metrics";
const bool kDefaultEnableReplacementMetrics = false;
// This will be found under the config directory.
constexpr char kApiKeyFile[] = "api_key.hex";
constexpr char kDefaultApiKey[] = "cobalt-default-api-key";
constexpr char kAnalyzerDevelTinkPublicKeyPath[] = "/pkg/data/keys/analyzer_devel_public";
constexpr char kShufflerDevelTinkPublicKeyPath[] = "/pkg/data/keys/shuffler_devel_public";
constexpr char kAnalyzerProdTinkPublicKeyPath[] = "/pkg/data/keys/analyzer_prod_public";
constexpr char kShufflerProdTinkPublicKeyPath[] = "/pkg/data/keys/shuffler_prod_public";
} // namespace
JSONHelper::JSONHelper(const std::string& path)
: config_file_contents_(json_parser_.ParseFromFile(path)) {}
template <typename T>
StatusOr<T> MakeBadTypeError(const std::string& key, const std::string& expected,
rapidjson::Type actual) {
static const char* kTypeNames[] = {"Null", "False", "True", "Object",
"Array", "String", "Number"};
return Status(util::StatusCode::INVALID_ARGUMENT,
fxl::Concatenate({"Key ", key, " is not of type ", expected, "."}),
fxl::Concatenate({"Key ", key, " is expected to be a ", expected,
", but was instead a ", std::string(kTypeNames[actual])}));
}
StatusOr<std::string> JSONHelper::GetString(const std::string& key) const {
CB_RETURN_IF_ERROR(EnsureKey(key));
if (!config_file_contents_[key].IsString()) {
return MakeBadTypeError<std::string>(key, "string", config_file_contents_[key].GetType());
}
return StatusOr(config_file_contents_[key].GetString());
}
StatusOr<bool> JSONHelper::GetBool(const std::string& key) const {
CB_RETURN_IF_ERROR(EnsureKey(key));
if (!config_file_contents_[key].IsBool()) {
return MakeBadTypeError<bool>(key, "bool", config_file_contents_[key].GetType());
}
return config_file_contents_[key].GetBool();
}
Status JSONHelper::EnsureKey(const std::string& key) const {
if (json_parser_.HasError()) {
return Status(util::StatusCode::INTERNAL, "Failed to parse json file.",
json_parser_.error_str());
}
if (!config_file_contents_.HasMember(key)) {
return Status(util::StatusCode::NOT_FOUND,
fxl::Concatenate({"Key ", key, " not present in the config."}));
}
return Status::OK;
}
namespace {
// Parse the cobalt environment value from the config data.
config::Environment LookupCobaltEnvironment(const std::string& environment_dir) {
auto environment_path = files::JoinPath(environment_dir, kCobaltEnvironmentFile);
std::string cobalt_environment;
if (files::ReadFileToString(environment_path, &cobalt_environment)) {
FX_LOGS(INFO) << "Loaded Cobalt environment from config file " << environment_path << ": "
<< cobalt_environment;
if (cobalt_environment == "LOCAL")
return config::Environment::LOCAL;
if (cobalt_environment == "PROD")
return config::Environment::PROD;
if (cobalt_environment == "DEVEL")
return config::Environment::DEVEL;
FX_LOGS(ERROR) << "Failed to parse the contents of config file " << environment_path << ": "
<< cobalt_environment
<< ". Falling back to default environment: " << kDefaultEnvironment;
} else {
FX_LOGS(ERROR) << "Failed to read config file " << environment_path
<< ". Falling back to default environment: " << kDefaultEnvironment;
}
return kDefaultEnvironment;
}
std::string LookupApiKeyOrDefault(const std::string& config_dir) {
auto api_key_path = files::JoinPath(config_dir, kApiKeyFile);
std::string api_key = util::ReadHexFileOrDefault(api_key_path, kDefaultApiKey);
if (api_key == kDefaultApiKey) {
FX_LOGS(INFO) << "LookupApiKeyOrDefault: Using default Cobalt API key.";
} else {
FX_LOGS(INFO) << "LookupApiKeyOrDefault: Using secret Cobalt API key.";
}
return api_key;
}
#define ASSIGN_OR_RETURN_DEFAULT(lhs, def, rexpr) \
ASSIGN_OR_RETURN_DEFAULT_IMPL(_status_or_value##__COUNTER__, lhs, def, rexpr)
#define ASSIGN_OR_RETURN_DEFAULT_IMPL(statusor, lhs, def, rexpr) \
auto statusor = (rexpr); \
if (!statusor.ok()) { \
auto status = statusor.status(); \
if (status.error_details().empty()) { \
FX_LOGS(ERROR) << "Failed to read from config. " << status.error_message() \
<< ". Using default."; \
} else { \
FX_LOGS(ERROR) << "Failed to read from config. " << status.error_message() << " (" \
<< status.error_details() << "). Using default."; \
} \
return def; \
} \
lhs = std::move(statusor.ValueOrDie())
cobalt::ReleaseStage LookupReleaseStage(const JSONHelper& json_helper) {
ASSIGN_OR_RETURN_DEFAULT(auto release_stage, kDefaultReleaseStage,
json_helper.GetString(kReleaseStageKey));
FX_LOGS(INFO) << "Loaded Cobalt release stage from config file: " << release_stage;
if (release_stage == "DEBUG") {
return cobalt::ReleaseStage::DEBUG;
} else if (release_stage == "FISHFOOD") {
return cobalt::ReleaseStage::FISHFOOD;
} else if (release_stage == "DOGFOOD") {
return cobalt::ReleaseStage::DOGFOOD;
} else if (release_stage == "GA") {
return cobalt::ReleaseStage::GA;
}
FX_LOGS(ERROR) << "Failed to parse the release stage: `" << release_stage
<< "`. Falling back to default of " << kDefaultReleaseStage << ".";
return kDefaultReleaseStage;
}
cobalt::CobaltServiceInterface::DataCollectionPolicy LookupDataCollectionPolicy(
const JSONHelper& json_helper) {
ASSIGN_OR_RETURN_DEFAULT(auto data_collection_policy, kDefaultDataCollectionPolicy,
json_helper.GetString(kDefaultDataCollectionPolicyKey));
FX_LOGS(INFO) << "Loaded Cobalt data collection policy from config file: "
<< data_collection_policy;
if (data_collection_policy == "DO_NOT_COLLECT") {
return cobalt::CobaltServiceInterface::DataCollectionPolicy::DO_NOT_COLLECT;
} else if (data_collection_policy == "DO_NOT_UPLOAD") {
return cobalt::CobaltServiceInterface::DataCollectionPolicy::DO_NOT_UPLOAD;
} else if (data_collection_policy == "COLLECT_AND_UPLOAD") {
return cobalt::CobaltServiceInterface::DataCollectionPolicy::COLLECT_AND_UPLOAD;
}
FX_LOGS(ERROR) << "Failed to parse the data collection policy: `" << data_collection_policy
<< "`. Falling back to default.";
return kDefaultDataCollectionPolicy;
}
bool LookupWatchForUserConsent(const JSONHelper& json_helper) {
ASSIGN_OR_RETURN_DEFAULT(auto watch_for_user_consent, kDefaultWatchForUserConsent,
json_helper.GetBool(kWatchForUserConsentKey));
return watch_for_user_consent;
}
bool LookupEnableReplacementMetrics(const JSONHelper& json_helper) {
ASSIGN_OR_RETURN_DEFAULT(auto enable_replacement_metrics, kDefaultEnableReplacementMetrics,
json_helper.GetBool(kEnableReplacementMetricsKey));
return enable_replacement_metrics;
}
} // namespace
FuchsiaConfigurationData::FuchsiaConfigurationData(const std::string& config_dir,
const std::string& environment_dir)
: backend_environment_(LookupCobaltEnvironment(environment_dir)),
backend_configuration_(config::ConfigurationData(backend_environment_)),
api_key_(LookupApiKeyOrDefault(config_dir)),
json_helper_(files::JoinPath(config_dir, kConfigFile)),
release_stage_(LookupReleaseStage(json_helper_)),
data_collection_policy_(LookupDataCollectionPolicy(json_helper_)),
watch_for_user_consent_(LookupWatchForUserConsent(json_helper_)),
enable_replacement_metrics_(LookupEnableReplacementMetrics(json_helper_)) {}
config::Environment FuchsiaConfigurationData::GetBackendEnvironment() const {
return backend_environment_;
}
const char* FuchsiaConfigurationData::AnalyzerPublicKeyPath() const {
if (backend_environment_ == config::DEVEL)
return kAnalyzerDevelTinkPublicKeyPath;
if (backend_environment_ == config::PROD)
return kAnalyzerProdTinkPublicKeyPath;
FX_LOGS(ERROR) << "Failed to handle any environments. Falling back to using analyzer key for "
"DEVEL environment.";
return kAnalyzerDevelTinkPublicKeyPath;
}
const char* FuchsiaConfigurationData::ShufflerPublicKeyPath() const {
switch (backend_environment_) {
case config::PROD:
return kShufflerProdTinkPublicKeyPath;
case config::DEVEL:
return kShufflerDevelTinkPublicKeyPath;
default: {
FX_LOGS(ERROR) << "Failed to handle environment enum: " << backend_environment_
<< ". Falling back to using shuffler key for DEVEL environment.";
return kShufflerDevelTinkPublicKeyPath;
}
}
}
int32_t FuchsiaConfigurationData::GetLogSourceId() const {
return backend_configuration_.GetLogSourceId();
}
cobalt::ReleaseStage FuchsiaConfigurationData::GetReleaseStage() const { return release_stage_; }
cobalt::CobaltServiceInterface::DataCollectionPolicy
FuchsiaConfigurationData::GetDataCollectionPolicy() const {
return data_collection_policy_;
}
bool FuchsiaConfigurationData::GetWatchForUserConsent() const { return watch_for_user_consent_; }
bool FuchsiaConfigurationData::GetEnableReplacementMetrics() const {
return enable_replacement_metrics_;
}
std::string FuchsiaConfigurationData::GetApiKey() const { return api_key_; }
} // namespace cobalt