Skip to content

Commit

Permalink
ZooKeeper proxy filter (envoyproxy#5991)
Browse files Browse the repository at this point in the history
This filter decodes the ZooKeeper wire protocol and emits
stats & metadata about requests, responses and events.

This wire protocol parsing is based on:

https://github.com/twitter/zktraffic
https://github.com/rgs1/zktraffic-cpp

The actual filter structure is based on the Mysql proxy filter.

Signed-off-by: Raul Gutierrez Segales <[email protected]>
  • Loading branch information
rgs1 authored and snowp committed Mar 25, 2019
1 parent 0ac3706 commit b771f99
Show file tree
Hide file tree
Showing 22 changed files with 2,241 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@
/*/extensions/filters/network/mysql_proxy @rshriram @venilnoronha @mattklein123
# quic extension
/*/extensions/quic_listeners/ @alyssawilk @danzh2010 @mattklein123 @mpwarres @wu-bin
# zookeeper_proxy extension
/*/extensions/filters/network/zookeeper_proxy @rgs1 @snowp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("//bazel:api_build_system.bzl", "api_proto_library_internal")

licenses(["notice"]) # Apache 2

api_proto_library_internal(
name = "zookeeper_proxy",
srcs = ["zookeeper_proxy.proto"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
syntax = "proto3";

package envoy.config.filter.network.zookeeper_proxy.v1alpha1;

option java_outer_classname = "ZookeeperProxyProto";
option java_multiple_files = true;
option java_package = "io.envoyproxy.envoy.config.filter.network.zookeeper_proxy.v1alpha1";
option go_package = "v1alpha1";

import "validate/validate.proto";
import "google/protobuf/wrappers.proto";

// [#protodoc-title: ZooKeeper proxy]
// ZooKeeper Proxy :ref:`configuration overview <config_network_filters_zookeeper_proxy>`.
message ZooKeeperProxy {
// The human readable prefix to use when emitting :ref:`statistics
// <config_network_filters_zookeeper_proxy_stats>`.
string stat_prefix = 1 [(validate.rules).string.min_bytes = 1];

// [#not-implemented-hide:] The optional path to use for writing ZooKeeper access logs.
// If the access log field is empty, access logs will not be written.
string access_log = 2;

// Messages — requests, responses and events — that are bigger than this value will
// be ignored. If it is not set, the default value is 1Mb.
//
// The value here should match the jute.maxbuffer property in your cluster configuration:
//
// https://zookeeper.apache.org/doc/r3.4.10/zookeeperAdmin.html#Unsafe+Options
//
// if that is set. If it isn't, ZooKeeper's default is also 1Mb.
google.protobuf.UInt32Value max_packet_bytes = 3;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ filters.
tcp_proxy_filter
thrift_proxy_filter
sni_cluster_filter
zookeeper_proxy_filter
92 changes: 92 additions & 0 deletions docs/root/configuration/network_filters/zookeeper_proxy_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
.. _config_network_filters_zookeeper_proxy:

ZooKeeper proxy
===============

The ZooKeeper proxy filter decodes the client protocol for
`Apache ZooKeeper <https://zookeeper.apache.org/>`_. It decodes the requests,
responses and events in the payload. Most opcodes known in
`ZooKeeper 3.5 <https://github.com/apache/zookeeper/blob/master/zookeeper-server/src/main/java/org/apache/zookeeper/ZooDefs.java>`_
are supported. The unsupported ones are related to SASL authentication.

.. attention::

The zookeeper_proxy filter is experimental and is currently under active
development. Capabilities will be expanded over time and the
configuration structures are likely to change.

.. _config_network_filters_zookeeper_proxy_config:

Configuration
-------------

The ZooKeeper proxy filter should be chained with the TCP proxy filter as shown
in the configuration snippet below:

.. code-block:: yaml
filter_chains:
- filters:
- name: envoy.filters.network.zookeeper_proxy
config:
stat_prefix: zookeeper
- name: envoy.tcp_proxy
config:
stat_prefix: tcp
cluster: ...
.. _config_network_filters_zookeeper_proxy_stats:

Statistics
----------

Every configured ZooKeeper proxy filter has statistics rooted at *zookeeper.<stat_prefix>.* with the
following statistics:

.. csv-table::
:header: Name, Type, Description
:widths: 1, 1, 2

decoder_error, Counter, Number of times a message wasn't decoded
request_bytes, Counter, Number of bytes in decoded request messages
connect_rq, Counter, Number of regular connect (non-readonly) requests
connect_readonly_rq, Counter, Number of connect requests with the readonly flag set
ping_rq, Counter, Number of ping requests
auth.<type>_rq, Counter, Number of auth requests for a given type
getdata_rq, Counter, Number of getdata requests
create_rq, Counter, Number of create requests
create2_rq, Counter, Number of create2 requests
setdata_rq, Counter, Number of setdata requests
getchildren_rq, Counter, Number of getchildren requests
getchildren2_rq, Counter, Number of getchildren2 requests
remove_rq, Counter, Number of delete requests
exists_rq, Counter, Number of stat requests
getacl_rq, Counter, Number of getacl requests
setacl_rq, Counter, Number of setacl requests
sync_rq, Counter, Number of sync requests
multi_rq, Counter, Number of multi transaction requests
reconfig_rq, Counter, Number of reconfig requests
close_rq, Counter, Number of close requests
setwatches_rq, Counter, Number of setwatches requests
checkwatches_rq, Counter, Number of checkwatches requests
removewatches_rq, Counter, Number of removewatches requests
check_rq, Counter, Number of check requests

.. _config_network_filters_zookeeper_proxy_dynamic_metadata:

Dynamic Metadata
----------------

The ZooKeeper filter emits the following dynamic metadata for each message parsed:

.. csv-table::
:header: Name, Type, Description
:widths: 1, 1, 2

<path>, string, "The path associated with the request, response or event"
<opname>, string, "The opname for the request, response or event"
<create_type>, string, "The string representation of the flags applied to the znode"
<bytes>, string, "The size of the request message in bytes"
<watch>, string, "True if a watch is being set, false otherwise"
<version>, string, "The version parameter, if any, given with the request"
1 change: 1 addition & 0 deletions docs/root/configuration/well_known_dynamic_metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ The following Envoy filters emit dynamic metadata that other filters can leverag
* :ref:`MySQL Proxy Filter <config_network_filters_mysql_proxy_dynamic_metadata>`
* :ref:`Role Based Access Control (RBAC) Filter <config_http_filters_rbac_dynamic_metadata>`
* :ref:`Role Based Access Control (RBAC) Network Filter <config_network_filters_rbac_dynamic_metadata>`
* :ref:`ZooKeeper Proxy Filter <config_network_filters_zookeeper_proxy_dynamic_metadata>`
2 changes: 2 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ Version history
* upstream: add cluster factory to allow creating and registering :ref:`custom cluster type<arch_overview_service_discovery_types_custom>`.
* tracing: added :ref:`verbose <envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.tracing>` to support logging annotations on spans.
* upstream: added support for host weighting and :ref:`locality weighting <arch_overview_load_balancing_locality_weighted_lb>` in the :ref:`ring hash load balancer <arch_overview_load_balancing_types_ring_hash>`, and added a :ref:`maximum_ring_size<envoy_api_field_Cluster.RingHashLbConfig.maximum_ring_size>` config parameter to strictly bound the ring size.
* zookeeper: added a ZooKeeper proxy filter that parses ZooKeeper messages (requests/responses/events).
Refer to ::ref:`ZooKeeper proxy<config_network_filters_zookeeper_proxy>` for more details.
* upstream: added configuration option to select any host when the fallback policy fails.
* upstream: stopped incrementing upstream_rq_total for HTTP/1 conn pool when request is circuit broken.

Expand Down
7 changes: 6 additions & 1 deletion source/common/common/enum_to_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ namespace Envoy {
/**
* Convert an int based enum to an int.
*/
template <typename T> uint32_t enumToInt(T val) { return static_cast<uint32_t>(val); }
template <typename T> constexpr uint32_t enumToInt(T val) { return static_cast<uint32_t>(val); }

/**
* Convert an int based enum to a signed int.
*/
template <typename T> constexpr int32_t enumToSignedInt(T val) { return static_cast<int32_t>(val); }
} // namespace Envoy
2 changes: 2 additions & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ EXTENSIONS = {
"envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config",
"envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config",
"envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config",
"envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config",

#
# Resource monitors
Expand Down Expand Up @@ -194,6 +195,7 @@ WINDOWS_EXTENSIONS = {
"envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config",
#"envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config",
#"envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config",
#"envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config",

#
# Stat sinks
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/filters/network/well_known_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class NetworkFilterNameValues {
const std::string Rbac = "envoy.filters.network.rbac";
// SNI Cluster filter
const std::string SniCluster = "envoy.filters.network.sni_cluster";
// ZooKeeper proxy filter
const std::string ZooKeeperProxy = "envoy.filters.network.zookeeper_proxy";

// Converts names from v1 to v2
const Config::V1Converter v1_converter_;
Expand Down
47 changes: 47 additions & 0 deletions source/extensions/filters/network/zookeeper_proxy/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
licenses(["notice"]) # Apache 2

# ZooKeeper proxy L7 network filter.
# Public docs: docs/root/configuration/network_filters/zookeeper_proxy_filter.rst

load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "proxy_lib",
srcs = [
"zookeeper_decoder.cc",
"zookeeper_filter.cc",
"zookeeper_utils.cc",
],
hdrs = [
"zookeeper_decoder.h",
"zookeeper_filter.h",
"zookeeper_utils.h",
],
deps = [
"//include/envoy/network:filter_interface",
"//include/envoy/server:filter_config_interface",
"//include/envoy/stats:stats_interface",
"//include/envoy/stats:stats_macros",
"//source/common/config:filter_json_lib",
"//source/common/network:filter_lib",
"//source/extensions/filters/network:well_known_names",
],
)

envoy_cc_library(
name = "config",
srcs = ["zookeeper_config.cc"],
hdrs = ["zookeeper_config.h"],
deps = [
":proxy_lib",
"//source/extensions/filters/network:well_known_names",
"//source/extensions/filters/network/common:factory_base_lib",
"@envoy_api//envoy/config/filter/network/zookeeper_proxy/v1alpha1:zookeeper_proxy_cc",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "extensions/filters/network/zookeeper_proxy/zookeeper_config.h"

#include <string>

#include "envoy/config/filter/network/zookeeper_proxy/v1alpha1/zookeeper_proxy.pb.validate.h"
#include "envoy/registry/registry.h"
#include "envoy/server/filter_config.h"

#include "common/common/logger.h"

#include "extensions/filters/network/zookeeper_proxy/zookeeper_filter.h"

namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace ZooKeeperProxy {

/**
* Config registration for the ZooKeeper proxy filter. @see NamedNetworkFilterConfigFactory.
*/
Network::FilterFactoryCb
NetworkFilters::ZooKeeperProxy::ZooKeeperConfigFactory::createFilterFactoryFromProtoTyped(
const envoy::config::filter::network::zookeeper_proxy::v1alpha1::ZooKeeperProxy& proto_config,
Server::Configuration::FactoryContext& context) {

ASSERT(!proto_config.stat_prefix().empty());

const std::string stat_prefix = fmt::format("{}.zookeeper.", proto_config.stat_prefix());
const uint32_t max_packet_bytes =
PROTOBUF_GET_WRAPPED_OR_DEFAULT(proto_config, max_packet_bytes, 1024 * 1024);

ZooKeeperFilterConfigSharedPtr filter_config(
std::make_shared<ZooKeeperFilterConfig>(stat_prefix, max_packet_bytes, context.scope()));
return [filter_config](Network::FilterManager& filter_manager) -> void {
filter_manager.addFilter(std::make_shared<ZooKeeperFilter>(filter_config));
};
}

/**
* Static registration for the ZooKeeper proxy filter. @see RegisterFactory.
*/
REGISTER_FACTORY(ZooKeeperConfigFactory, Server::Configuration::NamedNetworkFilterConfigFactory);

} // namespace ZooKeeperProxy
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include "envoy/config/filter/network/zookeeper_proxy/v1alpha1/zookeeper_proxy.pb.h"
#include "envoy/config/filter/network/zookeeper_proxy/v1alpha1/zookeeper_proxy.pb.validate.h"

#include "extensions/filters/network/common/factory_base.h"
#include "extensions/filters/network/well_known_names.h"
#include "extensions/filters/network/zookeeper_proxy/zookeeper_filter.h"

namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace ZooKeeperProxy {

/**
* Config registration for the ZooKeeper proxy filter.
*/
class ZooKeeperConfigFactory
: public Common::FactoryBase<
envoy::config::filter::network::zookeeper_proxy::v1alpha1::ZooKeeperProxy> {
public:
ZooKeeperConfigFactory() : FactoryBase(NetworkFilterNames::get().ZooKeeperProxy) {}

private:
Network::FilterFactoryCb createFilterFactoryFromProtoTyped(
const envoy::config::filter::network::zookeeper_proxy::v1alpha1::ZooKeeperProxy& proto_config,
Server::Configuration::FactoryContext& context) override;
};

} // namespace ZooKeeperProxy
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
Loading

0 comments on commit b771f99

Please sign in to comment.