diff --git a/plugins/common/jolokia2/client.go b/plugins/common/jolokia2/client.go index 307ef7fa936da..3e4e73a0a7409 100644 --- a/plugins/common/jolokia2/client.go +++ b/plugins/common/jolokia2/client.go @@ -77,7 +77,16 @@ type jolokiaTarget struct { Password string `json:"password,omitempty"` } -// Jolokia JSON response object. Example: { +// jolokiaOptions represents the options field in Jolokia 2.x responses. +// In Jolokia 2.x, the target is returned under request.options.target +// instead of request.target (Jolokia 1.x format). +type jolokiaOptions struct { + Target *jolokiaTarget `json:"target,omitempty"` +} + +// Jolokia JSON response object. Example for Jolokia 1.x: +// +// { // "request": { // "type": "read" // "mbean": "java.lang:type=Runtime", @@ -90,10 +99,35 @@ type jolokiaTarget struct { // "timestamp": 1488059309, // "status": 200 // } +// +// Example for Jolokia 2.x: +// +// { +// "request": { +// "type": "read" +// "mbean": "java.lang:type=Runtime", +// "attribute": "Uptime", +// "options": { +// "target": { +// "url": "service:jmx:rmi:///jndi/rmi://target:9010/jmxrmi" +// } +// } +// }, +// "value": 1214083, +// "timestamp": 1488059309, +// "status": 200 +// } type jolokiaResponse struct { - Request jolokiaRequest `json:"request"` - Value interface{} `json:"value"` - Status int `json:"status"` + Request jolokiaResponseRequest `json:"request"` + Value interface{} `json:"value"` + Status int `json:"status"` +} + +// jolokiaResponseRequest is the request object echoed back in a Jolokia response. +// It extends jolokiaRequest with the Options field for Jolokia 2.x compatibility. +type jolokiaResponseRequest struct { + jolokiaRequest + Options *jolokiaOptions `json:"options,omitempty"` } func NewClient(address string, config *ClientConfig) (*Client, error) { @@ -244,9 +278,14 @@ func makeReadResponses(jresponses []jolokiaResponse) []ReadResponse { RequestAttributes: rrequest.Attributes, RequestPath: rrequest.Path, } + // Check for target in Jolokia 1.x location (request.target) if jtarget := jr.Request.Target; jtarget != nil { rresponse.RequestTarget = jtarget.URL } + // Check for target in Jolokia 2.x location (request.options.target) + if rresponse.RequestTarget == "" && jr.Request.Options != nil && jr.Request.Options.Target != nil { + rresponse.RequestTarget = jr.Request.Options.Target.URL + } rresponses = append(rresponses, rresponse) } diff --git a/plugins/common/jolokia2/client_test.go b/plugins/common/jolokia2/client_test.go new file mode 100644 index 0000000000000..4ec63f0ffe275 --- /dev/null +++ b/plugins/common/jolokia2/client_test.go @@ -0,0 +1,161 @@ +package jolokia2 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMakeReadResponses_Jolokia1xTarget(t *testing.T) { + // Jolokia 1.x returns target directly in request.target + jresponses := []jolokiaResponse{ + { + Request: jolokiaResponseRequest{ + jolokiaRequest: jolokiaRequest{ + Type: "read", + Mbean: "java.lang:type=Runtime", + Attribute: "Uptime", + Target: &jolokiaTarget{ + URL: "service:jmx:rmi:///jndi/rmi://target:9010/jmxrmi", + }, + }, + }, + Value: 1214083, + Status: 200, + }, + } + + responses := makeReadResponses(jresponses) + + require.Len(t, responses, 1) + require.Equal(t, "service:jmx:rmi:///jndi/rmi://target:9010/jmxrmi", responses[0].RequestTarget) + require.Equal(t, "java.lang:type=Runtime", responses[0].RequestMbean) + require.Equal(t, 200, responses[0].Status) +} + +func TestMakeReadResponses_Jolokia2xTarget(t *testing.T) { + // Jolokia 2.x returns target in request.options.target + jresponses := []jolokiaResponse{ + { + Request: jolokiaResponseRequest{ + jolokiaRequest: jolokiaRequest{ + Type: "read", + Mbean: "java.lang:type=Runtime", + Attribute: "Uptime", + }, + Options: &jolokiaOptions{ + Target: &jolokiaTarget{ + URL: "service:jmx:rmi:///jndi/rmi://target:9010/jmxrmi", + }, + }, + }, + Value: 1214083, + Status: 200, + }, + } + + responses := makeReadResponses(jresponses) + + require.Len(t, responses, 1) + require.Equal(t, "service:jmx:rmi:///jndi/rmi://target:9010/jmxrmi", responses[0].RequestTarget) + require.Equal(t, "java.lang:type=Runtime", responses[0].RequestMbean) + require.Equal(t, 200, responses[0].Status) +} + +func TestMakeReadResponses_NoTarget(t *testing.T) { + // No target (direct agent connection, not proxy) + jresponses := []jolokiaResponse{ + { + Request: jolokiaResponseRequest{ + jolokiaRequest: jolokiaRequest{ + Type: "read", + Mbean: "java.lang:type=Runtime", + Attribute: "Uptime", + }, + }, + Value: 1214083, + Status: 200, + }, + } + + responses := makeReadResponses(jresponses) + + require.Len(t, responses, 1) + require.Empty(t, responses[0].RequestTarget) + require.Equal(t, "java.lang:type=Runtime", responses[0].RequestMbean) +} + +func TestMakeReadResponses_Jolokia1xTakesPrecedence(t *testing.T) { + // If both locations have a target, Jolokia 1.x location takes precedence + jresponses := []jolokiaResponse{ + { + Request: jolokiaResponseRequest{ + jolokiaRequest: jolokiaRequest{ + Type: "read", + Mbean: "java.lang:type=Runtime", + Attribute: "Uptime", + Target: &jolokiaTarget{ + URL: "service:jmx:rmi:///jndi/rmi://target1:9010/jmxrmi", + }, + }, + Options: &jolokiaOptions{ + Target: &jolokiaTarget{ + URL: "service:jmx:rmi:///jndi/rmi://target2:9010/jmxrmi", + }, + }, + }, + Value: 1214083, + Status: 200, + }, + } + + responses := makeReadResponses(jresponses) + + require.Len(t, responses, 1) + // Jolokia 1.x location (request.target) takes precedence + require.Equal(t, "service:jmx:rmi:///jndi/rmi://target1:9010/jmxrmi", responses[0].RequestTarget) +} + +func TestMakeReadResponses_MultipleTargets(t *testing.T) { + // Multiple responses with different targets (Jolokia 2.x format) + jresponses := []jolokiaResponse{ + { + Request: jolokiaResponseRequest{ + jolokiaRequest: jolokiaRequest{ + Type: "read", + Mbean: "java.lang:type=Runtime", + Attribute: "Uptime", + }, + Options: &jolokiaOptions{ + Target: &jolokiaTarget{ + URL: "service:jmx:rmi:///jndi/rmi://host1:9010/jmxrmi", + }, + }, + }, + Value: 1000, + Status: 200, + }, + { + Request: jolokiaResponseRequest{ + jolokiaRequest: jolokiaRequest{ + Type: "read", + Mbean: "java.lang:type=Runtime", + Attribute: "Uptime", + }, + Options: &jolokiaOptions{ + Target: &jolokiaTarget{ + URL: "service:jmx:rmi:///jndi/rmi://host2:9010/jmxrmi", + }, + }, + }, + Value: 2000, + Status: 200, + }, + } + + responses := makeReadResponses(jresponses) + + require.Len(t, responses, 2) + require.Equal(t, "service:jmx:rmi:///jndi/rmi://host1:9010/jmxrmi", responses[0].RequestTarget) + require.Equal(t, "service:jmx:rmi:///jndi/rmi://host2:9010/jmxrmi", responses[1].RequestTarget) +}