jenkinsci / jenkinsfile-runner

A command line tool to run Jenkinsfile as a function
MIT License
1.13k stars 292 forks source link

jenkinsfile-runner-1.0-beta-25 binary Unhandled exception when exploding WAR. #476

Open shadycuz opened 3 years ago

shadycuz commented 3 years ago

I get a shell into the vanilla image:

root@ffd1873bfc20:/# bash /app/bin/jenkinsfile-runner-launcher cli
No explicit version has been selected, using latest LTS
Running pipeline on jenkins 2.263.4
Exploding,/root/.jenkinsfile-runner/war/2.263.4/jenkins-war-2.263.4.warthis might take some time.
java.lang.RuntimeException: Unhandled exception
        at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.call(JenkinsLauncherCommand.java:59)
        at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.call(JenkinsLauncherCommand.java:35)
        at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
        at picocli.CommandLine.access$1200(CommandLine.java:145)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
        at picocli.CommandLine.execute(CommandLine.java:2058)
        at io.jenkins.jenkinsfile.runner.bootstrap.Bootstrap.main(Bootstrap.java:45)
Caused by: java.util.zip.ZipException: error in opening zip file
        at java.util.zip.ZipFile.open(Native Method)
        at java.util.zip.ZipFile.<init>(ZipFile.java:225)
        at java.util.zip.ZipFile.<init>(ZipFile.java:155)
        at java.util.jar.JarFile.<init>(JarFile.java:166)
        at java.util.jar.JarFile.<init>(JarFile.java:130)
        at io.jenkins.jenkinsfile.runner.bootstrap.Util.explodeWar(Util.java:60)
        at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.postConstruct(JenkinsLauncherCommand.java:91)
        at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.call(JenkinsLauncherCommand.java:56)
        ... 9 more
shadycuz commented 3 years ago

Well its be over 2 hours and I'm done trying to get this to run...

Please reply and I can help you fix all these issues.

Lambeaux commented 3 years ago

This is indeed an issue since the README explains default behavior when params are omitted. I'm not using the Docker image but the zip itself jenkinsfile-runner-1.0-beta-25.zip on Mac OS Mojave. Some things I've tried:

Downloading jenkins.war myself from https://www.jenkins.io/download/ and specifying it with -w appeared to address the issue. That being said:

Definitely some rough edges that can be addressed. The defaults for this thing fail in a variety of areas, two of which are (1) downloading the Jenkins war, and (2) the "vanilla" distribution and necessary plugins being insufficient to run the most basic example successfully.

It's also strange that the Jenkins war needs to be re-exploded for each subsequent run. I'd really prefer faster feedback.

Lambeaux commented 3 years ago

@shadycuz Might have a path forward for you ☝️ if you wanted to take another stab at it.

Lambeaux commented 3 years ago

Regarding the automatic download of the Jenkins war, I'll share the stacktrace again just in case. I didn't see any differences but I may have missed some.

$ ./bin/jenkinsfile-runner -f MY_PATH/Jenkinsfile -jv 2.263.2
Running pipeline on jenkins 2.263.2
Downloading jenkins 2.263.2...
Exploding,/Users/lambeaux/.jenkinsfile-runner/war/2.263.2/jenkins-war-2.263.2.warthis might take some time.
java.lang.RuntimeException: Unhandled exception
    at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.call(JenkinsLauncherCommand.java:59)
    at io.jenkins.jenkinsfile.runner.bootstrap.Bootstrap.call(Bootstrap.java:70)
    at io.jenkins.jenkinsfile.runner.bootstrap.Bootstrap.call(Bootstrap.java:21)
    at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
    at picocli.CommandLine.access$1200(CommandLine.java:145)
    at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
    at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
    at picocli.CommandLine.execute(CommandLine.java:2058)
    at io.jenkins.jenkinsfile.runner.bootstrap.Bootstrap.main(Bootstrap.java:45)
Caused by: java.util.zip.ZipException: error in opening zip file
    at java.util.zip.ZipFile.open(Native Method)
    at java.util.zip.ZipFile.<init>(ZipFile.java:219)
    at java.util.zip.ZipFile.<init>(ZipFile.java:149)
    at java.util.jar.JarFile.<init>(JarFile.java:166)
    at java.util.jar.JarFile.<init>(JarFile.java:130)
    at io.jenkins.jenkinsfile.runner.bootstrap.Util.explodeWar(Util.java:57)
    at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.postConstruct(JenkinsLauncherCommand.java:91)
    at io.jenkins.jenkinsfile.runner.bootstrap.commands.RunJenkinsfileCommand.postConstruct(RunJenkinsfileCommand.java:77)
    at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.call(JenkinsLauncherCommand.java:56)
    ... 10 more
oleg-nenashev commented 3 years ago

Downloading jenkins.war myself from https://www.jenkins.io/download/ and specifying it with -w appeared to address the issue. That being said:

Yes, it is how Jenkinsfile Runner is used normally in containers (pre-exploded WAR)

Exploding the war takes longer than I would have expected for a simple unzip operation,

Need to profile it to say anything, but unarchiving operation by @sladyn98 uses a very simple sequential unpacking algorithm in a single thread. Since Jenkins WAR includes MANY files, it is expected to be quite slow.

"vanilla" distribution and necessary plugins being insufficient to run the most basic example successfully.

Vanilla image includes all the plugins required for running. It is not a case for a stripped executable JAR. There you need to specify plugins using -p

It's also strange that the Jenkins war needs to be re-exploded for each subsequent run. I'd really prefer faster feedback.

It should not be required. If -w points to the exploded directory, Jenkins will just use it every time

Lambeaux commented 3 years ago

@oleg-nenashev Thanks for the info. Much appreciated. So just to clarify a few things:

Yes, it is how Jenkinsfile Runner is used normally in containers (pre-exploded WAR)

I was reading the detailed argument behaviors in the README and it sounded like omitting -w was acceptable. Would you accept a fix for this bug where Jenkins isn't quite getting downloaded and as a result we get the zip exception? Or is this only ever used, in practice, within a container where Jenkins is pre-provisioned? I ask because you alluded to "normal" use and I was curious even though it's not "normal", is downloading Jenkins on-the-fly still supported? The README makes me think 'yes'.

Need to profile it to say anything, but unarchiving operation by @sladyn98 uses a very simple sequential unpacking algorithm in a single thread. Since Jenkins WAR includes MANY files, it is expected to be quite slow.

Okay no worries. I might take a look at the unpacking, but if there's a way to only do it once then that's likely not necessary.

Vanilla image includes all the plugins required for running. It is not a case for a stripped executable JAR. There you need to specify plugins using -p

So when we say in the README that "Jenkinsfile Runner bundle includes the Jenkins core and a minimum set of plugins" - that only applies to the Docker image? Should we clarify that? Also, where in the code might I find the list of plugins that would normally ship with the docker image? I was looking for a plugins.txt in the packaging/docker and vanilla-package areas but didn't see any. Is it getting dynamically generated per the pom in vanilla-package by chance?

It should not be required. If -w points to the exploded directory, Jenkins will just use it every time

Aha! This is very helpful. I verified it indeed worked.

I'll consider submitting some README improvements (if you like) to make the above a little clearer to newcomers. I appreciate your time.

shadycuz commented 3 years ago

Same. I tried this project close to a year again and didn't get any where. Now I still can't.

I can gladly contribute but I need to know what is actually bugs and what I'm just doing wrong.

oleg-nenashev commented 3 years ago

Sure, happy to help. Will respond to the comments later today or tomorrow

shadycuz commented 3 years ago

Thanks @Lambeaux

I don't know what a WAR or (pre-exploded WAR) is. So I was kinda expecting that it would just work once I installed the bin/jenkinsfile-runner locally. Thankfully you know the correct questions to ask. Since the runner needs a WAR it seems I will most likely be using it from docker.

On a side note this might still be a real bug. Since I was inside of a container using jenkinsfile-runner-launcher and so it should have worked?

Lambeaux commented 3 years ago

@shadycuz No worries 👌 Jenkins is a Java app. The default packaging for a Java app is a JAR (Java Archive) which is basically just a compressed archive (like a .zip or .tar.gz) with Java bytecode and JVM metadata inside. WAR is just a variation on that (Web Archive) which is meant to be run on Java web servers like Jetty or Tomcat. In either case, to run these executables, you would need Java installed on the system.

Now, I suspect the WAR needs to be unpacked because jenkinsfile-runner isn't a web server, it's a standalone Java executable (JAR), so it's targeting specific execution paths in Jenkins itself and exposing them over a CLI for our benefit (so we don't need to use web requests to do stuff).

Since the runner needs a WAR it seems I will most likely be using it from docker.

I think there are plenty of other reasons to use Docker, but I'm not sure the distribution format being a WAR is a reason on its own (maybe due to other factors for your use cases). If anything, using Docker basically hides the fact that you would need to install Java to run Jenkinsfiles because the Docker image would provide that for you.

For example, I'm just working on a separate Java project that we test using Jenkins, and I'm using jenkinsfile-runner to test my pipelines locally. It's a pretty safe assumption that myself and team members already maintain JDKs on our machines so I wasn't really concerned with the Docker support. Although if managing JDK versions started to get unwieldy, then using Docker starts to become more appealing.

On a side note this might still be a real bug. Since I was inside of a container using jenkinsfile-runner-launcher and so it should have worked?

I took a closer look at your initial post and I think you're invoking it wrong but I'm not 100% sure. For now, all I know is that on my machine, I verified the initial set of commands found in the README Docker section do work out of the box:

docker pull jenkins/jenkinsfile-runner
docker run --rm -v MY_PATH/Jenkinsfile:/workspace/Jenkinsfile jenkins/jenkinsfile-runner

where my Jenkinsfile was the above bare-bones snippet I posted earlier. It also ran with Finished: SUCCESS as expected. So I don't think anything is broken regarding Docker, nor do I believe the README is misleading. Following the instructions worked fine.

Note that if your pipeline is more complex you'll likely have to extend the docker image with the plugins you need. I found a way to add plugins using the Jenkins CLI - I'll share some details about that next.

Lambeaux commented 3 years ago

Here's my story. I'm not sure if it's a recurring situation.

Ultimately I think learning the new concepts for newcomers should be an expectation if you want to do intermediate or advanced things; but not for the base "working" case in a README.

Some potential suggestions:

All that being said, I was able to get things working with the standalone zip of jenkinsfile-runner, without Docker. It was pretty straight forward and these steps might make a good foundation for the guide. Here's how:

Works great. Going to use this to iterate on my next automation. I'll post updates as I go.

Started
Resume disabled by user, switching to high-performance, low-durability mode.
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/folders/vj/c0gkmnv97tq77v6jwf_h9k8r0000gn/T/jenkinsfileRunner.tmp/jfr1272810717839708878.run/workspace/job
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Declarative: Checkout SCM)
[Pipeline] checkout
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Build)
[Pipeline] echo
Hello world!
[Pipeline] echo
message:
[Pipeline] echo
param2:
[Pipeline] sh
+ ls -la
total 8
drwxr-xr-x  3 lambeaux  staff   96 Mar 10 15:35 .
drwxr-xr-x  4 lambeaux  staff  128 Mar 10 15:35 ..
-rw-r--r--  1 lambeaux  staff  459 Mar  9 18:02 Jenkinsfile
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
Lambeaux commented 3 years ago

Note that while jenkinsfile-runner-1.0-beta-23 works like above, trying to do the same thing with jenkinsfile-runner-1.0-beta-25 does not work. The following is consistently hit.

Exploding,/cx/deploy/jenkinsfile-runner-1.0-beta-25/../jenkins.warthis might take some time.
java.lang.RuntimeException: Unhandled exception
    at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.call(JenkinsLauncherCommand.java:59)
    at io.jenkins.jenkinsfile.runner.bootstrap.Bootstrap.call(Bootstrap.java:70)
    at io.jenkins.jenkinsfile.runner.bootstrap.Bootstrap.call(Bootstrap.java:21)
    at picocli.CommandLine.executeUserObject(CommandLine.java:1933)
    at picocli.CommandLine.access$1200(CommandLine.java:145)
    at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2332)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2326)
    at picocli.CommandLine$RunLast.handle(CommandLine.java:2291)
    at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2159)
    at picocli.CommandLine.execute(CommandLine.java:2058)
    at io.jenkins.jenkinsfile.runner.bootstrap.Bootstrap.main(Bootstrap.java:45)
Caused by: java.lang.NoClassDefFoundError: com/cloudbees/plugins/credentials/CredentialsUnavailableException
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
    at java.lang.Class.getMethod0(Class.java:3018)
    at java.lang.Class.getMethod(Class.java:1784)
    at io.jenkins.jenkinsfile.runner.JenkinsfileRunnerLauncher.doLaunch(JenkinsfileRunnerLauncher.java:36)
    at io.jenkins.jenkinsfile.runner.JenkinsLauncher.launch(JenkinsLauncher.java:103)
    at io.jenkins.jenkinsfile.runner.App.run(App.java:21)
    at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.runJenkinsfileRunnerApp(JenkinsLauncherCommand.java:198)
    at io.jenkins.jenkinsfile.runner.bootstrap.commands.JenkinsLauncherCommand.call(JenkinsLauncherCommand.java:57)
    ... 10 more
Caused by: java.lang.ClassNotFoundException: com.cloudbees.plugins.credentials.CredentialsUnavailableException
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 20 more
shadycuz commented 3 years ago

@oleg-nenashev Can you turn on "discussions" for this repo? Or maybe @Lambeaux and I can move to gitter or something.

Would like to follow his process as we both are going down the same rabbit hole.

I'm going to update the title as well.

shadycuz commented 3 years ago

This issue with the exploding war on jenkinsfile-runner-1.0-beta-25 also affects the latest image in dockerhub and of course any image that also includes this version of the runner.

sladyn98 commented 3 years ago

In my current understanding of jenkinsfile-runner-1.0-beta-25 it downloads the war and then attempts to explode it using a basic extraction. This would need some additional testing with the pipeline you are running to figure out the exact reason of the failure. You could make a case for not exploding the war and falling back to telling the user to do it himself. With respect to your suggestions to the readme, I do agree there is definitely scope for certain improvements.

reftel commented 3 years ago

I get the same issue, and at least in my case, it seems that the problem is related to the URL that jenkinsfile-runner is attempting to download the file from:

% cat ~/.jenkinsfile-runner/war/2.277.1/jenkins-war-2.277.1.war
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://get.jenkins.io/war-stable/2.277.1/jenkins.war">here</a>.</p>
</body></html>

Downloading the file manually using curl -L -o ~/.jenkinsfile-runner/war/2.277.1/jenkins-war-2.277.1.war https://get.jenkins.io/war-stable/2.277.1/jenkins.war solves the problem.