diff --git a/.gitmodules b/.gitmodules index 4aab48e6..fca60520 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,8 @@ path = lib/googletest url = https://github.com/google/googletest ignore = dirty +[submodule "lib/opencensus-cpp"] + path = lib/opencensus-cpp + url = https://github.com/census-instrumentation/opencensus-cpp + ignore = dirty + diff --git a/lib/opencensus-cpp b/lib/opencensus-cpp new file mode 160000 index 00000000..f449234c --- /dev/null +++ b/lib/opencensus-cpp @@ -0,0 +1 @@ +Subproject commit f449234c435100413fad96608c6e9aeae7f07b5e diff --git a/src/.gitignore b/src/.gitignore index 4dee5759..def7fad5 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,4 +1,5 @@ init-submodules build-cpp-netlib build-yaml-cpp +build-opencensus-cpp metadatad diff --git a/src/Makefile b/src/Makefile index 9fd40c7f..7e2b21af 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,7 +23,15 @@ NETWORK_URI_DIR=$(CPP_NETLIB_DIR)/deps/uri NETWORK_URI_LIBDIR=$(NETWORK_URI_DIR)/src YAML_CPP_DIR=$(LIBDIR)/yaml-cpp YAML_CPP_LIBDIR=$(YAML_CPP_DIR) -SUBMODULE_DIRS=$(CPP_NETLIB_DIR) $(YAML_CPP_DIR) +OPENCENSUS_CPP_DIR=$(LIBDIR)/opencensus-cpp +OPENCENSUS_CPP_OPENCENSUS_DIR=$(OPENCENSUS_CPP_DIR)/opencensus +OPENCENSUS_CPP_OPENCENSUS_LIBDIR=$(OPENCENSUS_CPP_OPENCENSUS_DIR) +OPENCENSUS_CPP_ABSL_INCLUDEDIR=$(OPENCENSUS_CPP_DIR)/abseil-src +OPENCENSUS_CPP_ABSL_LIBDIR=$(OPENCENSUS_CPP_DIR)/abseil-build/absl +OPENCENSUS_CPP_PROMETHEUS_CPP_DIR=$(OPENCENSUS_CPP_DIR)/prometheus-src +OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_DIR=$(OPENCENSUS_CPP_PROMETHEUS_CPP_DIR)/core +OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_LIBDIR=$(OPENCENSUS_CPP_DIR)/prometheus-build/core +SUBMODULE_DIRS=$(CPP_NETLIB_DIR) $(YAML_CPP_DIR) $(OPENCENSUS_CPP_DIR) GIT=git GIT_VERSION=$(shell $(GIT) --version | grep -oh '[0-9]\+\.[0-9]\+\.[0-9]\+') @@ -33,14 +41,48 @@ CMAKE=cmake CPPFLAGS=\ -DAGENT_VERSION='$(PKG_VERSION)-$(PKG_RELEASE)' \ -DENABLE_DOCKER_METADATA \ - -I$(CPP_NETLIB_DIR) -I$(NETWORK_URI_DIR)/include -I$(YAML_CPP_DIR)/include + -I$(CPP_NETLIB_DIR) -I$(NETWORK_URI_DIR)/include -I$(YAML_CPP_DIR)/include \ + -I$(OPENCENSUS_CPP_DIR) -I$(OPENCENSUS_CPP_ABSL_INCLUDEDIR) \ + -I$(OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_DIR)/include CXXFLAGS=\ -std=c++11 -g -pthread -Wno-write-strings -Wno-deprecated -LDFLAGS=-L$(CPP_NETLIB_LIBDIR) -L$(NETWORK_URI_LIBDIR) -L$(YAML_CPP_LIBDIR) +LDFLAGS=\ + -L$(CPP_NETLIB_LIBDIR) \ + -L$(NETWORK_URI_LIBDIR) \ + -L$(YAML_CPP_LIBDIR) \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/stats \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/context \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/exporters/stats/prometheus \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/trace \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/tags \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/common/internal \ + -L$(OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_LIBDIR) \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/numeric \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/strings \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/synchronization \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/time \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/debugging \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/base LDLIBS=\ -lcppnetlib-client-connections -lcppnetlib-server-parsers -lnetwork-uri \ -lboost_program_options -lboost_system -lboost_thread -lboost_filesystem \ - -lpthread -lyajl -lssl -lcrypto -lyaml-cpp + -lpthread -lyajl -lssl -lcrypto -lyaml-cpp \ + -lopencensus_exporters_stats_prometheus \ + -lopencensus_exporters_stats_prometheus_utils \ + -lopencensus_stats_core -lopencensus_stats_recording \ + -lopencensus_tags -lopencensus_tags_context_util \ + -lopencensus_context \ + -lopencensus_trace_context_util \ + -lopencensus_trace_with_span \ + -lopencensus_trace \ + -lopencensus_common_random \ + -lprometheus-cpp-core \ + -labsl_strings -labsl_synchronization -labsl_time \ + -labsl_int128 \ + -labsl_stacktrace -labsl_symbolize \ + -labsl_demangle_internal -labsl_internal_debugging_internal \ + -labsl_base -labsl_dynamic_annotations -labsl_internal_spinlock_wait \ + -labsl_internal_malloc_internal -labsl_internal_throw_delegate SED_EXTRA=-e 's/-Wall/-Wall -Wno-deprecated/' UNAME_S=$(shell uname -s) @@ -71,6 +113,7 @@ SRCS=\ kubernetes.cc \ resource.cc \ oauth2.cc \ + metrics.cc \ logging.cc \ local_stream_http.cc \ local_stream_delegate.cc \ @@ -88,7 +131,33 @@ CPP_NETLIB_LIBS=\ $(NETWORK_URI_LIBDIR)/libnetwork-uri.a YAML_CPP_LIBS=\ $(YAML_CPP_LIBDIR)/libyaml-cpp.a -LIBS=$(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) +OPENCENSUS_CPP_LIBS=\ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_base.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_internal_malloc_internal.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_internal_throw_delegate.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_internal_spinlock_wait.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_dynamic_annotations.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/debugging/libabsl_demangle_internal.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/debugging/libabsl_internal_debugging_internal.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/debugging/libabsl_stacktrace.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/debugging/libabsl_symbolize.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/numeric/libabsl_int128.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/synchronization/libabsl_synchronization.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/strings/libabsl_strings.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/time/libabsl_time.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/exporters/stats/prometheus/libopencensus_exporters_stats_prometheus.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/exporters/stats/prometheus/libopencensus_exporters_stats_prometheus_utils.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/stats/libopencensus_stats_core.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/context/libopencensus_context.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/tags/libopencensus_tags.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/tags/libopencensus_tags_context_util.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/trace/libopencensus_trace.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/trace/libopencensus_trace_with_span.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/trace/libopencensus_trace_context_util.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/context/libopencensus_context.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/common/internal/libopencensus_common_random.a \ + $(OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_LIBDIR)/libprometheus-cpp-core.a +LIBS=$(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) $(OPENCENSUS_CPP_LIBS) sbindir=/opt/stackdriver/metadata/sbin INSTALL=/usr/bin/install @@ -167,7 +236,7 @@ clean: $(RM) metadatad $(OBJS) purge: clean - $(RM) -r init-submodules build-cpp-netlib build-yaml-cpp + $(RM) -r init-submodules build-cpp-netlib build-yaml-cpp build-opencensus-cpp (cd .. && git submodule deinit -f $(SUBMODULE_DIRS:../%=%)) init-submodules: @@ -217,12 +286,25 @@ build-yaml-cpp: $(YAML_CPP_DIR)/Makefile $(MAKE) touch build-yaml-cpp -cpp-netlib: $(CPP_NETLIB_LIBS) +$(OPENCENSUS_CPP_DIR)/Makefile: init-submodules + cd $(OPENCENSUS_CPP_DIR) && \ + $(CMAKE) -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS=-std=c++11 \ + -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ \ + -DBUILD_TESTING=OFF + +$(OPENCENSUS_CPP_LIBS): build-opencensus-cpp +build-opencensus-cpp: $(OPENCENSUS_CPP_DIR)/Makefile + cd $(OPENCENSUS_CPP_DIR) && \ + $(MAKE) + touch build-opencensus-cpp + +cpp-netlib: $(CPP_NETLIB_LIBS) yaml-cpp: $(YAML_CPP_LIBS) +opencensus-cpp: $(OPENCENSUS_CPP_LIBS) -submodules: cpp-netlib yaml-cpp +submodules: cpp-netlib yaml-cpp opencensus-cpp all: submodules metadatad -.PHONY: all submodules cpp-netlib yaml-cpp purge clean install deb rpm +.PHONY: all submodules cpp-netlib yaml-cpp opencensus-cpp purge clean install deb rpm diff --git a/src/api_server.cc b/src/api_server.cc index 71642f57..b31dece2 100644 --- a/src/api_server.cc +++ b/src/api_server.cc @@ -21,6 +21,7 @@ #include "configuration.h" #include "health_checker.h" #include "http_common.h" +#include "metrics.h" #include "logging.h" #include "store.h" @@ -84,6 +85,11 @@ MetadataApiServer::MetadataApiServer(const Configuration& config, std::shared_ptr conn) { HandleHealthz(request, conn); }}, + {{"GET", "/metrics"}, + [=](const HttpServer::request& request, + std::shared_ptr conn) { + HandleMetrics(request, conn); + }}, }, config_.VerboseLogging()), server_( HttpServer::options(dispatcher_) @@ -200,4 +206,18 @@ void MetadataApiServer::HandleHealthz( } } +void MetadataApiServer::HandleMetrics( + const HttpServer::request& request, + std::shared_ptr conn) { + std::string response = + ::google::Metrics::SerializeMetricsToPrometheusTextFormat(); + conn->set_status(HttpServer::connection::ok); + conn->set_headers(std::map({ + {"Connection", "close"}, + {"Content-Length", std::to_string(response.size())}, + {"Content-Type", "text/plain; version=0.0.4"}, + })); + conn->write(response); } + +} // namespace google diff --git a/src/api_server.h b/src/api_server.h index 687e8e8b..e78f9f82 100644 --- a/src/api_server.h +++ b/src/api_server.h @@ -77,6 +77,8 @@ class MetadataApiServer { std::shared_ptr conn); void HandleHealthz(const HttpServer::request& request, std::shared_ptr conn); + void HandleMetrics(const HttpServer::request& request, + std::shared_ptr conn); const Configuration& config_; const HealthChecker* health_checker_; diff --git a/src/metrics.cc b/src/metrics.cc new file mode 100644 index 00000000..acc2e809 --- /dev/null +++ b/src/metrics.cc @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Google Inc. + * + * 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 "metrics.h" + +#include +#include +#include + +namespace google { + +namespace { + +constexpr const char kCount[] = "1"; + +::opencensus::stats::TagKey MethodTagKey() { + static const auto method_tag_key = + ::opencensus::stats::TagKey::Register("method"); + return method_tag_key; +} + +} // namespace + +const char Metrics::kGceApiRequestErrors[] = + "container.googleapis.com/internal/metadata_agent/gce_api_request_errors"; + +void Metrics::RecordGceApiRequestErrors(int64_t value, + const std::string& method) { + ::opencensus::stats::Record( + {{GceApiRequestErrors(), value}}, + {{MethodTagKey(), method}}); +} + +::opencensus::stats::MeasureInt64 Metrics::GceApiRequestErrors() { + static const auto measure = Metrics::GceApiRequestErrorsInitialize(); + return measure; +} + +::opencensus::stats::MeasureInt64 Metrics::GceApiRequestErrorsInitialize() { + const auto measure = + ::opencensus::stats::MeasureInt64::Register( + kGceApiRequestErrors, + "Number of API request errors encountered.", + kCount); + Metrics::GceApiRequestErrorsCumulativeViewDescriptor().RegisterForExport(); + return measure; +} + +const ::opencensus::stats::ViewDescriptor + Metrics::GceApiRequestErrorsCumulativeViewDescriptor() { + return ::opencensus::stats::ViewDescriptor() + .set_name(kGceApiRequestErrors) + .set_measure(kGceApiRequestErrors) + .set_aggregation(::opencensus::stats::Aggregation::Count()) + .set_description("The total number of HTTP request errors.") + .add_column(MethodTagKey()); +} + +std::string Metrics::SerializeMetricsToPrometheusTextFormat() { + return ::prometheus::TextSerializer().Serialize( + ::opencensus::exporters::stats::PrometheusExporter().Collect()); +} + +} // namespace google diff --git a/src/metrics.h b/src/metrics.h new file mode 100644 index 00000000..05517116 --- /dev/null +++ b/src/metrics.h @@ -0,0 +1,47 @@ +/* + * Copyright 2018 Google Inc. + * + * 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. + **/ + +#ifndef METADATA_AGENT_METRICS_H_ +#define METADATA_AGENT_METRICS_H_ + +#include +#include + +namespace google { + +class Metrics { + public: + static const char kGceApiRequestErrors[]; + + // Record an API request error on a method, e.g. oauth2 is one of the methods. + static void RecordGceApiRequestErrors(int64_t value, const std::string& method); + + // Serialize all the available metrics as prometheus text format. + static std::string SerializeMetricsToPrometheusTextFormat(); + + // View Descriptor accessors. If the view descriptor variable is not + // initialized, these methods will initialize the variable. + static const ::opencensus::stats::ViewDescriptor + GceApiRequestErrorsCumulativeViewDescriptor(); + + private: + static ::opencensus::stats::MeasureInt64 GceApiRequestErrorsInitialize(); + static ::opencensus::stats::MeasureInt64 GceApiRequestErrors(); +}; + +} // namespace google + +#endif /* METADATA_AGENT_METRICS_H_ */ diff --git a/src/oauth2.cc b/src/oauth2.cc index c78776be..87ba3afd 100644 --- a/src/oauth2.cc +++ b/src/oauth2.cc @@ -32,6 +32,7 @@ #include "http_common.h" #include "json.h" #include "logging.h" +#include "metrics.h" #include "time.h" namespace http = boost::network::http; @@ -139,7 +140,7 @@ std::string Sign(const std::string& data, const PKey& pkey) { return std::string(reinterpret_cast(result.get()), actual_result_size); } -} +} // namespace json::value OAuth2::ComputeTokenFromCredentials() const { const std::string service_account_email = @@ -249,11 +250,12 @@ json::value OAuth2::ComputeTokenFromCredentials() const { return parsed_token; } catch (const json::Exception& e) { LOG(ERROR) << e.what(); - return nullptr; } catch (const boost::system::system_error& e) { LOG(ERROR) << "HTTP error: " << e.what(); - return nullptr; } + + ::google::Metrics::RecordGceApiRequestErrors(1, "oauth2"); + return nullptr; } json::value OAuth2::GetMetadataToken() const { diff --git a/src/oauth2.h b/src/oauth2.h index a27af2dd..93b4f1e0 100644 --- a/src/oauth2.h +++ b/src/oauth2.h @@ -34,15 +34,15 @@ constexpr const char kDefaultTokenEndpoint[] = class OAuth2 { public: OAuth2(const Environment& environment) - : OAuth2(environment, ExpirationImpl::New()) {} + : OAuth2(environment, ExpirationImpl::New()) {} std::string GetAuthHeaderValue(); protected: OAuth2(const Environment& environment, std::unique_ptr expiration) - : environment_(environment), - token_expiration_(std::move(expiration)), - token_endpoint_(kDefaultTokenEndpoint) {} + : environment_(environment), + token_expiration_(std::move(expiration)), + token_endpoint_(kDefaultTokenEndpoint) {} private: friend class OAuth2Test; diff --git a/test/Makefile b/test/Makefile index 9a86575b..4db8a760 100644 --- a/test/Makefile +++ b/test/Makefile @@ -32,23 +32,81 @@ NETWORK_URI_DIR=$(CPP_NETLIB_DIR)/deps/uri NETWORK_URI_LIBDIR=$(NETWORK_URI_DIR)/src YAML_CPP_DIR=$(LIBDIR)/yaml-cpp YAML_CPP_LIBDIR=$(YAML_CPP_DIR) +OPENCENSUS_CPP_DIR=$(LIBDIR)/opencensus-cpp +OPENCENSUS_CPP_OPENCENSUS_DIR=$(OPENCENSUS_CPP_DIR)/opencensus +OPENCENSUS_CPP_OPENCENSUS_LIBDIR=$(OPENCENSUS_CPP_OPENCENSUS_DIR) +OPENCENSUS_CPP_ABSL_INCLUDEDIR=$(OPENCENSUS_CPP_DIR)/abseil-src +OPENCENSUS_CPP_ABSL_LIBDIR=$(OPENCENSUS_CPP_DIR)/abseil-build/absl +OPENCENSUS_CPP_PROMETHEUS_CPP_DIR=$(OPENCENSUS_CPP_DIR)/prometheus-src +OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_DIR=$(OPENCENSUS_CPP_PROMETHEUS_CPP_DIR)/core +OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_LIBDIR=$(OPENCENSUS_CPP_DIR)/prometheus-build/core CPP_NETLIB_LIBS=\ $(CPP_NETLIB_LIBDIR)/libcppnetlib-client-connections.a \ $(CPP_NETLIB_LIBDIR)/libcppnetlib-server-parsers.a \ $(NETWORK_URI_LIBDIR)/libnetwork-uri.a YAML_CPP_LIBS=$(YAML_CPP_LIBDIR)/libyaml-cpp.a - +OPENCENSUS_CPP_LIBS=\ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_base.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_internal_malloc_internal.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_internal_throw_delegate.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_internal_spinlock_wait.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/base/libabsl_dynamic_annotations.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/debugging/libabsl_stacktrace.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/debugging/libabsl_symbolize.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/numeric/libabsl_int128.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/synchronization/libabsl_synchronization.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/strings/libabsl_strings.a \ + $(OPENCENSUS_CPP_ABSL_LIBDIR)/time/libabsl_time.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/exporters/stats/stdout/libopencensus_exporters_stats_stdout.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/stats/libopencensus_stats_core.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/stats/libopencensus_stats_test_utils.a \ + $(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/tags/libopencensus_tags.a \ + $(OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_LIBDIR)/libprometheus-cpp-core.a CPPFLAGS+= \ -isystem $(GTEST_DIR)/include -I$(GMOCK_DIR)/include \ - -I$(CPP_NETLIB_DIR) -I$(NETWORK_URI_DIR)/include -I$(YAML_CPP_DIR)/include + -I$(CPP_NETLIB_DIR) -I$(NETWORK_URI_DIR)/include -I$(YAML_CPP_DIR)/include \ + -I$(OPENCENSUS_CPP_DIR) -I$(OPENCENSUS_CPP_ABSL_INCLUDEDIR) \ + -I$(OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_DIR)/include CXXFLAGS=\ -std=c++11 -g -pthread -Wno-write-strings -Wno-deprecated -LDFLAGS=-L$(CPP_NETLIB_LIBDIR) -L$(NETWORK_URI_LIBDIR) -L$(YAML_CPP_LIBDIR) +LDFLAGS=\ + -L$(CPP_NETLIB_LIBDIR) \ + -L$(NETWORK_URI_LIBDIR) \ + -L$(YAML_CPP_LIBDIR) \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/stats \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/context \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/exporters/stats/prometheus \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/trace \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/tags \ + -L$(OPENCENSUS_CPP_OPENCENSUS_LIBDIR)/common/internal \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/numeric \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/strings \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/synchronization \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/time \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/debugging \ + -L$(OPENCENSUS_CPP_ABSL_LIBDIR)/base \ + -L$(OPENCENSUS_CPP_PROMETHEUS_CPP_CORE_LIBDIR) LDLIBS=\ -lcppnetlib-client-connections -lcppnetlib-server-parsers -lnetwork-uri \ -lboost_program_options -lboost_system -lboost_thread -lboost_filesystem \ - -lboost_regex -lpthread -lyajl -lssl -lcrypto -lyaml-cpp + -lboost_regex -lpthread -lyajl -lssl -lcrypto -lyaml-cpp \ + -lopencensus_exporters_stats_prometheus \ + -lopencensus_exporters_stats_prometheus_utils \ + -lopencensus_stats_core -lopencensus_stats_test_utils -lopencensus_stats_recording \ + -lopencensus_tags -lopencensus_tags_context_util \ + -lopencensus_context \ + -lopencensus_trace_context_util \ + -lopencensus_trace_with_span \ + -lopencensus_trace \ + -lopencensus_common_random \ + -lprometheus-cpp-core \ + -labsl_strings -labsl_synchronization -labsl_time \ + -labsl_int128 \ + -labsl_stacktrace -labsl_symbolize \ + -labsl_demangle_internal -labsl_internal_debugging_internal \ + -labsl_base -labsl_dynamic_annotations -labsl_internal_spinlock_wait \ + -labsl_internal_malloc_internal -labsl_internal_throw_delegate UNAME_S=$(shell uname -s) ifeq ($(UNAME_S),Darwin) @@ -76,6 +134,7 @@ TESTS=\ format_unittest \ health_checker_unittest \ instance_unittest \ + metrics_unittest \ json_unittest \ kubernetes_unittest \ logging_unittest \ @@ -115,14 +174,16 @@ init-submodules: $(SRC_DIR)/init-submodules: cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) -$(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp: $(SRC_DIR)/init-submodules +$(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp $(SRC_DIR)/build-opencensus-cpp: $(SRC_DIR)/init-submodules cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) $(CPP_NETLIB_LIBS): $(SRC_DIR)/build-cpp-netlib $(YAML_CPP_LIBS): $(SRC_DIR)/build-yaml-cpp -$(SRC_DIR)/%.o: $(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp $(SRC_DIR)/%.cc +$(OPENCENSUS_CPP_LIBS): $(SRC_DIR)/build-opencensus-cpp + +$(SRC_DIR)/%.o: $(SRC_DIR)/build-cpp-netlib $(SRC_DIR)/build-yaml-cpp $(SRC_DIR)/build-opencensus-cpp $(SRC_DIR)/%.cc cd $(SRC_DIR) && $(MAKE) $(@:$(SRC_DIR)/%=%) $(GTEST_SOURCEDIR)/gtest-all.cc: init-submodules @@ -139,13 +200,13 @@ gmock_main.o: $(GMOCK_SOURCEDIR)/gmock_main.cc $(GTEST_LIB): gtest-all.o gmock-all.o gmock_main.o $(AR) $(ARFLAGS) $@ $^ -$(TESTS): $(GTEST_LIB) $(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) +$(TESTS): $(GTEST_LIB) $(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) $(OPENCENSUS_CPP_LIBS) # All unittest objects depend on GTEST_LIB. # Some headers need CPP_NETLIB_LIBS and YAML_CPP_LIBS. -$(TESTS:%=%.o): $(GTEST_LIB) $(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) +$(TESTS:%=%.o): $(GTEST_LIB) $(CPP_NETLIB_LIBS) $(YAML_CPP_LIBS) $(OPENCENSUS_CPP_LIBS) -api_server_unittest: api_server_unittest.o $(SRC_DIR)/api_server.o $(SRC_DIR)/configuration.o $(SRC_DIR)/store.o $(SRC_DIR)/json.o $(SRC_DIR)/resource.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o $(SRC_DIR)/health_checker.o +api_server_unittest: api_server_unittest.o $(SRC_DIR)/api_server.o $(SRC_DIR)/metrics.o $(SRC_DIR)/configuration.o $(SRC_DIR)/store.o $(SRC_DIR)/json.o $(SRC_DIR)/resource.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o $(SRC_DIR)/health_checker.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ base64_unittest: base64_unittest.o $(SRC_DIR)/base64.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ @@ -165,11 +226,13 @@ kubernetes_unittest: kubernetes_unittest.o fake_http_server.o $(SRC_DIR)/kuberne $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ logging_unittest: logging_unittest.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ -oauth2_unittest: oauth2_unittest.o fake_clock.o fake_http_server.o $(SRC_DIR)/oauth2.o $(SRC_DIR)/base64.o $(SRC_DIR)/configuration.o $(SRC_DIR)/environment.o $(SRC_DIR)/format.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o +metrics_unittest: metrics_unittest.o $(SRC_DIR)/metrics.o + $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ +oauth2_unittest: oauth2_unittest.o fake_clock.o fake_http_server.o $(SRC_DIR)/oauth2.o $(SRC_DIR)/base64.o $(SRC_DIR)/configuration.o $(SRC_DIR)/environment.o $(SRC_DIR)/format.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/metrics.o $(SRC_DIR)/time.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ resource_unittest: resource_unittest.o $(SRC_DIR)/resource.o $(SRC_DIR)/json.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ -reporter_unittest: reporter_unittest.o fake_clock.o fake_http_server.o $(SRC_DIR)/reporter.o $(SRC_DIR)/base64.o $(SRC_DIR)/configuration.o $(SRC_DIR)/environment.o $(SRC_DIR)/format.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/oauth2.o $(SRC_DIR)/resource.o $(SRC_DIR)/store.o $(SRC_DIR)/time.o +reporter_unittest: reporter_unittest.o fake_clock.o fake_http_server.o $(SRC_DIR)/reporter.o $(SRC_DIR)/base64.o $(SRC_DIR)/configuration.o $(SRC_DIR)/environment.o $(SRC_DIR)/format.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/metrics.o $(SRC_DIR)/oauth2.o $(SRC_DIR)/resource.o $(SRC_DIR)/store.o $(SRC_DIR)/time.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ store_unittest: store_unittest.o $(SRC_DIR)/store.o $(SRC_DIR)/resource.o $(SRC_DIR)/json.o $(SRC_DIR)/logging.o $(SRC_DIR)/time.o $(SRC_DIR)/configuration.o $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@ diff --git a/test/metrics_unittest.cc b/test/metrics_unittest.cc new file mode 100644 index 00000000..8742f9d8 --- /dev/null +++ b/test/metrics_unittest.cc @@ -0,0 +1,81 @@ +/* + * Copyright 2018 Google Inc. + * + * 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 "../../src/metrics.h" + +#include +#include +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace google { + +TEST(SerializeToPrometheusTextTest, NoMetricExists) { + EXPECT_EQ("", + ::google::Metrics::SerializeMetricsToPrometheusTextFormat()); +} + +TEST(SerializeToPrometheusTextTest, SingleMetricAndViewRegisteredForExport) { + const char* measure_name = "test_measure"; + const char* view_name = "test_view"; + const ::opencensus::stats::MeasureInt64 test_measure = + ::opencensus::stats::MeasureInt64::Register( + measure_name, "description on test view", "1"); + const ::opencensus::stats::ViewDescriptor test_view_descriptor = + ::opencensus::stats::ViewDescriptor() + .set_name(view_name) + .set_description("description on test view") + .set_measure(measure_name) + .set_aggregation(opencensus::stats::Aggregation::Count()); + // Remove view and flush the result before the actual test. + ::opencensus::stats::StatsExporter::RemoveView(view_name); + ::opencensus::stats::testing::TestUtils::Flush(); + + test_view_descriptor.RegisterForExport(); + ::opencensus::stats::Record({{test_measure, 1}}); + // Flush to propagate existing records to views. + ::opencensus::stats::testing::TestUtils::Flush(); + EXPECT_THAT(::google::Metrics::SerializeMetricsToPrometheusTextFormat(), + ::testing::MatchesRegex( + "# HELP test_view_1 description on test view\n" + "# TYPE test_view_1 counter\n" + "test_view_1 1.000000 [0-9]*\n")); + + // Clean up the view which we registered for export. + ::opencensus::stats::StatsExporter::RemoveView(view_name); +} + +TEST(SerializeToPrometheusTextTest, RecordGceApiRequestErrors) { + // Remove view and flush the result before the actual test. + ::opencensus::stats::StatsExporter::RemoveView(::google::Metrics::kGceApiRequestErrors); + ::opencensus::stats::testing::TestUtils::Flush(); + + ::google::Metrics::RecordGceApiRequestErrors(1, "test_kind"); + // Flush to propagate existing records to views. + ::opencensus::stats::testing::TestUtils::Flush(); + + EXPECT_THAT(::google::Metrics::SerializeMetricsToPrometheusTextFormat(), + ::testing::MatchesRegex( + "# HELP container_googleapis_com_internal_metadata_agent_gce_api_request_errors_1 The total number of HTTP request errors.\n" + "# TYPE container_googleapis_com_internal_metadata_agent_gce_api_request_errors_1 counter\n" + "container_googleapis_com_internal_metadata_agent_gce_api_request_errors_1\\{method=\"test_kind\"\\} 1.000000 [0-9]*\n" + )); + + // Clean up the view which we registered for export. + ::opencensus::stats::StatsExporter::RemoveView(::google::Metrics::kGceApiRequestErrors); +} + +} // namespace google diff --git a/test/oauth2_unittest.cc b/test/oauth2_unittest.cc index 142307de..e94aa9e0 100644 --- a/test/oauth2_unittest.cc +++ b/test/oauth2_unittest.cc @@ -15,6 +15,8 @@ **/ #include "../src/oauth2.h" + +#include "../src/metrics.h" #include "environment_util.h" #include "fake_clock.h" #include "fake_http_server.h" @@ -22,6 +24,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include +#include #include namespace google { @@ -118,6 +122,54 @@ TEST_F(OAuth2Test, GetAuthHeaderValueUsingTokenFromMetadataServerAsFallback) { EXPECT_EQ("Bearer the-access-token", auth.GetAuthHeaderValue()); } +TEST_F(OAuth2Test, PropagateGceApiRequestErrorsCumulativeToView) { + testing::FakeServer oauth_server; + testing::TemporaryFile credentials_file( + std::string(test_info_->name()) + "_creds.json", + "{\"client_email\":\"user@example.com\",\"private_key\":\"some_key\"}"); + Configuration config(std::istringstream( + "CredentialsFile: '" + credentials_file.FullPath().native() + "'\n" + )); + Environment environment(config); + OAuth2 auth(environment); + SetTokenEndpointForTest(&auth, oauth_server.GetUrl() + "/oauth2/v3/token"); + + // Remove existing view on kGceApiRequestErrors to avoid other tests + // affecting the result, then flush to propagate existing records + // to views, including records from other tests. + ::opencensus::stats::StatsExporter::RemoveView( + ::google::Metrics::kGceApiRequestErrors); + ::opencensus::stats::testing::TestUtils::Flush(); + + // We want to test this auth.GetAuthHeaderValue() that it records and + // register the view, but as inside the auth.GetAuthHeaderValue() we + // use singleton pattern, once we remove the view in previous section, it is + // unable to add the view back within auth.GetAuthHeaderValue(). Add a view + // to export here helps us to test the data inside the view, it also makes + // the test fragile if auth.GetAuthHeaderValue() does not register view + // correctly. + // TODO: Fix this hack if we can contribute to opencensus that when we add + // a view, we only add view if a view name is not existed. Current + // StatsExporter::AddView always add a new view and destroy the old one, + // which is not suitable for our + // Metrics::GceApiRequestErrorsCumulativeViewDescriptor(). + ::google::Metrics::GceApiRequestErrorsCumulativeViewDescriptor(). + RegisterForExport(); + auth.GetAuthHeaderValue(); + // Flush to propagate existing records to views. + ::opencensus::stats::testing::TestUtils::Flush(); + + EXPECT_THAT(::opencensus::stats::StatsExporter::GetViewData(), + ::testing::Contains(::testing::Pair( + Metrics::GceApiRequestErrorsCumulativeViewDescriptor(), + ::testing::Property( + &::opencensus::stats::ViewData::int_data, + ::testing::UnorderedElementsAre( + ::testing::Pair( + ::testing::ElementsAre("oauth2"), + 1)))))); +} + TEST_F(OAuth2Test, GetAuthHeaderValueTokenJsonMissingField) { testing::FakeServer metadata_server; // JSON is missing "expires_in" field.