diff --git a/interpreter/k8s/src/main/scala/io/buoyant/transformer/k8s/DaemonSetTransformerInitializer.scala b/interpreter/k8s/src/main/scala/io/buoyant/transformer/k8s/DaemonSetTransformerInitializer.scala index 80a76ec984..b9470a9187 100644 --- a/interpreter/k8s/src/main/scala/io/buoyant/transformer/k8s/DaemonSetTransformerInitializer.scala +++ b/interpreter/k8s/src/main/scala/io/buoyant/transformer/k8s/DaemonSetTransformerInitializer.scala @@ -44,11 +44,13 @@ case class DaemonSetTransformerConfig( override def mk(params: Params): NameTreeTransformer = { val client = mkClient(params).configured(param.Label("client")) def mkNs(ns: String) = Api(client.newService(dst)).withNamespace(ns) - val namer = new MultiNsNamer(Path.empty, None, mkNs) + + val namer = new MultiNsNamer(prefix, None, mkNs) + val daemonSet = namer.bind(NameTree.Leaf(Path.Utf8(namespace, port, service))) if (hostNetwork.getOrElse(false)) - new MetadataGatewayTransformer(prefix, daemonSet, Metadata.nodeName) + new MetadataGatewayTransformer(prefix, daemonSet, Metadata.nodeName, namer.adminHandlers) else - new SubnetGatewayTransformer(prefix, daemonSet, Netmask("255.255.255.0")) + new SubnetGatewayTransformer(prefix, daemonSet, Netmask("255.255.255.0"), namer.adminHandlers) } } diff --git a/interpreter/subnet/src/main/scala/io/buoyant/transformer/SubnetGatewayTransformer.scala b/interpreter/subnet/src/main/scala/io/buoyant/transformer/SubnetGatewayTransformer.scala index bbe5f2ab19..e72c0390c6 100644 --- a/interpreter/subnet/src/main/scala/io/buoyant/transformer/SubnetGatewayTransformer.scala +++ b/interpreter/subnet/src/main/scala/io/buoyant/transformer/SubnetGatewayTransformer.scala @@ -2,7 +2,9 @@ package io.buoyant.transformer import com.twitter.finagle.Name.Bound import com.twitter.finagle._ +import com.twitter.finagle.naming.NameInterpreter import com.twitter.util.{Activity, Future, Var} +import io.buoyant.admin.Admin import io.buoyant.namer.{DelegateTree, DelegatingNameTreeTransformer, RichActivity} /** @@ -12,23 +14,29 @@ import io.buoyant.namer.{DelegateTree, DelegatingNameTreeTransformer, RichActivi class SubnetGatewayTransformer( prefix: Path, gatewayTree: Activity[NameTree[Bound]], - netmask: Netmask -) extends GatewayTransformer(prefix, gatewayTree, netmask.local) + netmask: Netmask, + handlers: Seq[Admin.Handler] = Seq.empty +) extends GatewayTransformer(prefix, gatewayTree, netmask.local, handlers) class MetadataGatewayTransformer( prefix: Path, gatewayTree: Activity[NameTree[Bound]], - metadataField: String + metadataField: String, + handlers: Seq[Admin.Handler] = Seq.empty ) extends GatewayTransformer(prefix, gatewayTree, { case (Address.Inet(_, a), Address.Inet(_, b)) => a.get(metadataField) == b.get(metadataField) case _ => true -}) +}, + handlers) class GatewayTransformer( prefix: Path, gatewayTree: Activity[NameTree[Bound]], - gatewayPredicate: (Address, Address) => Boolean -) extends DelegatingNameTreeTransformer { + gatewayPredicate: (Address, Address) => Boolean, + handlers: Seq[Admin.Handler] = Seq.empty +) extends DelegatingNameTreeTransformer with Admin.WithHandlers { + + override def adminHandlers: Seq[Admin.Handler] = handlers override protected def transformDelegate(tree: DelegateTree[Bound]): Future[DelegateTree[Bound]] = gatewayTree.toFuture.map { gateways => diff --git a/linkerd/admin/src/main/scala/io/buoyant/linkerd/admin/LinkerdAdmin.scala b/linkerd/admin/src/main/scala/io/buoyant/linkerd/admin/LinkerdAdmin.scala index ea295513f2..6c7b92ff15 100644 --- a/linkerd/admin/src/main/scala/io/buoyant/linkerd/admin/LinkerdAdmin.scala +++ b/linkerd/admin/src/main/scala/io/buoyant/linkerd/admin/LinkerdAdmin.scala @@ -10,7 +10,7 @@ import com.twitter.server.handler.ResourceHandler import io.buoyant.admin.Admin.{Handler, NavItem} import io.buoyant.admin.names.{BoundNamesHandler, DelegateApiHandler, DelegateHandler} import io.buoyant.admin.{Admin, ConfigHandler, StaticFilter, _} -import io.buoyant.namer.EnumeratingNamer +import io.buoyant.namer.{EnumeratingNamer, WithNameTreeTransformer} import io.buoyant.router.{Http, RoutingFactory} object LinkerdAdmin { @@ -83,6 +83,22 @@ object LinkerdAdmin { } } + def extractTransformersNamerHandlers(namers: Seq[Namer]): Seq[Admin.Handler] = { + namers.flatMap(namer => + namer match { + case withNameTreeTransformer: WithNameTreeTransformer => + withNameTreeTransformer.transformers.toSeq.flatMap(transformer => + transformer match { + case withHandlers: Admin.WithHandlers => + withHandlers.adminHandlers + case _ => + Seq() + }) + case _ => + Seq() + }) + } + def extractInterpreterHandlers(routers: Seq[Router]): Seq[Admin.Handler] = { routers.flatMap { router => router.interpreter match { @@ -96,6 +112,21 @@ object LinkerdAdmin { } } + def extractInterpreterTransformerHandlers(routers: Seq[Router]): Seq[Admin.Handler] = { + routers.flatMap(router => + router.interpreter match { + case withNameTreeTransformer: WithNameTreeTransformer => + withNameTreeTransformer.transformers.toSeq.flatMap(transformer => + transformer match { + case withHandlers: Admin.WithHandlers => withHandlers.adminHandlers + case _ => + Seq() + }) + case _ => + Seq() + }) + } + def apply(lc: Linker.LinkerConfig, linker: Linker): Seq[Handler] = { val navItems = Seq( NavItem("dtab", "delegator"), @@ -119,7 +150,9 @@ object LinkerdAdmin { linker.namers.map(_._2) ++ linker.routers ++ linker.telemeters - ) ++ extractInterpreterHandlers(linker.routers)) + ) ++ extractTransformersNamerHandlers(linker.namers.map(_._2)) + ++ extractInterpreterHandlers(linker.routers) + ++ extractInterpreterTransformerHandlers(linker.routers)) .map { case Handler(url, service, css) => val adminFilter = new AdminFilter(adminHandler, css) diff --git a/linkerd/docs/transformer.md b/linkerd/docs/transformer.md index fbbfe9c085..89618cba27 100644 --- a/linkerd/docs/transformer.md +++ b/linkerd/docs/transformer.md @@ -67,7 +67,9 @@ This can be used to redirect traffic to a reverse-proxy that runs as a daemonset. This transformer assumes that there is a Kubernetes service for the daemonset -which can be used to find all pods in the daemonset. +which can be used to find all pods in the daemonset. The internal state of each daemonset namer can be viewed at the +admin endpoint: `/namer_state/io.l5d.k8s.daemonset/$namespace/$port/$service.json`, where $namespace/$port/$service are the required key configs for the DaemonSetTransformer. + Key | Default Value | Description --- | ------------- | ----------- diff --git a/namer/core/src/main/scala/io/buoyant/namer/NameTreeTransformer.scala b/namer/core/src/main/scala/io/buoyant/namer/NameTreeTransformer.scala index 6182b7dbd0..db10911149 100644 --- a/namer/core/src/main/scala/io/buoyant/namer/NameTreeTransformer.scala +++ b/namer/core/src/main/scala/io/buoyant/namer/NameTreeTransformer.scala @@ -5,12 +5,18 @@ import com.twitter.finagle.naming.NameInterpreter import com.twitter.finagle._ import com.twitter.util.{Activity, Future} import io.buoyant.admin.Admin +import io.buoyant.admin.Admin.Handler /** * A NameTreeTransformer performs some kind of transformation on bound * NameTrees. These transformers are generally applied to the output of a * NameInterpreter to do post-processing. */ + +trait WithNameTreeTransformer { + def transformers: Seq[NameTreeTransformer] +} + trait NameTreeTransformer { protected def transform(tree: NameTree[Name.Bound]): Activity[NameTree[Name.Bound]] @@ -19,7 +25,7 @@ trait NameTreeTransformer { * Create a new NameInterpreter by applying this transformer to the output of * an existing one. */ - def wrap(underlying: NameInterpreter): NameInterpreter = new NameInterpreter with Admin.WithHandlers { + def wrap(underlying: NameInterpreter): NameInterpreter = new NameInterpreter with Admin.WithHandlers with WithNameTreeTransformer { override def bind(dtab: Dtab, path: Path): Activity[NameTree[Bound]] = underlying.bind(dtab, path).flatMap(transform) @@ -27,9 +33,16 @@ trait NameTreeTransformer { case withHandlers: Admin.WithHandlers => withHandlers.adminHandlers case _ => Nil } + + override def transformers: Seq[NameTreeTransformer] = underlying match { + case withNameTreeTransformer: WithNameTreeTransformer => withNameTreeTransformer.transformers ++ Seq(getSelf()) + case _ => Seq(getSelf()) + } } - def wrap(underlying: Namer): Namer = new Namer with Admin.WithHandlers { + def getSelf(): NameTreeTransformer = this + + def wrap(underlying: Namer): Namer = new Namer with Admin.WithHandlers with WithNameTreeTransformer { private[this] def isBound(tree: NameTree[Name]): Boolean = { tree match { case NameTree.Neg | NameTree.Empty | NameTree.Fail => true @@ -49,6 +62,11 @@ trait NameTreeTransformer { } } + override def transformers: Seq[NameTreeTransformer] = underlying match { + case withNameTreeTransformer: WithNameTreeTransformer => withNameTreeTransformer.transformers ++ Seq(getSelf()) + case _ => Seq(getSelf()) + } + override def adminHandlers: Seq[Admin.Handler] = underlying match { case withHandlers: Admin.WithHandlers => withHandlers.adminHandlers case _ => Nil @@ -66,7 +84,7 @@ trait DelegatingNameTreeTransformer extends NameTreeTransformer { protected def transformDelegate(tree: DelegateTree[Name.Bound]): Future[DelegateTree[Name.Bound]] /** Like wrap, but preserving the ability of the NameInterpreter to delegate */ - def delegatingWrap(underlying: NameInterpreter with Delegator): NameInterpreter with Delegator = new NameInterpreter with Delegator with Admin.WithHandlers { + def delegatingWrap(underlying: NameInterpreter with Delegator): NameInterpreter with Delegator = new NameInterpreter with Delegator with Admin.WithHandlers with WithNameTreeTransformer { override def bind(dtab: Dtab, path: Path): Activity[NameTree[Bound]] = underlying.bind(dtab, path).flatMap(transform) @@ -82,6 +100,11 @@ trait DelegatingNameTreeTransformer extends NameTreeTransformer { case withHandlers: Admin.WithHandlers => withHandlers.adminHandlers case _ => Nil } + + override def transformers: Seq[NameTreeTransformer] = underlying match { + case withNameTreeTransformer: WithNameTreeTransformer => withNameTreeTransformer.transformers ++ Seq(getSelf()) + case _ => Seq(getSelf()) + } } }