kubernetes-client icon indicating copy to clipboard operation
kubernetes-client copied to clipboard

No httpclient implementations found on the context classloader in Groovy 3.0.11 in JMeter 5.5

Open QAInsights opened this issue 3 years ago • 12 comments
trafficstars

Describe the bug

Below is my code where I am trying to get the list of pods from the default namespace in Groovy 3.0.11 in JMeter 5.5.

But it is throwing the No httpclient implementations found on the context classloader, please ensure your classpath includes an implementation jar error. Please see the below error for more details.

import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Grab(group='io.fabric8', module='kubernetes-client', version='6.0.0-RC1')
@Grab(group='io.fabric8', module='knative-client', version='6.0.0-RC1')
@Grab(group='io.fabric8', module='kubernetes-client-api', version='6.0.0-RC1')
@Grab(group='org.slf4j', module='slf4j-api', version='2.0.0-alpha7')

Logger logger = LoggerFactory.getLogger(this.class);

KubernetesClient k8s = new KubernetesClientBuilder().build()

k8s.pods().inNamespace("default").list().getItems()
.forEach(pod ->
        println pod.getMetadata().getName()
)

Error

javax.script.ScriptException: io.fabric8.kubernetes.client.KubernetesClientException: An error has occurred.
javax.script.ScriptException: io.fabric8.kubernetes.client.KubernetesClientException: An error has occurred.
	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:320) ~[groovy-jsr223-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.jsr223.GroovyCompiledScript.eval(GroovyCompiledScript.java:71) ~[groovy-jsr223-3.0.11.jar:3.0.11]
	at javax.script.CompiledScript.eval(CompiledScript.java:93) ~[java.scripting:?]
	at org.apache.jmeter.util.JSR223TestElement.processFileOrScript(JSR223TestElement.java:217) ~[ApacheJMeter_core.jar:5.5]
	at org.apache.jmeter.protocol.java.sampler.JSR223Sampler.sample(JSR223Sampler.java:72) ~[ApacheJMeter_java.jar:5.5]
	at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:651) ~[ApacheJMeter_core.jar:5.5]
	at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:570) ~[ApacheJMeter_core.jar:5.5]
	at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:501) ~[ApacheJMeter_core.jar:5.5]
	at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:268) ~[ApacheJMeter_core.jar:5.5]
	at java.lang.Thread.run(Thread.java:833) ~[?:?]
Caused by: io.fabric8.kubernetes.client.KubernetesClientException: An error has occurred.
	at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:103) ~[?:?]
	at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:97) ~[?:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder.build(KubernetesClientBuilder.java:76) ~[?:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder$build.call(Unknown Source) ~[?:?]
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130) ~[groovy-3.0.11.jar:3.0.11]
	at Script7.run(Script7.groovy:13) ~[?:?]
	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317) ~[groovy-jsr223-3.0.11.jar:3.0.11]
	... 9 more
Caused by: java.lang.reflect.InvocationTargetException
	at jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:79) ~[?:?]
	at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?]
	at java.lang.reflect.Constructor.newInstance(Constructor.java:483) ~[?:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder.build(KubernetesClientBuilder.java:69) ~[?:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder$build.call(Unknown Source) ~[?:?]
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130) ~[groovy-3.0.11.jar:3.0.11]
	at Script7.run(Script7.groovy:13) ~[?:?]
	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317) ~[groovy-jsr223-3.0.11.jar:3.0.11]
	... 9 more
Caused by: io.fabric8.kubernetes.client.KubernetesClientException: No httpclient implementations found on the context classloader, please ensure your classpath includes an implementation jar
	at io.fabric8.kubernetes.client.utils.HttpClientUtils.createHttpClient(HttpClientUtils.java:171) ~[?:?]
	at io.fabric8.kubernetes.client.DefaultKubernetesClient.<init>(DefaultKubernetesClient.java:153) ~[?:?]
	at jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:67) ~[?:?]
	at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[?:?]
	at java.lang.reflect.Constructor.newInstance(Constructor.java:483) ~[?:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder.build(KubernetesClientBuilder.java:69) ~[?:?]
	at io.fabric8.kubernetes.client.KubernetesClientBuilder$build.call(Unknown Source) ~[?:?]
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130) ~[groovy-3.0.11.jar:3.0.11]
	at Script7.run(Script7.groovy:13) ~[?:?]
	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317) ~[groovy-jsr223-3.0.11.jar:3.0.11]
	... 9 more

Fabric8 Kubernetes Client version

6.0.0-RC1

Steps to reproduce

  1. import the fabric8 directives
  2. Execute the above code in JMeter 5.5, which has Groovy 3.0.11
  3. Exceptions can be seen.

Expected behavior

No errors.

Runtime

Kubernetes (vanilla)

Kubernetes API Server version

other (please specify in additional context)

Environment

Windows, Linux, other (please specify in additional context)

Fabric8 Kubernetes Client Logs

No response

Additional context

  • tried in 5.12.2 as well, same issue

QAInsights avatar Jul 05 '22 00:07 QAInsights

It seems like JMeter's Groovy engine is not loading the kubernetes-client module. Could you try removing the kubernetes-client-api dependency?

manusa avatar Jul 05 '22 07:07 manusa

@manusa removing kubernetes-client-api throwing the below error.

2022-07-05 10:06:13,726 ERROR o.a.j.p.j.s.JSR223Sampler: Problem in JSR223 script JSR223 Sampler, message: javax.script.ScriptException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script5.groovy: 1: unable to resolve class io.fabric8.kubernetes.client.KubernetesClient
 @ line 1, column 1.
   import io.fabric8.kubernetes.client.KubernetesClient;
   ^

Script5.groovy: 2: unable to resolve class io.fabric8.kubernetes.client.KubernetesClientBuilder
 @ line 2, column 1.
   import io.fabric8.kubernetes.client.KubernetesClientBuilder;
   ^

2 errors

javax.script.ScriptException: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script5.groovy: 1: unable to resolve class io.fabric8.kubernetes.client.KubernetesClient
 @ line 1, column 1.
   import io.fabric8.kubernetes.client.KubernetesClient;
   ^

Script5.groovy: 2: unable to resolve class io.fabric8.kubernetes.client.KubernetesClientBuilder
 @ line 2, column 1.
   import io.fabric8.kubernetes.client.KubernetesClientBuilder;
   ^

2 errors

	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.compile(GroovyScriptEngineImpl.java:183) ~[groovy-jsr223-3.0.11.jar:3.0.11]
	at org.apache.jmeter.util.JSR223TestElement.processFileOrScript(JSR223TestElement.java:211) ~[ApacheJMeter_core.jar:5.5]
	at org.apache.jmeter.protocol.java.sampler.JSR223Sampler.sample(JSR223Sampler.java:72) ~[ApacheJMeter_java.jar:5.5]
	at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:651) ~[ApacheJMeter_core.jar:5.5]
	at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:570) ~[ApacheJMeter_core.jar:5.5]
	at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:501) ~[ApacheJMeter_core.jar:5.5]
	at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:268) ~[ApacheJMeter_core.jar:5.5]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]
Caused by: org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script5.groovy: 1: unable to resolve class io.fabric8.kubernetes.client.KubernetesClient
 @ line 1, column 1.
   import io.fabric8.kubernetes.client.KubernetesClient;
   ^

Script5.groovy: 2: unable to resolve class io.fabric8.kubernetes.client.KubernetesClientBuilder
 @ line 2, column 1.
   import io.fabric8.kubernetes.client.KubernetesClientBuilder;
   ^

2 errors

	at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:292) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.control.CompilationUnit$ISourceUnitOperation.doPhaseOperation(CompilationUnit.java:914) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:627) ~[groovy-3.0.11.jar:3.0.11]
	at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:389) ~[groovy-3.0.11.jar:3.0.11]
	at groovy.lang.GroovyClassLoader.lambda$parseClass$3(GroovyClassLoader.java:332) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.runtime.memoize.StampedCommonCache.compute(StampedCommonCache.java:163) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.runtime.memoize.StampedCommonCache.getAndPut(StampedCommonCache.java:154) ~[groovy-3.0.11.jar:3.0.11]
	at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:330) ~[groovy-3.0.11.jar:3.0.11]
	at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:314) ~[groovy-3.0.11.jar:3.0.11]
	at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:257) ~[groovy-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.getScriptClass(GroovyScriptEngineImpl.java:336) ~[groovy-jsr223-3.0.11.jar:3.0.11]
	at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.compile(GroovyScriptEngineImpl.java:181) ~[groovy-jsr223-3.0.11.jar:3.0.11]
	... 7 more

QAInsights avatar Jul 05 '22 14:07 QAInsights

@manusa I was able to fix it by adding all the fabric8 jars in one folder and associate it with JMeter. Not sure, why Grab is not working :(

Grab is downloading all the jars and keep it in the home directory, but somehow Groovy is not loading them.

QAInsights avatar Jul 05 '22 20:07 QAInsights

I will try to reproduce. It seems that it's skipping the kubernetes-client module for some reason. Since there's no explicit class loading in your script (which is how it should be) for any of its provided classes, this might be causing some issues.

manusa avatar Jul 06 '22 05:07 manusa

With a quick glimpse at the code I am seeing that we are not consistent with the way we are loading classes and services (spi).

The safest approach IMHO is to always follow the chain:

  1. Load class / service with no class loader reference.
  2. Fall back to the current class class loader
  3. Fall back to TCCL

This used to be encapsulated in the logic of Adapters and should be used as a reference. Note, that in the current impl of Adapters (1) has been completely removed (2) which I think in some edge cases it may be problematic.

iocanel avatar Jul 07 '22 07:07 iocanel

^^^ @shawkins

With a quick glimpse at the code I am seeing that we are not consistent with the way we are loading classes and services (spi).

But do you think this is related to this error? https://github.com/fabric8io/kubernetes-client/issues/4248#issuecomment-1175104075 seems to highlight that for some reason Groovy is not loading the kubernetes-client module or its transitive dependencies.

manusa avatar Jul 07 '22 08:07 manusa

The GroovyScriptEngine is using it's own class loader. I would assume that dependencies are loaded through that class loader. Java SPI on the other hand most probably is not using that class loader, so falling back to TCL or the class loader that loaded the KubernetesClient.class most probably would sovle this.

iocanel avatar Jul 07 '22 08:07 iocanel

In other words, yeah I think that the issue will be solved by leveraging TCL / current class classloader.

iocanel avatar Jul 07 '22 08:07 iocanel

so falling back to TCL or the class loader that loaded the KubernetesClient.class most probably would sovle this.

The code already uses the TCCL: https://github.com/fabric8io/kubernetes-client/blob/10e9f188911ef00e66d3b5731e073ccc6d38f8da/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/HttpClientUtils.java#L157

See https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html#load-java.lang.Class- that clarifies that 1 and 3 on your list are the same.

As for falling back to 2, the class loader of the current class, generally that means the application has mishandled the TCCL - in this case if groovy / jmeter is doing something odd that is plausible, but we should first ensure that the http dependency is present.

shawkins avatar Jul 07 '22 11:07 shawkins

See https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html#load-java.lang.Class- that clarifies that 1 and 3 on your list are the same.

touché

Then maybe the issue is that the TCL is not set ? Or not being set to something that has not visibility to the class. In this case using (2) could solve the issue.

As for falling back to 2, the class loader of the current class, generally that means the application has mishandled the TCCL - in this case if groovy / jmeter is doing something odd that is plausible, but we should first ensure that the http dependency is present.

Let's do that. But do you really feel that @Grab is not bringing in transitives?

iocanel avatar Jul 08 '22 08:07 iocanel

But do you really feel that @Grab is not bringing in transitives?

Yes, that is what I'd infer from: https://github.com/fabric8io/kubernetes-client/issues/4248#issuecomment-1175104075

There he should be grapping kubernetes-client but classes from kubernetes-client-api aren't loading - that implies it's not grabbing transitive dependencies.

shawkins avatar Jul 08 '22 11:07 shawkins

I'm testing this with Groovy (4.0.3 ), and it works for me.

Tested Script:

@Grab(group='io.fabric8', module='kubernetes-client', version='6.0.0-RC1')
@Grab(group='io.fabric8', module='kubernetes-client-api', version='6.0.0-RC1')

import io.fabric8.kubernetes.client.KubernetesClient
import io.fabric8.kubernetes.client.KubernetesClientBuilder

try (KubernetesClient client = new KubernetesClientBuilder().build()) {

    client.pods().inNamespace("default").list().getItems()
        .forEach(pod ->
            println pod.getMetadata().getName()
        )
}

The same script on JMeter fails. However, note that JMeter is using Groovy 3.0.11. I'm not sure if the classloader issues are caused by the groovy engine or JMeter itself when invoking the scripts.

manusa avatar Jul 12 '22 10:07 manusa

This issue has been automatically marked as stale because it has not had any activity since 90 days. It will be closed if no further activity occurs within 7 days. Thank you for your contributions!

stale[bot] avatar Oct 17 '22 15:10 stale[bot]

facing the same issue with gradle:

io.fabric8.kubernetes.client.KubernetesClientException: No httpclient implementations found on the context classloader, please ensure your classpath includes an implementation jar
    at io.fabric8.kubernetes.client.utils.HttpClientUtils.getHttpClientFactory(HttpClientUtils.java:164)
    at io.fabric8.kubernetes.client.KubernetesClientBuilder.build(KubernetesClientBuilder.java:76)
    at com.metalbear.mirrord.KubeDataProvider.<init>(KubeDataProvider.kt:8)
    at com.metalbear.mirrord.MirrordListener.processStartScheduled$lambda-3(MirrordListener.kt:43)
    at com.intellij.openapi.application.TransactionGuardImpl.runWithWritingAllowed(TransactionGuardImpl.java:209)
    at com.intellij.openapi.application.TransactionGuardImpl.access$100(TransactionGuardImpl.java:21)
    at ...

just added the dependency like so:

dependencies {
    implementation("com.github.zafarkhaja:java-semver:0.9.0")
    implementation("io.fabric8:kubernetes-client:6.2.0") {
        exclude(group = "org.slf4j", module = "slf4j-api")
    }
}

Would really appreciate any solutions, thanks

infiniteregrets avatar Nov 05 '22 16:11 infiniteregrets

should be addressed by #4689

manusa avatar Jan 24 '23 17:01 manusa

I'm closing the issue as complete, as the fix should be available and functional since the last release https://github.com/fabric8io/kubernetes-client/releases/tag/v6.4.0

Please, feel free to open a new issue in case the problem persists. It would also be great if any of the affected users could provide feedback and confirm that the fix is working.

manusa avatar Jan 31 '23 06:01 manusa

I still see this issue in a very basic test app using the latest 6.4.0 JARS. See attached screengrab from Intelij. All JARs are downloaded locally and placed in a libs folder.

fabric8_2023-01-31_135740

oreillymj avatar Jan 31 '23 13:01 oreillymj

Hi @oreillymj This is not any of the scenarios mentioned in the issue (Gradle or JMeter). If you add any of the kubernetes-httpclient-xxx.jar files and place it in your classpath, it should work.

manusa avatar Jan 31 '23 14:01 manusa

I recreated the project with Maven, and see it pulls in a bunch more dependencies. I needed okhttp3, okio, logging intercepter + more. Somehow when building with downloaded JAR's, I wasn't getting NoClassDef errors at run time (in the Intelij IDE), so it's difficult to figure out what I was missing.

oreillymj avatar Jan 31 '23 17:01 oreillymj