mirror of https://github.com/linkerd/linkerd2.git
proxy: Support destination label matching for tap (#817)
Now, the tap server may specify that requests should be matched by destination label. For example, if the controller's Destination service returns the labels: `{"service": "users", "namespace": "prod"}` for an endpoint, then tap would be able to specify a match like `namespace=prod` to match requests destined to that namespace.
This commit is contained in:
parent
c26955186b
commit
b968682a10
|
@ -54,6 +54,15 @@ impl Arbitrary for observe_request::match_::Seq {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Arbitrary for observe_request::match_::Label {
|
||||||
|
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
||||||
|
observe_request::match_::Label {
|
||||||
|
key: Arbitrary::arbitrary(g),
|
||||||
|
value: Arbitrary::arbitrary(g),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Arbitrary for observe_request::match_::Tcp {
|
impl Arbitrary for observe_request::match_::Tcp {
|
||||||
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
||||||
observe_request::match_::Tcp {
|
observe_request::match_::Tcp {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::net;
|
use std::net;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@ pub(super) enum Match {
|
||||||
Not(Box<Match>),
|
Not(Box<Match>),
|
||||||
Source(TcpMatch),
|
Source(TcpMatch),
|
||||||
Destination(TcpMatch),
|
Destination(TcpMatch),
|
||||||
|
DestinationLabel(LabelMatch),
|
||||||
Http(HttpMatch),
|
Http(HttpMatch),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +33,12 @@ pub enum InvalidMatch {
|
||||||
Unimplemented,
|
Unimplemented,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(super) struct LabelMatch {
|
||||||
|
key: String,
|
||||||
|
value: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(super) enum TcpMatch {
|
pub(super) enum TcpMatch {
|
||||||
// Inclusive
|
// Inclusive
|
||||||
|
@ -97,6 +105,36 @@ impl Match {
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Match::DestinationLabel(ref label) => match *ev {
|
||||||
|
Event::StreamRequestOpen(ref req) | Event::StreamRequestFail(ref req, _) => {
|
||||||
|
match req.dst_labels() {
|
||||||
|
None => false,
|
||||||
|
Some(ref b) => {
|
||||||
|
match b.borrow().as_ref() {
|
||||||
|
None => false,
|
||||||
|
Some(ref labels) => label.matches(labels.as_map()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::StreamResponseOpen(ref rsp, _) |
|
||||||
|
Event::StreamResponseFail(ref rsp, _) |
|
||||||
|
Event::StreamResponseEnd(ref rsp, _) => {
|
||||||
|
match rsp.request.dst_labels() {
|
||||||
|
None => false,
|
||||||
|
Some(ref b) => {
|
||||||
|
match b.borrow().as_ref() {
|
||||||
|
None => false,
|
||||||
|
Some(ref labels) => label.matches(labels.as_map()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
|
||||||
Match::Http(ref http) => match *ev {
|
Match::Http(ref http) => match *ev {
|
||||||
Event::StreamRequestOpen(ref req) | Event::StreamRequestFail(ref req, _) => {
|
Event::StreamRequestOpen(ref req) | Event::StreamRequestFail(ref req, _) => {
|
||||||
http.matches(req)
|
http.matches(req)
|
||||||
|
@ -153,7 +191,9 @@ impl<'a> TryFrom<&'a observe_request::match_::Match> for Match {
|
||||||
|
|
||||||
match_::Match::Destination(ref dst) => Match::Destination(TcpMatch::try_from(dst)?),
|
match_::Match::Destination(ref dst) => Match::Destination(TcpMatch::try_from(dst)?),
|
||||||
|
|
||||||
match_::Match::DestinationLabel(..) => return Err(InvalidMatch::Unimplemented),
|
match_::Match::DestinationLabel(ref label) => {
|
||||||
|
Match::DestinationLabel(LabelMatch::try_from(label)?)
|
||||||
|
}
|
||||||
|
|
||||||
match_::Match::Http(ref http) => Match::Http(HttpMatch::try_from(http)?),
|
match_::Match::Http(ref http) => Match::Http(HttpMatch::try_from(http)?),
|
||||||
};
|
};
|
||||||
|
@ -162,6 +202,29 @@ impl<'a> TryFrom<&'a observe_request::match_::Match> for Match {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== impl LabelMatch ======
|
||||||
|
|
||||||
|
impl LabelMatch {
|
||||||
|
fn matches(&self, labels: &HashMap<String, String>) -> bool {
|
||||||
|
labels.get(&self.key) == Some(&self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TryFrom<&'a observe_request::match_::Label> for LabelMatch {
|
||||||
|
type Err = InvalidMatch;
|
||||||
|
|
||||||
|
fn try_from(m: &observe_request::match_::Label) -> Result<Self, InvalidMatch> {
|
||||||
|
if m.key.is_empty() || m.value.is_empty() {
|
||||||
|
return Err(InvalidMatch::Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LabelMatch {
|
||||||
|
key: m.key.clone(),
|
||||||
|
value: m.value.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ===== impl TcpMatch ======
|
// ===== impl TcpMatch ======
|
||||||
|
|
||||||
impl TcpMatch {
|
impl TcpMatch {
|
||||||
|
@ -340,6 +403,15 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use conduit_proxy_controller_grpc::*;
|
use conduit_proxy_controller_grpc::*;
|
||||||
|
|
||||||
|
impl Arbitrary for LabelMatch {
|
||||||
|
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
||||||
|
Self {
|
||||||
|
key: Arbitrary::arbitrary(g),
|
||||||
|
value: Arbitrary::arbitrary(g),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Arbitrary for TcpMatch {
|
impl Arbitrary for TcpMatch {
|
||||||
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
fn arbitrary<G: Gen>(g: &mut G) -> Self {
|
||||||
if g.gen::<bool>() {
|
if g.gen::<bool>() {
|
||||||
|
@ -405,6 +477,22 @@ mod tests {
|
||||||
m.matches(&addr) == matches
|
m.matches(&addr) == matches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn labels_from_proto(label: observe_request::match_::Label) -> bool {
|
||||||
|
let err: Option<InvalidMatch> =
|
||||||
|
if label.key.is_empty() || label.value.is_empty() {
|
||||||
|
Some(InvalidMatch::Empty)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
err == LabelMatch::try_from(&label).err()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label_matches(l: LabelMatch, labels: HashMap<String, String>) -> bool {
|
||||||
|
let matches = labels.get(&l.key) == Some(&l.value);
|
||||||
|
l.matches(&labels) == matches
|
||||||
|
}
|
||||||
|
|
||||||
fn http_from_proto(http: observe_request::match_::Http) -> bool {
|
fn http_from_proto(http: observe_request::match_::Http) -> bool {
|
||||||
use self::observe_request::match_::http;
|
use self::observe_request::match_::http;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue