blob: 2e3fb04aaa5ff421a09d21bd06d207c3f8201bbb [file] [log] [blame]
// Copyright 2017 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 <memory>
#include <string>
#include <vector>
#include "util/gcs/gcs_util.h"
#include "glog/logging.h"
#include "third_party/google-api-cpp-client/service_apis/storage/google/storage_api/storage_api.h"
#include "third_party/google-api-cpp-client/src/googleapis/client/auth/oauth2_authorization.h"
#include "third_party/google-api-cpp-client/src/googleapis/client/auth/oauth2_service_authorization.h"
#include "third_party/google-api-cpp-client/src/googleapis/client/data/data_reader.h"
#include "third_party/google-api-cpp-client/src/googleapis/client/transport/curl_http_transport.h"
#include "third_party/google-api-cpp-client/src/googleapis/client/transport/http_transport.h"
#include "third_party/google-api-cpp-client/src/googleapis/strings/stringpiece.h"
#include "util/log_based_metrics.h"
#include "util/pem_util.h"
using google_storage_api::BucketsResource_GetMethod;
using google_storage_api::ObjectsResource_InsertMethod;
using google_storage_api::StorageService;
using googleapis::client::CurlHttpTransportFactory;
using googleapis::client::DataReader;
using googleapis::client::HttpTransportLayerConfig;
using googleapis::client::NewUnmanagedInMemoryDataReader;
using googleapis::client::NewUnmanagedIstreamDataReader;
using googleapis::client::OAuth2Credential;
using googleapis::client::OAuth2ServiceAccountFlow;
namespace cobalt {
namespace util {
namespace gcs {
// Stackdriver metric constants
namespace {
const char kInitFailure[] = "gcs-util-init-failure";
const char kUploadFailure[] = "gcs-util-upload-failure";
const char kPingFailure[] = "gcs-util-ping-failure";
} // namespace
struct GcsUtil::Impl {
googleapis::client::OAuth2Credential oauth_credential_;
std::unique_ptr<google_storage_api::StorageService> storage_service_;
std::unique_ptr<googleapis::client::OAuth2ServiceAccountFlow> oauth_flow_;
std::unique_ptr<googleapis::client::HttpTransportLayerConfig> http_config_;
};
GcsUtil::GcsUtil() : impl_(new Impl()) {}
GcsUtil::~GcsUtil() {}
bool GcsUtil::InitFromDefaultPaths() {
// When ReportMaster is deployed to Google Container Engine, the environment
// variable "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH" is set in the file
// //kubernetes/cobalt_common/Dockerfile
char* p = std::getenv("GRPC_DEFAULT_SSL_ROOTS_FILE_PATH");
if (!p) {
LOG_STACKDRIVER_COUNT_METRIC(ERROR, kInitFailure)
<< "The environment variable GRPC_DEFAULT_SSL_ROOTS_FILE_PATH "
"is not set.";
return false;
}
std::string ca_certs_path(p);
// When ReportMaster is deployed to Google Container Engine, the environment
// variable "COBALT_GCS_SERVICE_ACCOUNT_CREDENTIALS" is set in the
// file //kubernets/report_master/Dockerfile
p = std::getenv("COBALT_GCS_SERVICE_ACCOUNT_CREDENTIALS");
if (!p) {
LOG_STACKDRIVER_COUNT_METRIC(ERROR, kInitFailure)
<< "The environment variable "
"COBALT_GCS_SERVICE_ACCOUNT_CREDENTIALS is not set.";
return false;
}
std::string service_account_json_path(p);
return Init(ca_certs_path, service_account_json_path);
}
bool GcsUtil::Init(const std::string ca_certs_path,
const std::string& service_account_json_path) {
// Set up HttpTransportLayer.
impl_->http_config_.reset(new HttpTransportLayerConfig);
impl_->http_config_->ResetDefaultTransportFactory(
new CurlHttpTransportFactory(impl_->http_config_.get()));
impl_->http_config_->mutable_default_transport_options()->set_cacerts_path(
ca_certs_path);
// Set up OAuth 2.0 flow for a service account.
googleapis::util::Status status;
impl_->oauth_flow_.reset(new OAuth2ServiceAccountFlow(
impl_->http_config_->NewDefaultTransport(&status)));
if (!status.ok()) {
LOG_STACKDRIVER_COUNT_METRIC(ERROR, kInitFailure)
<< "GcsUitl::Init(). Error creating new Http transport: "
<< status.ToString();
return false;
}
// Load the the contents of the service account json into a string.
std::string json;
PemUtil::ReadTextFile(service_account_json_path, &json);
if (json.empty()) {
LOG_STACKDRIVER_COUNT_METRIC(ERROR, kInitFailure)
<< "GcsUitl::Init(). Unable to read service account json from: "
<< service_account_json_path;
return false;
}
// Initialize the flow with the contents of the service account json.
impl_->oauth_flow_->InitFromJson(json);
impl_->oauth_flow_->set_default_scopes(
StorageService::SCOPES::DEVSTORAGE_READ_WRITE);
// Connect the credential with the AuthFlow.
impl_->oauth_credential_.set_flow(impl_->oauth_flow_.get());
// Construct the storage service.
impl_->storage_service_.reset(
new StorageService(impl_->http_config_->NewDefaultTransport(&status)));
if (!status.ok()) {
LOG_STACKDRIVER_COUNT_METRIC(ERROR, kInitFailure)
<< "GcsUitl::Init(). Error creating new Http transport: "
<< status.ToString();
return false;
}
return true;
}
bool GcsUtil::Upload(const std::string& bucket, const std::string& path,
const std::string mime_type, const char* data,
size_t num_bytes, uint32_t timeout_seconds) {
googleapis::StringPiece str;
str.set(data, num_bytes);
return Upload(bucket, path, mime_type, NewUnmanagedInMemoryDataReader(str),
timeout_seconds);
}
bool GcsUtil::Upload(const std::string& bucket, const std::string& path,
const std::string mime_type, std::istream* stream,
uint32_t timeout_seconds) {
return Upload(bucket, path, mime_type, NewUnmanagedIstreamDataReader(stream),
timeout_seconds);
}
bool GcsUtil::Upload(const std::string& bucket, const std::string& path,
const std::string mime_type, void* data_reader,
uint32_t timeout_seconds) {
// Build the request.
// Note that according to the comments on the method
// MediaUploader::set_media_content_reader() in //third_party/ \
// google-api-cpp-client/src/googleapis/client/service/media_uploader.h
// this takes ownership of |data_reader|.
std::unique_ptr<ObjectsResource_InsertMethod> request(
impl_->storage_service_->get_objects().NewInsertMethod(
&(impl_->oauth_credential_), bucket, nullptr, mime_type.c_str(),
reinterpret_cast<DataReader*>(data_reader)));
request->set_name(path);
request->mutable_http_request()->mutable_options()->set_timeout_ms(
1000 * timeout_seconds);
// Execute the request.
Json::Value value;
google_storage_api::Object response(&value);
auto status = request->ExecuteAndParseResponse(&response);
if (status.ok()) {
return true;
}
LOG_STACKDRIVER_COUNT_METRIC(ERROR, kUploadFailure)
<< "Error attempting upload: " << status.ToString();
return false;
}
bool GcsUtil::Ping(const std::string& bucket) {
// Construct the request.
std::unique_ptr<BucketsResource_GetMethod> request(
impl_->storage_service_->get_buckets().NewGetMethod(
&(impl_->oauth_credential_), bucket));
// Execute the request.
auto status = request->Execute();
if (status.ok()) {
return true;
}
LOG_STACKDRIVER_COUNT_METRIC(ERROR, kPingFailure)
<< "Error attempting to ping bucket: " << status.ToString();
return false;
}
} // namespace gcs
} // namespace util
} // namespace cobalt