Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a client_state.json admin endpoint to expose the client address set #1768

Merged
merged 2 commits into from Jan 5, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,41 @@
package com.twitter.finagle.client.buoyant

import com.twitter.finagle.{Addr, Address, Service}
import com.twitter.finagle.client.ClientRegistry
import com.twitter.finagle.http.{MediaType, Request, Response}
import com.twitter.finagle.loadbalancer.LoadBalancerFactory
import com.twitter.util.Future
import io.buoyant.config.Parser

class ClientStateHandler extends Service[Request, Response] {

private[this] val mapper = Parser.jsonObjectMapper(Nil)

override def apply(request: Request): Future[Response] = {
val entries = ClientRegistry.registrants.map { entry =>
val LoadBalancerFactory.Dest(va) = entry.params[LoadBalancerFactory.Dest]
va.changes.toFuture().map(entry.addr -> _)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we limit this to entries where entry.addr is a Name or Path so that we don't encode weird things for clients that don't do name resolution?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why va.changes.toFuture() and not va.sample()?

}
Future.collect(entries.toSeq).map { entries =>
val clientState = entries.map {
case (client, addr) =>
val state = addr match {
case Addr.Bound(addresses, meta) =>
addresses.map {
case Address.Inet(isa, _) => isa.getHostString
case a => a.toString
}.toSeq
case Addr.Failed(why) => s"Failed: ${why.getMessage}"
case a@Addr.Pending => a.toString
case a@Addr.Neg => a.toString
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait what is the type of state? This seems a little annoying to decode if the type is "a list or maybe a string".

Perhaps better to have a case class that encodes e.g. "state": "bound", "addrs": [] etc (i'd bet that we even have this somewhere already for namerd's http api)

client -> state
}.toMap

val response = Response()
response.contentType = MediaType.Json + ";charset=UTF-8"
response.contentString = mapper.writeValueAsString(clientState)
response
}
}
}
Expand Up @@ -2,6 +2,7 @@ package io.buoyant.linkerd
package admin

import com.twitter.finagle._
import com.twitter.finagle.client.buoyant.ClientStateHandler
import com.twitter.finagle.http.Request
import com.twitter.finagle.naming.buoyant.DstBindingFactory
import com.twitter.finagle.naming.NameInterpreter
Expand Down Expand Up @@ -42,6 +43,8 @@ object LinkerdAdmin {
)
}

val clientState: Handler = Handler("/client_state.json", new ClientStateHandler)

def static(adminHandler: AdminHandler): Seq[Handler] = Seq(
Handler("/", new DashboardHandler(adminHandler)),
Handler("/files/", StaticFilter.andThen(ResourceHandler.fromDirectoryOrJar(
Expand Down Expand Up @@ -102,6 +105,6 @@ object LinkerdAdmin {
static(adminHandler) ++ config(lc) ++
boundNames(linker.namers.map { case (_, n) => n }) ++
delegator(adminHandler, linker.routers) ++
extHandlers
extHandlers :+ clientState
}
}