Chapter 1. Pluggable Discovery API You can use the Pluggable Discovery API endpoint, /api/ <version> /discovery , to register an external plug-in with Cryostat and provide information about discoverable application targets to Cryostat. Note As an alternative to the Pluggable Discovery API, you can use the Cryostat agent as a Cryostat discovery plug-in. The Cryostat agent is implemented as a Java Instrumentation Agent that acts as a plug-in for applications that run on the JVM. The Cryostat agent provides an HTTP API that offers greater deployment flexibility than a JMX port due to the agent's dual role as a discovery plug-in. You can configure your target applications to use the agent's HTTP API for both detection and connectivity with Cryostat. For more information about configuring your target applications to use the Cryostat agent, see Configuring Java applications . 1.1. Pluggable Discovery API overview You can use the Pluggable Discovery API endpoint, /api/ <version> /discovery , to register an external plug-in with Cryostat and then provide information about discoverable application targets to Cryostat. A plug-in can either unregister itself after successfully registering with Cryostat or continually push updates to Cryostat about a target application. Note The purpose of the registration operation is to enhance Cryostat security and to maintain data consistency between the plug-in and Cryostat. The Pluggable Discovery API provides a more flexible way to integrate Cryostat into your deployment schema than the Red Hat OpenShift service account mechanism. Consider an example where you need to write a plug-in program that creates a static map of application IP addresses to port numbers. The plug-in can use the Pluggable Discovery API to transport this information to Cryostat, so that Cryostat can better connect with the target application. The Pluggable Discovery API uses the following endpoints to manage requests sent from the /api/v2.2/discovery endpoint and Cryostat: Discovery registation endpoint Discovery registration check endpoint Discovery POST endpoint Discovery deregistration endpoint The Pluggable Discovery API also uses a Discovery GET endpoint to manage GET requests sent from the /api/v3/discovery endpoint and Cryostat. Note The Discovery GET endpoint is available for use with /api/v3/discovery only. All other Discovery endpoints are available for use with /api/v2.2/discovery only. For more information about these Discovery endpoints, see the subsequent document sections. Before you can use the API's endpoint to interact with Cryostat, you must ensure that the client, which is the plug-in's source, meets the following prerequisites: Accepts JSON responses. Can send HTTP requests to Cryostat. Can enter the correct Cryostat credentials in a POST request's Authorization header. Can receive GET and POST requests from Cryostat. Publishes information about discoverable targets in JSON by sending POST requests to Cryostat. If you need to register your own discovery plug-in program with Cryostat, you can disable the Cryostat built-in discovery mechanism by using the spec.targetDiscoveryOptions.disableBuiltInDiscovery field in the Cryostat CR. Disabling the built-in discovery mechanism helps to reduce duplicate definitions that might open in the Cryostat web console if both the plug-in and Cryostat can access the same target application information. Note If Cryostat detects that two similar definitions point to the same JVM, Cryostat stores any archived recordings in the same storage location that each definition accesses. You can also consider preserving the setting for the Cryostat built-in discovery implementation and then complete one of the following actions: Create a program that attaches a service locator to the implementation. Modify a target application to send target information directly to the implementation. Note The previously listed actions are outside the scope of the Configuring advanced Cryostat configurations document. 1.2. Discovery registration endpoint The Pluggable Discovery API uses a Discovery registration endpoint to register a discovered plug-in with Cryostat. This endpoint manages GET and POST requests from the plug-in and Cryostat. If you do not register a plug-in with Cryostat, the plug-in cannot provide target information to Cryostat. Note The Discovery registration endpoint is available for use with /api/v2.2/discovery only. When your discovery plug-in program sends a POST request to Cryostat for registration purposes, Cryostat reads the callback URL and sends a GET request to the plug-in. If the plug-in correctly responds to the GET request then Cryostat accepts the registration request by responding to the initial POST request. This process ensures that the plug-in is active and available to Cryostat. A failed request might indicate that the plug-in failed or went offline. In this case, the endpoint removes the plug-in's information from the database on Red Hat OpenShift. Note After the registration process, Cryostat sends regular POST requests to the plug-in to ensure that the plug-in is still running. You can also specify id and token elements in your GET or POST request. These elements are optional, but you can consider using them in situations where you want to reuse registration information for a plug-in that was previously registered with Cryostat. Cryostat creates a token for the registered plug-in, and this token contains expiry and authorization information. If a POST request contains valid id and token information, Cryostat can reuse the plug-in registration information and refreshes the token. If a request contains only an id element or a token element, you must re-register the plug-in with Cryostat. After Cryostat sends a POST request to the plug-in's callback component, the plug-in might send a POST request to Cryostat to refresh the plug-in's registration details. The plug-in must include its id and token information in its request. Cryostat can then use the Discovery registration endpoint to refresh the plug-in's details. Cryostat sends a response that includes the updated token to the plug-in, and the plug-in can use this token for future requests to Cryostat. Note Cryostat can issue a POST request to the plug-in at regular intervals to remind the plug-in about re-registering with Cryostat by using the same id and token information. If the plug-in ignores this request, the token might expire and the plug-in must complete a full registration with Cryostat. Example of a POST request sent from an external plug-in { "realm": "my-plugin", "callback": "http://my-plugin.my-namespace.svc.local:1234/callback" } Example of a POST response that Cryostat sends to a plug-in { "data": { "result": { "id": "922dd4f4-9d7c-4ae2-8982-0903868226a6", "token": " <key_value> " } }, "meta": { "status": "Created", "type": "application/json" } 1.3. Discovery registration check endpoint The Pluggable Discovery API uses a Discovery registration check endpoint to enable plug-ins to periodically check their own registration status on a Cryostat server instance. Note The Discovery registration check endpoint is available for use with /api/v2.2/discovery only. The Discovery registration check endpoint manages GET requests from the plug-in to Cryostat. By using this endpoint, external plug-ins can periodically verify that the Cryostat server instance to which they are registered is still active and recognizes the prior registration of the plug-in. Similar to how the Cryostat callback URL endpoint checks plug-in instances, where Cryostat reads the callback URL and sends a GET request to the plug-in, the Discovery registration check endpoint works in the same way, but sends the request in the opposite direction. That is, the plug-in sends a GET request to the Cryostat server to check its registration status on the Cryostat server. If the request fails, for example, if an Unexpected 401 or Unexpected 404 error response is received, the plug-in can discard its existing registration information and attempt to register again. Example of a GET request sent from an external plug-in Example of a Cryostat response when the GET request check succeeds and Cryostat recognizes the current registration of the plug-in HTTP/1.1 200 OK content-encoding: gzip content-length: 86 content-type: application/json { "data": { "result": null }, "meta": { "mimeType": "JSON", "status": "OK" } } Example of a Cryostat response when the GET request check fails because Cryostat does not recognize the plug-in registration details HTTP/1.1 404 Not Found content-encoding: gzip content-length: 95 content-type: application/json { "data": { "reason": null }, "meta": { "status": "Not Found", "type": "text/plain" } } 1.4. Discovery GET endpoint The Discovery GET endpoint collates deployment schemas and opens these schemas in a hierarchical tree view, so that Cryostat can interact with any registered discoverable plug-ins to integrate with a specific deployment schema. Note The Discovery GET endpoint is available for use with /api/v3/discovery only. When you create a deployment with at least one pod, and a service maps to the deployment or pod on Red Hat OpenShift, Red Hat OpenShift creates an Endpoints object for all combinations of the Pod IP address and the Service port. The Discovery GET endpoint receives requests and collates Endpoints and deployment schema information in JSON format. The following example demonstrates how this endpoint opens results in a hierarchical tree view in JSON format. In the example, the root of the tree is the UNIVERSE node. This node contains child Realm node types, which stem from Cryostat's built-in discovery mechanism and the plug-ins discovered by the Pluggable Discovery API. { "data": { "result": { "children": [ { "children": [], "labels": {}, "name": "Custom Targets", "nodeType": "Realm" }, { "children": [ { "labels": {}, "name": "service:jmx:rmi:///jndi/rmi://cryostat:9091/jmxrmi", "nodeType": "JVM", "target": { "alias": "io.cryostat.Cryostat", "annotations": { "cryostat": { "HOST": "cryostat", "JAVA_MAIN": "io.cryostat.Cryostat", "PORT": "9091", "REALM": "JDP" }, "platform": {} }, "connectUrl": "service:jmx:rmi:///jndi/rmi://cryostat:9091/jmxrmi", "labels": {} } } ], "labels": {}, "name": "JDP", "nodeType": "Realm" } ], "labels": {}, "name": "Universe", "nodeType": "Universe" } }, "meta": { "status": "OK", "type": "application/json" } } 1.5. Discovery POST endpoint A plug-in registered with Cryostat sends a POST /api/v2.2/discovery/:id request to the Discovery POST endpoint. The id parameter of the request refers to the ID of the registered plug-in. This endpoint processes any subtrees associated with the plug-in and then maps a deployment schema. Note The Discovery POST endpoint is available for use with /api/v2.2/discovery only. The Cryostat POST request defines a subtree REALM node, so that the plug-in endpoint can publish its child node types in the REALM node during the request process. The plug-in takes responsibility for providing the correct information for theCryostat REALM node and for placing node types in the correct subtree positions. The plug-in must provide a token with the POST request that it sends to the Discovery POST endpoint, so that the plug-in can bypass the authorization header check that the plug-in previously passed. Note After a plug-in sends updates to Cryostat, Cryostat replaces the information that a plug-in has sent. Cryostat stores this information in a database. A plug-in must send a complete list or tree of discoverable targets to Cryostat on each request. Example of a plug-in's POST request that details important target application information [ { "labels": {}, "nodeType": "JVM", "name": "service:jmx:rmi:///jndi/rmi://myapp.svc.local:9091/jmxrmi", "nodeType": "JVM", "target": { "alias": "com.MyApp", "annotations": { "cryostat": {}, "platform": {} }, "connectUrl": "service:jmx:rmi:///jndi/rmi://myapp.svc.local:9091/jmxrmi", "labels": {} } } ] Example of Cryostat response to the plug-in's POST request { "data": { "result": null }, "meta": { "mimeType": "JSON", "status": "OK" } } 1.6. Discovery deregistration endpoint If a plug-in registered with Cryostat as a plug-in , and then the plug-in needs to shutdown, the plug-in typically issues a request to un-register itself as a plug-in from Cryostat. Cryostat sends a DELETE /api/v2.2/discovery/:id?token=:token request to the Discovery deregistration endpoint, which then unregisters the plug-in from Cryostat. Note The Discovery deregistration endpoint is available for use with /api/v2.2/discovery only. Cryostat sends regular POST requests to the plug-in after the registration process to ensure that the plug-in is still running. If the plug-in does not respond to one of these requests, Cryostat starts the process of unregistering the plug-in. The endpoint removes both the plug-in's REALM subtree from the discovery schema and the plug-in's registration information from Cryostat. Example of DELETE /api/v2.2/discovery/:id?token=:token request that the endpoint processes { "data": { "result": "bcc0f3a6-dc48-402e-a3d6-9fbb63beff78" }, "meta": { "mimeType": "JSON", "status": "OK" } } 1.7. Error codes The Pluggable Discovery API returns messages when any of its endpoints either completes a task or experiences issues when registering a plug-in with Cryostat. An endpoint can return any of the following message types when the endpoint attempts to register a plug-in with Cryostat based on GET and POST requests: 200 : The endpoint successfully completes the task. For example, the Discovery deregistration endpoint returns a message in JSON format with the unregistered plug-in defined in the id element of the message. 400 : JSON document structure is invalid or the id element was written in an invalid format. 401 : Plug-in did not pass the Authorization header step for registration. If the token expired or you unregistered the plug-in, the endpoint also returns this error message. 404 : Plug-in id element was not found. Plug-in might have failed a callback check. Consider re-registering the plug-in.

Commands:
realm: my-plugin
callback: http://my-plugin.my-namespace.svc.local:1234/callback
{ "data": { "result": { "id": "922dd4f4-9d7c-4ae2-8982-0903868226a6", "token": " <key_value> " } }, "meta": { "status": "Created", "type": "application/json" }
http -v https://my-cryostat.my-namespace.cluster.local:8181/api/v2.2/discovery/<plugin-registration-id>?token=<current-plugin-registration-token>
HTTP/1.1 200 OK content-encoding: gzip content-length: 86 content-type: application/json { "data": { "result": null }, "meta": { "mimeType": "JSON", "status": "OK" } }
HTTP/1.1 404 Not Found content-encoding: gzip content-length: 95 content-type: application/json { "data": { "reason": null }, "meta": { "status": "Not Found", "type": "text/plain" } }
{ "data": { "result": { "children": [ { "children": [], "labels": {}, "name": "Custom Targets", "nodeType": "Realm" }, { "children": [ { "labels": {}, "name": "service:jmx:rmi:///jndi/rmi://cryostat:9091/jmxrmi", "nodeType": "JVM", "target": { "alias": "io.cryostat.Cryostat", "annotations": { "cryostat": { "HOST": "cryostat", "JAVA_MAIN": "io.cryostat.Cryostat", "PORT": "9091", "REALM": "JDP" }, "platform": {} }, "connectUrl": "service:jmx:rmi:///jndi/rmi://cryostat:9091/jmxrmi", "labels": {} } } ], "labels": {}, "name": "JDP", "nodeType": "Realm" } ], "labels": {}, "name": "Universe", "nodeType": "Universe" } }, "meta": { "status": "OK", "type": "application/json" } }
[ { "labels": {}, "nodeType": "JVM", "name": "service:jmx:rmi:///jndi/rmi://myapp.svc.local:9091/jmxrmi", "nodeType": "JVM", "target": { "alias": "com.MyApp", "annotations": { "cryostat": {}, "platform": {} }, "connectUrl": "service:jmx:rmi:///jndi/rmi://myapp.svc.local:9091/jmxrmi", "labels": {} } } ]
{ "data": { "result": null }, "meta": { "mimeType": "JSON", "status": "OK" } }
{ "data": { "result": "bcc0f3a6-dc48-402e-a3d6-9fbb63beff78" }, "meta": { "mimeType": "JSON", "status": "OK" } }