Skip to content

Commit 181065a

Browse files
authored
Release/v1.24 - backport of envoyproxy/envoy#24302 (#24851)
* attributes: implement xds info (#24302) Signed-off-by: Kuat Yessenov [email protected] Commit Message: Support look up of xDS info from expressions. Additional Description: Promotes Wasm-specific attributes to first-class (usable in access log, for example), and reduces some code duplication. Risk Level: low (new attributes) Testing: unit Docs Changes: yes Release Notes: yes Related: #24023 Signed-off-by: Carl Eastman <[email protected]>
1 parent 6964587 commit 181065a

File tree

11 files changed

+334
-115
lines changed

11 files changed

+334
-115
lines changed

changelogs/current.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,8 @@ removed_config_or_runtime:
3434
# *Normally occurs at the end of the* :ref:`deprecation period <deprecated>`
3535

3636
new_features:
37+
- area: attributes
38+
change: |
39+
added :ref:`attributes <arch_overview_attributes>` for looking up xDS configuration information.
3740
3841
deprecated:

docs/root/intro/arch_overview/advanced/attributes.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,24 @@ Data exchanged between filters is available as the following attributes:
166166
Note that these attributes may change during the life of a request as the data can be
167167
updated by filters at any point.
168168

169+
Configuration attributes
170+
----------------------------
171+
172+
Configuration identifiers and metadata related to the handling of the request or the connection is available as the
173+
following attributes:
174+
175+
.. csv-table::
176+
:header: Attribute, Type, Description
177+
:widths: 1, 1, 4
178+
179+
xds.cluster_name, string, Upstream cluster name
180+
xds.cluster_metadata, :ref:`Metadata<envoy_v3_api_msg_config.core.v3.metadata>`, Upstream cluster metadata
181+
xds.route_name, string, Route name
182+
xds.route_metadata, :ref:`Metadata<envoy_v3_api_msg_config.core.v3.metadata>`, Route metadata
183+
xds.upstream_host_metadata, :ref:`Metadata<envoy_v3_api_msg_config.core.v3.metadata>`, Upstream host metadata
184+
xds.filter_chain_name, string, Listener filter chain name
185+
186+
169187
Wasm attributes
170188
---------------
171189

source/extensions/common/wasm/context.cc

Lines changed: 22 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,9 @@ WasmResult serializeValue(Filters::Common::Expr::CelValue value, std::string* re
425425
}
426426

427427
#define PROPERTY_TOKENS(_f) \
428-
_f(METADATA) _f(REQUEST) _f(RESPONSE) _f(CONNECTION) _f(UPSTREAM) _f(NODE) _f(SOURCE) \
429-
_f(DESTINATION) _f(LISTENER_DIRECTION) _f(LISTENER_METADATA) _f(CLUSTER_NAME) \
430-
_f(CLUSTER_METADATA) _f(ROUTE_NAME) _f(ROUTE_METADATA) _f(PLUGIN_NAME) \
431-
_f(UPSTREAM_HOST_METADATA) _f(PLUGIN_ROOT_ID) _f(PLUGIN_VM_ID) _f(CONNECTION_ID) \
432-
_f(FILTER_STATE)
428+
_f(NODE) _f(LISTENER_DIRECTION) _f(LISTENER_METADATA) _f(CLUSTER_NAME) _f(CLUSTER_METADATA) \
429+
_f(ROUTE_NAME) _f(ROUTE_METADATA) _f(PLUGIN_NAME) _f(UPSTREAM_HOST_METADATA) \
430+
_f(PLUGIN_ROOT_ID) _f(PLUGIN_VM_ID) _f(CONNECTION_ID)
433431

434432
static inline std::string downCase(std::string s) {
435433
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
@@ -444,12 +442,31 @@ enum class PropertyToken { PROPERTY_TOKENS(_DECLARE) };
444442
static absl::flat_hash_map<std::string, PropertyToken> property_tokens = {PROPERTY_TOKENS(_PAIR)};
445443
#undef _PAIR
446444

445+
absl::optional<google::api::expr::runtime::CelValue>
446+
Context::FindValue(absl::string_view name, Protobuf::Arena* arena) const {
447+
return findValue(name, arena, false);
448+
}
449+
447450
absl::optional<google::api::expr::runtime::CelValue>
448451
Context::findValue(absl::string_view name, Protobuf::Arena* arena, bool last) const {
449452
using google::api::expr::runtime::CelProtoWrapper;
450453
using google::api::expr::runtime::CelValue;
451454

452455
const StreamInfo::StreamInfo* info = getConstRequestStreamInfo();
456+
// In order to delegate to the StreamActivation method, we have to set the
457+
// context properties to match the Wasm context properties in all callbacks
458+
// (e.g. onLog or onEncodeHeaders) for the duration of the call.
459+
activation_info_ = info;
460+
activation_request_headers_ = request_headers_ ? request_headers_ : access_log_request_headers_;
461+
activation_response_headers_ =
462+
response_headers_ ? response_headers_ : access_log_response_headers_;
463+
activation_response_trailers_ =
464+
response_trailers_ ? response_trailers_ : access_log_response_trailers_;
465+
auto value = StreamActivation::FindValue(name, arena);
466+
resetActivation();
467+
if (value) {
468+
return value;
469+
}
453470

454471
// Convert into a dense token to enable a jump table implementation.
455472
auto part_token = property_tokens.find(name);
@@ -473,62 +490,20 @@ Context::findValue(absl::string_view name, Protobuf::Arena* arena, bool last) co
473490
}
474491

475492
switch (part_token->second) {
476-
case PropertyToken::METADATA:
477-
if (info) {
478-
return CelProtoWrapper::CreateMessage(&info->dynamicMetadata(), arena);
479-
}
480-
break;
481-
case PropertyToken::REQUEST:
482-
if (info) {
483-
return CelValue::CreateMap(Protobuf::Arena::Create<Filters::Common::Expr::RequestWrapper>(
484-
arena, *arena, request_headers_ ? request_headers_ : access_log_request_headers_, *info));
485-
}
486-
break;
487-
case PropertyToken::RESPONSE:
488-
if (info) {
489-
return CelValue::CreateMap(Protobuf::Arena::Create<Filters::Common::Expr::ResponseWrapper>(
490-
arena, *arena, response_headers_ ? response_headers_ : access_log_response_headers_,
491-
response_trailers_ ? response_trailers_ : access_log_response_trailers_, *info));
492-
}
493-
break;
494-
case PropertyToken::CONNECTION:
495-
if (info) {
496-
return CelValue::CreateMap(
497-
Protobuf::Arena::Create<Filters::Common::Expr::ConnectionWrapper>(arena, *info));
498-
}
499-
break;
500493
case PropertyToken::CONNECTION_ID: {
501494
auto conn = getConnection();
502495
if (conn) {
503496
return CelValue::CreateUint64(conn->id());
504497
}
505498
break;
506499
}
507-
case PropertyToken::UPSTREAM:
508-
if (info) {
509-
return CelValue::CreateMap(
510-
Protobuf::Arena::Create<Filters::Common::Expr::UpstreamWrapper>(arena, *info));
511-
}
512-
break;
513500
case PropertyToken::NODE:
514501
if (root_local_info_) {
515502
return CelProtoWrapper::CreateMessage(&root_local_info_->node(), arena);
516503
} else if (plugin_) {
517504
return CelProtoWrapper::CreateMessage(&plugin()->localInfo().node(), arena);
518505
}
519506
break;
520-
case PropertyToken::SOURCE:
521-
if (info) {
522-
return CelValue::CreateMap(
523-
Protobuf::Arena::Create<Filters::Common::Expr::PeerWrapper>(arena, *info, false));
524-
}
525-
break;
526-
case PropertyToken::DESTINATION:
527-
if (info) {
528-
return CelValue::CreateMap(
529-
Protobuf::Arena::Create<Filters::Common::Expr::PeerWrapper>(arena, *info, true));
530-
}
531-
break;
532507
case PropertyToken::LISTENER_DIRECTION:
533508
if (plugin_) {
534509
return CelValue::CreateInt64(plugin()->direction());
@@ -582,13 +557,6 @@ Context::findValue(absl::string_view name, Protobuf::Arena* arena, bool last) co
582557
return CelValue::CreateStringView(toAbslStringView(root_id()));
583558
case PropertyToken::PLUGIN_VM_ID:
584559
return CelValue::CreateStringView(toAbslStringView(wasm()->vm_id()));
585-
case PropertyToken::FILTER_STATE:
586-
if (info) {
587-
return Protobuf::Arena::Create<Filters::Common::Expr::FilterStateWrapper>(arena,
588-
info->filterState())
589-
->Produce(arena);
590-
}
591-
break;
592560
}
593561
return {};
594562
}

source/extensions/common/wasm/context.h

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Context : public proxy_wasm::ContextBase,
109109
public Http::StreamFilter,
110110
public Network::ConnectionCallbacks,
111111
public Network::Filter,
112-
public google::api::expr::runtime::BaseActivation,
112+
public Filters::Common::Expr::StreamActivation,
113113
public std::enable_shared_from_this<Context> {
114114
public:
115115
Context(); // Testing.
@@ -272,16 +272,10 @@ class Context : public proxy_wasm::ContextBase,
272272
void onStatsUpdate(Envoy::Stats::MetricSnapshot& snapshot);
273273

274274
// CEL evaluation
275-
std::vector<const google::api::expr::runtime::CelFunction*>
276-
FindFunctionOverloads(absl::string_view) const override {
277-
return {};
278-
}
279275
absl::optional<google::api::expr::runtime::CelValue>
280276
findValue(absl::string_view name, Protobuf::Arena* arena, bool last) const;
281277
absl::optional<google::api::expr::runtime::CelValue>
282-
FindValue(absl::string_view name, Protobuf::Arena* arena) const override {
283-
return findValue(name, arena, false);
284-
}
278+
FindValue(absl::string_view name, Protobuf::Arena* arena) const override;
285279

286280
// Foreign function state
287281
virtual void setForeignData(absl::string_view data_name, std::unique_ptr<StorageObject> data) {

source/extensions/filters/common/expr/context.cc

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,18 +293,53 @@ absl::optional<CelValue> FilterStateWrapper::operator[](CelValue key) const {
293293
object != nullptr) {
294294
const CelState* cel_state = dynamic_cast<const CelState*>(object);
295295
if (cel_state) {
296-
return cel_state->exprValue(arena_, false);
296+
return cel_state->exprValue(&arena_, false);
297297
} else if (object != nullptr) {
298298
absl::optional<std::string> serialized = object->serializeAsString();
299299
if (serialized.has_value()) {
300-
std::string* out = ProtobufWkt::Arena::Create<std::string>(arena_, serialized.value());
300+
std::string* out = ProtobufWkt::Arena::Create<std::string>(&arena_, serialized.value());
301301
return CelValue::CreateBytes(out);
302302
}
303303
}
304304
}
305305
return {};
306306
}
307307

308+
absl::optional<CelValue> XDSWrapper::operator[](CelValue key) const {
309+
if (!key.IsString()) {
310+
return {};
311+
}
312+
auto value = key.StringOrDie().value();
313+
if (value == ClusterName) {
314+
const auto cluster_info = info_.upstreamClusterInfo();
315+
if (cluster_info && cluster_info.value()) {
316+
return CelValue::CreateString(&cluster_info.value()->name());
317+
}
318+
} else if (value == ClusterMetadata) {
319+
const auto cluster_info = info_.upstreamClusterInfo();
320+
if (cluster_info && cluster_info.value()) {
321+
return CelProtoWrapper::CreateMessage(&cluster_info.value()->metadata(), &arena_);
322+
}
323+
} else if (value == RouteName) {
324+
if (info_.route() && info_.route()->routeEntry()) {
325+
return CelValue::CreateString(&info_.route()->routeEntry()->routeName());
326+
}
327+
} else if (value == RouteMetadata) {
328+
if (info_.route()) {
329+
return CelProtoWrapper::CreateMessage(&info_.route()->metadata(), &arena_);
330+
}
331+
} else if (value == UpstreamHostMetadata) {
332+
const auto upstream_info = info_.upstreamInfo();
333+
if (upstream_info && upstream_info->upstreamHost()) {
334+
return CelProtoWrapper::CreateMessage(upstream_info->upstreamHost()->metadata().get(),
335+
&arena_);
336+
}
337+
} else if (value == FilterChainName) {
338+
return CelValue::CreateString(&info_.filterChainName());
339+
}
340+
return {};
341+
}
342+
308343
} // namespace Expr
309344
} // namespace Common
310345
} // namespace Filters

source/extensions/filters/common/expr/context.h

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include "source/common/singleton/const_singleton.h"
1010

1111
#include "eval/public/cel_value.h"
12-
#include "eval/public/cel_value_producer.h"
1312
#include "eval/public/containers/container_backed_list_impl.h"
1413
#include "eval/public/structs/cel_proto_wrapper.h"
1514

@@ -81,6 +80,15 @@ constexpr absl::string_view Upstream = "upstream";
8180
constexpr absl::string_view UpstreamLocalAddress = "local_address";
8281
constexpr absl::string_view UpstreamTransportFailureReason = "transport_failure_reason";
8382

83+
// xDS configuration context properties
84+
constexpr absl::string_view XDS = "xds";
85+
constexpr absl::string_view ClusterName = "cluster_name";
86+
constexpr absl::string_view ClusterMetadata = "cluster_metadata";
87+
constexpr absl::string_view RouteName = "route_name";
88+
constexpr absl::string_view RouteMetadata = "route_metadata";
89+
constexpr absl::string_view UpstreamHostMetadata = "upstream_host_metadata";
90+
constexpr absl::string_view FilterChainName = "filter_chain_name";
91+
8492
class WrapperFieldValues {
8593
public:
8694
using ContainerBackedListImpl = google::api::expr::runtime::ContainerBackedListImpl;
@@ -141,28 +149,23 @@ template <class T> class HeadersWrapper : public google::api::expr::runtime::Cel
141149
// Wrapper for accessing properties from internal data structures.
142150
// Note that CEL assumes no ownership of the underlying data, so temporary
143151
// data must be arena-allocated.
144-
class BaseWrapper : public google::api::expr::runtime::CelMap,
145-
public google::api::expr::runtime::CelValueProducer {
152+
class BaseWrapper : public google::api::expr::runtime::CelMap {
146153
public:
154+
BaseWrapper(Protobuf::Arena& arena) : arena_(arena) {}
147155
int size() const override { return 0; }
148-
CelValue Produce(ProtobufWkt::Arena* arena) override {
149-
// Producer is unique per evaluation arena since activation is re-created.
150-
arena_ = arena;
151-
return CelValue::CreateMap(this);
152-
}
153156
absl::StatusOr<const google::api::expr::runtime::CelList*> ListKeys() const override {
154157
return absl::UnimplementedError("ListKeys() is not implemented");
155158
}
156159

157160
protected:
158-
ProtobufWkt::Arena* arena_;
161+
ProtobufWkt::Arena& arena_;
159162
};
160163

161164
class RequestWrapper : public BaseWrapper {
162165
public:
163166
RequestWrapper(Protobuf::Arena& arena, const Http::RequestHeaderMap* headers,
164167
const StreamInfo::StreamInfo& info)
165-
: headers_(arena, headers), info_(info) {}
168+
: BaseWrapper(arena), headers_(arena, headers), info_(info) {}
166169
absl::optional<CelValue> operator[](CelValue key) const override;
167170

168171
private:
@@ -174,7 +177,7 @@ class ResponseWrapper : public BaseWrapper {
174177
public:
175178
ResponseWrapper(Protobuf::Arena& arena, const Http::ResponseHeaderMap* headers,
176179
const Http::ResponseTrailerMap* trailers, const StreamInfo::StreamInfo& info)
177-
: headers_(arena, headers), trailers_(arena, trailers), info_(info) {}
180+
: BaseWrapper(arena), headers_(arena, headers), trailers_(arena, trailers), info_(info) {}
178181
absl::optional<CelValue> operator[](CelValue key) const override;
179182

180183
private:
@@ -185,7 +188,8 @@ class ResponseWrapper : public BaseWrapper {
185188

186189
class ConnectionWrapper : public BaseWrapper {
187190
public:
188-
ConnectionWrapper(const StreamInfo::StreamInfo& info) : info_(info) {}
191+
ConnectionWrapper(Protobuf::Arena& arena, const StreamInfo::StreamInfo& info)
192+
: BaseWrapper(arena), info_(info) {}
189193
absl::optional<CelValue> operator[](CelValue key) const override;
190194

191195
private:
@@ -194,7 +198,8 @@ class ConnectionWrapper : public BaseWrapper {
194198

195199
class UpstreamWrapper : public BaseWrapper {
196200
public:
197-
UpstreamWrapper(const StreamInfo::StreamInfo& info) : info_(info) {}
201+
UpstreamWrapper(Protobuf::Arena& arena, const StreamInfo::StreamInfo& info)
202+
: BaseWrapper(arena), info_(info) {}
198203
absl::optional<CelValue> operator[](CelValue key) const override;
199204

200205
private:
@@ -203,32 +208,33 @@ class UpstreamWrapper : public BaseWrapper {
203208

204209
class PeerWrapper : public BaseWrapper {
205210
public:
206-
PeerWrapper(const StreamInfo::StreamInfo& info, bool local) : info_(info), local_(local) {}
211+
PeerWrapper(Protobuf::Arena& arena, const StreamInfo::StreamInfo& info, bool local)
212+
: BaseWrapper(arena), info_(info), local_(local) {}
207213
absl::optional<CelValue> operator[](CelValue key) const override;
208214

209215
private:
210216
const StreamInfo::StreamInfo& info_;
211217
const bool local_;
212218
};
213219

214-
class MetadataProducer : public google::api::expr::runtime::CelValueProducer {
220+
class FilterStateWrapper : public BaseWrapper {
215221
public:
216-
MetadataProducer(const envoy::config::core::v3::Metadata& metadata) : metadata_(metadata) {}
217-
CelValue Produce(ProtobufWkt::Arena* arena) override {
218-
return CelProtoWrapper::CreateMessage(&metadata_, arena);
219-
}
222+
FilterStateWrapper(Protobuf::Arena& arena, const StreamInfo::FilterState& filter_state)
223+
: BaseWrapper(arena), filter_state_(filter_state) {}
224+
absl::optional<CelValue> operator[](CelValue key) const override;
220225

221226
private:
222-
const envoy::config::core::v3::Metadata& metadata_;
227+
const StreamInfo::FilterState& filter_state_;
223228
};
224229

225-
class FilterStateWrapper : public BaseWrapper {
230+
class XDSWrapper : public BaseWrapper {
226231
public:
227-
FilterStateWrapper(const StreamInfo::FilterState& filter_state) : filter_state_(filter_state) {}
232+
XDSWrapper(Protobuf::Arena& arena, const StreamInfo::StreamInfo& info)
233+
: BaseWrapper(arena), info_(info) {}
228234
absl::optional<CelValue> operator[](CelValue key) const override;
229235

230236
private:
231-
const StreamInfo::FilterState& filter_state_;
237+
const StreamInfo::StreamInfo& info_;
232238
};
233239

234240
} // namespace Expr

0 commit comments

Comments
 (0)