diff --git a/google/cloud/spanner/CMakeLists.txt b/google/cloud/spanner/CMakeLists.txt index 079a2f45ab7fe..a3a61f64f170d 100644 --- a/google/cloud/spanner/CMakeLists.txt +++ b/google/cloud/spanner/CMakeLists.txt @@ -18,8 +18,10 @@ include(GoogleapisConfig) set(DOXYGEN_PROJECT_NAME "Google Cloud Spanner C++ Client") set(DOXYGEN_PROJECT_BRIEF "A C++ Client Library for Google Cloud Spanner") set(DOXYGEN_PROJECT_NUMBER "${PROJECT_VERSION}") -set(DOXYGEN_EXAMPLE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/samples - ${CMAKE_CURRENT_SOURCE_DIR}/quickstart) +set(DOXYGEN_EXAMPLE_PATH + ${CMAKE_CURRENT_SOURCE_DIR}/samples + ${CMAKE_CURRENT_SOURCE_DIR}/admin/samples + ${CMAKE_CURRENT_SOURCE_DIR}/quickstart) set(DOXYGEN_EXCLUDE_SYMBOLS "internal" "spanner_admin_internal" "spanner_internal" "spanner_testing" "examples") diff --git a/google/cloud/spanner/doc/spanner-main.dox b/google/cloud/spanner/doc/spanner-main.dox index d01cafc1e3773..bf496c9a85bf9 100644 --- a/google/cloud/spanner/doc/spanner-main.dox +++ b/google/cloud/spanner/doc/spanner-main.dox @@ -49,11 +49,7 @@ the Cloud Spanner C++ client library API. @include quickstart.cc -## API Notes - -The following are general notes about using the library. - -### Environment Variables +## Environment Variables There are several environment variables that can be set to configure certain behaviors in the library. @@ -134,6 +130,36 @@ there is no value. @snippet samples.cc example-status-or +## Override the default endpoint + +In some cases, you may need to override the default endpoint used by the client +library. Use the `google::cloud::EndpointOption` when initializing the client +library to change this default. For example, this will override the default +endpoint for `google::cloud::spanner::Client`: + +@snippet client_samples.cc set-client-endpoint + +Follow these links for additional examples + [DatabaseAdminClient](@ref DatabaseAdminClient-set-endpoint-snippet) + [InstanceAdminClient](@ref InstanceAdminClient-set-endpoint-snippet) + +## Override the authentication configuration + +Some applications cannot use the default authentication mechanism (known as +[Application Default Credentials]). You can override this default using +`google::cloud::UnifiedCredentialsOption`. The following example shows how +to explicitly load a service account key file. + +@snippet client_samples.cc with-service-account + +Keep in mind that we chose this as an example because it is relatively easy to +understand. Consult the [Best practices for managing service account keys] +guide for more details. + +Follow these links for additional examples + [DatabaseAdminClient](@ref DatabaseAdminClient-with-service-account-snippet) + [InstanceAdminClient](@ref InstanceAdminClient-with-service-account-snippet) + ## Retry, Backoff, and Idempotency Policies. The library automatically retries requests that fail with transient errors, and @@ -169,3 +195,39 @@ period between retries. [status-or-header]: https://github.com/googleapis/google-cloud-cpp/blob/main/google/cloud/status_or.h */ + +/*! @page Client-set-endpoint-snippet Override Client Default Endpoint + +@snippet google/cloud/spanner/samples/client_samples.cc set-client-endpoint + +*/ + +/*! @page Client-with-service-account-snippet Override Client Default Authentication + +@snippet google/cloud/spanner/samples/client_samples.cc with-service-account + +*/ + +/*! @page DatabaseAdminClient-set-endpoint-snippet Override DatabaseAdminClient Default Endpoint + +@snippet google/cloud/spanner/admin/samples/database_admin_client_samples.cc set-client-endpoint + +*/ + +/*! @page DatabaseAdminClient-with-service-account-snippet Override DatabaseAdminClient Default Authentication + +@snippet google/cloud/spanner/admin/samples/database_admin_client_samples.cc with-service-account + +*/ + +/*! @page InstanceAdminClient-set-endpoint-snippet Override InstanceAdminClient Default Endpoint + +@snippet google/cloud/spanner/admin/samples/instance_admin_client_samples.cc set-client-endpoint + +*/ + +/*! @page InstanceAdminClient-with-service-account-snippet Override InstanceAdminClient Default Authentication + +@snippet google/cloud/spanner/admin/samples/instance_admin_client_samples.cc with-service-account + +*/ diff --git a/google/cloud/spanner/samples/CMakeLists.txt b/google/cloud/spanner/samples/CMakeLists.txt index e6883aedd0bcd..42f9bd5f047ad 100644 --- a/google/cloud/spanner/samples/CMakeLists.txt +++ b/google/cloud/spanner/samples/CMakeLists.txt @@ -15,8 +15,9 @@ # ~~~ function (spanner_client_define_samples) - set(spanner_client_integration_samples # cmake-format: sort - postgresql_samples.cc samples.cc) + set(spanner_client_integration_samples + # cmake-format: sort + client_samples.cc postgresql_samples.cc samples.cc) set(spanner_client_unit_samples # cmake-format: sort mock_execute_query.cc) diff --git a/google/cloud/spanner/samples/client_samples.cc b/google/cloud/spanner/samples/client_samples.cc new file mode 100644 index 0000000000000..e11d4a437a5b0 --- /dev/null +++ b/google/cloud/spanner/samples/client_samples.cc @@ -0,0 +1,112 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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 "google/cloud/spanner/admin/database_admin_client.h" +#include "google/cloud/spanner/admin/instance_admin_client.h" +#include "google/cloud/spanner/client.h" +#include "google/cloud/spanner/testing/random_database_name.h" +#include "google/cloud/spanner/testing/random_instance_name.h" +#include "google/cloud/credentials.h" +#include "google/cloud/internal/getenv.h" +#include "google/cloud/internal/random.h" +#include "google/cloud/testing_util/example_driver.h" +#include +#include +#include + +namespace { + +void SetClientEndpoint(std::vector const& argv) { + if (argv.size() != 3) { + throw google::cloud::testing_util::Usage{ + "set-client-endpoint "}; + } + //! [set-client-endpoint] + namespace spanner = ::google::cloud::spanner; + [](std::string const& project_id, std::string const& instance_id, + std::string const& database_id) { + auto options = google::cloud::Options{}.set( + "private.googleapis.com"); + return spanner::Client(spanner::MakeConnection( + spanner::Database(project_id, instance_id, database_id), options)); + } + //! [set-client-endpoint] + (argv.at(0), argv.at(1), argv.at(2)); +} + +void WithServiceAccount(std::vector const& argv) { + if (argv.size() != 4) { + throw google::cloud::testing_util::Usage{ + "with-service-account " + ""}; + } + //! [with-service-account] + namespace spanner = ::google::cloud::spanner; + [](std::string const& project_id, std::string const& instance_id, + std::string const& database_id, std::string const& keyfile) { + auto is = std::ifstream(keyfile); + is.exceptions(std::ios::badbit); + auto contents = std::string(std::istreambuf_iterator(is.rdbuf()), {}); + auto options = + google::cloud::Options{}.set( + google::cloud::MakeServiceAccountCredentials(contents)); + return spanner::Client(spanner::MakeConnection( + spanner::Database(project_id, instance_id, database_id), options)); + } + //! [with-service-account] + (argv.at(0), argv.at(1), argv.at(2), argv.at(3)); +} + +void AutoRun(std::vector const& argv) { + using ::google::cloud::internal::GetEnv; + namespace examples = ::google::cloud::testing_util; + + if (!argv.empty()) throw examples::Usage{"auto"}; + examples::CheckEnvironmentVariablesAreSet({ + "GOOGLE_CLOUD_PROJECT", + "GOOGLE_CLOUD_CPP_TEST_SERVICE_ACCOUNT_KEYFILE", + }); + auto const emulator = GetEnv("SPANNER_EMULATOR_HOST").has_value(); + auto const project_id = GetEnv("GOOGLE_CLOUD_PROJECT").value(); + auto generator = google::cloud::internal::DefaultPRNG(std::random_device{}()); + auto const instance_id = + google::cloud::spanner_testing::RandomInstanceName(generator); + auto const database_id = + google::cloud::spanner_testing::RandomDatabaseName(generator); + auto const keyfile = + GetEnv("GOOGLE_CLOUD_CPP_TEST_SERVICE_ACCOUNT_KEYFILE").value(); + + std::cout << "\nRunning SetClientEndpoint() example" << std::endl; + SetClientEndpoint({project_id, instance_id, database_id}); + + if (!emulator) { + // On the emulator this blocks, as the emulator does not support credentials + // that require SSL. + std::cout << "\nRunning WithServiceAccount() example" << std::endl; + WithServiceAccount({project_id, instance_id, database_id, keyfile}); + } + + std::cout << "\nAutoRun done" << std::endl; +} + +} // namespace + +int main(int argc, char* argv[]) { // NOLINT(bugprone-exception-escape) + google::cloud::testing_util::Example example({ + {"set-client-endpoint", SetClientEndpoint}, + {"with-service-account", WithServiceAccount}, + {"auto", AutoRun}, + }); + return example.Run(argc, argv); +} diff --git a/google/cloud/spanner/samples/spanner_client_integration_samples.bzl b/google/cloud/spanner/samples/spanner_client_integration_samples.bzl index 67dba42eac42e..eb7a284f9f4ae 100644 --- a/google/cloud/spanner/samples/spanner_client_integration_samples.bzl +++ b/google/cloud/spanner/samples/spanner_client_integration_samples.bzl @@ -17,6 +17,7 @@ """Automatically generated unit tests list - DO NOT EDIT.""" spanner_client_integration_samples = [ + "client_samples.cc", "postgresql_samples.cc", "samples.cc", ]