Developing a Custom Namer Plugin for Linkerd

In this article, we will explore how to create a simple custom namer plugin for Linkerd. This example will focus on a minimal implementation that uses hardcoded addresses for demonstration purposes.

Initializer Class

The initializer is responsible for registering your custom namer configuration with Linkerd. Below is a basic implementation:

package com.consg

import io.buoyant.namer.NamerInitializer

class MyInitializer extends NamerInitializer {
  val configClass = classOf[MyConfig]
  override def configId = "com.consg.MyConfig"
}

object MyInitializer extends MyInitializer

Configuration Class

The configuration class defines the parameters for your custom namer. Here’s how you can set it up:

package com.consg

import com.fasterxml.jackson.annotation.JsonIgnore
import com.twitter.finagle.{Path, Stack}
import io.buoyant.config.types.Directory
import io.buoyant.namer.NamerConfig

case class MyConfig(rootDir: Directory) extends NamerConfig {

  @JsonIgnore
  override def defaultPrefix: Path = Path.read("/my.namer")

  @JsonIgnore
  def newNamer(params: Stack.Params) = {
    println("params: " + params)
    new MyNamer(rootDir.path, prefix)
  }
}

Namer Implementation

The namer itself is where the logic for resolving names to addresses is implemented. Below is a simple example:

package com.consg

import java.nio.file.{Path => NioPath}

import com.twitter.finagle._
import com.twitter.util._
import io.buoyant.namer.EnumeratingNamer

class MyNamer(rootDir: NioPath, prefix: Path) extends EnumeratingNamer {

  def lookup(path: Path): Activity[NameTree[Name]] = {
    println("lookup path: " + path)

    val address1 = Address("127.0.0.1", 7777)
    val addr = Addr.Bound(Set(address1), Addr.Metadata.empty)
    val varr = Var.apply(addr)

    Activity.value(NameTree.Leaf(Name.Bound(varr, path, path)))
  }

  override def getAllNames: Activity[Set[Path]] = {
    println("getAllNames!")
    Activity.value(Set.apply(Path.read("animal")))
  }
}

Build Configuration

To build your plugin, you will need to set up your build.sbt file as follows:

name := "plug"
version := "1"
scalaVersion := "2.12.1"

libraryDependencies += "io.buoyant" % "linkerd-core_2.12" % "1.3.2" % "provided"

Make sure to expose your initializer in the META-INF/services/io.buoyant.namer.NamerInitializer file.

Plugin Deployment

After building your plugin JAR, place it in the plugins directory of your Linkerd installation (e.g., linkerd-1.3.1/plugins).

Linkerd Configuration

To use your custom namer, configure Linkerd with the following YAML:

namers:
- kind: com.consg.MyConfig
  rootDir: disco

routers:
- protocol: http
  dtab: /svc => /#/my.namer;
  servers:
  - port: 8080

Troubleshooting

If you encounter errors such as InvalidTypeIdException, it may indicate that your plugin is not being loaded correctly. Ensure that your JAR is in the correct directory and that the service is properly registered.

For further assistance, you can refer to the GitHub repository containing the example code.