| // 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 |