Skip to content

Commit 0db8bbf

Browse files
authored
feat: add utilities to detect gcp (#14655)
* feat: add utilities to detect gcp spacing address feedback wip add windows header fix header do not initialize variable from getenv do not initialize variable from getenv use DWORD typings cast fix win32 test split logic across separate files use public / private methods fixes add additional windows tests * run checkers * simplify number of classes * win32 fixes * tidy fixes * remove stdlib.h * do not search root namespace * address feedback * use move on config * return statusor, remove unused headers, move declaration * fix status code * fix namespaces * use make_status helpers, log failures * fix win32 build * checkers
1 parent c6cc910 commit 0db8bbf

File tree

7 files changed

+436
-0
lines changed

7 files changed

+436
-0
lines changed

google/cloud/google_cloud_cpp_common.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ google_cloud_cpp_common_hdrs = [
4444
"internal/credentials_impl.h",
4545
"internal/debug_future_status.h",
4646
"internal/debug_string.h",
47+
"internal/detect_gcp.h",
48+
"internal/detect_gcp_impl.h",
4749
"internal/diagnostics_pop.inc",
4850
"internal/diagnostics_push.inc",
4951
"internal/disable_deprecation_warnings.inc",
@@ -126,6 +128,7 @@ google_cloud_cpp_common_srcs = [
126128
"internal/credentials_impl.cc",
127129
"internal/debug_future_status.cc",
128130
"internal/debug_string.cc",
131+
"internal/detect_gcp_impl.cc",
129132
"internal/error_context.cc",
130133
"internal/filesystem.cc",
131134
"internal/format_time_point.cc",

google/cloud/google_cloud_cpp_common.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ add_library(
6969
internal/debug_future_status.h
7070
internal/debug_string.cc
7171
internal/debug_string.h
72+
internal/detect_gcp.h
73+
internal/detect_gcp_impl.cc
74+
internal/detect_gcp_impl.h
7275
internal/diagnostics_pop.inc
7376
internal/diagnostics_push.inc
7477
internal/disable_deprecation_warnings.inc
@@ -366,6 +369,7 @@ if (BUILD_TESTING)
366369
internal/credentials_impl_test.cc
367370
internal/debug_future_status_test.cc
368371
internal/debug_string_test.cc
372+
internal/detect_gcp_test.cc
369373
internal/error_context_test.cc
370374
internal/filesystem_test.cc
371375
internal/format_time_point_test.cc

google/cloud/google_cloud_cpp_common_unit_tests.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ google_cloud_cpp_common_unit_tests = [
3636
"internal/credentials_impl_test.cc",
3737
"internal/debug_future_status_test.cc",
3838
"internal/debug_string_test.cc",
39+
"internal/detect_gcp_test.cc",
3940
"internal/error_context_test.cc",
4041
"internal/filesystem_test.cc",
4142
"internal/format_time_point_test.cc",

google/cloud/internal/detect_gcp.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_DETECT_GCP_H
16+
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_DETECT_GCP_H
17+
18+
#include "google/cloud/version.h"
19+
#include <memory>
20+
#include <string>
21+
22+
namespace google {
23+
namespace cloud {
24+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
25+
namespace internal {
26+
27+
/**
28+
* Interface for attempting to detect if running in a Google Cloud Platform
29+
* (GCP) environment.
30+
*
31+
* This code is split across WIN32 and other as the detection logic differs
32+
* slightly due to needing to make platform specific calls.
33+
*/
34+
class GcpDetector {
35+
public:
36+
virtual ~GcpDetector() = default;
37+
virtual bool IsGoogleCloudBios() = 0;
38+
virtual bool IsGoogleCloudServerless() = 0;
39+
};
40+
41+
std::shared_ptr<GcpDetector> MakeGcpDetector();
42+
43+
} // namespace internal
44+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
45+
} // namespace cloud
46+
} // namespace google
47+
48+
#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_DETECT_GCP_H
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/internal/detect_gcp_impl.h"
16+
#include "google/cloud/internal/filesystem.h"
17+
#include "google/cloud/internal/getenv.h"
18+
#include "google/cloud/internal/make_status.h"
19+
#include "google/cloud/log.h"
20+
#include "absl/strings/ascii.h"
21+
#include "absl/strings/string_view.h"
22+
#include <algorithm>
23+
#include <fstream>
24+
#include <iostream>
25+
#include <memory>
26+
#include <string>
27+
#include <vector>
28+
#ifdef _WIN32
29+
#include <winreg.h>
30+
#include <wtypes.h>
31+
#endif
32+
33+
namespace google {
34+
namespace cloud {
35+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
36+
namespace internal {
37+
38+
StatusOr<std::string> GcpDetectorImpl::GetBiosInformation() const {
39+
#ifdef _WIN32
40+
DWORD size{};
41+
LONG result = RegGetValueA(config_.key, config_.sub_key.c_str(),
42+
config_.value_key.c_str(), RRF_RT_REG_SZ, nullptr,
43+
nullptr, &size);
44+
45+
if (result != ERROR_SUCCESS) {
46+
return UnknownError(
47+
"error querying registry",
48+
GCP_ERROR_INFO()
49+
.WithMetadata("key", std::to_string((ULONG_PTR)config_.key))
50+
.WithMetadata("sub_key", config_.sub_key)
51+
.WithMetadata("value_key", config_.value_key)
52+
.WithMetadata("win32_error_code", std::to_string(result)));
53+
}
54+
55+
std::string contents;
56+
contents.resize(size / sizeof(char));
57+
result = RegGetValueA(config_.key, config_.sub_key.c_str(),
58+
config_.value_key.c_str(), RRF_RT_REG_SZ, nullptr,
59+
&contents[0], &size);
60+
61+
if (result != ERROR_SUCCESS) {
62+
return UnknownError(
63+
"error querying registry",
64+
GCP_ERROR_INFO()
65+
.WithMetadata("key", std::to_string((ULONG_PTR)config_.key))
66+
.WithMetadata("sub_key", config_.sub_key)
67+
.WithMetadata("value_key", config_.value_key)
68+
.WithMetadata("win32_error_code", std::to_string(result)));
69+
}
70+
71+
DWORD content_length = size / sizeof(char);
72+
content_length--; // Exclude NUL written by WIN32
73+
contents.resize(content_length);
74+
75+
return contents;
76+
#else // _WIN32
77+
auto product_name_status = status(config_.path);
78+
if (!exists(product_name_status)) {
79+
return NotFoundError("file does not exist", GCP_ERROR_INFO().WithMetadata(
80+
"filename", config_.path));
81+
}
82+
83+
std::ifstream product_name_file(config_.path);
84+
std::string contents;
85+
if (!product_name_file.is_open()) {
86+
return UnknownError("unable to open file", GCP_ERROR_INFO().WithMetadata(
87+
"filename", config_.path));
88+
}
89+
90+
std::getline(product_name_file, contents);
91+
product_name_file.close();
92+
93+
return contents;
94+
#endif
95+
}
96+
97+
bool GcpDetectorImpl::IsGoogleCloudBios() {
98+
auto status = GetBiosInformation();
99+
if (!status.ok()) {
100+
GCP_LOG(WARNING) << status.status();
101+
return false;
102+
}
103+
104+
auto bios_information = status.value();
105+
absl::StripAsciiWhitespace(&bios_information);
106+
107+
return bios_information == "Google" ||
108+
bios_information == "Google Compute Engine";
109+
}
110+
111+
bool GcpDetectorImpl::IsGoogleCloudServerless() {
112+
return std::any_of(config_.env_variables.begin(), config_.env_variables.end(),
113+
[](std::string const& env_var) {
114+
return GetEnv(env_var.c_str()).has_value();
115+
});
116+
}
117+
118+
std::shared_ptr<GcpDetector> MakeGcpDetector() {
119+
auto config = GcpDetectorImpl::GcpDetectorConfig{};
120+
config.env_variables = {"CLOUD_RUN_JOB", "FUNCTION_NAME", "K_SERVICE"};
121+
#ifdef _WIN32
122+
config.key = HKEY_LOCAL_MACHINE;
123+
config.sub_key = "SYSTEM\\HardwareConfig\\Current";
124+
config.value_key = "SystemProductName";
125+
#else // _WIN32
126+
config.path = "/sys/class/dmi/id/product_name";
127+
#endif
128+
129+
return std::make_shared<GcpDetectorImpl>(config);
130+
}
131+
132+
} // namespace internal
133+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
134+
} // namespace cloud
135+
} // namespace google
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_DETECT_GCP_IMPL_H
16+
#define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_DETECT_GCP_IMPL_H
17+
18+
#include "google/cloud/internal/detect_gcp.h"
19+
#include "google/cloud/status_or.h"
20+
#include "google/cloud/version.h"
21+
#include <memory>
22+
#include <string>
23+
#include <vector>
24+
#ifdef _WIN32
25+
#include <wtypes.h>
26+
#endif
27+
28+
namespace google {
29+
namespace cloud {
30+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
31+
namespace internal {
32+
33+
class GcpDetectorImpl : public GcpDetector {
34+
public:
35+
#ifdef _WIN32
36+
struct GcpDetectorConfig {
37+
HKEY key;
38+
std::string sub_key;
39+
std::string value_key;
40+
std::vector<std::string> env_variables;
41+
};
42+
#else // _WIN32
43+
struct GcpDetectorConfig {
44+
std::string path;
45+
std::vector<std::string> env_variables;
46+
};
47+
#endif
48+
explicit GcpDetectorImpl(GcpDetectorConfig config)
49+
: config_(std::move(config)) {};
50+
bool IsGoogleCloudBios() override;
51+
bool IsGoogleCloudServerless() override;
52+
53+
private:
54+
StatusOr<std::string> GetBiosInformation() const;
55+
GcpDetectorConfig config_;
56+
};
57+
58+
} // namespace internal
59+
GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
60+
} // namespace cloud
61+
} // namespace google
62+
63+
#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_DETECT_GCP_IMPL_H

0 commit comments

Comments
 (0)