Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

 

Implementations would use the Configurable to register one or more Jersey resources. They can use the WorkerConfig to get any plugin specific configuration.

Code Block
interface ConnectRestExtension extends Closeable{
    void register(ConnectRestExtensionContext restPluginContext);
}
 
interface ConnectRestExtensionContext{
    Configurable configurable();
    WorkerConfig workerConfig();
    ConnectClusterState clusterState();
}
 
class ConnectRestExtensionContextImpl implements ConnectRestExtensionContext{
    private final Configurable configurable;
    private final WorkerConfig workerConfig;
    private final ConnectClusterState clusterState;
 
    ConnectRestExtensionContext(Configurable configurable, WorkerConfig workerConfig, ConnectClusterState clusterState){
        this.configurable = configurable;
        this.workerConfig = workerConfig;
        this.clusterState = clusterState;
    }
 
    public ResourceConfigConfigurable configurable(){
        return this.configurable;
    }
 
    public WorkerConfig workerConfig(){
        return this.workerConfig;
    }
 
    public ConnectClusterState clusterState(){
        return this.clusterState;
    }
}

 

We will be introducing another new public API ConnectClusterState which will at present provide some of the read only methods from the Herder.  The change would also include a default implementation ConnectClusterStateImpl in the connect runtime that will delegate to the underlying Herder. This will be useful when you want to add new resources like healthcheck, monitoring, etc.

Code Block
interface ConnectClusterState{
    /**
     * Get a list of connectors currently running in this cluster. This is a full list of connectors in the cluster gathered
     * from the current configuration.
     */
    Collection<String> connectors();
 
    /**
     * Lookup the current status of a connector.
     * @param connName name of the connector
     */
    ConnectorStateInfo connectorStatus(String connName);
 
}
 
class ConnectClusterStateImpl implements ConnectClusterState{
    private final Herder herder;
 
    public ConnectClusterStateImpl(Herder herder){
        this.herder = herder;
    }
 
    @Override
    Collection<String> connectors(){
        //delegate to herder
    }
 
    @Override
    ConnectorStateInfo connectorStatus(String connName);{
        //delegate to herder
    }
}
Rest Extension Integration with Connect

The plugin's would be registered in the RestServer.start(Herder herder) method after registering the default Connect resources. Connect Run Ru time would implement provide an implementation of Configurable interface that woudl would do the following.

  • Constructed with the ResourceConfig available in the RestServer
  • Will check if resource is already registered. If not, it would delegate to ResourceConfig. If already registered would log a warning message.
  • For non-register methods would just delegate to the ResourceConfig instance

The above approach helps alleviate any issues that could arise if Extension accidentally reregister the 

Code Block
class ConnectRestConfigurable implements Configurable{
	ResourceConfig resourceConfig;
 
	public ConnectRestConfigurable(ResourceConfig resourceConfig) {
		this.resourceConfig = resourceConfig;
	}
 
	//implement methods and delegate to resourceConfig
}

 

The close for the plugins would be invoked as part of the stop() in the same class. 

The new extension class and its dependencies would need to be as part of the plugin path. Hence ConnectRestExtension would be defined as a new plugin to be loaded by the PluginClassLoader.The plugin would be looked up based on Java's Service Provider API instead of the Reflections scan that is used for other plugins. This will help in terms of not adding class loader cost that is associated in scanning the classes today for other plugins. Hence the implementation must provide a `META-INF/services/org.apache.kafka.connect.rest.ConnectRestExtension` as part of the jar file containing the fully qualified implementation class .

Example

Consider the following example that defines a single plugin to add an authenticating filter and a health check resource.

Code Block
class  ExampleConnectRestExtension implements ConnectRestExtension{
    public void register(ConnectRestExtensionContext restPluginContext){
        restPluginContext.configurable().register(new AuthenticationFilter(restPluginContext.workerConfig()));
        restPluginContext.configurable().register(new HealthCheckResource(restPluginContext.workerConfig(), restPluginContext.clusterState()));
    }
 
    public void close() {
        //close any resources
    }
}
 
class AuthenticationFilter implements ContainerRequestFilter {
 
  public AuthenticationFilter(WorkerConfig workerConfig){
    //set up filter
  }
     
  @Override
  public void filter(ContainerRequestContext requestContext) {
        //authentication logic
  }
 
}
 
@Path("/connect")
class HealthCheckResource {
 
    public HealthCheckResource(WorkerConfig workerConfig, ConnectClusterState clusterState){
        //initialize resource
    }
 
    @path("/health")
    public void healthcheck(){
        //check herder health
    }
}

The above illustrated plugin can then be configured in the worker's configuration as below

 

Code Block
titleconnect-worker.properties
 // configure plugin
rest.plugins=com.example.ExampleConnectPlugin

 For the RestExtension to be loaded, the jar should be packaged with the following file that lists the  fully qualified name of the ConnectRestExtension implementation

META-INF/services/org.apache.kafka.connect.rest.ConnectRestExtension
com.example.ExampleConnectRestExtension

Reference Implementation

The KIP proposes to include a refernce implementation that allows users to authenticate incoming Basic Auth headers against a properties file containing list of username & passwords.

...