bring back original network test for metadata server detection

pull/17398/head
Yihua Zhang 6 years ago
parent 9bd5b3778a
commit 750e80ea1c
  1. 134
      src/core/lib/security/credentials/google_default/google_default_credentials.cc
  2. 76
      test/core/security/credentials_test.cc

@ -49,9 +49,11 @@
/* -- Default credentials. -- */
static int g_compute_engine_detection_done = 0;
static int g_need_compute_engine_creds = 0;
static int g_metadata_server_detection_done = 0;
static int g_metadata_server_available = 0;
static int g_is_on_gce = 0;
static gpr_mu g_state_mu;
static gpr_mu* g_polling_mu;
static gpr_once g_once = GPR_ONCE_INIT;
static grpc_core::internal::grpc_gce_tenancy_checker g_gce_tenancy_checker =
grpc_alts_is_running_on_gcp;
@ -89,15 +91,20 @@ static grpc_security_status google_default_create_security_connector(
bool use_alts =
is_grpclb_load_balancer || is_backend_from_grpclb_load_balancer;
grpc_security_status status = GRPC_SECURITY_ERROR;
/* Return failure if ALTS is selected but not running on GCE. */
if (use_alts && !g_is_on_gce) {
goto end;
}
status = use_alts ? c->alts_creds->vtable->create_security_connector(
c->alts_creds, call_creds, target, args, sc, new_args)
: c->ssl_creds->vtable->create_security_connector(
c->ssl_creds, call_creds, target, args, sc, new_args);
/* grpclb-specific channel args are removed from the channel args set
* to ensure backends and fallback adresses will have the same set of channel
* args. By doing that, it guarantees the connections to backends will not be
* torn down and re-connected when switching in and out of fallback mode.
*/
/* grpclb-specific channel args are removed from the channel args set
* to ensure backends and fallback adresses will have the same set of channel
* args. By doing that, it guarantees the connections to backends will not be
* torn down and re-connected when switching in and out of fallback mode.
*/
end:
if (use_alts) {
static const char* args_to_remove[] = {
GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER,
@ -113,6 +120,93 @@ static grpc_channel_credentials_vtable google_default_credentials_vtable = {
google_default_credentials_destruct,
google_default_create_security_connector, nullptr};
static void on_metadata_server_detection_http_response(void* user_data,
grpc_error* error) {
compute_engine_detector* detector =
static_cast<compute_engine_detector*>(user_data);
if (error == GRPC_ERROR_NONE && detector->response.status == 200 &&
detector->response.hdr_count > 0) {
/* Internet providers can return a generic response to all requests, so
it is necessary to check that metadata header is present also. */
size_t i;
for (i = 0; i < detector->response.hdr_count; i++) {
grpc_http_header* header = &detector->response.hdrs[i];
if (strcmp(header->key, "Metadata-Flavor") == 0 &&
strcmp(header->value, "Google") == 0) {
detector->success = 1;
break;
}
}
}
gpr_mu_lock(g_polling_mu);
detector->is_done = 1;
GRPC_LOG_IF_ERROR(
"Pollset kick",
grpc_pollset_kick(grpc_polling_entity_pollset(&detector->pollent),
nullptr));
gpr_mu_unlock(g_polling_mu);
}
static void destroy_pollset(void* p, grpc_error* e) {
grpc_pollset_destroy(static_cast<grpc_pollset*>(p));
}
static int is_metadata_server_reachable() {
compute_engine_detector detector;
grpc_httpcli_request request;
grpc_httpcli_context context;
grpc_closure destroy_closure;
/* The http call is local. If it takes more than one sec, it is for sure not
on compute engine. */
grpc_millis max_detection_delay = GPR_MS_PER_SEC;
grpc_pollset* pollset =
static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
grpc_pollset_init(pollset, &g_polling_mu);
detector.pollent = grpc_polling_entity_create_from_pollset(pollset);
detector.is_done = 0;
detector.success = 0;
memset(&detector.response, 0, sizeof(detector.response));
memset(&request, 0, sizeof(grpc_httpcli_request));
request.host = (char*)GRPC_COMPUTE_ENGINE_DETECTION_HOST;
request.http.path = (char*)"/";
grpc_httpcli_context_init(&context);
grpc_resource_quota* resource_quota =
grpc_resource_quota_create("google_default_credentials");
grpc_httpcli_get(
&context, &detector.pollent, resource_quota, &request,
grpc_core::ExecCtx::Get()->Now() + max_detection_delay,
GRPC_CLOSURE_CREATE(on_metadata_server_detection_http_response, &detector,
grpc_schedule_on_exec_ctx),
&detector.response);
grpc_resource_quota_unref_internal(resource_quota);
grpc_core::ExecCtx::Get()->Flush();
/* Block until we get the response. This is not ideal but this should only be
called once for the lifetime of the process by the default credentials. */
gpr_mu_lock(g_polling_mu);
while (!detector.is_done) {
grpc_pollset_worker* worker = nullptr;
if (!GRPC_LOG_IF_ERROR(
"pollset_work",
grpc_pollset_work(grpc_polling_entity_pollset(&detector.pollent),
&worker, GRPC_MILLIS_INF_FUTURE))) {
detector.is_done = 1;
detector.success = 0;
}
}
gpr_mu_unlock(g_polling_mu);
grpc_httpcli_context_destroy(&context);
GRPC_CLOSURE_INIT(&destroy_closure, destroy_pollset,
grpc_polling_entity_pollset(&detector.pollent),
grpc_schedule_on_exec_ctx);
grpc_pollset_shutdown(grpc_polling_entity_pollset(&detector.pollent),
&destroy_closure);
g_polling_mu = nullptr;
grpc_core::ExecCtx::Get()->Flush();
gpr_free(grpc_polling_entity_pollset(&detector.pollent));
grpc_http_response_destroy(&detector.response);
return detector.success;
}
/* Takes ownership of creds_path if not NULL. */
static grpc_error* create_default_creds_from_path(
char* creds_path, grpc_call_credentials** creds) {
@ -182,7 +276,6 @@ grpc_channel_credentials* grpc_google_default_credentials_create(void) {
grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Failed to create Google credentials");
grpc_error* err;
int need_compute_engine_creds = 0;
grpc_core::ExecCtx exec_ctx;
GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
@ -202,16 +295,25 @@ grpc_channel_credentials* grpc_google_default_credentials_create(void) {
error = grpc_error_add_child(error, err);
gpr_mu_lock(&g_state_mu);
/* At last try to see if we're on compute engine (do the detection only once
since it requires a network test). */
if (!g_compute_engine_detection_done) {
g_need_compute_engine_creds = g_gce_tenancy_checker();
g_compute_engine_detection_done = 1;
/* Try a platform-provided hint for GCE. */
if (!g_metadata_server_detection_done) {
g_is_on_gce = g_gce_tenancy_checker();
g_metadata_server_detection_done = g_is_on_gce;
g_metadata_server_available = g_is_on_gce;
}
/* TODO: Add a platform-provided hint for GAE. */
/* Do a network test for metadata server. */
if (!g_metadata_server_detection_done) {
bool detected = is_metadata_server_reachable();
/* Do not cache detecion result if netowrk test returns false. */
g_metadata_server_detection_done = detected;
g_metadata_server_available = detected;
}
need_compute_engine_creds = g_need_compute_engine_creds;
gpr_mu_unlock(&g_state_mu);
if (need_compute_engine_creds) {
if (g_metadata_server_available) {
call_creds = grpc_google_compute_engine_credentials_create(nullptr);
if (call_creds == nullptr) {
error = grpc_error_add_child(
@ -259,7 +361,7 @@ void grpc_flush_cached_google_default_credentials(void) {
grpc_core::ExecCtx exec_ctx;
gpr_once_init(&g_once, init_default_credentials);
gpr_mu_lock(&g_state_mu);
g_compute_engine_detection_done = 0;
g_metadata_server_detection_done = 0;
gpr_mu_unlock(&g_state_mu);
}

@ -919,6 +919,22 @@ static void test_google_default_creds_refresh_token(void) {
gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
}
static int default_creds_metadata_server_detection_httpcli_get_success_override(
const grpc_httpcli_request* request, grpc_millis deadline,
grpc_closure* on_done, grpc_httpcli_response* response) {
*response = http_response(200, "");
grpc_http_header* headers =
static_cast<grpc_http_header*>(gpr_malloc(sizeof(*headers) * 1));
headers[0].key = gpr_strdup("Metadata-Flavor");
headers[0].value = gpr_strdup("Google");
response->hdr_count = 1;
response->hdrs = headers;
GPR_ASSERT(strcmp(request->http.path, "/") == 0);
GPR_ASSERT(strcmp(request->host, "metadata.google.internal") == 0);
GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE);
return 1;
}
static char* null_well_known_creds_path_getter(void) { return nullptr; }
static bool test_gce_tenancy_checker(void) {
@ -963,26 +979,73 @@ static void test_google_default_creds_gce(void) {
grpc_override_well_known_credentials_path_getter(nullptr);
}
static void test_no_google_default_creds(void) {
static void test_google_default_creds_non_gce(void) {
grpc_core::ExecCtx exec_ctx;
expected_md emd[] = {
{"authorization", "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"}};
request_metadata_state* state =
make_request_metadata_state(GRPC_ERROR_NONE, emd, GPR_ARRAY_SIZE(emd));
grpc_auth_metadata_context auth_md_ctx = {test_service_url, test_method,
nullptr, nullptr};
grpc_flush_cached_google_default_credentials();
gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
grpc_override_well_known_credentials_path_getter(
null_well_known_creds_path_getter);
set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
g_test_gce_tenancy_checker_called = false;
g_test_is_on_gce = false;
/* Simulate a successful detection of metadata server. */
grpc_httpcli_set_override(
default_creds_metadata_server_detection_httpcli_get_success_override,
httpcli_post_should_not_be_called);
grpc_composite_channel_credentials* creds =
reinterpret_cast<grpc_composite_channel_credentials*>(
grpc_google_default_credentials_create());
/* Verify that the default creds actually embeds a GCE creds. */
GPR_ASSERT(creds != nullptr);
GPR_ASSERT(creds->call_creds != nullptr);
grpc_httpcli_set_override(compute_engine_httpcli_get_success_override,
httpcli_post_should_not_be_called);
run_request_metadata_test(creds->call_creds, auth_md_ctx, state);
grpc_core::ExecCtx::Get()->Flush();
GPR_ASSERT(g_test_gce_tenancy_checker_called == true);
/* Cleanup. */
grpc_channel_credentials_unref(&creds->base);
grpc_httpcli_set_override(nullptr, nullptr);
grpc_override_well_known_credentials_path_getter(nullptr);
}
static int default_creds_gce_detection_httpcli_get_failure_override(
const grpc_httpcli_request* request, grpc_millis deadline,
grpc_closure* on_done, grpc_httpcli_response* response) {
/* No magic header. */
GPR_ASSERT(strcmp(request->http.path, "/") == 0);
GPR_ASSERT(strcmp(request->host, "metadata.google.internal") == 0);
*response = http_response(200, "");
GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE);
return 1;
}
static void test_no_google_default_creds(void) {
grpc_flush_cached_google_default_credentials();
gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, ""); /* Reset. */
grpc_override_well_known_credentials_path_getter(
null_well_known_creds_path_getter);
set_gce_tenancy_checker_for_testing(test_gce_tenancy_checker);
g_test_gce_tenancy_checker_called = false;
g_test_is_on_gce = false;
grpc_httpcli_set_override(
default_creds_gce_detection_httpcli_get_failure_override,
httpcli_post_should_not_be_called);
/* Simulate a successful detection of GCE. */
GPR_ASSERT(grpc_google_default_credentials_create() == nullptr);
/* Try a second one. GCE detection should not occur anymore. */
/* Try a second one. GCE detection should occur again. */
g_test_gce_tenancy_checker_called = false;
GPR_ASSERT(grpc_google_default_credentials_create() == nullptr);
GPR_ASSERT(g_test_gce_tenancy_checker_called == false);
GPR_ASSERT(g_test_gce_tenancy_checker_called == true);
/* Cleanup. */
grpc_override_well_known_credentials_path_getter(nullptr);
grpc_httpcli_set_override(nullptr, nullptr);
}
typedef enum {
@ -1233,6 +1296,7 @@ int main(int argc, char** argv) {
test_google_default_creds_auth_key();
test_google_default_creds_refresh_token();
test_google_default_creds_gce();
test_google_default_creds_non_gce();
test_no_google_default_creds();
test_metadata_plugin_success();
test_metadata_plugin_failure();

Loading…
Cancel
Save