diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f088bc4f645..6beddffa81d4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1050,6 +1050,9 @@ protobuf_generate_grpc_cpp_with_import_path_correction( protobuf_generate_grpc_cpp_with_import_path_correction( third_party/envoy-api/envoy/service/discovery/v3/discovery.proto envoy/service/discovery/v3/discovery.proto ) +protobuf_generate_grpc_cpp_with_import_path_correction( + third_party/envoy-api/envoy/service/status/v3/csds.proto envoy/service/status/v3/csds.proto +) protobuf_generate_grpc_cpp_with_import_path_correction( third_party/envoy-api/envoy/type/http/v3/cookie.proto envoy/type/http/v3/cookie.proto ) @@ -37406,6 +37409,50 @@ add_executable(xds_client_test ${_gRPC_PROTO_GENS_DIR}/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.pb.h ${_gRPC_PROTO_GENS_DIR}/test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/certs.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/certs.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/certs.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/certs.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/clusters.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/clusters.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/clusters.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/clusters.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/config_dump.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/config_dump.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/config_dump.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/config_dump.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/config_dump_shared.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/config_dump_shared.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/config_dump_shared.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/config_dump_shared.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/init_dump.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/init_dump.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/init_dump.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/init_dump.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/listeners.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/listeners.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/listeners.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/listeners.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/memory.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/memory.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/memory.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/memory.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/metrics.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/metrics.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/metrics.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/metrics.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/mutex_stats.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/mutex_stats.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/mutex_stats.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/mutex_stats.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/server_info.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/server_info.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/server_info.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/server_info.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/tap.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/tap.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/tap.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/admin/v3/tap.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/annotations/deprecation.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/annotations/deprecation.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/annotations/deprecation.pb.h @@ -37414,6 +37461,34 @@ add_executable(xds_client_test ${_gRPC_PROTO_GENS_DIR}/envoy/annotations/resource.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/annotations/resource.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/annotations/resource.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/accesslog/v3/accesslog.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/accesslog/v3/accesslog.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/accesslog/v3/accesslog.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/accesslog/v3/accesslog.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/bootstrap/v3/bootstrap.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/bootstrap/v3/bootstrap.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/bootstrap/v3/bootstrap.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/bootstrap/v3/bootstrap.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/circuit_breaker.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/circuit_breaker.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/circuit_breaker.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/circuit_breaker.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/cluster.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/cluster.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/cluster.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/cluster.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/filter.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/filter.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/filter.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/filter.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/outlier_detection.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/outlier_detection.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/outlier_detection.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/cluster/v3/outlier_detection.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/common/matcher/v3/matcher.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/common/matcher/v3/matcher.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/common/matcher/v3/matcher.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/common/matcher/v3/matcher.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/config/core/v3/address.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/config/core/v3/address.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/config/core/v3/address.pb.h @@ -37486,6 +37561,130 @@ add_executable(xds_client_test ${_gRPC_PROTO_GENS_DIR}/envoy/config/core/v3/udp_socket_config.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/config/core/v3/udp_socket_config.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/config/core/v3/udp_socket_config.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/endpoint.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/endpoint.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/endpoint.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/endpoint.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/endpoint_components.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/endpoint_components.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/endpoint_components.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/endpoint_components.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/load_report.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/load_report.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/load_report.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/endpoint/v3/load_report.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/api_listener.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/api_listener.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/api_listener.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/api_listener.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/listener.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/listener.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/listener.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/listener.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/listener_components.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/listener_components.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/listener_components.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/listener_components.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/quic_config.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/quic_config.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/quic_config.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/quic_config.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/udp_listener_config.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/udp_listener_config.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/udp_listener_config.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/listener/v3/udp_listener_config.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/metrics/v3/metrics_service.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/metrics/v3/metrics_service.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/metrics/v3/metrics_service.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/metrics/v3/metrics_service.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/metrics/v3/stats.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/metrics/v3/stats.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/metrics/v3/stats.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/metrics/v3/stats.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/overload/v3/overload.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/overload/v3/overload.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/overload/v3/overload.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/overload/v3/overload.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/route.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/route.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/route.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/route.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/route_components.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/route_components.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/route_components.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/route_components.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/scoped_route.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/scoped_route.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/scoped_route.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/route/v3/scoped_route.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/tap/v3/common.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/tap/v3/common.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/tap/v3/common.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/tap/v3/common.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/datadog.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/datadog.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/datadog.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/datadog.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/dynamic_ot.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/dynamic_ot.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/dynamic_ot.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/dynamic_ot.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/http_tracer.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/http_tracer.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/http_tracer.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/http_tracer.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/lightstep.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/lightstep.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/lightstep.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/lightstep.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/opentelemetry.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/opentelemetry.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/opentelemetry.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/opentelemetry.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/service.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/service.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/service.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/service.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/skywalking.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/skywalking.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/skywalking.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/skywalking.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/trace.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/trace.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/trace.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/trace.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/xray.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/xray.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/xray.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/xray.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/zipkin.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/zipkin.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/zipkin.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/config/trace/v3/zipkin.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/data/accesslog/v3/accesslog.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/data/accesslog/v3/accesslog.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/data/accesslog/v3/accesslog.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/data/accesslog/v3/accesslog.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/cert.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/cert.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/cert.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/cert.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/common.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/common.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/common.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/common.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/secret.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/secret.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/secret.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/secret.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/tls.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/tls.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/tls.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/tls.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/service/discovery/v3/ads.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/service/discovery/v3/ads.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/service/discovery/v3/ads.pb.h @@ -37494,6 +37693,10 @@ add_executable(xds_client_test ${_gRPC_PROTO_GENS_DIR}/envoy/service/discovery/v3/discovery.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/service/discovery/v3/discovery.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/service/discovery/v3/discovery.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/service/status/v3/csds.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/service/status/v3/csds.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/service/status/v3/csds.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/service/status/v3/csds.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/type/matcher/v3/filter_state.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/type/matcher/v3/filter_state.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/type/matcher/v3/filter_state.pb.h @@ -37538,6 +37741,14 @@ add_executable(xds_client_test ${_gRPC_PROTO_GENS_DIR}/envoy/type/matcher/v3/value.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/type/matcher/v3/value.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/type/matcher/v3/value.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/type/metadata/v3/metadata.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/type/metadata/v3/metadata.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/type/metadata/v3/metadata.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/type/metadata/v3/metadata.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/type/tracing/v3/custom_tag.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/type/tracing/v3/custom_tag.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/envoy/type/tracing/v3/custom_tag.pb.h + ${_gRPC_PROTO_GENS_DIR}/envoy/type/tracing/v3/custom_tag.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/envoy/type/v3/hash_policy.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/type/v3/hash_policy.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/envoy/type/v3/hash_policy.pb.h @@ -37578,6 +37789,14 @@ add_executable(xds_client_test ${_gRPC_PROTO_GENS_DIR}/google/api/annotations.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/google/api/annotations.pb.h ${_gRPC_PROTO_GENS_DIR}/google/api/annotations.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/google/api/expr/v1alpha1/checked.pb.cc + ${_gRPC_PROTO_GENS_DIR}/google/api/expr/v1alpha1/checked.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/google/api/expr/v1alpha1/checked.pb.h + ${_gRPC_PROTO_GENS_DIR}/google/api/expr/v1alpha1/checked.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/google/api/expr/v1alpha1/syntax.pb.cc + ${_gRPC_PROTO_GENS_DIR}/google/api/expr/v1alpha1/syntax.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/google/api/expr/v1alpha1/syntax.pb.h + ${_gRPC_PROTO_GENS_DIR}/google/api/expr/v1alpha1/syntax.grpc.pb.h ${_gRPC_PROTO_GENS_DIR}/google/api/http.pb.cc ${_gRPC_PROTO_GENS_DIR}/google/api/http.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/google/api/http.pb.h @@ -37666,6 +37885,50 @@ add_executable(xds_client_test ${_gRPC_PROTO_GENS_DIR}/xds/core/v3/resource_name.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/xds/core/v3/resource_name.pb.h ${_gRPC_PROTO_GENS_DIR}/xds/core/v3/resource_name.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/cel.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/cel.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/cel.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/cel.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/domain.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/domain.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/domain.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/domain.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/http_inputs.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/http_inputs.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/http_inputs.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/http_inputs.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/ip.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/ip.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/ip.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/ip.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/matcher.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/matcher.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/matcher.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/matcher.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/range.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/range.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/range.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/range.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/regex.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/regex.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/regex.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/regex.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/string.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/string.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/string.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/matcher/v3/string.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/cel.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/cel.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/cel.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/cel.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/range.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/range.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/range.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/range.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/typed_struct.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/typed_struct.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/typed_struct.pb.h + ${_gRPC_PROTO_GENS_DIR}/xds/type/v3/typed_struct.grpc.pb.h src/cpp/client/global_callback_hook.cc src/cpp/util/status.cc test/core/event_engine/event_engine_test_utils.cc diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index c314fb39328db..64062d5fd6a22 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -24063,8 +24063,26 @@ targets: - third_party/protoc-gen-validate/validate/validate.h src: - test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.proto + - third_party/envoy-api/envoy/admin/v3/certs.proto + - third_party/envoy-api/envoy/admin/v3/clusters.proto + - third_party/envoy-api/envoy/admin/v3/config_dump.proto + - third_party/envoy-api/envoy/admin/v3/config_dump_shared.proto + - third_party/envoy-api/envoy/admin/v3/init_dump.proto + - third_party/envoy-api/envoy/admin/v3/listeners.proto + - third_party/envoy-api/envoy/admin/v3/memory.proto + - third_party/envoy-api/envoy/admin/v3/metrics.proto + - third_party/envoy-api/envoy/admin/v3/mutex_stats.proto + - third_party/envoy-api/envoy/admin/v3/server_info.proto + - third_party/envoy-api/envoy/admin/v3/tap.proto - third_party/envoy-api/envoy/annotations/deprecation.proto - third_party/envoy-api/envoy/annotations/resource.proto + - third_party/envoy-api/envoy/config/accesslog/v3/accesslog.proto + - third_party/envoy-api/envoy/config/bootstrap/v3/bootstrap.proto + - third_party/envoy-api/envoy/config/cluster/v3/circuit_breaker.proto + - third_party/envoy-api/envoy/config/cluster/v3/cluster.proto + - third_party/envoy-api/envoy/config/cluster/v3/filter.proto + - third_party/envoy-api/envoy/config/cluster/v3/outlier_detection.proto + - third_party/envoy-api/envoy/config/common/matcher/v3/matcher.proto - third_party/envoy-api/envoy/config/core/v3/address.proto - third_party/envoy-api/envoy/config/core/v3/backoff.proto - third_party/envoy-api/envoy/config/core/v3/base.proto @@ -24083,8 +24101,40 @@ targets: - third_party/envoy-api/envoy/config/core/v3/socket_option.proto - third_party/envoy-api/envoy/config/core/v3/substitution_format_string.proto - third_party/envoy-api/envoy/config/core/v3/udp_socket_config.proto + - third_party/envoy-api/envoy/config/endpoint/v3/endpoint.proto + - third_party/envoy-api/envoy/config/endpoint/v3/endpoint_components.proto + - third_party/envoy-api/envoy/config/endpoint/v3/load_report.proto + - third_party/envoy-api/envoy/config/listener/v3/api_listener.proto + - third_party/envoy-api/envoy/config/listener/v3/listener.proto + - third_party/envoy-api/envoy/config/listener/v3/listener_components.proto + - third_party/envoy-api/envoy/config/listener/v3/quic_config.proto + - third_party/envoy-api/envoy/config/listener/v3/udp_listener_config.proto + - third_party/envoy-api/envoy/config/metrics/v3/metrics_service.proto + - third_party/envoy-api/envoy/config/metrics/v3/stats.proto + - third_party/envoy-api/envoy/config/overload/v3/overload.proto + - third_party/envoy-api/envoy/config/route/v3/route.proto + - third_party/envoy-api/envoy/config/route/v3/route_components.proto + - third_party/envoy-api/envoy/config/route/v3/scoped_route.proto + - third_party/envoy-api/envoy/config/tap/v3/common.proto + - third_party/envoy-api/envoy/config/trace/v3/datadog.proto + - third_party/envoy-api/envoy/config/trace/v3/dynamic_ot.proto + - third_party/envoy-api/envoy/config/trace/v3/http_tracer.proto + - third_party/envoy-api/envoy/config/trace/v3/lightstep.proto + - third_party/envoy-api/envoy/config/trace/v3/opentelemetry.proto + - third_party/envoy-api/envoy/config/trace/v3/service.proto + - third_party/envoy-api/envoy/config/trace/v3/skywalking.proto + - third_party/envoy-api/envoy/config/trace/v3/trace.proto + - third_party/envoy-api/envoy/config/trace/v3/xray.proto + - third_party/envoy-api/envoy/config/trace/v3/zipkin.proto + - third_party/envoy-api/envoy/data/accesslog/v3/accesslog.proto + - third_party/envoy-api/envoy/extensions/transport_sockets/tls/v3/cert.proto + - third_party/envoy-api/envoy/extensions/transport_sockets/tls/v3/common.proto + - third_party/envoy-api/envoy/extensions/transport_sockets/tls/v3/secret.proto + - third_party/envoy-api/envoy/extensions/transport_sockets/tls/v3/tls.proto + - third_party/envoy-api/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto - third_party/envoy-api/envoy/service/discovery/v3/ads.proto - third_party/envoy-api/envoy/service/discovery/v3/discovery.proto + - third_party/envoy-api/envoy/service/status/v3/csds.proto - third_party/envoy-api/envoy/type/matcher/v3/filter_state.proto - third_party/envoy-api/envoy/type/matcher/v3/http_inputs.proto - third_party/envoy-api/envoy/type/matcher/v3/metadata.proto @@ -24096,6 +24146,8 @@ targets: - third_party/envoy-api/envoy/type/matcher/v3/string.proto - third_party/envoy-api/envoy/type/matcher/v3/struct.proto - third_party/envoy-api/envoy/type/matcher/v3/value.proto + - third_party/envoy-api/envoy/type/metadata/v3/metadata.proto + - third_party/envoy-api/envoy/type/tracing/v3/custom_tag.proto - third_party/envoy-api/envoy/type/v3/hash_policy.proto - third_party/envoy-api/envoy/type/v3/http.proto - third_party/envoy-api/envoy/type/v3/http_status.proto @@ -24106,6 +24158,8 @@ targets: - third_party/envoy-api/envoy/type/v3/semantic_version.proto - third_party/envoy-api/envoy/type/v3/token_bucket.proto - third_party/googleapis/google/api/annotations.proto + - third_party/googleapis/google/api/expr/v1alpha1/checked.proto + - third_party/googleapis/google/api/expr/v1alpha1/syntax.proto - third_party/googleapis/google/api/http.proto - third_party/googleapis/google/api/httpbody.proto - third_party/googleapis/google/rpc/status.proto @@ -24128,6 +24182,17 @@ targets: - third_party/xds/xds/core/v3/resource.proto - third_party/xds/xds/core/v3/resource_locator.proto - third_party/xds/xds/core/v3/resource_name.proto + - third_party/xds/xds/type/matcher/v3/cel.proto + - third_party/xds/xds/type/matcher/v3/domain.proto + - third_party/xds/xds/type/matcher/v3/http_inputs.proto + - third_party/xds/xds/type/matcher/v3/ip.proto + - third_party/xds/xds/type/matcher/v3/matcher.proto + - third_party/xds/xds/type/matcher/v3/range.proto + - third_party/xds/xds/type/matcher/v3/regex.proto + - third_party/xds/xds/type/matcher/v3/string.proto + - third_party/xds/xds/type/v3/cel.proto + - third_party/xds/xds/type/v3/range.proto + - third_party/xds/xds/type/v3/typed_struct.proto - src/cpp/client/global_callback_hook.cc - src/cpp/util/status.cc - test/core/event_engine/event_engine_test_utils.cc diff --git a/src/core/xds/xds_client/xds_client.cc b/src/core/xds/xds_client/xds_client.cc index 9c36ce184b1a5..4261f6519c4ed 100644 --- a/src/core/xds/xds_client/xds_client.cc +++ b/src/core/xds/xds_client/xds_client.cc @@ -311,7 +311,7 @@ class XdsClient::XdsChannel::AdsCall final resources_seen; uint64_t num_valid_resources = 0; uint64_t num_invalid_resources = 0; - Timestamp update_time_ = Timestamp::Now(); + Timestamp update_time = Timestamp::Now(); RefCountedPtr read_delay_handle; }; void ParseResource(size_t idx, absl::string_view type_url, @@ -1020,16 +1020,27 @@ void XdsClient::XdsChannel::AdsCall::ParseResource( context->read_delay_handle); } resource_state.SetNacked(context->version, decode_status.ToString(), - context->update_time_); + context->update_time); ++context->num_invalid_resources; return; } // Resource is valid. ++context->num_valid_resources; - // If it didn't change, ignore it. - if (resource_state.HasResource() && + // Check if the resource has changed. + const bool resource_identical = + resource_state.HasResource() && context->type->ResourcesEqual(resource_state.resource().get(), - decode_result.resource->get())) { + decode_result.resource->get()); + // If not changed, keep using the current decoded resource object. + // This should avoid wasting memory, since external watchers may be + // holding refs to the current object. + if (resource_identical) decode_result.resource = resource_state.resource(); + // Update the resource state. + resource_state.SetAcked(std::move(*decode_result.resource), + std::string(serialized_resource), context->version, + context->update_time); + // If the resource didn't change, inhibit watcher notifications. + if (resource_identical) { GRPC_TRACE_LOG(xds_client, INFO) << "[xds_client " << xds_client() << "] " << context->type_url << " resource " << resource_name << " identical to current, ignoring."; @@ -1042,10 +1053,6 @@ void XdsClient::XdsChannel::AdsCall::ParseResource( } return; } - // Update the resource state. - resource_state.SetAcked(std::move(*decode_result.resource), - std::string(serialized_resource), context->version, - context->update_time_); // Notify watchers. xds_client()->NotifyWatchersOnResourceChanged(resource_state.resource(), resource_state.watchers(), @@ -1336,7 +1343,9 @@ void XdsClient::ResourceState::SetNacked(const std::string& version, void XdsClient::ResourceState::SetDoesNotExist() { resource_.reset(); + serialized_proto_.clear(); client_status_ = ClientResourceStatus::DOES_NOT_EXIST; + failed_version_.clear(); } absl::string_view XdsClient::ResourceState::CacheStateString() const { diff --git a/src/core/xds/xds_client/xds_client.h b/src/core/xds/xds_client/xds_client.h index f0b853238a6d1..5b5fd05ab59bd 100644 --- a/src/core/xds/xds_client/xds_client.h +++ b/src/core/xds/xds_client/xds_client.h @@ -139,8 +139,7 @@ class XdsClient : public DualRefCounted { Mutex* mu() ABSL_LOCK_RETURNED(&mu_) { return &mu_; } // Dumps the active xDS config to the provided - // envoy.service.status.v3.ClientConfig message including the config status - // (e.g., CLIENT_REQUESTED, CLIENT_ACKED, CLIENT_NACKED). + // envoy.service.status.v3.ClientConfig message. void DumpClientConfig(std::set* string_pool, upb_Arena* arena, envoy_service_status_v3_ClientConfig* client_config) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&mu_); diff --git a/test/core/xds/BUILD b/test/core/xds/BUILD index 3d831f908f545..8f209ab297257 100644 --- a/test/core/xds/BUILD +++ b/test/core/xds/BUILD @@ -182,6 +182,7 @@ grpc_cc_test( "//test/core/test_util:scoped_env_var", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/service/discovery/v3:pkg_cc_proto", + "@envoy_api//envoy/service/status/v3:pkg_cc_proto", ], ) diff --git a/test/core/xds/xds_client_test.cc b/test/core/xds/xds_client_test.cc index e9ba3f2b00561..c15d3215d7048 100644 --- a/test/core/xds/xds_client_test.cc +++ b/test/core/xds/xds_client_test.cc @@ -15,7 +15,6 @@ // // TODO(roth): Add the following tests: -// - tests for DumpClientConfigBinary() // - tests for load-reporting APIs? (or maybe move those out of XdsClient?) #include "src/core/xds/xds_client/xds_client.h" @@ -42,6 +41,7 @@ #include "absl/types/variant.h" #include "envoy/config/core/v3/base.pb.h" #include "envoy/service/discovery/v3/discovery.pb.h" +#include "envoy/service/status/v3/csds.pb.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "src/core/lib/iomgr/timer_manager.h" @@ -70,8 +70,10 @@ // IWYU pragma: no_include "google/protobuf/json/json.h" // IWYU pragma: no_include "google/protobuf/util/json_util.h" +using envoy::admin::v3::ClientResourceStatus; using envoy::service::discovery::v3::DiscoveryRequest; using envoy::service::discovery::v3::DiscoveryResponse; +using envoy::service::status::v3::ClientConfig; using grpc_event_engine::experimental::FuzzingEventEngine; namespace grpc_core { @@ -80,6 +82,13 @@ namespace { constexpr absl::string_view kDefaultXdsServerUrl = "default_xds_server"; +constexpr Timestamp kTime0 = + Timestamp::FromMillisecondsAfterProcessEpoch(10000); +constexpr Timestamp kTime1 = + Timestamp::FromMillisecondsAfterProcessEpoch(15000); +constexpr Timestamp kTime2 = + Timestamp::FromMillisecondsAfterProcessEpoch(20000); + class XdsClientTest : public ::testing::Test { protected: // A fake bootstrap implementation that allows tests to populate the @@ -755,6 +764,7 @@ class XdsClientTest : public ::testing::Test { } void SetUp() override { + time_cache_.TestOnlySetNow(kTime0); event_engine_ = std::make_shared( FuzzingEventEngine::Options(), fuzzing_event_engine::Actions()); grpc_timer_manager_set_start_threaded(false); @@ -891,28 +901,34 @@ class XdsClientTest : public ::testing::Test { // request against the client's node info. void CheckRequestNode(const DiscoveryRequest& request, SourceLocation location = SourceLocation()) { + return CheckNode(request.node(), location); + } + + // Helper function to check the contents of a node message against the + // client's node info. + void CheckNode(const envoy::config::core::v3::Node& node, + SourceLocation location = SourceLocation()) { // These fields come from the bootstrap config. - EXPECT_EQ(request.node().id(), xds_client_->bootstrap().node()->id()) + EXPECT_EQ(node.id(), xds_client_->bootstrap().node()->id()) << location.file() << ":" << location.line(); - EXPECT_EQ(request.node().cluster(), - xds_client_->bootstrap().node()->cluster()) + EXPECT_EQ(node.cluster(), xds_client_->bootstrap().node()->cluster()) << location.file() << ":" << location.line(); - EXPECT_EQ(request.node().locality().region(), + EXPECT_EQ(node.locality().region(), xds_client_->bootstrap().node()->locality_region()) << location.file() << ":" << location.line(); - EXPECT_EQ(request.node().locality().zone(), + EXPECT_EQ(node.locality().zone(), xds_client_->bootstrap().node()->locality_zone()) << location.file() << ":" << location.line(); - EXPECT_EQ(request.node().locality().sub_zone(), + EXPECT_EQ(node.locality().sub_zone(), xds_client_->bootstrap().node()->locality_sub_zone()) << location.file() << ":" << location.line(); if (xds_client_->bootstrap().node()->metadata().empty()) { - EXPECT_FALSE(request.node().has_metadata()) + EXPECT_FALSE(node.has_metadata()) << location.file() << ":" << location.line(); } else { std::string metadata_json_str; auto status = - MessageToJsonString(request.node().metadata(), &metadata_json_str, + MessageToJsonString(node.metadata(), &metadata_json_str, GRPC_CUSTOM_JSONUTIL::JsonPrintOptions()); ASSERT_TRUE(status.ok()) << status << " on " << location.file() << ":" << location.line(); @@ -927,12 +943,23 @@ class XdsClientTest : public ::testing::Test { << ":\nexpected: " << JsonDump(expected) << "\nactual: " << JsonDump(*metadata_json); } - EXPECT_EQ(request.node().user_agent_name(), "foo agent") + EXPECT_EQ(node.user_agent_name(), "foo agent") << location.file() << ":" << location.line(); - EXPECT_EQ(request.node().user_agent_version(), "foo version") + EXPECT_EQ(node.user_agent_version(), "foo version") << location.file() << ":" << location.line(); } + ClientConfig DumpCsds(SourceLocation location = SourceLocation()) { + std::string client_config_serialized = + XdsClientTestPeer(xds_client_.get()).TestDumpClientConfig(); + ClientConfig client_config_proto; + CHECK(client_config_proto.ParseFromString(client_config_serialized)) + << "at " << location.file() << ":" << location.line(); + CheckNode(client_config_proto.node(), location); + return client_config_proto; + } + + ScopedTimeCache time_cache_; std::shared_ptr event_engine_; RefCountedPtr transport_factory_; RefCountedPtr xds_client_; @@ -951,6 +978,124 @@ MATCHER_P3(ResourceCountLabelsEq, xds_authority, resource_type, cache_state, return ok; } +MATCHER_P(TimestampProtoEq, timestamp, "equals timestamp") { + gpr_timespec ts = {arg.seconds(), arg.nanos(), GPR_CLOCK_REALTIME}; + return ::testing::ExplainMatchResult( + timestamp, Timestamp::FromTimespecRoundDown(ts), result_listener); +} + +// Matches a CSDS ClientConfig proto. +// +// The resource_fields argument is a matcher that must validate the +// xds_config, version_info, and last_updated fields. Examples are +// CsdsResourceFields() and CsdsNoResourceFields(). +// +// The error_fields argument is a matcher that must validate the +// error_state field. Examples are CsdsErrorFields(), +// CsdsErrorDetailsOnly(), and CsdsNoErrorFields(). +MATCHER_P5(CsdsResourceEq, client_status, type_url, name, resource_fields, + error_fields, "equals CSDS resource") { + bool ok = true; + ok &= ::testing::ExplainMatchResult(arg.client_status(), client_status, + result_listener); + ok &= ::testing::ExplainMatchResult( + absl::StrCat("type.googleapis.com/", type_url), arg.type_url(), + result_listener); + ok &= ::testing::ExplainMatchResult(name, arg.name(), result_listener); + ok &= ::testing::ExplainMatchResult(resource_fields, arg, result_listener); + ok &= ::testing::ExplainMatchResult(error_fields, arg, result_listener); + return ok; +} + +// Validates the resource fields in a CSDS ClientConfig proto. Intended +// for use with CsdsResourceEq(). +MATCHER_P3(CsdsResourceFields, resource, version, last_updated, + "CSDS resource fields") { + bool ok = true; + ok &= ::testing::ExplainMatchResult(resource, arg.xds_config().value(), + result_listener); + ok &= ::testing::ExplainMatchResult(version, arg.version_info(), + result_listener); + ok &= ::testing::ExplainMatchResult(last_updated, arg.last_updated(), + result_listener); + return ok; +} + +// Validates the resource fields are not present in a CSDS ClientConfig +// proto. Intended for use with CsdsResourceEq(). +MATCHER(CsdsNoResourceFields, "CSDS has no resource fields") { + bool ok = true; + ok &= ::testing::ExplainMatchResult(::testing::IsFalse(), + arg.has_xds_config(), result_listener); + ok &= ::testing::ExplainMatchResult("", arg.version_info(), result_listener); + ok &= ::testing::ExplainMatchResult(::testing::IsFalse(), + arg.has_last_updated(), result_listener); + return ok; +} + +// Validates the error fields in a CSDS ClientConfig proto. Intended +// for use with CsdsResourceEq(). +MATCHER_P3(CsdsErrorFields, error_details, error_version, error_time, + "CSDS error fields") { + bool ok = true; + ok &= ::testing::ExplainMatchResult( + error_details, arg.error_state().details(), result_listener); + ok &= ::testing::ExplainMatchResult( + error_version, arg.error_state().version_info(), result_listener); + ok &= ::testing::ExplainMatchResult( + error_time, arg.error_state().last_update_attempt(), result_listener); + return ok; +} + +// Same as CsdsErrorFields, but expects the error details without a +// version or timestamp. +MATCHER_P(CsdsErrorDetailsOnly, error_details, "CSDS error details only") { + bool ok = true; + ok &= ::testing::ExplainMatchResult( + error_details, arg.error_state().details(), result_listener); + ok &= ::testing::ExplainMatchResult("", arg.error_state().version_info(), + result_listener); + ok &= ::testing::ExplainMatchResult( + ::testing::IsFalse(), arg.error_state().has_last_update_attempt(), + result_listener); + return ok; +} + +// Validates that there is no error in a CSDS ClientConfig proto. Intended +// for use with CsdsResourceEq(). +MATCHER(CsdsNoErrorFields, "CSDS has no error fields") { + return ::testing::ExplainMatchResult(::testing::IsFalse(), + arg.has_error_state(), result_listener); +} + +// Convenient wrapper for ACKED resources in CSDS. +MATCHER_P5(CsdsResourceAcked, type_url, name, resource, version, last_updated, + "equals CSDS ACKED resource") { + return ::testing::ExplainMatchResult( + CsdsResourceEq(ClientResourceStatus::ACKED, type_url, name, + CsdsResourceFields(resource, version, last_updated), + CsdsNoErrorFields()), + arg, result_listener); +} + +// Convenient wrapper for REQUESTED resources in CSDS. +MATCHER_P2(CsdsResourceRequested, type_url, name, + "equals CSDS requested resource") { + return ::testing::ExplainMatchResult( + CsdsResourceEq(ClientResourceStatus::REQUESTED, type_url, name, + CsdsNoResourceFields(), CsdsNoErrorFields()), + arg, result_listener); +} + +// Convenient wrapper for DOES_NOT_EXIST resources in CSDS. +MATCHER_P2(CsdsResourceDoesNotExist, type_url, name, + "equals CSDS does-not-exist resource") { + return ::testing::ExplainMatchResult( + CsdsResourceEq(ClientResourceStatus::DOES_NOT_EXIST, type_url, name, + CsdsNoResourceFields(), CsdsNoErrorFields()), + arg, result_listener); +} + TEST_F(XdsClientTest, BasicWatch) { InitXdsClient(); // Metrics should initially be empty. @@ -961,6 +1106,9 @@ TEST_F(XdsClientTest, BasicWatch) { EXPECT_THAT(GetResourceCounts(), ::testing::ElementsAre()); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre()); EXPECT_THAT(metrics_reporter_->server_failures(), ::testing::ElementsAre()); + // CSDS should initially be empty. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), ::testing::ElementsAre()); // Start a watch for "foo1". auto watcher = StartFooWatch("foo1"); // Check metrics. @@ -976,6 +1124,11 @@ TEST_F(XdsClientTest, BasicWatch) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // CSDS should show that the resource has been requested. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // Watcher should initially not see any resource reported. EXPECT_FALSE(watcher->HasEvent()); // XdsClient should have created an ADS stream. @@ -1016,6 +1169,12 @@ TEST_F(XdsClientTest, BasicWatch) { 1))); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre(::testing::Pair( kDefaultXdsServerUrl, true))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1035,6 +1194,9 @@ TEST_F(XdsClientTest, BasicWatch) { ::testing::ElementsAre(), ::testing::_)); EXPECT_THAT(GetResourceCounts(), ::testing::ElementsAre()); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre()); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), ::testing::ElementsAre()); } TEST_F(XdsClientTest, UpdateFromServer) { @@ -1079,6 +1241,12 @@ TEST_F(XdsClientTest, UpdateFromServer) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1087,6 +1255,8 @@ TEST_F(XdsClientTest, UpdateFromServer) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"foo1"}); // Server sends an updated version of the resource. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("2") @@ -1111,6 +1281,12 @@ TEST_F(XdsClientTest, UpdateFromServer) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "2", TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1165,6 +1341,12 @@ TEST_F(XdsClientTest, MultipleWatchersForSameResource) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1211,6 +1393,12 @@ TEST_F(XdsClientTest, MultipleWatchersForSameResource) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "2", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1244,6 +1432,11 @@ TEST_F(XdsClientTest, SubscribeToMultipleResources) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // CSDS should show that the resource has been requested. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // XdsClient should have created an ADS stream. auto stream = WaitForAdsStream(); ASSERT_TRUE(stream != nullptr); @@ -1280,6 +1473,12 @@ TEST_F(XdsClientTest, SubscribeToMultipleResources) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1302,6 +1501,15 @@ TEST_F(XdsClientTest, SubscribeToMultipleResources) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), + "foo2"))); // XdsClient should have sent a subscription request on the ADS stream. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1310,6 +1518,8 @@ TEST_F(XdsClientTest, SubscribeToMultipleResources) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"foo1", "foo2"}); // Send a response. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("1") @@ -1317,10 +1527,10 @@ TEST_F(XdsClientTest, SubscribeToMultipleResources) { .AddFooResource(XdsFooResource("foo2", 7)) .Serialize()); // XdsClient should have delivered the response to the watcher. - resource = watcher2->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, "foo2"); - EXPECT_EQ(resource->value, 7); + auto resource2 = watcher2->WaitForNextResource(); + ASSERT_NE(resource2, nullptr); + EXPECT_EQ(resource2->name, "foo2"); + EXPECT_EQ(resource2->value, 7); // Check metric data. EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::ElementsAre(::testing::Pair( @@ -1334,6 +1544,16 @@ TEST_F(XdsClientTest, SubscribeToMultipleResources) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 2))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo2", resource2->AsJsonString(), "1", + TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1350,6 +1570,12 @@ TEST_F(XdsClientTest, SubscribeToMultipleResources) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo2", + resource2->AsJsonString(), "1", TimestampProtoEq(kTime1)))); // XdsClient should send an unsubscription request. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1407,6 +1633,8 @@ TEST_F(XdsClientTest, UpdateContainsOnlyChangedResource) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"foo1", "foo2"}); // Send a response. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("1") @@ -1414,10 +1642,10 @@ TEST_F(XdsClientTest, UpdateContainsOnlyChangedResource) { .AddFooResource(XdsFooResource("foo2", 7)) .Serialize()); // XdsClient should have delivered the response to the watcher. - resource = watcher2->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, "foo2"); - EXPECT_EQ(resource->value, 7); + auto resource2 = watcher2->WaitForNextResource(); + ASSERT_NE(resource2, nullptr); + EXPECT_EQ(resource2->name, "foo2"); + EXPECT_EQ(resource2->value, 7); // Check metric data. EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::ElementsAre(::testing::Pair( @@ -1431,6 +1659,16 @@ TEST_F(XdsClientTest, UpdateContainsOnlyChangedResource) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 2))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo2", resource2->AsJsonString(), "1", + TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1439,6 +1677,8 @@ TEST_F(XdsClientTest, UpdateContainsOnlyChangedResource) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"foo1", "foo2"}); // Server sends an update for "foo1". The response does not contain "foo2". + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime2); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("2") @@ -1463,6 +1703,16 @@ TEST_F(XdsClientTest, UpdateContainsOnlyChangedResource) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 2))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "2", + TimestampProtoEq(kTime2)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo2", resource2->AsJsonString(), "1", + TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1498,6 +1748,11 @@ TEST_F(XdsClientTest, ResourceValidationFailure) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // CSDS should show that the resource has been requested. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // Watcher should initially not see any resource reported. EXPECT_FALSE(watcher->HasEvent()); // XdsClient should have created an ADS stream. @@ -1541,6 +1796,16 @@ TEST_F(XdsClientTest, ResourceValidationFailure) { XdsFooResourceType::Get()->type_url(), "nacked"), 1))); + // CSDS should show that the resource has been NACKed. + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceEq( + ClientResourceStatus::NACKED, XdsFooResourceType::Get()->type_url(), + "foo1", CsdsNoResourceFields(), + CsdsErrorFields("INVALID_ARGUMENT: errors validating JSON: " + "[field:value error:is not a number]", + "1", TimestampProtoEq(kTime0))))); // XdsClient should NACK the update. // Note that version_info is not populated in the request. request = WaitForRequest(stream.get()); @@ -1565,6 +1830,8 @@ TEST_F(XdsClientTest, ResourceValidationFailure) { "[field:value error:is not a number] (node ID:xds_client_test)") << *error; // Now server sends an updated version of the resource. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("2") @@ -1597,6 +1864,12 @@ TEST_F(XdsClientTest, ResourceValidationFailure) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "2", TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1627,6 +1900,11 @@ TEST_F(XdsClientTest, ResourceValidationFailureMultipleResources) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // CSDS should show that the resource has been requested. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // XdsClient should have created an ADS stream. auto stream = WaitForAdsStream(); ASSERT_TRUE(stream != nullptr); @@ -1649,6 +1927,14 @@ TEST_F(XdsClientTest, ResourceValidationFailureMultipleResources) { XdsFooResourceType::Get()->type_url(), "requested"), 2))); + // CSDS should show that both resourcees have been requested. + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::ElementsAre( + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), "foo1"), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), + "foo2"))); // Client should send another request. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1667,6 +1953,15 @@ TEST_F(XdsClientTest, ResourceValidationFailureMultipleResources) { XdsFooResourceType::Get()->type_url(), "requested"), 3))); + // Check CSDS. + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), "foo1"), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), "foo2"), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), + "foo3"))); // Client should send another request. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1685,6 +1980,16 @@ TEST_F(XdsClientTest, ResourceValidationFailureMultipleResources) { XdsFooResourceType::Get()->type_url(), "requested"), 4))); + // Check CSDS. + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), "foo1"), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), "foo2"), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), "foo3"), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), + "foo4"))); // Client should send another request. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1773,6 +2078,29 @@ TEST_F(XdsClientTest, ResourceValidationFailureMultipleResources) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // Check CSDS. + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceEq( + ClientResourceStatus::NACKED, + XdsFooResourceType::Get()->type_url(), "foo1", + CsdsNoResourceFields(), + CsdsErrorFields("INVALID_ARGUMENT: errors validating JSON: " + "[field:value error:is not a number]", + "1", TimestampProtoEq(kTime0))), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), "foo2"), + CsdsResourceEq( + ClientResourceStatus::NACKED, + XdsFooResourceType::Get()->type_url(), "foo3", + CsdsNoResourceFields(), + CsdsErrorFields("INVALID_ARGUMENT: JSON parsing failed: " + "[JSON parse error at index 15]", + "1", TimestampProtoEq(kTime0))), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), "foo4", + resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)))); // XdsClient should NACK the update. // There was one good resource, so the version will be updated. request = WaitForRequest(stream.get()); @@ -1851,6 +2179,12 @@ TEST_F(XdsClientTest, ResourceValidationFailureForCachedResource) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -1859,6 +2193,8 @@ TEST_F(XdsClientTest, ResourceValidationFailureForCachedResource) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"foo1"}); // Send an update containing an invalid resource. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("2") @@ -1891,6 +2227,17 @@ TEST_F(XdsClientTest, ResourceValidationFailureForCachedResource) { XdsFooResourceType::Get()->type_url(), "nacked_but_cached"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceEq( + ClientResourceStatus::NACKED, + XdsFooResourceType::Get()->type_url(), "foo1", + CsdsResourceFields(resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsErrorFields("INVALID_ARGUMENT: errors validating JSON: " + "[field:value error:is not a number]", + "2", TimestampProtoEq(kTime1))))); // XdsClient should NACK the update. // Note that version_info is set to the previous version in this request, // because there were no valid resources in it. @@ -1973,6 +2320,12 @@ TEST_F(XdsClientTest, WildcardCapableResponseWithEmptyResource) { XdsClient::kOldStyleAuthority, XdsWildcardCapableResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsWildcardCapableResourceType::Get()->type_url(), "wc1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should NACK the update. // There was one good resource, so the version will be updated. request = WaitForRequest(stream.get()); @@ -2037,6 +2390,12 @@ TEST_F(XdsClientTest, ResourceDeletion) { XdsClient::kOldStyleAuthority, XdsWildcardCapableResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsWildcardCapableResourceType::Get()->type_url(), "wc1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -2067,6 +2426,11 @@ TEST_F(XdsClientTest, ResourceDeletion) { XdsWildcardCapableResourceType::Get()->type_url(), "does_not_exist"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceDoesNotExist( + XdsWildcardCapableResourceType::Get()->type_url(), "wc1"))); // Start a new watcher for the same resource. It should immediately // receive the same does-not-exist notification. auto watcher2 = StartWildcardCapableWatch("wc1"); @@ -2079,6 +2443,8 @@ TEST_F(XdsClientTest, ResourceDeletion) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"wc1"}); // Server sends the resource again. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsWildcardCapableResourceType::Get()->type_url()) .set_version_info("3") @@ -2108,6 +2474,12 @@ TEST_F(XdsClientTest, ResourceDeletion) { XdsClient::kOldStyleAuthority, XdsWildcardCapableResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsWildcardCapableResourceType::Get()->type_url(), "wc1", + resource->AsJsonString(), "3", TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -2167,6 +2539,12 @@ TEST_F(XdsClientTest, ResourceDeletionIgnoredWhenConfigured) { XdsClient::kOldStyleAuthority, XdsWildcardCapableResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsWildcardCapableResourceType::Get()->type_url(), "wc1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -2176,6 +2554,9 @@ TEST_F(XdsClientTest, ResourceDeletionIgnoredWhenConfigured) { /*resource_names=*/{"wc1"}); // Server now sends a response without the resource, thus indicating // it's been deleted. + // We increment time to make sure that the CSDS data does NOT get a + // new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsWildcardCapableResourceType::Get()->type_url()) .set_version_info("2") @@ -2198,6 +2579,12 @@ TEST_F(XdsClientTest, ResourceDeletionIgnoredWhenConfigured) { XdsClient::kOldStyleAuthority, XdsWildcardCapableResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsWildcardCapableResourceType::Get()->type_url(), "wc1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // Start a new watcher for the same resource. It should immediately // receive the cached resource. auto watcher2 = StartWildcardCapableWatch("wc1"); @@ -2213,6 +2600,8 @@ TEST_F(XdsClientTest, ResourceDeletionIgnoredWhenConfigured) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"wc1"}); // Server sends a new value for the resource. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime2); stream->SendMessageToClient( ResponseBuilder(XdsWildcardCapableResourceType::Get()->type_url()) .set_version_info("3") @@ -2242,6 +2631,12 @@ TEST_F(XdsClientTest, ResourceDeletionIgnoredWhenConfigured) { XdsClient::kOldStyleAuthority, XdsWildcardCapableResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsWildcardCapableResourceType::Get()->type_url(), "wc1", + resource->AsJsonString(), "3", TimestampProtoEq(kTime2)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -2648,6 +3043,11 @@ TEST_F(XdsClientTest, ResourceDoesNotExistUponTimeout) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // CSDS should show that the resource has been requested. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // XdsClient should have created an ADS stream. auto stream = WaitForAdsStream(); ASSERT_TRUE(stream != nullptr); @@ -2671,6 +3071,11 @@ TEST_F(XdsClientTest, ResourceDoesNotExistUponTimeout) { XdsFooResourceType::Get()->type_url(), "does_not_exist"), 1))); + // CSDS should show that the resource has been requested. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceDoesNotExist( + XdsFooResourceType::Get()->type_url(), "foo1"))); // Start a new watcher for the same resource. It should immediately // receive the same does-not-exist notification. auto watcher2 = StartFooWatch("foo1"); @@ -2704,6 +3109,12 @@ TEST_F(XdsClientTest, ResourceDoesNotExistUponTimeout) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -2738,6 +3149,11 @@ TEST_F(XdsClientTest, ResourceDoesNotExistAfterStreamRestart) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // CSDS should show that the resource has been requested. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // Watcher should initially not see any resource reported. EXPECT_FALSE(watcher->HasEvent()); // XdsClient should have created an ADS stream. @@ -2771,6 +3187,11 @@ TEST_F(XdsClientTest, ResourceDoesNotExistAfterStreamRestart) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // CSDS should show that the resource has been requested. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // XdsClient should create a new stream. stream = WaitForAdsStream(); ASSERT_TRUE(stream != nullptr); @@ -2794,6 +3215,11 @@ TEST_F(XdsClientTest, ResourceDoesNotExistAfterStreamRestart) { XdsFooResourceType::Get()->type_url(), "does_not_exist"), 1))); + // CSDS should show that the resource has been requested. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceDoesNotExist( + XdsFooResourceType::Get()->type_url(), "foo1"))); // Server now sends the requested resource. stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) @@ -2819,6 +3245,12 @@ TEST_F(XdsClientTest, ResourceDoesNotExistAfterStreamRestart) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient sends an ACK. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -2862,6 +3294,11 @@ TEST_F(XdsClientTest, DoesNotExistTimerNotStartedUntilSendCompletes) { XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // CSDS should show that the resource has been requested. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // The ADS stream uses wait_for_ready inside the XdsTransport interface, // so when the channel connects, the already-started stream will proceed. stream->CompleteSendMessageFromClient(); @@ -2875,6 +3312,11 @@ TEST_F(XdsClientTest, DoesNotExistTimerNotStartedUntilSendCompletes) { XdsFooResourceType::Get()->type_url(), "does_not_exist"), 1))); + // CSDS should show that the resource has been requested. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceDoesNotExist( + XdsFooResourceType::Get()->type_url(), "foo1"))); // Now server sends a response. stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) @@ -2894,6 +3336,12 @@ TEST_F(XdsClientTest, DoesNotExistTimerNotStartedUntilSendCompletes) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -2960,6 +3408,12 @@ TEST_F(XdsClientTest, ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -2985,6 +3439,15 @@ TEST_F(XdsClientTest, XdsFooResourceType::Get()->type_url(), "requested"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), + "foo2"))); // XdsClient sends a request to subscribe to the new resource. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3002,6 +3465,10 @@ TEST_F(XdsClientTest, XdsFooResourceType::Get()->type_url(), "requested"), 1))); + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo2"))); watcher = StartFooWatch("foo1"); EXPECT_THAT(GetResourceCounts(), ::testing::ElementsAre(::testing::Pair( @@ -3009,7 +3476,16 @@ TEST_F(XdsClientTest, XdsFooResourceType::Get()->type_url(), "requested"), 2))); + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::ElementsAre( + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), "foo1"), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), + "foo2"))); // Now send a response from the server containing both foo1 and foo2. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("1") @@ -3025,10 +3501,10 @@ TEST_F(XdsClientTest, EXPECT_EQ(resource->name, "foo1"); EXPECT_EQ(resource->value, 6); // For foo2, the watcher should receive notification for the new resource. - resource = watcher2->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, "foo2"); - EXPECT_EQ(resource->value, 7); + auto resource2 = watcher2->WaitForNextResource(); + ASSERT_NE(resource2, nullptr); + EXPECT_EQ(resource2->name, "foo2"); + EXPECT_EQ(resource2->value, 7); // Check metric data. EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::ElementsAre(::testing::Pair( @@ -3042,6 +3518,16 @@ TEST_F(XdsClientTest, ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 2))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "1", + TimestampProtoEq(kTime1)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo2", resource2->AsJsonString(), "1", + TimestampProtoEq(kTime1)))); // Now we finally tell XdsClient that its previous send_message op is // complete. stream->CompleteSendMessageFromClient(); @@ -3105,6 +3591,12 @@ TEST_F(XdsClientTest, DoNotSendDoesNotExistForCachedResource) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3145,7 +3637,15 @@ TEST_F(XdsClientTest, DoNotSendDoesNotExistForCachedResource) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // Now server sends a response. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("1") @@ -3169,6 +3669,12 @@ TEST_F(XdsClientTest, DoNotSendDoesNotExistForCachedResource) { 1))); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre(::testing::Pair( kDefaultXdsServerUrl, true))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3224,6 +3730,12 @@ TEST_F(XdsClientTest, ResourceWrappedInResourceMessage) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3278,6 +3790,12 @@ TEST_F(XdsClientTest, MultipleResourceTypes) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3297,6 +3815,8 @@ TEST_F(XdsClientTest, MultipleResourceTypes) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"bar1"}); // Send a response. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsBarResourceType::Get()->type_url()) .set_version_info("2") @@ -3331,6 +3851,16 @@ TEST_F(XdsClientTest, MultipleResourceTypes) { XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceAcked(XdsBarResourceType::Get()->type_url(), + "bar1", resource2->AsJsonString(), "2", + TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3450,6 +3980,12 @@ TEST_F(XdsClientTest, Federation) { 1))); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre(::testing::Pair( kDefaultXdsServerUrl, true))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3477,6 +4013,15 @@ TEST_F(XdsClientTest, Federation) { ::testing::ElementsAre( ::testing::Pair(kDefaultXdsServerUrl, true), ::testing::Pair(authority_server.server_uri(), true))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceRequested(XdsFooResourceType::Get()->type_url(), + kXdstpResourceName))); // XdsClient will create a new stream to the server for this authority. auto stream2 = WaitForAdsStream(authority_server); ASSERT_TRUE(stream2 != nullptr); @@ -3491,6 +4036,8 @@ TEST_F(XdsClientTest, Federation) { /*resource_names=*/{kXdstpResourceName}); CheckRequestNode(*request); // Should be present on the first request. // Send a response. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream2->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("2") @@ -3498,10 +4045,10 @@ TEST_F(XdsClientTest, Federation) { .AddFooResource(XdsFooResource(kXdstpResourceName, 3)) .Serialize()); // XdsClient should have delivered the response to the watcher. - resource = watcher2->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, kXdstpResourceName); - EXPECT_EQ(resource->value, 3); + auto resource2 = watcher2->WaitForNextResource(); + ASSERT_NE(resource2, nullptr); + EXPECT_EQ(resource2->name, kXdstpResourceName); + EXPECT_EQ(resource2->value, 3); // Check metric data. EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::ElementsAre( @@ -3529,6 +4076,17 @@ TEST_F(XdsClientTest, Federation) { ::testing::ElementsAre( ::testing::Pair(kDefaultXdsServerUrl, true), ::testing::Pair(authority_server.server_uri(), true))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + kXdstpResourceName, resource2->AsJsonString(), "2", + TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream2.get()); ASSERT_TRUE(request.has_value()); @@ -3594,6 +4152,12 @@ TEST_F(XdsClientTest, FederationAuthorityDefaultsToTopLevelXdsServer) { 1))); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre(::testing::Pair( kDefaultXdsServerUrl, true))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3615,6 +4179,8 @@ TEST_F(XdsClientTest, FederationAuthorityDefaultsToTopLevelXdsServer) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"foo1", kXdstpResourceName}); // Send a response. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("2") @@ -3622,10 +4188,10 @@ TEST_F(XdsClientTest, FederationAuthorityDefaultsToTopLevelXdsServer) { .AddFooResource(XdsFooResource(kXdstpResourceName, 3)) .Serialize()); // XdsClient should have delivered the response to the watcher. - resource = watcher2->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, kXdstpResourceName); - EXPECT_EQ(resource->value, 3); + auto resource2 = watcher2->WaitForNextResource(); + ASSERT_NE(resource2, nullptr); + EXPECT_EQ(resource2->name, kXdstpResourceName); + EXPECT_EQ(resource2->value, 3); // Check metric data. EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::ElementsAre(::testing::Pair( @@ -3646,6 +4212,17 @@ TEST_F(XdsClientTest, FederationAuthorityDefaultsToTopLevelXdsServer) { 1))); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre(::testing::Pair( kDefaultXdsServerUrl, true))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + kXdstpResourceName, resource2->AsJsonString(), "2", + TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3801,6 +4378,12 @@ TEST_F(XdsClientTest, FederationChannelFailureReportedToWatchers) { 1))); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre(::testing::Pair( kDefaultXdsServerUrl, true))); + // Check CSDS data. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", TimestampProtoEq(kTime0)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -3831,6 +4414,8 @@ TEST_F(XdsClientTest, FederationChannelFailureReportedToWatchers) { /*resource_names=*/{kXdstpResourceName}); CheckRequestNode(*request); // Should be present on the first request. // Send a response. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream2->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("2") @@ -3838,10 +4423,10 @@ TEST_F(XdsClientTest, FederationChannelFailureReportedToWatchers) { .AddFooResource(XdsFooResource(kXdstpResourceName, 3)) .Serialize()); // XdsClient should have delivered the response to the watcher. - resource = watcher2->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, kXdstpResourceName); - EXPECT_EQ(resource->value, 3); + auto resource2 = watcher2->WaitForNextResource(); + ASSERT_NE(resource2, nullptr); + EXPECT_EQ(resource2->name, kXdstpResourceName); + EXPECT_EQ(resource2->value, 3); // Check metric data. EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::ElementsAre( @@ -3869,6 +4454,17 @@ TEST_F(XdsClientTest, FederationChannelFailureReportedToWatchers) { ::testing::ElementsAre( ::testing::Pair(kDefaultXdsServerUrl, true), ::testing::Pair(authority_server.server_uri(), true))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT( + csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "1", + TimestampProtoEq(kTime0)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + kXdstpResourceName, resource2->AsJsonString(), "2", + TimestampProtoEq(kTime1)))); // XdsClient should have sent an ACK message to the xDS server. request = WaitForRequest(stream2.get()); ASSERT_TRUE(request.has_value()); @@ -4009,6 +4605,11 @@ TEST_F(XdsClientTest, FallbackAndRecover) { 1))); EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::IsEmpty(), ::testing::_, ::testing::ElementsAre())); + // CSDS should show that the resource has been requested. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // XdsClient should have created an ADS stream. auto stream = WaitForAdsStream(); ASSERT_TRUE(stream != nullptr); @@ -4044,6 +4645,12 @@ TEST_F(XdsClientTest, FallbackAndRecover) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 1))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre(CsdsResourceAcked( + XdsFooResourceType::Get()->type_url(), "foo1", + resource->AsJsonString(), "20", TimestampProtoEq(kTime0)))); // Result (remote): Client sends ACK to server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -4127,6 +4734,8 @@ TEST_F(XdsClientTest, FallbackAndRecover) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"foo1", "foo2"}); // Input: Fallback server sends a response with both resources. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime1); stream2->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("5") @@ -4139,10 +4748,10 @@ TEST_F(XdsClientTest, FallbackAndRecover) { ASSERT_NE(resource, nullptr); EXPECT_EQ(resource->name, "foo1"); EXPECT_EQ(resource->value, 20); - resource = watcher2->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, "foo2"); - EXPECT_EQ(resource->value, 30); + auto resource2 = watcher2->WaitForNextResource(); + ASSERT_NE(resource2, nullptr); + EXPECT_EQ(resource2->name, "foo2"); + EXPECT_EQ(resource2->value, 30); // Result (local): Metrics show an update from fallback server. EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::ElementsAre( @@ -4165,6 +4774,16 @@ TEST_F(XdsClientTest, FallbackAndRecover) { ResourceCountLabelsEq(XdsClient::kOldStyleAuthority, XdsFooResourceType::Get()->type_url(), "acked"), 2))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "5", + TimestampProtoEq(kTime1)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo2", resource2->AsJsonString(), "5", + TimestampProtoEq(kTime1)))); // Result (remote): Client sends ACK to fallback server. request = WaitForRequest(stream2.get()); ASSERT_TRUE(request.has_value()); @@ -4173,6 +4792,8 @@ TEST_F(XdsClientTest, FallbackAndRecover) { /*error_detail=*/absl::OkStatus(), /*resource_names=*/{"foo1", "foo2"}); // Input: Primary server sends a response containing both resources. + // We increment time to make sure that the CSDS data gets a new timestamp. + time_cache_.TestOnlySetNow(kTime2); stream->SendMessageToClient( ResponseBuilder(XdsFooResourceType::Get()->type_url()) .set_version_info("15") @@ -4180,6 +4801,15 @@ TEST_F(XdsClientTest, FallbackAndRecover) { .AddFooResource(XdsFooResource("foo1", 35)) .AddFooResource(XdsFooResource("foo2", 25)) .Serialize()); + // Result (local): Resources are delivered to watchers. + resource = watcher->WaitForNextResource(); + ASSERT_NE(resource, nullptr); + EXPECT_EQ(resource->name, "foo1"); + EXPECT_EQ(resource->value, 35); + resource2 = watcher2->WaitForNextResource(); + ASSERT_NE(resource2, nullptr); + EXPECT_EQ(resource2->name, "foo2"); + EXPECT_EQ(resource2->value, 25); // Result (local): Metrics show that we've closed the channel to the fallback // server and received resource updates from the primary server. EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( @@ -4196,17 +4826,18 @@ TEST_F(XdsClientTest, FallbackAndRecover) { ::testing::ElementsAre(::testing::Pair(kDefaultXdsServerUrl, 1)))); EXPECT_THAT(GetServerConnections(), ::testing::ElementsAre(::testing::Pair( kDefaultXdsServerUrl, true))); + // Check CSDS data. + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::UnorderedElementsAre( + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo1", resource->AsJsonString(), "15", + TimestampProtoEq(kTime2)), + CsdsResourceAcked(XdsFooResourceType::Get()->type_url(), + "foo2", resource2->AsJsonString(), "15", + TimestampProtoEq(kTime2)))); // Result (remote): The stream to the fallback server has been orphaned. EXPECT_TRUE(stream2->IsOrphaned()); - // Result (local): Resources are delivered to watchers. - resource = watcher->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, "foo1"); - EXPECT_EQ(resource->value, 35); - resource = watcher2->WaitForNextResource(); - ASSERT_NE(resource, nullptr); - EXPECT_EQ(resource->name, "foo2"); - EXPECT_EQ(resource->value, 25); // Result (remote): Client sends ACK to server. request = WaitForRequest(stream.get()); ASSERT_TRUE(request.has_value()); @@ -4254,6 +4885,11 @@ TEST_F(XdsClientTest, FallbackReportsError) { EXPECT_TRUE(metrics_reporter_->WaitForMetricsReporterData( ::testing::_, ::testing::_, ::testing::ElementsAre(::testing::Pair(kDefaultXdsServerUrl, 1)))); + // CSDS should show that the resource has been requested. + ClientConfig csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); // Fallback happens now stream = WaitForAdsStream(fallback_server); ASSERT_NE(stream, nullptr); @@ -4274,6 +4910,10 @@ TEST_F(XdsClientTest, FallbackReportsError) { ::testing::ElementsAre( ::testing::Pair(kDefaultXdsServerUrl, 1), ::testing::Pair(fallback_server.server_uri(), 1)))); + csds = DumpCsds(); + EXPECT_THAT(csds.generic_xds_configs(), + ::testing::ElementsAre(CsdsResourceRequested( + XdsFooResourceType::Get()->type_url(), "foo1"))); auto error = watcher->WaitForNextError(); ASSERT_TRUE(error.has_value()); EXPECT_THAT(error->code(), absl::StatusCode::kUnavailable); diff --git a/test/core/xds/xds_client_test_peer.h b/test/core/xds/xds_client_test_peer.h index d65b9763748fe..7e34859cff5df 100644 --- a/test/core/xds/xds_client_test_peer.h +++ b/test/core/xds/xds_client_test_peer.h @@ -32,12 +32,16 @@ class XdsClientTestPeer { public: explicit XdsClientTestPeer(XdsClient* xds_client) : xds_client_(xds_client) {} - void TestDumpClientConfig() { + std::string TestDumpClientConfig() { upb::Arena arena; - auto client_config = envoy_service_status_v3_ClientConfig_new(arena.ptr()); + auto* client_config = envoy_service_status_v3_ClientConfig_new(arena.ptr()); std::set string_pool; MutexLock lock(xds_client_->mu()); xds_client_->DumpClientConfig(&string_pool, arena.ptr(), client_config); + size_t output_length; + char* output = envoy_service_status_v3_ClientConfig_serialize( + client_config, arena.ptr(), &output_length); + return std::string(output, output_length); } struct ResourceCountLabels {