blob: bb732fd825b1d03b7e64500e4d04dcd0d87640b4 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "analyzer/store/bigtable_admin.h"
#include <glog/logging.h>
#include <google/bigtable/admin/v2/bigtable_table_admin.grpc.pb.h>
#include <utility>
#include "analyzer/store/bigtable_flags.h"
#include "analyzer/store/bigtable_names.h"
namespace cobalt {
namespace analyzer {
namespace store {
using google::bigtable::admin::v2::BigtableTableAdmin;
using google::bigtable::admin::v2::ColumnFamily;
using google::bigtable::admin::v2::CreateTableRequest;
using google::bigtable::admin::v2::GetTableRequest;
using grpc::ClientContext;
typedef google::bigtable::admin::v2::Table BtTable;
std::shared_ptr<BigtableAdmin> BigtableAdmin::CreateFromFlagsOrDie() {
// See https://developers.google.com/identity/protocols/ \
// application-default-credentials
// for an explanation of grpc::GoogleDefaultCredentials(). When running
// on GKE this should cause the service account to be used. When running
// on a developer's machine this might either use the user's oauth credentials
// or a service account if the user has installed one. To use a service
// account the library looks for a key file located at the path specified in
// the environment variable GOOGLE_APPLICATION_CREDENTIALS.
CHECK_NE("", FLAGS_bigtable_project_name);
CHECK_NE("", FLAGS_bigtable_instance_id);
auto creds = grpc::GoogleDefaultCredentials();
CHECK(creds);
LOG(INFO) << "Connecting to CloudBigtable admin API at "
<< kCloudBigtableAdminUri;
return std::shared_ptr<BigtableAdmin>(new BigtableAdmin(
kCloudBigtableAdminUri, creds, FLAGS_bigtable_project_name,
FLAGS_bigtable_instance_id));
}
BigtableAdmin::BigtableAdmin(
const std::string& uri,
std::shared_ptr<grpc::ChannelCredentials> credentials,
std::string project_name, std::string instance_id)
: channel_(grpc::CreateChannel(uri, credentials)),
stub_(BigtableTableAdmin::NewStub(channel_)),
project_name_(std::move(project_name)),
instance_id_(std::move(instance_id)) {}
bool BigtableAdmin::WaitForConnected(
std::chrono::system_clock::time_point deadline) {
return channel_->WaitForConnected(deadline);
}
bool BigtableAdmin::CreateTablesIfNecessary() {
return CreateTableIfNecessary(kObservationsTableId) &&
CreateTableIfNecessary(kReportMetadataTableId) &&
CreateTableIfNecessary(kReportRowsTableId);
}
bool BigtableAdmin::CreateTableIfNecessary(std::string table_id) {
ClientContext get_ctx;
GetTableRequest get_req;
BtTable get_resp;
get_req.set_name(
BigtableNames::FullTableName(project_name_, instance_id_, table_id));
// If the table exists, do nothing.
grpc::Status get_s = stub_->GetTable(&get_ctx, get_req, &get_resp);
if (get_s.ok()) {
return true;
}
// Otherwise, create the table.
CreateTableRequest create_req;
BtTable create_resp;
create_req.set_parent(
BigtableNames::TableParentName(project_name_, instance_id_));
create_req.set_table_id(std::move(table_id));
// Set up columns.
BtTable* create_req_tab = create_req.mutable_table();
(*create_req_tab->mutable_column_families())[kDataColumnFamilyName] =
ColumnFamily();
// Do the request.
ClientContext create_ctx;
grpc::Status create_s =
stub_->CreateTable(&create_ctx, create_req, &create_resp);
if (!create_s.ok() && create_s.error_code() != grpc::ALREADY_EXISTS) {
std::string error_message = create_s.error_message();
// In practice it appears that the Bigtable Emulator does not in fact
// return the appropriate error code, ALREADY_EXISTS, but rather returns
// UNKNOWN. We found that we are able to detect the problem in this case
// by looking for the text 'already exists'.
if (error_message.find("already exists") == std::string::npos) {
LOG(ERROR) << "Can't create table: " << create_s.error_message()
<< " error code=" << create_s.error_code();
return false;
}
}
return true;
}
} // namespace store
} // namespace analyzer
} // namespace cobalt