diff --git a/pom.xml b/pom.xml
index 2fdb894f9..33df66566 100644
--- a/pom.xml
+++ b/pom.xml
@@ -321,7 +321,7 @@
- -Dcryostat.discovery.jdp.enabled=true -Dcryostat.discovery.podman.enabled=true -XX:+FlightRecorder -XX:StartFlightRecording=filename=name=onstart,settings=default,disk=true,maxage=5m -Dcom.sun.management.jmxremote.autodiscovery=true -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9091 -Dcom.sun.management.jmxremote.rmi.port=9091 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.local.only=false
+ -Dcryostat.discovery.jdp.enabled=true -Dcryostat.discovery.podman.enabled=true -XX:+FlightRecorder -XX:StartFlightRecording=filename/tmp,=name=onstart,settings=default,disk=true,maxage=5m -Dcom.sun.management.jmxremote.autodiscovery=true -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9091 -Dcom.sun.management.jmxremote.rmi.port=9091 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.local.only=false
diff --git a/schema/openapi.yaml b/schema/openapi.yaml
index d8b24ed8c..ca6280b3a 100644
--- a/schema/openapi.yaml
+++ b/schema/openapi.yaml
@@ -24,6 +24,36 @@ components:
type: string
type: object
type: object
+ Annotations_Flat:
+ properties:
+ cryostat:
+ additionalProperties:
+ type: string
+ type: object
+ platform:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ ApplicationHealth:
+ properties:
+ build:
+ $ref: '#/components/schemas/BuildInfo'
+ cryostatVersion:
+ type: string
+ dashboardAvailable:
+ type: boolean
+ dashboardConfigured:
+ type: boolean
+ datasourceAvailable:
+ type: boolean
+ datasourceConfigured:
+ type: boolean
+ reportsAvailable:
+ type: boolean
+ reportsConfigured:
+ type: boolean
+ type: object
ArchivedRecording:
properties:
archivedTime:
@@ -54,7 +84,80 @@ components:
$ref: '#/components/schemas/ArchivedRecording'
type: array
type: object
- Data:
+ AuthResponse:
+ properties:
+ username:
+ type: string
+ type: object
+ BuildInfo:
+ properties:
+ git:
+ $ref: '#/components/schemas/GitInfo'
+ gitinfo:
+ $ref: '#/components/schemas/GitInfo'
+ type: object
+ ContentType:
+ enum:
+ - NONE
+ - BYTES
+ - TIMESTAMP
+ - MILLIS
+ - NANOS
+ - TICKS
+ - ADDRESS
+ - OS_THREAD
+ - JAVA_THREAD
+ - STACK_TRACE
+ - CLASS
+ - PERCENTAGE
+ type: string
+ Credential:
+ properties:
+ id:
+ format: int64
+ type: integer
+ matchExpression:
+ $ref: '#/components/schemas/MatchExpression'
+ password:
+ pattern: \S
+ type: string
+ writeOnly: true
+ username:
+ pattern: \S
+ type: string
+ writeOnly: true
+ required:
+ - matchExpression
+ - username
+ - password
+ type: object
+ CredentialMatchResult:
+ properties:
+ id:
+ format: int64
+ type: integer
+ matchExpression:
+ $ref: '#/components/schemas/MatchExpression'
+ targets:
+ items:
+ $ref: '#/components/schemas/Target'
+ type: array
+ type: object
+ CredentialTestResult:
+ enum:
+ - SUCCESS
+ - FAILURE
+ - NA
+ type: string
+ DashboardUrl:
+ properties:
+ grafanaDashboardUrl:
+ type: string
+ type: object
+ DatasourceUrl:
+ properties:
+ grafanaDatasourceUrl:
+ type: string
type: object
DiscoveryNode:
properties:
@@ -82,6 +185,28 @@ components:
- nodeType
- labels
type: object
+ DiscoveryNode_Flat:
+ properties:
+ id:
+ format: int64
+ type: integer
+ labels:
+ additionalProperties:
+ type: string
+ type: object
+ name:
+ pattern: \S
+ type: string
+ nodeType:
+ pattern: \S
+ type: string
+ target:
+ $ref: '#/components/schemas/Target_Flat'
+ required:
+ - name
+ - nodeType
+ - labels
+ type: object
DiscoveryPlugin:
properties:
builtin:
@@ -98,6 +223,22 @@ components:
- id
- realm
type: object
+ DiscoveryPlugin_Flat:
+ properties:
+ builtin:
+ readOnly: true
+ type: boolean
+ callback:
+ format: uri
+ type: string
+ id:
+ $ref: '#/components/schemas/UUID_Flat'
+ realm:
+ $ref: '#/components/schemas/DiscoveryNode_Flat'
+ required:
+ - id
+ - realm
+ type: object
Evaluation:
properties:
explanation:
@@ -111,8 +252,63 @@ components:
summary:
type: string
type: object
+ Event:
+ properties:
+ clazz:
+ type: string
+ description:
+ type: string
+ fields:
+ items:
+ $ref: '#/components/schemas/Field'
+ type: array
+ id:
+ type: string
+ location:
+ $ref: '#/components/schemas/Location'
+ methodDescriptor:
+ type: string
+ methodName:
+ type: string
+ name:
+ type: string
+ parameters:
+ items:
+ $ref: '#/components/schemas/MethodParameter'
+ type: array
+ path:
+ type: string
+ recordStackTrace:
+ type: boolean
+ returnValue:
+ $ref: '#/components/schemas/MethodReturnValue'
+ useRethrow:
+ type: boolean
+ type: object
+ Field:
+ properties:
+ contentType:
+ $ref: '#/components/schemas/ContentType'
+ converter:
+ type: string
+ description:
+ type: string
+ event:
+ $ref: '#/components/schemas/Event'
+ expression:
+ type: string
+ name:
+ type: string
+ relationKey:
+ type: string
+ type: object
FileUpload:
type: object
+ GitInfo:
+ properties:
+ hash:
+ type: string
+ type: object
Instant:
example: 2022-03-10T16:15:50Z
format: date-time
@@ -160,6 +356,12 @@ components:
toDisk:
type: boolean
type: object
+ Location:
+ enum:
+ - ENTRY
+ - EXIT
+ - WRAP
+ type: string
MatchExpression:
properties:
id:
@@ -183,13 +385,6 @@ components:
$ref: '#/components/schemas/Target'
type: array
type: object
- Meta:
- properties:
- status:
- type: string
- type:
- type: string
- type: object
Metadata:
properties:
expiry:
@@ -199,6 +394,79 @@ components:
type: string
type: object
type: object
+ MethodParameter:
+ properties:
+ contentType:
+ $ref: '#/components/schemas/ContentType'
+ converter:
+ type: string
+ description:
+ type: string
+ event:
+ $ref: '#/components/schemas/Event'
+ index:
+ format: int32
+ type: integer
+ name:
+ type: string
+ relationKey:
+ type: string
+ type: object
+ MethodReturnValue:
+ properties:
+ contentType:
+ $ref: '#/components/schemas/ContentType'
+ converter:
+ type: string
+ description:
+ type: string
+ event:
+ $ref: '#/components/schemas/Event'
+ name:
+ type: string
+ relationKey:
+ type: string
+ type: object
+ PluginRegistration:
+ properties:
+ env:
+ additionalProperties:
+ type: string
+ type: object
+ id:
+ type: string
+ token:
+ type: string
+ type: object
+ ProbeResponse:
+ properties:
+ description:
+ type: string
+ name:
+ type: string
+ type: object
+ ProbeTemplate:
+ properties:
+ allowConverter:
+ type: boolean
+ allowToString:
+ type: boolean
+ classPrefix:
+ type: string
+ events:
+ items:
+ $ref: '#/components/schemas/Event'
+ type: array
+ fileName:
+ type: string
+ type: object
+ ProbeTemplateResponse:
+ properties:
+ description:
+ type: string
+ name:
+ type: string
+ type: object
RecordingState:
enum:
- NEW
@@ -211,9 +479,10 @@ components:
properties:
matchExpression:
type: string
- targets:
+ targetIds:
items:
- $ref: '#/components/schemas/Target'
+ format: int64
+ type: integer
type: array
type: object
Rule:
@@ -322,6 +591,34 @@ components:
- labels
- annotations
type: object
+ Target_Flat:
+ properties:
+ agent:
+ readOnly: true
+ type: boolean
+ alias:
+ pattern: \S
+ type: string
+ annotations:
+ $ref: '#/components/schemas/Annotations_Flat'
+ connectUrl:
+ format: uri
+ type: string
+ id:
+ format: int64
+ type: integer
+ jvmId:
+ type: string
+ labels:
+ additionalProperties:
+ type: string
+ type: object
+ required:
+ - connectUrl
+ - alias
+ - labels
+ - annotations
+ type: object
Template:
properties:
description:
@@ -342,13 +639,10 @@ components:
format: uuid
pattern: '[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}'
type: string
- V2Response:
- properties:
- data:
- $ref: '#/components/schemas/Data'
- meta:
- $ref: '#/components/schemas/Meta'
- type: object
+ UUID_Flat:
+ format: uuid
+ pattern: '[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}'
+ type: string
info:
contact:
email: cryostat-development@googlegroups.com
@@ -362,39 +656,6 @@ info:
version: 4.0.0-snapshot
openapi: 3.0.3
paths:
- /api/beta/credentials/{connectUrl}:
- post:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- type: string
- requestBody:
- content:
- application/x-www-form-urlencoded:
- schema:
- properties:
- password:
- type: string
- username:
- type: string
- type: object
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/V2Response'
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Credential Check
/api/beta/fs/recordings:
get:
responses:
@@ -413,7 +674,7 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
+ - Archived Recordings
/api/beta/fs/recordings/{jvmId}:
get:
parameters:
@@ -438,7 +699,7 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
+ - Archived Recordings
/api/beta/fs/recordings/{jvmId}/{filename}:
delete:
parameters:
@@ -462,23 +723,23 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/beta/fs/recordings/{jvmId}/{filename}/upload:
- post:
+ - Archived Recordings
+ /api/beta/recordings/{connectUrl}/{filename}:
+ delete:
parameters:
- in: path
- name: filename
+ name: connectUrl
required: true
schema:
type: string
- in: path
- name: jvmId
+ name: filename
required: true
schema:
type: string
responses:
- "200":
- description: OK
+ "204":
+ description: No Content
"401":
description: Not Authorized
"403":
@@ -486,17 +747,22 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/beta/matchExpressions:
+ - Archived Recordings
+ /api/beta/recordings/{jvmId}:
get:
- responses:
- "200":
- content:
- application/json:
- schema:
+ parameters:
+ - in: path
+ name: jvmId
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
items:
- additionalProperties: {}
- type: object
+ $ref: '#/components/schemas/ArchivedRecording'
type: array
description: OK
"401":
@@ -506,20 +772,30 @@ paths:
security:
- SecurityScheme: []
tags:
- - Match Expressions
+ - Archived Recordings
post:
+ parameters:
+ - in: path
+ name: jvmId
+ required: true
+ schema:
+ type: string
requestBody:
content:
- application/json:
+ application/x-www-form-urlencoded:
schema:
- $ref: '#/components/schemas/RequestData'
+ properties:
+ labels:
+ $ref: '#/components/schemas/JsonObject'
+ maxFiles:
+ format: int32
+ type: integer
+ recording:
+ $ref: '#/components/schemas/FileUpload'
+ type: object
responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/V2Response'
- description: OK
+ "201":
+ description: Created
"401":
description: Not Authorized
"403":
@@ -527,8 +803,8 @@ paths:
security:
- SecurityScheme: []
tags:
- - Match Expressions
- /api/beta/matchExpressions/{id}:
+ - Archived Recordings
+ /api/v4/activedownload/{id}:
get:
parameters:
- in: path
@@ -540,33 +816,10 @@ paths:
responses:
"200":
content:
- application/json:
+ application/octet-stream:
schema:
- $ref: '#/components/schemas/MatchedExpression'
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Match Expressions
- /api/beta/recordings/{connectUrl}/{filename}:
- delete:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- type: string
- - in: path
- name: filename
- required: true
- schema:
- type: string
- responses:
- "200":
+ format: binary
+ type: string
description: OK
"401":
description: Not Authorized
@@ -575,46 +828,27 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/beta/recordings/{connectUrl}/{filename}/upload:
+ - Active Recordings Download
+ /api/v4/auth:
post:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- type: string
- - in: path
- name: filename
- required: true
- schema:
- type: string
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/AuthResponse'
description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
tags:
- - Recordings
- /api/beta/recordings/{jvmId}:
+ - Auth
+ /api/v4/credentials:
get:
- parameters:
- - in: path
- name: jvmId
- required: true
- schema:
- type: string
responses:
"200":
content:
application/json:
schema:
items:
- $ref: '#/components/schemas/ArchivedRecording'
+ $ref: '#/components/schemas/CredentialMatchResult'
type: array
description: OK
"401":
@@ -624,62 +858,26 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
+ - Credentials
post:
- parameters:
- - in: path
- name: jvmId
- required: true
- schema:
- type: string
requestBody:
content:
application/x-www-form-urlencoded:
schema:
properties:
- labels:
- $ref: '#/components/schemas/JsonObject'
- maxFiles:
- format: int32
- type: integer
- recording:
- $ref: '#/components/schemas/FileUpload'
+ matchExpression:
+ type: string
+ password:
+ type: string
+ username:
+ type: string
type: object
- responses:
- "201":
- description: Created
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Recordings
- /api/v1/grafana_dashboard_url:
- get:
- responses:
- "200":
- description: OK
- tags:
- - Health
- /api/v1/grafana_datasource_url:
- get:
- responses:
- "200":
- description: OK
- tags:
- - Health
- /api/v1/recordings:
- get:
responses:
"200":
content:
application/json:
schema:
- items:
- $ref: '#/components/schemas/ArchivedRecording'
- type: array
+ $ref: '#/components/schemas/Credential'
description: OK
"401":
description: Not Authorized
@@ -688,25 +886,32 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
+ - Credentials
+ /api/v4/credentials/test/{targetId}:
post:
+ parameters:
+ - in: path
+ name: targetId
+ required: true
+ schema:
+ format: int64
+ type: integer
requestBody:
content:
application/x-www-form-urlencoded:
schema:
properties:
- labels:
- $ref: '#/components/schemas/JsonObject'
- recording:
- $ref: '#/components/schemas/FileUpload'
+ password:
+ type: string
+ username:
+ type: string
type: object
responses:
"200":
content:
application/json:
schema:
- additionalProperties: {}
- type: object
+ $ref: '#/components/schemas/CredentialTestResult'
description: OK
"401":
description: Not Authorized
@@ -715,15 +920,16 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v1/recordings/{filename}:
+ - Credentials
+ /api/v4/credentials/{id}:
delete:
parameters:
- in: path
- name: filename
+ name: id
required: true
schema:
- type: string
+ format: int64
+ type: integer
responses:
"204":
description: No Content
@@ -734,18 +940,21 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v1/reports/{recordingName}:
+ - Credentials
get:
- deprecated: true
parameters:
- in: path
- name: recordingName
+ name: id
required: true
schema:
- type: string
+ format: int64
+ type: integer
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/CredentialMatchResult'
description: OK
"401":
description: Not Authorized
@@ -754,11 +963,15 @@ paths:
security:
- SecurityScheme: []
tags:
- - Reports
- /api/v1/targets:
+ - Credentials
+ /api/v4/discovery:
get:
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DiscoveryNode'
description: OK
"401":
description: Not Authorized
@@ -767,22 +980,19 @@ paths:
security:
- SecurityScheme: []
tags:
- - Targets
- /api/v1/targets/{connectUrl}/events:
- get:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- - in: query
- name: q
- schema:
- type: string
+ - Discovery
+ post:
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PluginRegistration'
description: OK
"401":
description: Not Authorized
@@ -791,58 +1001,38 @@ paths:
security:
- SecurityScheme: []
tags:
- - Events
- /api/v1/targets/{connectUrl}/recordingOptions:
- get:
+ - Discovery
+ /api/v4/discovery/{id}:
+ delete:
parameters:
- in: path
- name: connectUrl
+ name: id
required: true
schema:
- format: uri
- type: string
- responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Recordings
- patch:
- parameters:
- - in: path
- name: connectUrl
- required: true
+ $ref: '#/components/schemas/UUID'
+ - in: query
+ name: token
schema:
- format: uri
type: string
responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
+ "204":
+ description: No Content
tags:
- - Recordings
- /api/v1/targets/{connectUrl}/recordings:
+ - Discovery
get:
parameters:
- in: path
- name: connectUrl
+ name: id
required: true
schema:
- format: uri
+ $ref: '#/components/schemas/UUID'
+ - in: query
+ name: token
+ schema:
type: string
responses:
- "200":
- description: OK
+ "204":
+ description: No Content
"401":
description: Not Authorized
"403":
@@ -850,118 +1040,45 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
+ - Discovery
post:
parameters:
- in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Recordings
- /api/v1/targets/{connectUrl}/recordings/{recordingName}:
- delete:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- - in: path
- name: recordingName
- required: true
- schema:
- type: string
- responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Recordings
- patch:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- - in: path
- name: recordingName
+ name: id
required: true
+ schema:
+ $ref: '#/components/schemas/UUID'
+ - in: query
+ name: token
schema:
type: string
requestBody:
content:
- text/plain:
+ application/json:
schema:
- type: string
- responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Recordings
- /api/v1/targets/{connectUrl}/recordings/{recordingName}/upload:
- post:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- - in: path
- name: recordingName
- required: true
- schema:
- type: string
+ items:
+ $ref: '#/components/schemas/DiscoveryNode'
+ type: array
responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
+ "201":
+ description: Created
tags:
- - Recordings
- /api/v1/targets/{connectUrl}/snapshot:
- post:
+ - Discovery
+ /api/v4/discovery_plugins:
+ get:
parameters:
- - in: path
- name: connectUrl
- required: true
+ - in: query
+ name: realm
schema:
- format: uri
type: string
responses:
"200":
content:
- application/json: {}
+ application/json:
+ schema:
+ items:
+ $ref: '#/components/schemas/DiscoveryPlugin_Flat'
+ type: array
description: OK
"401":
description: Not Authorized
@@ -970,18 +1087,21 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v1/targets/{connectUrl}/templates:
+ - Discovery
+ /api/v4/discovery_plugins/{id}:
get:
parameters:
- in: path
- name: connectUrl
+ name: id
required: true
schema:
- format: uri
- type: string
+ $ref: '#/components/schemas/UUID'
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DiscoveryPlugin'
description: OK
"401":
description: Not Authorized
@@ -990,28 +1110,24 @@ paths:
security:
- SecurityScheme: []
tags:
- - Event Templates
- /api/v1/targets/{connectUrl}/templates/{templateName}/type/{templateType}:
+ - Discovery
+ /api/v4/download/{encodedKey}:
get:
parameters:
- in: path
- name: connectUrl
+ name: encodedKey
required: true
schema:
- format: uri
type: string
- - in: path
- name: templateName
- required: true
+ - in: query
+ name: f
schema:
type: string
- - in: path
- name: templateType
- required: true
- schema:
- $ref: '#/components/schemas/TemplateType'
responses:
"200":
+ content:
+ application/json:
+ schema: {}
description: OK
"401":
description: Not Authorized
@@ -1020,23 +1136,15 @@ paths:
security:
- SecurityScheme: []
tags:
- - Event Templates
- /api/v1/targets/{targetId}/reports/{recordingName}:
+ - Archived Recordings
+ /api/v4/event_templates:
get:
- deprecated: true
- parameters:
- - in: path
- name: recordingName
- required: true
- schema:
- type: string
- - in: path
- name: targetId
- required: true
- schema:
- type: string
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Template'
description: OK
"401":
description: Not Authorized
@@ -1045,8 +1153,7 @@ paths:
security:
- SecurityScheme: []
tags:
- - Reports
- /api/v1/templates:
+ - Event Templates
post:
requestBody:
content:
@@ -1058,6 +1165,10 @@ paths:
type: object
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Template'
description: OK
"401":
description: Not Authorized
@@ -1067,7 +1178,7 @@ paths:
- SecurityScheme: []
tags:
- Event Templates
- /api/v1/templates/{templateName}:
+ /api/v4/event_templates/{templateName}:
delete:
parameters:
- in: path
@@ -1076,8 +1187,8 @@ paths:
schema:
type: string
responses:
- "200":
- description: OK
+ "204":
+ description: No Content
"401":
description: Not Authorized
"403":
@@ -1086,17 +1197,20 @@ paths:
- SecurityScheme: []
tags:
- Event Templates
- /api/v2.1/auth:
+ /api/v4/grafana/{encodedKey}:
post:
+ parameters:
+ - in: path
+ name: encodedKey
+ required: true
+ schema:
+ type: string
responses:
"200":
- description: OK
- tags:
- - Auth
- /api/v2.1/discovery:
- get:
- responses:
- "200":
+ content:
+ text/plain:
+ schema:
+ type: string
description: OK
"401":
description: Not Authorized
@@ -1105,52 +1219,50 @@ paths:
security:
- SecurityScheme: []
tags:
- - Discovery
- /api/v2.1/logout:
- post:
+ - Archived Recordings
+ /api/v4/grafana_dashboard_url:
+ get:
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DashboardUrl'
description: OK
tags:
- - Auth
- /api/v2.1/targets/{connectUrl}/templates/{templateName}/type/{templateType}:
+ - Health
+ /api/v4/grafana_datasource_url:
get:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- - in: path
- name: templateName
- required: true
- schema:
- type: string
- - in: path
- name: templateType
- required: true
- schema:
- $ref: '#/components/schemas/TemplateType'
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DatasourceUrl'
description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
tags:
- - Event Templates
- /api/v2.2/credentials:
+ - Health
+ /api/v4/logout:
+ post:
+ responses:
+ "200":
+ content:
+ application/json:
+ schema: {}
+ description: OK
+ tags:
+ - Auth
+ /api/v4/matchExpressions:
get:
responses:
"200":
content:
application/json:
schema:
- $ref: '#/components/schemas/V2Response'
+ items:
+ additionalProperties: {}
+ type: object
+ type: array
description: OK
"401":
description: Not Authorized
@@ -1159,23 +1271,20 @@ paths:
security:
- SecurityScheme: []
tags:
- - Credentials
+ - Match Expressions
post:
requestBody:
content:
- application/x-www-form-urlencoded:
+ application/json:
schema:
- properties:
- matchExpression:
- type: string
- password:
- type: string
- username:
- type: string
- type: object
+ $ref: '#/components/schemas/RequestData'
responses:
- "201":
- description: Created
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MatchedExpression'
+ description: OK
"401":
description: Not Authorized
"403":
@@ -1183,9 +1292,9 @@ paths:
security:
- SecurityScheme: []
tags:
- - Credentials
- /api/v2.2/credentials/{id}:
- delete:
+ - Match Expressions
+ /api/v4/matchExpressions/{id}:
+ get:
parameters:
- in: path
name: id
@@ -1194,8 +1303,12 @@ paths:
format: int64
type: integer
responses:
- "204":
- description: No Content
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MatchedExpression'
+ description: OK
"401":
description: Not Authorized
"403":
@@ -1203,87 +1316,64 @@ paths:
security:
- SecurityScheme: []
tags:
- - Credentials
+ - Match Expressions
+ /api/v4/probes:
get:
- parameters:
- - in: path
- name: id
- required: true
- schema:
- format: int64
- type: integer
responses:
"200":
content:
application/json:
schema:
- $ref: '#/components/schemas/V2Response'
+ items:
+ $ref: '#/components/schemas/ProbeTemplateResponse'
+ type: array
description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
tags:
- - Credentials
- /api/v2.2/discovery:
+ - JMC Agent Templates
post:
requestBody:
content:
- application/json:
+ application/x-www-form-urlencoded:
schema:
- $ref: '#/components/schemas/JsonObject'
+ properties:
+ name:
+ type: string
+ probeTemplate:
+ $ref: '#/components/schemas/FileUpload'
+ type: object
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ProbeTemplate'
description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
tags:
- - Discovery
- /api/v2.2/discovery/{id}:
+ - JMC Agent Templates
+ /api/v4/probes/{probeTemplateName}:
delete:
parameters:
- in: path
- name: id
+ name: probeTemplateName
required: true
- schema:
- $ref: '#/components/schemas/UUID'
- - in: query
- name: token
schema:
type: string
+ responses:
+ "204":
+ description: No Content
+ tags:
+ - JMC Agent Templates
+ /api/v4/recordings:
+ get:
responses:
"200":
content:
application/json:
schema:
- additionalProperties:
- additionalProperties:
- type: string
- type: object
- type: object
+ items:
+ $ref: '#/components/schemas/ArchivedRecording'
+ type: array
description: OK
- tags:
- - Discovery
- get:
- parameters:
- - in: path
- name: id
- required: true
- schema:
- $ref: '#/components/schemas/UUID'
- - in: query
- name: token
- schema:
- type: string
- responses:
- "204":
- description: No Content
"401":
description: Not Authorized
"403":
@@ -1291,43 +1381,26 @@ paths:
security:
- SecurityScheme: []
tags:
- - Discovery
+ - Archived Recordings
post:
- parameters:
- - in: path
- name: id
- required: true
- schema:
- $ref: '#/components/schemas/UUID'
- - in: query
- name: token
- schema:
- type: string
requestBody:
content:
- application/json:
+ application/x-www-form-urlencoded:
schema:
- items:
- $ref: '#/components/schemas/DiscoveryNode'
- type: array
+ properties:
+ labels:
+ $ref: '#/components/schemas/JsonObject'
+ recording:
+ $ref: '#/components/schemas/FileUpload'
+ type: object
responses:
"200":
content:
application/json:
schema:
- additionalProperties:
- additionalProperties:
- type: string
- type: object
+ additionalProperties: {}
type: object
description: OK
- tags:
- - Discovery
- /api/v2.2/graphql:
- get:
- responses:
- "200":
- description: OK
"401":
description: Not Authorized
"403":
@@ -1335,11 +1408,18 @@ paths:
security:
- SecurityScheme: []
tags:
- - Graph QL
- post:
+ - Archived Recordings
+ /api/v4/recordings/{filename}:
+ delete:
+ parameters:
+ - in: path
+ name: filename
+ required: true
+ schema:
+ type: string
responses:
- "200":
- description: OK
+ "204":
+ description: No Content
"401":
description: Not Authorized
"403":
@@ -1347,55 +1427,42 @@ paths:
security:
- SecurityScheme: []
tags:
- - Graph QL
- /api/v2/probes:
+ - Archived Recordings
+ /api/v4/reports/{encodedKey}:
get:
- responses:
- "200":
- description: OK
- tags:
- - JMC Agent
- /api/v2/probes/{probeTemplateName}:
- delete:
parameters:
- in: path
- name: probeTemplateName
- required: true
- schema:
- type: string
- responses:
- "200":
- description: OK
- tags:
- - JMC Agent
- post:
- parameters:
- - in: path
- name: probeTemplateName
+ name: encodedKey
required: true
schema:
type: string
- requestBody:
- content:
- application/x-www-form-urlencoded:
- schema:
- properties:
- probeTemplate:
- $ref: '#/components/schemas/FileUpload'
- type: object
responses:
"200":
+ content:
+ application/json:
+ schema:
+ additionalProperties:
+ $ref: '#/components/schemas/AnalysisResult'
+ type: object
description: OK
+ "401":
+ description: Not Authorized
+ "403":
+ description: Not Allowed
+ security:
+ - SecurityScheme: []
tags:
- - JMC Agent
- /api/v2/rules:
+ - Reports
+ /api/v4/rules:
get:
responses:
"200":
content:
application/json:
schema:
- $ref: '#/components/schemas/V2Response'
+ items:
+ $ref: '#/components/schemas/Rule'
+ type: array
description: OK
"401":
description: Not Authorized
@@ -1465,343 +1532,16 @@ paths:
type: integer
name:
type: string
- preservedArchives:
- format: int32
- type: integer
- type: object
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/V2Response'
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Rules
- /api/v2/rules/{name}:
- delete:
- parameters:
- - in: path
- name: name
- required: true
- schema:
- type: string
- - in: query
- name: clean
- schema:
- type: boolean
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/V2Response'
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Rules
- get:
- parameters:
- - in: path
- name: name
- required: true
- schema:
- type: string
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/V2Response'
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Rules
- patch:
- parameters:
- - in: path
- name: name
- required: true
- schema:
- type: string
- - in: query
- name: clean
- schema:
- type: boolean
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/JsonObject'
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/V2Response'
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Rules
- /api/v2/targets:
- post:
- parameters:
- - in: query
- name: dryrun
- schema:
- type: boolean
- - in: query
- name: storeCredentials
- schema:
- type: boolean
- requestBody:
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/Target'
- application/x-www-form-urlencoded:
- schema:
- properties:
- alias:
- type: string
- connectUrl:
- format: uri
- type: string
- password:
- type: string
- username:
- type: string
- type: object
- multipart/form-data:
- schema:
- properties:
- alias:
- type: string
- connectUrl:
- format: uri
- type: string
- password:
- type: string
- username:
- type: string
- type: object
- responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Custom Discovery
- /api/v2/targets/{connectUrl}:
- delete:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Custom Discovery
- /api/v2/targets/{connectUrl}/events:
- get:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- - in: query
- name: q
- schema:
- type: string
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/V2Response'
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Events
- /api/v2/targets/{connectUrl}/probes:
- delete:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- responses:
- "200":
- description: OK
- tags:
- - JMC Agent
- get:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- responses:
- "200":
- description: OK
- tags:
- - JMC Agent
- /api/v2/targets/{connectUrl}/probes/{probeTemplateName}:
- post:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- - in: path
- name: probeTemplateName
- required: true
- schema:
- type: string
- responses:
- "200":
- description: OK
- tags:
- - JMC Agent
- /api/v2/targets/{connectUrl}/snapshot:
- post:
- parameters:
- - in: path
- name: connectUrl
- required: true
- schema:
- format: uri
- type: string
- responses:
- "200":
- content:
- application/json: {}
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Recordings
- /api/v3/activedownload/{id}:
- get:
- parameters:
- - in: path
- name: id
- required: true
- schema:
- format: int64
- type: integer
- responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Recordings
- /api/v3/discovery:
- get:
- responses:
- "200":
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/DiscoveryNode'
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Discovery
- /api/v3/discovery_plugins:
- get:
- parameters:
- - in: query
- name: realm
- schema:
- type: string
- responses:
- "200":
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Discovery
- /api/v3/discovery_plugins/{id}:
- get:
- parameters:
- - in: path
- name: id
- required: true
- schema:
- $ref: '#/components/schemas/UUID'
+ preservedArchives:
+ format: int32
+ type: integer
+ type: object
responses:
"200":
content:
application/json:
schema:
- $ref: '#/components/schemas/DiscoveryPlugin'
+ $ref: '#/components/schemas/Rule'
description: OK
"401":
description: Not Authorized
@@ -1810,22 +1550,22 @@ paths:
security:
- SecurityScheme: []
tags:
- - Discovery
- /api/v3/download/{encodedKey}:
- get:
+ - Rules
+ /api/v4/rules/{name}:
+ delete:
parameters:
- in: path
- name: encodedKey
+ name: name
required: true
schema:
type: string
- in: query
- name: f
+ name: clean
schema:
- type: string
+ type: boolean
responses:
- "200":
- description: OK
+ "204":
+ description: No Content
"401":
description: Not Authorized
"403":
@@ -1833,17 +1573,20 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v3/event_templates:
+ - Rules
get:
+ parameters:
+ - in: path
+ name: name
+ required: true
+ schema:
+ type: string
responses:
"200":
content:
application/json:
schema:
- items:
- $ref: '#/components/schemas/Template'
- type: array
+ $ref: '#/components/schemas/Rule'
description: OK
"401":
description: Not Authorized
@@ -1852,60 +1595,29 @@ paths:
security:
- SecurityScheme: []
tags:
- - Event Templates
- post:
- requestBody:
- content:
- application/x-www-form-urlencoded:
- schema:
- properties:
- template:
- $ref: '#/components/schemas/FileUpload'
- type: object
- responses:
- "201":
- description: Created
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Event Templates
- /api/v3/event_templates/{templateName}:
- delete:
+ - Rules
+ patch:
parameters:
- in: path
- name: templateName
+ name: name
required: true
schema:
type: string
- responses:
- "204":
- description: No Content
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Event Templates
- /api/v3/grafana/{encodedKey}:
- post:
- parameters:
- - in: path
- name: encodedKey
- required: true
+ - in: query
+ name: clean
schema:
- type: string
+ type: boolean
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/JsonObject'
responses:
"200":
content:
- text/plain:
+ application/json:
schema:
- type: string
+ $ref: '#/components/schemas/Rule'
description: OK
"401":
description: Not Authorized
@@ -1914,86 +1626,73 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v3/probes:
+ - Rules
+ /api/v4/targets:
get:
responses:
"200":
content:
application/json:
schema:
- $ref: '#/components/schemas/V2Response'
- description: OK
- tags:
- - JMC Agent
- /api/v3/probes/{probeTemplateName}:
- delete:
- parameters:
- - in: path
- name: probeTemplateName
- required: true
- schema:
- type: string
- responses:
- "200":
+ items:
+ $ref: '#/components/schemas/Target'
+ type: array
description: OK
+ "401":
+ description: Not Authorized
+ "403":
+ description: Not Allowed
+ security:
+ - SecurityScheme: []
tags:
- - JMC Agent
+ - Targets
post:
parameters:
- - in: path
- name: probeTemplateName
- required: true
+ - in: query
+ name: dryrun
schema:
- type: string
+ type: boolean
+ - in: query
+ name: storeCredentials
+ schema:
+ type: boolean
requestBody:
content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Target'
application/x-www-form-urlencoded:
schema:
properties:
- probeTemplate:
- $ref: '#/components/schemas/FileUpload'
+ alias:
+ type: string
+ connectUrl:
+ format: uri
+ type: string
+ password:
+ type: string
+ username:
+ type: string
+ type: object
+ multipart/form-data:
+ schema:
+ properties:
+ alias:
+ type: string
+ connectUrl:
+ format: uri
+ type: string
+ password:
+ type: string
+ username:
+ type: string
type: object
- responses:
- "200":
- description: OK
- tags:
- - JMC Agent
- /api/v3/reports/{encodedKey}:
- get:
- parameters:
- - in: path
- name: encodedKey
- required: true
- schema:
- type: string
- responses:
- "200":
- content:
- application/json:
- schema:
- additionalProperties:
- $ref: '#/components/schemas/AnalysisResult'
- type: object
- description: OK
- "401":
- description: Not Authorized
- "403":
- description: Not Allowed
- security:
- - SecurityScheme: []
- tags:
- - Reports
- /api/v3/targets:
- get:
responses:
"200":
content:
application/json:
schema:
- items:
- $ref: '#/components/schemas/Target'
- type: array
+ $ref: '#/components/schemas/Target'
description: OK
"401":
description: Not Authorized
@@ -2002,8 +1701,8 @@ paths:
security:
- SecurityScheme: []
tags:
- - Targets
- /api/v3/targets/{id}:
+ - Custom Discovery
+ /api/v4/targets/{id}:
delete:
parameters:
- in: path
@@ -2013,8 +1712,8 @@ paths:
format: int64
type: integer
responses:
- "200":
- description: OK
+ "204":
+ description: No Content
"401":
description: Not Authorized
"403":
@@ -2046,7 +1745,7 @@ paths:
- SecurityScheme: []
tags:
- Targets
- /api/v3/targets/{id}/event_templates:
+ /api/v4/targets/{id}/event_templates:
get:
parameters:
- in: path
@@ -2071,8 +1770,8 @@ paths:
security:
- SecurityScheme: []
tags:
- - Event Templates
- /api/v3/targets/{id}/event_templates/{templateType}/{templateName}:
+ - Target Event Templates
+ /api/v4/targets/{id}/event_templates/{templateType}/{templateName}:
get:
parameters:
- in: path
@@ -2093,6 +1792,10 @@ paths:
$ref: '#/components/schemas/TemplateType'
responses:
"200":
+ content:
+ application/xml:
+ schema:
+ type: string
description: OK
"401":
description: Not Authorized
@@ -2101,8 +1804,8 @@ paths:
security:
- SecurityScheme: []
tags:
- - Event Templates
- /api/v3/targets/{id}/events:
+ - Target Event Templates
+ /api/v4/targets/{id}/events:
get:
parameters:
- in: path
@@ -2132,7 +1835,7 @@ paths:
- SecurityScheme: []
tags:
- Events
- /api/v3/targets/{id}/probes:
+ /api/v4/targets/{id}/probes:
delete:
parameters:
- in: path
@@ -2142,10 +1845,10 @@ paths:
format: int64
type: integer
responses:
- "200":
- description: OK
+ "204":
+ description: No Content
tags:
- - JMC Agent
+ - JMC Agent Probes
get:
parameters:
- in: path
@@ -2159,11 +1862,13 @@ paths:
content:
application/json:
schema:
- $ref: '#/components/schemas/V2Response'
+ items:
+ $ref: '#/components/schemas/ProbeResponse'
+ type: array
description: OK
tags:
- - JMC Agent
- /api/v3/targets/{id}/probes/{probeTemplateName}:
+ - JMC Agent Probes
+ /api/v4/targets/{id}/probes/{probeTemplateName}:
post:
parameters:
- in: path
@@ -2178,15 +1883,15 @@ paths:
schema:
type: string
responses:
- "200":
- description: OK
+ "201":
+ description: Created
tags:
- - JMC Agent
- /api/v3/targets/{id}/recordingOptions:
+ - JMC Agent Probes
+ /api/v4/targets/{targetId}/recordingOptions:
get:
parameters:
- in: path
- name: id
+ name: targetId
required: true
schema:
format: int64
@@ -2206,11 +1911,11 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
+ - Recording Options
patch:
parameters:
- in: path
- name: id
+ name: targetId
required: true
schema:
format: int64
@@ -2242,12 +1947,12 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v3/targets/{id}/recordings:
+ - Recording Options
+ /api/v4/targets/{targetId}/recordings:
get:
parameters:
- in: path
- name: id
+ name: targetId
required: true
schema:
format: int64
@@ -2268,11 +1973,11 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
+ - Active Recordings
post:
parameters:
- in: path
- name: id
+ name: targetId
required: true
schema:
format: int64
@@ -2317,6 +2022,10 @@ paths:
type: object
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LinkedRecordingDescriptor'
description: OK
"401":
description: Not Authorized
@@ -2325,21 +2034,25 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v3/targets/{id}/snapshot:
- post:
+ - Active Recordings
+ /api/v4/targets/{targetId}/recordings/{remoteId}:
+ delete:
parameters:
- in: path
- name: id
+ name: remoteId
+ required: true
+ schema:
+ format: int64
+ type: integer
+ - in: path
+ name: targetId
required: true
schema:
format: int64
type: integer
responses:
- "200":
- content:
- application/json: {}
- description: OK
+ "204":
+ description: No Content
"401":
description: Not Authorized
"403":
@@ -2347,9 +2060,8 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v3/targets/{targetId}/recordings/{remoteId}:
- delete:
+ - Active Recordings
+ get:
parameters:
- in: path
name: remoteId
@@ -2364,8 +2076,13 @@ paths:
format: int64
type: integer
responses:
- "204":
- description: No Content
+ "200":
+ content:
+ application/octet-stream:
+ schema:
+ format: binary
+ type: string
+ description: OK
"401":
description: Not Authorized
"403":
@@ -2373,7 +2090,7 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
+ - Active Recordings
patch:
parameters:
- in: path
@@ -2407,8 +2124,8 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v3/targets/{targetId}/recordings/{remoteId}/upload:
+ - Active Recordings
+ /api/v4/targets/{targetId}/recordings/{remoteId}/upload:
post:
parameters:
- in: path
@@ -2437,10 +2154,9 @@ paths:
security:
- SecurityScheme: []
tags:
- - Recordings
- /api/v3/targets/{targetId}/reports/{recordingId}:
+ - Active Recordings
+ /api/v4/targets/{targetId}/reports/{recordingId}:
get:
- deprecated: true
parameters:
- in: path
name: recordingId
@@ -2471,7 +2187,31 @@ paths:
- SecurityScheme: []
tags:
- Reports
- /api/v3/tls/certs:
+ /api/v4/targets/{targetId}/snapshot:
+ post:
+ parameters:
+ - in: path
+ name: targetId
+ required: true
+ schema:
+ format: int64
+ type: integer
+ responses:
+ "200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LinkedRecordingDescriptor'
+ description: OK
+ "401":
+ description: Not Authorized
+ "403":
+ description: Not Allowed
+ security:
+ - SecurityScheme: []
+ tags:
+ - Snapshots
+ /api/v4/tls/certs:
get:
responses:
"200":
@@ -2488,6 +2228,10 @@ paths:
get:
responses:
"200":
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ApplicationHealth'
description: OK
tags:
- Health
diff --git a/schema/schema.graphql b/schema/schema.graphql
index f471cf40e..0ee8fa1a6 100644
--- a/schema/schema.graphql
+++ b/schema/schema.graphql
@@ -52,12 +52,10 @@ type ArchivedRecording {
archivedTime: BigInteger!
doDelete: ArchivedRecording!
doPutMetadata(metadataInput: MetadataLabelsInput): ArchivedRecording!
- "URL for GET request to retrieve the JFR binary file content of this recording"
downloadUrl: String
jvmId: String
metadata: Metadata
name: String
- "URL for GET request to retrieve a JSON formatted Automated Analysis Report of this recording"
reportUrl: String
size: BigInteger!
}
diff --git a/schema/update.bash b/schema/update.bash
index 967bafc73..0fd286d32 100755
--- a/schema/update.bash
+++ b/schema/update.bash
@@ -1,8 +1,15 @@
#!/usr/bin/env bash
+set -x
+
DIR="$(dirname "$(readlink -f "$0")")"
-"${DIR}"/../mvnw -f "${DIR}/../pom.xml" -B -U clean compile test-compile
+if ! command -v httpz && ! command -v wget; then
+ echo "No HTTPie or wget?"
+ exit 1
+fi
+
+"${DIR}"/../mvnw -f "${DIR}/../pom.xml" -B -U -Dspotless.check.skip clean compile test-compile
"${DIR}"/../mvnw -f "${DIR}/../pom.xml" -B -U -Dmaven.test.skip -Dquarkus.quinoa=false -Dspotless.check.skip -Dquarkus.smallrye-openapi.info-title="Cryostat API" clean quarkus:dev &
pid="$!"
function cleanup() {
@@ -16,12 +23,26 @@ while true; do
if [ "${counter}" -gt 10 ]; then
exit 1
fi
- if wget --spider http://localhost:8181/health; then
- break
- else
- counter=$((counter + 1))
- sleep "${2:-10}"
+ if command -v http; then
+ if http :8181/health/liveness; then
+ break
+ else
+ counter=$((counter + 1))
+ sleep "${2:-10}"
+ fi
+ elif command -v wget; then
+ if wget --tries=1 --spider http://localhost:8181/health/liveness; then
+ break
+ else
+ counter=$((counter + 1))
+ sleep "${2:-10}"
+ fi
fi
done
-wget http://localhost:8181/api -O - | yq -P 'sort_keys(..)' > "${DIR}/openapi.yaml"
-wget http://localhost:8181/api/v3/graphql/schema.graphql -O "${DIR}/schema.graphql"
+if command -v http; then
+ http --pretty=format --body :8181/api | yq -P 'sort_keys(..)' > "${DIR}/openapi.yaml"
+ http --pretty=format --body :8181/api/v4/graphql/schema.graphql > "${DIR}/schema.graphql"
+elif command -v wget; then
+ wget http://localhost:8181/api -O - | yq -P 'sort_keys(..)' > "${DIR}/openapi.yaml"
+ wget http://localhost:8181/api/v4/graphql/schema.graphql -O "${DIR}/schema.graphql"
+fi
diff --git a/src/main/java/io/cryostat/BuildInfo.java b/src/main/java/io/cryostat/BuildInfo.java
index dce54b9fb..36343cb23 100644
--- a/src/main/java/io/cryostat/BuildInfo.java
+++ b/src/main/java/io/cryostat/BuildInfo.java
@@ -19,6 +19,7 @@
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@@ -29,7 +30,7 @@ public class BuildInfo {
private static final String RESOURCE_LOCATION = "META-INF/gitinfo";
- @Inject Logger logger;
+ @Inject @JsonIgnore Logger logger;
private final GitInfo gitinfo = new GitInfo();
diff --git a/src/main/java/io/cryostat/ExceptionMappers.java b/src/main/java/io/cryostat/ExceptionMappers.java
index 9a05221c7..03139f07b 100644
--- a/src/main/java/io/cryostat/ExceptionMappers.java
+++ b/src/main/java/io/cryostat/ExceptionMappers.java
@@ -35,6 +35,7 @@
import org.projectnessie.cel.tools.ScriptException;
import software.amazon.awssdk.services.s3.model.NoSuchBucketException;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
+import software.amazon.awssdk.services.s3.model.S3Exception;
public class ExceptionMappers {
@@ -56,6 +57,16 @@ public RestResponse mapNoSuchBucketException(NoSuchBucketException ex) {
return RestResponse.status(HttpResponseStatus.BAD_GATEWAY.code());
}
+ @ServerExceptionMapper
+ public RestResponse mapS3Exception(S3Exception ex) {
+ logger.error(ex);
+ switch (ex.statusCode()) {
+ case 404:
+ return RestResponse.notFound();
+ }
+ return RestResponse.status(HttpResponseStatus.BAD_GATEWAY.code());
+ }
+
@ServerExceptionMapper
public RestResponse mapConstraintViolationException(ConstraintViolationException ex) {
logger.warn(ex);
diff --git a/src/main/java/io/cryostat/Health.java b/src/main/java/io/cryostat/Health.java
index 097efdf75..b9a867753 100644
--- a/src/main/java/io/cryostat/Health.java
+++ b/src/main/java/io/cryostat/Health.java
@@ -18,7 +18,6 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@@ -36,7 +35,6 @@
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;
@@ -72,7 +70,7 @@ class Health {
@Blocking
@Path("/health")
@PermitAll
- public Response health() {
+ public ApplicationHealth health() {
CompletableFuture datasourceAvailable = new CompletableFuture<>();
CompletableFuture dashboardAvailable = new CompletableFuture<>();
CompletableFuture reportsAvailable = new CompletableFuture<>();
@@ -95,25 +93,15 @@ public Response health() {
reportsAvailable.complete(true);
}
- return Response.ok(
- Map.of(
- "cryostatVersion",
- String.format("v%s", version),
- "build",
- buildInfo,
- "dashboardConfigured",
- dashboardURL.isPresent(),
- "dashboardAvailable",
- dashboardAvailable.join(),
- "datasourceConfigured",
- datasourceURL.isPresent(),
- "datasourceAvailable",
- datasourceAvailable.join(),
- "reportsConfigured",
- reportsConfigured,
- "reportsAvailable",
- reportsAvailable.join()))
- .build();
+ return new ApplicationHealth(
+ String.format("v%s", version),
+ buildInfo,
+ dashboardURL.isPresent(),
+ dashboardAvailable.join(),
+ datasourceURL.isPresent(),
+ datasourceAvailable.join(),
+ reportsConfigured,
+ reportsAvailable.join());
}
@GET
@@ -128,23 +116,23 @@ public Response health() {
public void liveness() {}
@GET
- @Path("/api/v1/grafana_dashboard_url")
+ @Path("/api/v4/grafana_dashboard_url")
@PermitAll
@Produces({MediaType.APPLICATION_JSON})
- public Response grafanaDashboardUrl() {
+ public DashboardUrl grafanaDashboardUrl() {
String url =
dashboardExternalURL.orElseGet(
() -> dashboardURL.orElseThrow(() -> new BadRequestException()));
-
- return Response.ok(Map.of("grafanaDashboardUrl", url)).build();
+ return new DashboardUrl(url);
}
@GET
- @Path("/api/v1/grafana_datasource_url")
+ @Path("/api/v4/grafana_datasource_url")
@PermitAll
@Produces({MediaType.APPLICATION_JSON})
- public Response grafanaDatasourceUrl() {
- return Response.ok(Map.of("grafanaDatasourceUrl", datasourceURL)).build();
+ public DatasourceUrl grafanaDatasourceUrl() {
+ String url = datasourceURL.orElseThrow(() -> new BadRequestException());
+ return new DatasourceUrl(url);
}
private void checkUri(
@@ -180,4 +168,18 @@ private void checkUri(
future.complete(false);
}
}
+
+ static record ApplicationHealth(
+ String cryostatVersion,
+ BuildInfo build,
+ boolean dashboardConfigured,
+ boolean dashboardAvailable,
+ boolean datasourceConfigured,
+ boolean datasourceAvailable,
+ boolean reportsConfigured,
+ boolean reportsAvailable) {}
+
+ static record DashboardUrl(String grafanaDashboardUrl) {}
+
+ static record DatasourceUrl(String grafanaDatasourceUrl) {}
}
diff --git a/src/main/java/io/cryostat/JsonRequestFilter.java b/src/main/java/io/cryostat/JsonRequestFilter.java
index c61651b27..6108380d0 100644
--- a/src/main/java/io/cryostat/JsonRequestFilter.java
+++ b/src/main/java/io/cryostat/JsonRequestFilter.java
@@ -39,11 +39,10 @@ public class JsonRequestFilter implements ContainerRequestFilter {
static final Set disallowedFields = Set.of("id");
static final Set allowedPathPatterns =
Set.of(
- "/api/v2.2/discovery",
- "/api/v2/rules/[\\w]+",
- "/api/beta/matchExpressions",
- "/api/v2.2/graphql",
- "/api/v3/graphql");
+ "/api/v4/discovery",
+ "/api/v4/rules/[\\w]+",
+ "/api/v4/matchExpressions",
+ "/api/v4/graphql");
private final Map compiledPatterns = new HashMap<>();
@Inject ObjectMapper objectMapper;
diff --git a/src/main/java/io/cryostat/MessageCodecs.java b/src/main/java/io/cryostat/MessageCodecs.java
index 81e7b1b5c..1ceb1ff62 100644
--- a/src/main/java/io/cryostat/MessageCodecs.java
+++ b/src/main/java/io/cryostat/MessageCodecs.java
@@ -15,7 +15,7 @@
*/
package io.cryostat;
-import io.cryostat.recordings.Recordings.LinkedRecordingDescriptor;
+import io.cryostat.recordings.ActiveRecordings.LinkedRecordingDescriptor;
import io.quarkus.runtime.StartupEvent;
import io.quarkus.vertx.LocalEventBusCodec;
diff --git a/src/main/java/io/cryostat/V2Response.java b/src/main/java/io/cryostat/V2Response.java
deleted file mode 100644
index 6f0ccfd4f..000000000
--- a/src/main/java/io/cryostat/V2Response.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright The Cryostat Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.cryostat;
-
-import java.util.Objects;
-
-import jakarta.annotation.Nullable;
-import jakarta.ws.rs.core.Response;
-
-public record V2Response(Meta meta, Data data) {
- public static V2Response json(Response.Status status, Object payload) {
- Data data;
- if (status.getFamily().equals(Response.Status.Family.CLIENT_ERROR)
- || status.getFamily().equals(Response.Status.Family.SERVER_ERROR)) {
- data = new ErrorData(payload);
- } else {
- data = new PayloadData(payload);
- }
- return new V2Response(new Meta("application/json", status.getReasonPhrase()), data);
- }
-
- // FIXME the type and status should both come from an enum and be non-null
- public record Meta(String type, String status) {
- public Meta {
- Objects.requireNonNull(type);
- Objects.requireNonNull(status);
- }
- }
-
- interface Data {}
-
- public static class PayloadData implements Data {
- @Nullable Object result;
-
- public PayloadData(Object payload) {
- this.result = payload;
- }
-
- public Object getResult() {
- return result;
- }
- }
-
- public static class ErrorData implements Data {
- String reason;
-
- public ErrorData(Object payload) {
- if (payload instanceof Exception) {
- this.reason = ((Exception) Objects.requireNonNull(payload)).getMessage();
- } else {
- this.reason = Objects.requireNonNull(payload).toString();
- }
- }
-
- public String getReason() {
- return reason;
- }
- }
-}
diff --git a/src/main/java/io/cryostat/credentials/CredentialCheck.java b/src/main/java/io/cryostat/credentials/CredentialCheck.java
deleted file mode 100644
index 62497cd42..000000000
--- a/src/main/java/io/cryostat/credentials/CredentialCheck.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright The Cryostat Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.cryostat.credentials;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Optional;
-
-import io.cryostat.V2Response;
-import io.cryostat.targets.Target;
-import io.cryostat.targets.TargetConnectionManager;
-
-import io.smallrye.common.annotation.Blocking;
-import io.smallrye.mutiny.Uni;
-import jakarta.annotation.security.RolesAllowed;
-import jakarta.inject.Inject;
-import jakarta.ws.rs.POST;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.Response.Status;
-import org.jboss.resteasy.reactive.RestForm;
-import org.jboss.resteasy.reactive.RestPath;
-
-@Path("")
-public class CredentialCheck {
-
- @Inject TargetConnectionManager connectionManager;
-
- @POST
- @Blocking
- @RolesAllowed("read")
- @Path("/api/beta/credentials/{connectUrl}")
- public Uni checkCredentialForTarget(
- @RestPath String connectUrl, @RestForm String username, @RestForm String password)
- throws URISyntaxException {
- Target target = Target.getTargetByConnectUrl(new URI(connectUrl));
- return connectionManager
- .executeDirect(
- target,
- Optional.empty(),
- (conn) -> {
- conn.connect();
- return CredentialTestResult.NA;
- })
- .onFailure()
- .recoverWithUni(
- () -> {
- Credential cred = new Credential();
- cred.username = username;
- cred.password = password;
- return connectionManager
- .executeDirect(
- target,
- Optional.of(cred),
- (conn) -> {
- conn.connect();
- return CredentialTestResult.SUCCESS;
- })
- .onFailure(
- t ->
- connectionManager.isJmxAuthFailure(t)
- || connectionManager.isAgentAuthFailure(
- t))
- .recoverWithItem(t -> CredentialTestResult.FAILURE);
- })
- .map(r -> V2Response.json(Status.OK, r));
- }
-
- static enum CredentialTestResult {
- SUCCESS,
- FAILURE,
- NA;
- }
-}
diff --git a/src/main/java/io/cryostat/credentials/Credentials.java b/src/main/java/io/cryostat/credentials/Credentials.java
index 4d81ab6d4..2eee81ed7 100644
--- a/src/main/java/io/cryostat/credentials/Credentials.java
+++ b/src/main/java/io/cryostat/credentials/Credentials.java
@@ -15,24 +15,30 @@
*/
package io.cryostat.credentials;
-import java.net.URI;
-import java.util.HashMap;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
-import io.cryostat.V2Response;
import io.cryostat.expressions.MatchExpression;
import io.cryostat.expressions.MatchExpression.TargetMatcher;
+import io.cryostat.targets.Target;
+import io.cryostat.targets.TargetConnectionManager;
+import io.smallrye.common.annotation.Blocking;
+import io.smallrye.mutiny.Uni;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
+import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.UriInfo;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestPath;
@@ -40,44 +46,89 @@
import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
import org.projectnessie.cel.tools.ScriptException;
-@Path("/api/v2.2/credentials")
+@Path("/api/v4/credentials")
public class Credentials {
+ @Inject TargetConnectionManager connectionManager;
@Inject TargetMatcher targetMatcher;
@Inject Logger logger;
+ @POST
+ @Blocking
+ @RolesAllowed("read")
+ @Path("/test/{targetId}")
+ public Uni checkCredentialForTarget(
+ @RestPath long targetId, @RestForm String username, @RestForm String password)
+ throws URISyntaxException {
+ Target target = Target.getTargetById(targetId);
+ return connectionManager
+ .executeDirect(
+ target,
+ Optional.empty(),
+ (conn) -> {
+ conn.connect();
+ return CredentialTestResult.NA;
+ })
+ .onFailure()
+ .recoverWithUni(
+ () -> {
+ Credential cred = new Credential();
+ cred.username = username;
+ cred.password = password;
+ return connectionManager
+ .executeDirect(
+ target,
+ Optional.of(cred),
+ (conn) -> {
+ conn.connect();
+ return CredentialTestResult.SUCCESS;
+ })
+ .onFailure(
+ t ->
+ connectionManager.isJmxAuthFailure(t)
+ || connectionManager.isAgentAuthFailure(
+ t))
+ .recoverWithItem(t -> CredentialTestResult.FAILURE);
+ });
+ }
+
+ @Blocking
@GET
@RolesAllowed("read")
- public V2Response list() {
- List credentials = Credential.listAll();
- return V2Response.json(
- Response.Status.OK,
- credentials.stream()
- .map(
- c -> {
- try {
- return Credentials.safeResult(c, targetMatcher);
- } catch (ScriptException e) {
- logger.warn(e);
- return null;
- }
- })
- .filter(Objects::nonNull)
- .toList());
+ public List list() {
+ return Credential.listAll().stream()
+ .map(
+ c -> {
+ try {
+ return safeResult(c, targetMatcher);
+ } catch (ScriptException e) {
+ logger.warn(e);
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .toList();
}
+ @Blocking
@GET
@RolesAllowed("read")
@Path("/{id}")
- public V2Response get(@RestPath long id) throws ScriptException {
- Credential credential = Credential.find("id", id).singleResult();
- return V2Response.json(Response.Status.OK, safeMatchedResult(credential, targetMatcher));
+ public CredentialMatchResult get(@RestPath long id) {
+ try {
+ Credential credential = Credential.find("id", id).singleResult();
+ return safeResult(credential, targetMatcher);
+ } catch (ScriptException e) {
+ logger.error("Error retrieving credential", e);
+ throw new InternalServerErrorException(e);
+ }
}
@Transactional
@POST
@RolesAllowed("write")
- public RestResponse create(
+ public RestResponse create(
+ @Context UriInfo uriInfo,
@RestForm String matchExpression,
@RestForm String username,
@RestForm String password) {
@@ -88,7 +139,9 @@ public RestResponse create(
credential.username = username;
credential.password = password;
credential.persist();
- return ResponseBuilder.created(URI.create("/api/v2.2/credentials/" + credential.id))
+ return ResponseBuilder.created(
+ uriInfo.getAbsolutePathBuilder().path(Long.toString(credential.id)).build())
+ .entity(credential)
.build();
}
@@ -100,33 +153,30 @@ public void delete(@RestPath long id) {
Credential.find("id", id).singleResult().delete();
}
- static Map notificationResult(Credential credential) throws ScriptException {
- Map result = new HashMap<>();
- result.put("id", credential.id);
- result.put("matchExpression", credential.matchExpression);
+ static CredentialMatchResult notificationResult(Credential credential) throws ScriptException {
// TODO populating this on the credential post-persist hook leads to a database validation
// error because the expression ends up getting defined twice with the same ID, somehow.
// Populating this field with 0 means the UI is inaccurate when a new credential is first
// defined, but after a refresh the data correctly updates.
- result.put("numMatchingTargets", 0);
- return result;
+ return new CredentialMatchResult(credential, List.of());
}
- static Map safeResult(Credential credential, TargetMatcher matcher)
+ static CredentialMatchResult safeResult(Credential credential, TargetMatcher matcher)
throws ScriptException {
- Map result = new HashMap<>();
- result.put("id", credential.id);
- result.put("matchExpression", credential.matchExpression);
- result.put(
- "numMatchingTargets", matcher.match(credential.matchExpression).targets().size());
- return result;
+ var matchedTargets = matcher.match(credential.matchExpression).targets();
+ return new CredentialMatchResult(credential, matchedTargets);
}
- static Map safeMatchedResult(Credential credential, TargetMatcher matcher)
- throws ScriptException {
- Map result = new HashMap<>();
- result.put("matchExpression", credential.matchExpression);
- result.put("targets", matcher.match(credential.matchExpression).targets());
- return result;
+ static record CredentialMatchResult(
+ long id, MatchExpression matchExpression, Collection targets) {
+ CredentialMatchResult(Credential credential, Collection targets) {
+ this(credential.id, credential.matchExpression, new ArrayList<>(targets));
+ }
+ }
+
+ static enum CredentialTestResult {
+ SUCCESS,
+ FAILURE,
+ NA;
}
}
diff --git a/src/main/java/io/cryostat/discovery/CustomDiscovery.java b/src/main/java/io/cryostat/discovery/CustomDiscovery.java
index 09d37d612..d53719053 100644
--- a/src/main/java/io/cryostat/discovery/CustomDiscovery.java
+++ b/src/main/java/io/cryostat/discovery/CustomDiscovery.java
@@ -26,7 +26,6 @@
import java.util.regex.Pattern;
import io.cryostat.ConfigProperties;
-import io.cryostat.V2Response;
import io.cryostat.credentials.Credential;
import io.cryostat.expressions.MatchExpression;
import io.cryostat.targets.JvmIdException;
@@ -40,12 +39,15 @@
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
+import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.UriInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -55,6 +57,7 @@
import org.jboss.resteasy.reactive.RestPath;
import org.jboss.resteasy.reactive.RestQuery;
import org.jboss.resteasy.reactive.RestResponse;
+import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
@ApplicationScoped
@Path("")
@@ -74,38 +77,38 @@ public class CustomDiscovery {
@Transactional(rollbackOn = {JvmIdException.class})
@POST
- @Path("/api/v2/targets")
+ @Path("/api/v4/targets")
@Consumes(MediaType.APPLICATION_JSON)
@RolesAllowed("write")
- public Response create(
- Target target, @RestQuery boolean dryrun, @RestQuery boolean storeCredentials) {
+ public RestResponse create(
+ @Context UriInfo uriInfo,
+ Target target,
+ @RestQuery boolean dryrun,
+ @RestQuery boolean storeCredentials) {
try {
if (!uriUtil.validateUri(target.connectUrl)) {
- return Response.status(Response.Status.BAD_REQUEST)
- .entity(
- String.format(
- "The provided URI \"%s\" is unacceptable with the"
- + " current URI range settings.",
- target.connectUrl))
- .build();
+ throw new BadRequestException(
+ String.format(
+ "The provided URI \"%s\" is unacceptable with the"
+ + " current URI range settings.",
+ target.connectUrl));
}
// Continue with target creation if URI is valid...
} catch (Exception e) {
logger.error("Target validation failed", e);
- return Response.status(Response.Status.BAD_REQUEST)
- .entity(V2Response.json(Response.Status.BAD_REQUEST, e))
- .build();
+ throw new BadRequestException("Target validation failed", e);
}
// TODO handle credentials embedded in JSON body
- return doV2Create(target, Optional.empty(), dryrun, storeCredentials);
+ return doCreate(uriInfo, target, Optional.empty(), dryrun, storeCredentials);
}
@Transactional
@POST
- @Path("/api/v2/targets")
+ @Path("/api/v4/targets")
@Consumes({MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED})
@RolesAllowed("write")
- public Response create(
+ public RestResponse createForm(
+ @Context UriInfo uriInfo,
@RestForm URI connectUrl,
@RestForm String alias,
@RestForm String username,
@@ -126,10 +129,11 @@ public Response create(
credential.password = password;
}
- return doV2Create(target, Optional.ofNullable(credential), dryrun, storeCredentials);
+ return doCreate(uriInfo, target, Optional.ofNullable(credential), dryrun, storeCredentials);
}
- Response doV2Create(
+ RestResponse doCreate(
+ UriInfo uriInfo,
Target target,
Optional credential,
boolean dryrun,
@@ -137,21 +141,15 @@ Response doV2Create(
try {
target.connectUrl = sanitizeConnectUrl(target.connectUrl.toString());
if (!uriUtil.validateUri(target.connectUrl)) {
- return Response.status(Response.Status.BAD_REQUEST)
- .entity(
- String.format(
- "The provided URI \"%s\" is unacceptable with the"
- + " current URI range settings.",
- target.connectUrl))
- .build();
+ throw new BadRequestException(
+ String.format(
+ "The provided URI \"%s\" is unacceptable with the"
+ + " current URI range settings.",
+ target.connectUrl));
}
if (Target.find("connectUrl", target.connectUrl).singleResultOptional().isPresent()) {
- return Response.status(Response.Status.BAD_REQUEST.getStatusCode())
- .entity(
- V2Response.json(
- Response.Status.BAD_REQUEST, "Duplicate connection URL"))
- .build();
+ throw new BadRequestException("Duplicate connection URL");
}
try {
@@ -166,14 +164,8 @@ Response doV2Create(
.atMost(timeout);
if (Target.find("jvmId", jvmId).count() > 0) {
- return Response.status(Response.Status.BAD_REQUEST.getStatusCode())
- .entity(
- V2Response.json(
- Response.Status.BAD_REQUEST,
- String.format(
- "Target with JVM ID \"%s\" already discovered",
- jvmId)))
- .build();
+ throw new BadRequestException(
+ String.format("Target with JVM ID \"%s\" already discovered", jvmId));
}
} catch (Exception e) {
logger.error("Target connection failed", e);
@@ -185,15 +177,11 @@ Response doV2Create(
: connectionManager.isServiceTypeFailure(e)
? "Unexpected service type on port"
: "Target connection failed";
- return Response.status(Response.Status.BAD_REQUEST.getStatusCode())
- .entity(V2Response.json(Response.Status.BAD_REQUEST, msg))
- .build();
+ throw new BadRequestException(msg, e);
}
if (dryrun) {
- return Response.accepted()
- .entity(V2Response.json(Response.Status.ACCEPTED, target))
- .build();
+ return ResponseBuilder.accepted(target).build();
}
target.persist();
@@ -212,48 +200,31 @@ Response doV2Create(
node.persist();
realm.persist();
- return Response.created(URI.create("/api/v3/targets/" + target.id))
- .entity(V2Response.json(Response.Status.CREATED, target))
+ return ResponseBuilder.created(
+ uriInfo.getAbsolutePathBuilder().path(Long.toString(target.id)).build())
+ .entity(target)
.build();
} catch (Exception e) {
if (ExceptionUtils.indexOfType(e, ConstraintViolationException.class) >= 0) {
logger.warn("Invalid target definition", e);
- return Response.status(Response.Status.BAD_REQUEST.getStatusCode())
- .entity(
- V2Response.json(
- Response.Status.BAD_REQUEST, "Duplicate connection URL"))
- .build();
+ throw new BadRequestException("Duplicate connection URL", e);
}
logger.error("Unknown error", e);
- return Response.serverError()
- .entity(V2Response.json(Response.Status.INTERNAL_SERVER_ERROR, e))
- .build();
+ throw new InternalServerErrorException(e);
}
}
@Transactional
@DELETE
- @Path("/api/v2/targets/{connectUrl}")
- @RolesAllowed("write")
- public Response delete(@RestPath URI connectUrl) throws URISyntaxException {
- Target target = Target.getTargetByConnectUrl(connectUrl);
- return Response.status(RestResponse.Status.PERMANENT_REDIRECT)
- .location(URI.create(String.format("/api/v3/targets/%d", target.id)))
- .build();
- }
-
- @Transactional
- @DELETE
- @Path("/api/v3/targets/{id}")
+ @Path("/api/v4/targets/{id}")
@RolesAllowed("write")
- public Response delete(@RestPath long id) throws URISyntaxException {
+ public void delete(@RestPath long id) throws URISyntaxException {
Target target = Target.find("id", id).singleResult();
DiscoveryNode realm = DiscoveryNode.getRealm(REALM).orElseThrow();
realm.children.remove(target.discoveryNode);
target.discoveryNode.parent = null;
realm.persist();
target.delete();
- return Response.noContent().build();
}
private URI sanitizeConnectUrl(String in) throws URISyntaxException, MalformedURLException {
diff --git a/src/main/java/io/cryostat/discovery/Discovery.java b/src/main/java/io/cryostat/discovery/Discovery.java
index e45a28705..ad8bdb7db 100644
--- a/src/main/java/io/cryostat/discovery/Discovery.java
+++ b/src/main/java/io/cryostat/discovery/Discovery.java
@@ -37,6 +37,7 @@
import io.cryostat.targets.TargetConnectionManager;
import io.cryostat.util.URIUtil;
+import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.JOSEException;
@@ -63,15 +64,12 @@
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.RestPath;
import org.jboss.resteasy.reactive.RestQuery;
-import org.jboss.resteasy.reactive.RestResponse;
-import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
@@ -151,25 +149,16 @@ void onStop(@Observes ShutdownEvent evt) throws SchedulerException {
}
@GET
- @Path("/api/v2.1/discovery")
- @RolesAllowed("read")
- public Response getv21() {
- return Response.status(RestResponse.Status.PERMANENT_REDIRECT)
- .location(URI.create("/api/v3/discovery"))
- .build();
- }
-
- @GET
- @Path("/api/v3/discovery")
+ @Path("/api/v4/discovery")
@RolesAllowed("read")
public DiscoveryNode get() {
return DiscoveryNode.getUniverse();
}
@GET
- @Path("/api/v2.2/discovery/{id}")
+ @Path("/api/v4/discovery/{id}")
@RolesAllowed("read")
- public RestResponse checkRegistration(
+ public void checkRegistration(
@Context RoutingContext ctx, @RestPath UUID id, @RestQuery String token)
throws SocketException,
UnknownHostException,
@@ -179,17 +168,16 @@ public RestResponse checkRegistration(
URISyntaxException {
DiscoveryPlugin plugin = DiscoveryPlugin.find("id", id).singleResult();
jwtValidator.validateJwt(ctx, plugin, token, true);
- return ResponseBuilder.ok().build();
}
@Transactional
@POST
- @Path("/api/v2.2/discovery")
+ @Path("/api/v4/discovery")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@RolesAllowed("write")
@SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
- public Response register(@Context RoutingContext ctx, JsonObject body)
+ public PluginRegistration register(@Context RoutingContext ctx, JsonObject body)
throws URISyntaxException,
MalformedURLException,
JOSEException,
@@ -206,13 +194,11 @@ public Response register(@Context RoutingContext ctx, JsonObject body)
// URI range validation
if (!uriUtil.validateUri(callbackUri)) {
- return Response.status(Response.Status.BAD_REQUEST)
- .entity(
- String.format(
- "cryostat.target.callback of \"%s\" is unacceptable with the"
- + " current URI range settings",
- callbackUri))
- .build();
+ throw new BadRequestException(
+ String.format(
+ "cryostat.target.callback of \"%s\" is unacceptable with the"
+ + " current URI range settings",
+ callbackUri));
}
// TODO apply URI range validation to the remote address
@@ -295,39 +281,21 @@ public Response register(@Context RoutingContext ctx, JsonObject body)
String token = jwtFactory.createDiscoveryPluginJwt(plugin, remoteAddress, location);
// TODO implement more generic env map passing by some platform detection
- // strategy or
- // generalized config properties
+ // strategy or generalized config properties
var envMap = new HashMap();
String insightsProxy = System.getenv("INSIGHTS_PROXY");
if (StringUtils.isNotBlank(insightsProxy)) {
envMap.put("INSIGHTS_SVC", insightsProxy);
}
- return Response.created(location)
- .entity(
- Map.of(
- "meta",
- Map.of(
- "mimeType", "JSON",
- "status", "OK"),
- "data",
- Map.of(
- "result",
- Map.of(
- "id",
- plugin.id.toString(),
- "token",
- token,
- "env",
- envMap))))
- .build();
+ return new PluginRegistration(plugin.id.toString(), token, envMap);
}
@Transactional
@POST
- @Path("/api/v2.2/discovery/{id}")
+ @Path("/api/v4/discovery/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@PermitAll
- public Map> publish(
+ public void publish(
@Context RoutingContext ctx,
@RestPath UUID id,
@RestQuery String token,
@@ -351,22 +319,13 @@ public Map> publish(
b.persist();
}
plugin.persist();
-
- return Map.of(
- "meta",
- Map.of(
- "mimeType", "JSON",
- "status", "OK"),
- "data",
- Map.of("result", plugin.id.toString()));
}
@Transactional
@DELETE
- @Path("/api/v2.2/discovery/{id}")
+ @Path("/api/v4/discovery/{id}")
@PermitAll
- public Map> deregister(
- @Context RoutingContext ctx, @RestPath UUID id, @RestQuery String token)
+ public void deregister(@Context RoutingContext ctx, @RestPath UUID id, @RestQuery String token)
throws SocketException,
UnknownHostException,
MalformedURLException,
@@ -386,36 +345,23 @@ public Map> deregister(
for (var key : jobKeys) {
scheduler.deleteJob(key);
}
-
plugin.delete();
- return Map.of(
- "meta",
- Map.of(
- "mimeType", "JSON",
- "status", "OK"),
- "data",
- Map.of("result", plugin.id.toString()));
}
@GET
- @Path("/api/v3/discovery_plugins")
+ @JsonView(DiscoveryNode.Views.Flat.class)
+ @Path("/api/v4/discovery_plugins")
@RolesAllowed("read")
- public Response getPlugins(@RestQuery String realm) throws JsonProcessingException {
+ public List getPlugins(@RestQuery String realm)
+ throws JsonProcessingException {
// TODO filter for the matching realm name within the DB query
- List plugins =
- DiscoveryPlugin.findAll().list().stream()
- .filter(p -> StringUtils.isBlank(realm) || p.realm.name.equals(realm))
- .toList();
- return Response.ok()
- .entity(
- mapper.writerWithView(DiscoveryNode.Views.Flat.class)
- .writeValueAsString(plugins))
- .type(MediaType.APPLICATION_JSON)
- .build();
+ return DiscoveryPlugin.findAll().list().stream()
+ .filter(p -> StringUtils.isBlank(realm) || p.realm.name.equals(realm))
+ .toList();
}
@GET
- @Path("/api/v3/discovery_plugins/{id}")
+ @Path("/api/v4/discovery_plugins/{id}")
@RolesAllowed("read")
public DiscoveryPlugin getPlugin(@RestPath UUID id) throws JsonProcessingException {
return DiscoveryPlugin.find("id", id).singleResult();
@@ -479,4 +425,6 @@ private InetAddress getRemoteAddress(RoutingContext ctx) {
}
return addr;
}
+
+ static record PluginRegistration(String id, String token, Map env) {}
}
diff --git a/src/main/java/io/cryostat/discovery/DiscoveryJwtFactory.java b/src/main/java/io/cryostat/discovery/DiscoveryJwtFactory.java
index e033a1410..ffedc557f 100644
--- a/src/main/java/io/cryostat/discovery/DiscoveryJwtFactory.java
+++ b/src/main/java/io/cryostat/discovery/DiscoveryJwtFactory.java
@@ -55,7 +55,7 @@ public class DiscoveryJwtFactory {
public static final String RESOURCE_CLAIM = "resource";
public static final String REALM_CLAIM = "realm";
- static final String DISCOVERY_API_PATH = "/api/v2.2/discovery/";
+ static final String DISCOVERY_API_PATH = "/api/v4/discovery/";
@Inject JWSSigner signer;
@Inject JWSVerifier verifier;
diff --git a/src/main/java/io/cryostat/events/EventTemplates.java b/src/main/java/io/cryostat/events/EventTemplates.java
index dade41164..5beac994b 100644
--- a/src/main/java/io/cryostat/events/EventTemplates.java
+++ b/src/main/java/io/cryostat/events/EventTemplates.java
@@ -16,17 +16,15 @@
package io.cryostat.events;
import java.io.IOException;
-import java.net.URI;
import java.util.ArrayList;
import java.util.List;
+import io.cryostat.core.FlightRecorderException;
import io.cryostat.core.templates.MutableTemplateService.InvalidXmlException;
import io.cryostat.libcryostat.sys.FileSystem;
import io.cryostat.libcryostat.templates.InvalidEventTemplateException;
import io.cryostat.libcryostat.templates.Template;
import io.cryostat.libcryostat.templates.TemplateType;
-import io.cryostat.targets.Target;
-import io.cryostat.util.HttpMimeType;
import io.smallrye.common.annotation.Blocking;
import jakarta.annotation.security.RolesAllowed;
@@ -34,17 +32,20 @@
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
+import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.HttpHeaders;
-import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.UriInfo;
+import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestPath;
import org.jboss.resteasy.reactive.RestResponse;
+import org.jboss.resteasy.reactive.RestResponse.ResponseBuilder;
import org.jboss.resteasy.reactive.multipart.FileUpload;
-@Path("")
+@Path("/api/v4/event_templates")
public class EventTemplates {
public static final Template ALL_EVENTS_TEMPLATE =
@@ -62,140 +63,62 @@ public class EventTemplates {
@Inject Logger logger;
@GET
- @Path("/api/v1/targets/{connectUrl}/templates")
+ @Blocking
@RolesAllowed("read")
- public Response listTemplatesV1(@RestPath URI connectUrl) throws Exception {
- Target target = Target.getTargetByConnectUrl(connectUrl);
- return Response.status(RestResponse.Status.PERMANENT_REDIRECT)
- .location(
- URI.create(String.format("/api/v3/targets/%d/event_templates", target.id)))
- .build();
+ public List listTemplates() throws Exception {
+ var list = new ArrayList();
+ list.add(ALL_EVENTS_TEMPLATE);
+ list.addAll(customTemplateService.getTemplates());
+ return list;
}
- @POST
- @Path("/api/v1/templates")
- @RolesAllowed("write")
- public Response postTemplatesV1(@RestForm("template") FileUpload body) {
- return Response.status(RestResponse.Status.PERMANENT_REDIRECT)
- .location(URI.create("/api/v3/event_templates"))
- .build();
+ @GET
+ @Blocking
+ @RolesAllowed("read")
+ public Template getTemplate(@RestPath String templateName)
+ throws IOException, FlightRecorderException {
+ if (StringUtils.isBlank(templateName)) {
+ throw new BadRequestException();
+ }
+ return customTemplateService.getTemplates().stream()
+ .filter(t -> t.getName().equals(templateName))
+ .findFirst()
+ .orElseThrow();
}
@POST
- @Path("/api/v3/event_templates")
@RolesAllowed("write")
- public void postTemplates(@RestForm("template") FileUpload body) throws IOException {
+ public RestResponse postTemplates(
+ @Context UriInfo uriInfo, @RestForm("template") FileUpload body) throws IOException {
if (body == null || body.filePath() == null || !"template".equals(body.name())) {
throw new BadRequestException();
}
try (var stream = fs.newInputStream(body.filePath())) {
- customTemplateService.addTemplate(stream);
+ var template = customTemplateService.addTemplate(stream);
+ return ResponseBuilder.created(
+ uriInfo.getAbsolutePathBuilder().path(template.getName()).build())
+ .entity(template)
+ .build();
} catch (InvalidEventTemplateException | InvalidXmlException e) {
throw new BadRequestException(e);
}
}
- @DELETE
- @Path("/api/v1/templates/{templateName}")
- @RolesAllowed("write")
- public Response deleteTemplatesV1(@RestPath String templateName) {
- return Response.status(RestResponse.Status.PERMANENT_REDIRECT)
- .location(URI.create(String.format("/api/v3/event_templates/%s", templateName)))
- .build();
- }
-
@DELETE
@Blocking
- @Path("/api/v3/event_templates/{templateName}")
+ @Path("/{templateName}")
@RolesAllowed("write")
- public void deleteTemplates(@RestPath String templateName) {
- customTemplateService.deleteTemplate(templateName);
- }
-
- @GET
- @Path("/api/v1/targets/{connectUrl}/templates/{templateName}/type/{templateType}")
- @RolesAllowed("read")
- public Response getTargetTemplateV1(
- @RestPath URI connectUrl,
- @RestPath String templateName,
- @RestPath TemplateType templateType) {
- Target target = Target.getTargetByConnectUrl(connectUrl);
- return Response.status(RestResponse.Status.PERMANENT_REDIRECT)
- .location(
- URI.create(
- String.format(
- "/api/v3/targets/%d/event_templates/%s/%s",
- target.id, templateType, templateName)))
- .build();
- }
-
- @GET
- @Path("/api/v2.1/targets/{connectUrl}/templates/{templateName}/type/{templateType}")
- @RolesAllowed("read")
- public Response getTargetTemplateV2_1(
- @RestPath URI connectUrl,
- @RestPath String templateName,
- @RestPath TemplateType templateType) {
- Target target = Target.getTargetByConnectUrl(connectUrl);
- return Response.status(RestResponse.Status.PERMANENT_REDIRECT)
- .location(
- URI.create(
- String.format(
- "/api/v3/targets/%d/event_templates/%s/%s",
- target.id, templateType, templateName)))
- .build();
- }
-
- @GET
- @Blocking
- @Path("/api/v3/event_templates")
- @RolesAllowed("read")
- public List listTemplates() throws Exception {
- var list = new ArrayList();
- list.add(ALL_EVENTS_TEMPLATE);
- list.addAll(customTemplateService.getTemplates());
- return list;
- }
-
- @GET
- @Blocking
- @Path("/api/v3/targets/{id}/event_templates")
- @RolesAllowed("read")
- public List listTargetTemplates(@RestPath long id) throws Exception {
- Target target = Target.find("id", id).singleResult();
- var list = new ArrayList();
- list.add(ALL_EVENTS_TEMPLATE);
- list.addAll(targetTemplateServiceFactory.create(target).getTemplates());
- list.addAll(customTemplateService.getTemplates());
- return list;
- }
-
- @GET
- @Blocking
- @Path("/api/v3/targets/{id}/event_templates/{templateType}/{templateName}")
- @RolesAllowed("read")
- public Response getTargetTemplate(
- @RestPath long id, @RestPath TemplateType templateType, @RestPath String templateName)
- throws Exception {
- Target target = Target.find("id", id).singleResult();
- String xml;
- switch (templateType) {
- case TARGET:
- xml =
- targetTemplateServiceFactory
- .create(target)
- .getXml(templateName, templateType)
- .orElseThrow();
- break;
- case CUSTOM:
- xml = customTemplateService.getXml(templateName, templateType).orElseThrow();
- break;
- default:
- throw new BadRequestException();
+ public void deleteTemplate(@RestPath String templateName) throws FlightRecorderException {
+ if (StringUtils.isBlank(templateName)) {
+ throw new BadRequestException();
+ }
+ if (ALL_EVENTS_TEMPLATE.getName().equals(templateName)) {
+ throw new BadRequestException();
+ }
+ if (!customTemplateService.getTemplates().stream()
+ .anyMatch(t -> t.getName().equals(templateName))) {
+ throw new NotFoundException();
}
- return Response.status(RestResponse.Status.OK)
- .header(HttpHeaders.CONTENT_TYPE, HttpMimeType.JFC.mime())
- .entity(xml)
- .build();
+ customTemplateService.deleteTemplate(templateName);
}
}
diff --git a/src/main/java/io/cryostat/events/Events.java b/src/main/java/io/cryostat/events/Events.java
index b59f8a4c2..ed2c11593 100644
--- a/src/main/java/io/cryostat/events/Events.java
+++ b/src/main/java/io/cryostat/events/Events.java
@@ -15,7 +15,6 @@
*/
package io.cryostat.events;
-import java.net.URI;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -23,7 +22,6 @@
import org.openjdk.jmc.flightrecorder.configuration.events.IEventTypeInfo;
-import io.cryostat.V2Response;
import io.cryostat.targets.Target;
import io.cryostat.targets.TargetConnectionManager;
@@ -31,12 +29,10 @@
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.RestPath;
import org.jboss.resteasy.reactive.RestQuery;
-import org.jboss.resteasy.reactive.RestResponse;
@Path("")
public class Events {
@@ -45,30 +41,7 @@ public class Events {
@Inject Logger logger;
@GET
- @Path("/api/v1/targets/{connectUrl}/events")
- @RolesAllowed("read")
- public Response listEventsV1(@RestPath URI connectUrl, @RestQuery String q) throws Exception {
- logger.tracev("connectUrl: %s", connectUrl.toString());
- Target target = Target.getTargetByConnectUrl(connectUrl);
- return Response.status(RestResponse.Status.PERMANENT_REDIRECT)
- .location(
- URI.create(
- String.format(
- "/api/v3/targets/%d/events%s",
- target.id, q == null ? "" : "?q=" + q)))
- .build();
- }
-
- @GET
- @Path("/api/v2/targets/{connectUrl}/events")
- @RolesAllowed("read")
- public V2Response listEventsV2(@RestPath URI connectUrl, @RestQuery String q) throws Exception {
- return V2Response.json(
- Response.Status.OK, searchEvents(Target.getTargetByConnectUrl(connectUrl), q));
- }
-
- @GET
- @Path("/api/v3/targets/{id}/events")
+ @Path("/api/v4/targets/{id}/events")
@RolesAllowed("read")
public List listEvents(@RestPath long id, @RestQuery String q)
throws Exception {
diff --git a/src/main/java/io/cryostat/events/TargetEventTemplates.java b/src/main/java/io/cryostat/events/TargetEventTemplates.java
new file mode 100644
index 000000000..b9d2592b4
--- /dev/null
+++ b/src/main/java/io/cryostat/events/TargetEventTemplates.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright The Cryostat Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.cryostat.events;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import io.cryostat.libcryostat.templates.Template;
+import io.cryostat.libcryostat.templates.TemplateType;
+import io.cryostat.targets.Target;
+
+import io.smallrye.common.annotation.Blocking;
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.BadRequestException;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.NotFoundException;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.reactive.RestPath;
+
+@Path("/api/v4/targets/{id}/event_templates")
+public class TargetEventTemplates {
+
+ public static final Template ALL_EVENTS_TEMPLATE =
+ new Template(
+ "ALL",
+ "Enable all available events in the target JVM, with default option values."
+ + " This will be very expensive and is intended primarily for testing"
+ + " Cryostat's own capabilities.",
+ "Cryostat",
+ TemplateType.TARGET);
+
+ @Inject TargetTemplateService.Factory targetTemplateServiceFactory;
+ @Inject S3TemplateService customTemplateService;
+ @Inject Logger logger;
+
+ @GET
+ @Blocking
+ @RolesAllowed("read")
+ public List listTargetTemplates(@RestPath long id) throws Exception {
+ Target target = Target.find("id", id).singleResult();
+ var list = new ArrayList();
+ list.add(ALL_EVENTS_TEMPLATE);
+ Comparator comparator =
+ Comparator.comparing(Template::getType)
+ .thenComparing(Comparator.comparing(Template::getName))
+ .thenComparing(Comparator.comparing(Template::getProvider));
+ list.addAll(targetTemplateServiceFactory.create(target).getTemplates());
+ list.addAll(customTemplateService.getTemplates());
+ Collections.sort(list, comparator);
+ return list;
+ }
+
+ @GET
+ @Blocking
+ @Path("/{templateType}/{templateName}")
+ @RolesAllowed("read")
+ @Produces(MediaType.APPLICATION_XML)
+ public String getTargetTemplate(
+ @RestPath long id, @RestPath TemplateType templateType, @RestPath String templateName)
+ throws Exception {
+ if (ALL_EVENTS_TEMPLATE.getName().equals(templateName)
+ && ALL_EVENTS_TEMPLATE.getType().equals(templateType)) {
+ throw new BadRequestException();
+ }
+ Target target = Target.find("id", id).singleResult();
+ String xml;
+ switch (templateType) {
+ case TARGET:
+ xml =
+ targetTemplateServiceFactory
+ .create(target)
+ .getXml(templateName, templateType)
+ .orElseThrow(() -> new NotFoundException());
+ break;
+ case CUSTOM:
+ xml =
+ customTemplateService
+ .getXml(templateName, templateType)
+ .orElseThrow(() -> new NotFoundException());
+ break;
+ default:
+ throw new BadRequestException();
+ }
+ return xml;
+ }
+}
diff --git a/src/main/java/io/cryostat/expressions/MatchExpressions.java b/src/main/java/io/cryostat/expressions/MatchExpressions.java
index b6f8f2e62..1d9532f6e 100644
--- a/src/main/java/io/cryostat/expressions/MatchExpressions.java
+++ b/src/main/java/io/cryostat/expressions/MatchExpressions.java
@@ -15,14 +15,11 @@
*/
package io.cryostat.expressions;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
-import io.cryostat.V2Response;
import io.cryostat.expressions.MatchExpression.MatchedExpression;
import io.cryostat.targets.Target;
@@ -33,12 +30,11 @@
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
-import jakarta.ws.rs.core.Response;
import org.jboss.logging.Logger;
import org.jboss.resteasy.reactive.RestPath;
import org.projectnessie.cel.tools.ScriptException;
-@Path("/api/beta/matchExpressions")
+@Path("/api/v4/matchExpressions")
public class MatchExpressions {
@Inject MatchExpression.TargetMatcher targetMatcher;
@@ -47,23 +43,14 @@ public class MatchExpressions {
@POST
@RolesAllowed("read")
@Blocking
- // FIXME in a later API version this request should not accept full target objects from the
- // client but instead only a list of IDs, which will then be pulled from the target discovery
- // database for testing
- public V2Response test(RequestData requestData) throws ScriptException {
+ public MatchedExpression test(RequestData requestData) throws ScriptException {
var targets = new HashSet();
- // don't trust the client to provide the whole Target object to be tested, just extract the
- // connectUrl they provide and use that to look up the Target definition as we know it.
- Optional.ofNullable(requestData.targets)
- .orElseGet(() -> List.of())
- .forEach(
- t ->
- Target.find("connectUrl", t.connectUrl)
- .singleResultOptional()
- .ifPresent(targets::add));
- var matched =
- targetMatcher.match(new MatchExpression(requestData.matchExpression), targets);
- return V2Response.json(Response.Status.OK, matched);
+ if (requestData.targetIds == null) {
+ targets.addAll(Target.listAll());
+ } else {
+ requestData.targetIds.forEach(id -> targets.add(Target.getTargetById(id)));
+ }
+ return targetMatcher.match(new MatchExpression(requestData.matchExpression), targets);
}
@GET
@@ -83,16 +70,12 @@ public Multi