mschuchard/jenkins-devops-libs

Error: `unexpected token: def @ line 152, column1.` when using v1.3.0

glarizza opened this issue · 9 comments

Hello!

Was giving this library a spin to determine whether it might be helpful for me or not and I ran into an error. My Jenkinsfile is located here and looks like the following:

@Library('github.com/mschuchard/jenkins-devops-libs@v1.3.0')_

pipeline {
    agent any

    stages {
       stage('Validate') {
            steps {
              script {
                echo "Dir is: ${env.WORKSPACE}/terraform"
                sh "ls -lah ${env.WORKSPACE}/terraform"
                sh "cat ${env.WORKSPACE}/terraform/main.tf"

                // Try to invoke the shared library
                terraform.init {
                  dir = "${env.WORKSPACE}/terraform"
                }

                terraform.validate {
                  dir = "${env.WORKSPACE}/terraform"
                }
              }
            }
        }
        stage('Cleanup') {
            steps {
              echo "Cleanup goes here..."
              //sh "rm -Rf ${env.WORKSPACE}"
            }
        }
    }
}

When I run that code I get the following error from Jenkins:

org.jenkinsci.plugins.workflow.cps.CpsCompilationErrorsException: startup failed:
/var/lib/jenkins/jobs/CK/jobs/gary-from-jenkinsfile/builds/24/libs/github.com/mschuchard/jenkins-devops-libs/vars/terraform.groovy: 152: unexpected token: def @ line 152, column 1.
   def import(body) {
   ^

1 error

	at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
	at org.codehaus.groovy.control.ErrorCollector.addFatalError(ErrorCollector.java:150)
	at org.codehaus.groovy.control.ErrorCollector.addError(ErrorCollector.java:120)
	at org.codehaus.groovy.control.ErrorCollector.addError(ErrorCollector.java:132)
	at org.codehaus.groovy.control.SourceUnit.addError(SourceUnit.java:350)
	at org.codehaus.groovy.antlr.AntlrParserPlugin.transformCSTIntoAST(AntlrParserPlugin.java:144)
	at org.codehaus.groovy.antlr.AntlrParserPlugin.parseCST(AntlrParserPlugin.java:110)
	at org.codehaus.groovy.control.SourceUnit.parse(SourceUnit.java:234)
<...snip...>

I'm not sure exactly what happened there but I get the error anytime I try to call any of the terraform methods from the shared library. Usually when I see that error I look to the previous code to see if I've left something dangling, but I'm a bit out of my wheelhouse here. Have you run into this yet? I tried downgrading to v1.2.1 but that gave me a different error:

[Pipeline] End of Pipeline
org.jenkinsci.plugins.workflow.cps.CpsCompilationErrorsException: startup failed:
/var/lib/jenkins/jobs/CK/jobs/gary-from-jenkinsfile/builds/22/libs/github.com/mschuchard/jenkins-devops-libs/vars/terraform.groovy: 229: The current scope already contains a variable of the name config
 @ line 229, column 7.
     def config = [:]
         ^

/var/lib/jenkins/jobs/CK/jobs/gary-from-jenkinsfile/builds/22/libs/github.com/mschuchard/jenkins-devops-libs/vars/terraform.groovy: 274: The current scope already contains a variable of the name config
 @ line 274, column 7.
     def config = [:]
         ^

2 errors

	at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
	at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:958)
<...snip...>

Any help you can provide would be appreciated! Also let me know if I can provide any more info to help debug. Thanks!

Booted up my full Jenkins Vagrant test box, loaded up the TF test pipeline, and can confirm this recently broke. Initial assumption is the problem here is that import is a reserved word and not allowed for a method name. Going to take some hacks at this in the near future.

By the way, I used one of your repos as additional documentation for the PE library.

Ok my assumption about the reserved word was right, but after I fixed that I also ran into:

/var/lib/jenkins/jobs/tf test/builds/3/libs/jenkins-devops-libs/vars/terraform.groovy: 304: The current scope already contains a variable of the name config
@ line 304, column 7.
Map config = [:]

which I think may be because of a recent change in the Pipeline API around passing Closures in global var methods that was not really communicated outside of a small documentation change.

I will take a harder look at that in the next few days, because it will probably be a larger effort.

Yeah, and it also sounds similar to the error I hit when I downgraded. I'm looking to help someone out with wiring up a Jenkins pipeline to do terraform fmt/validate and then return plans to PRs. I've done this in the past with some ad-hoc code but was looking to build a library for it. This library definitely looks promising, so if I can get it working reliably and understand the extension points it would be great to use!

And that's funny about the PE library docs :) Are they still in the docs somewhere? I saw Shadowsoft under your name and thought you might have been doing some Puppet consulting. Small world!

Ok, it was an issue with method arguments, but actually was due to what I think was an errant replace all in a cleanup commit. Try the latest and see if that works.

As for the fmt capability, I will not have plans to personally implement a method for that myself. This is because to my knowledge modern editors all have built-in auto-formatting for TF (have seen it in VSCode and wrote it for Atom myself; believe IntelliJ and Sublime have it too). My expectation is that people will use the auto-formatting in editors prior to commits. If you have different use cases though, then feel free to go for it!

Just ran from latest, and init and validate are working properly, but I get this error at the end of the run:

[Pipeline] End of Pipeline
java.lang.NullPointerException: Cannot get property 'WORKSPACE' on null object
	at org.codehaus.groovy.runtime.NullObject.getProperty(NullObject.java:60)
	at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:174)
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:456)
	at org.kohsuke.groovy.sandbox.impl.Checker$6.call(Checker.java:293)
	at org.kohsuke.groovy.sandbox.impl.Checker.checkedGetProperty(Checker.java:295)
	at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.getProperty(SandboxInvoker.java:29)
	at com.cloudbees.groovy.cps.impl.PropertyAccessBlock.rawGet(PropertyAccessBlock.java:20)
	at WorkflowScript.run(WorkflowScript:38)
	at terraform.init(terraform.groovy:120)
	at WorkflowScript.run(WorkflowScript:37)

I'm not setting workspace at all, so it looks like there's a conditional check that's missing.

For fmt, it's not necessarily the CAPABILITY I'm looking for so much as a check to determine whether terraform fmt NEEDS to be run. The intent is to use this library for code within a PR to do the following:

  1. Has terraform fmt been run on this code, or does that need to be done?
  2. Does validate return successfully, or are there other syntax errors?
  3. What does a plan look like for this PR?

fmt and validate cover the syntactical checks, and plan is more for visibility. So having an fmt method that will return back whether changes were made (or simply the exit code from the command) is all that I'm looking for.

Actually one comment to clarify - the init and validate did NOT work, I had a shell command running init and plan that executed before terraform.init, so THAT'S what I was seeing. The error I linked above is what was stopping terraform.init to run.

You ran into this Jenkins Pipeline bug. This is probably one of the reasons that the input type for shared library methods has changed from recommended Closure to recommended Map.

My common workaround for this is something like localEnv = env and then:

terraform.init {
  dir = "${localEnv.WORKSPACE}/terraform"
}

Eventually the methods in Jenkins libraries (including this one) will probably be refactored to avoid this bug if they are actively supported, but for now we have to deal with that bug.

Yep, that's exactly it. I was able to work around it before I saw your edited comment, and I'm getting a successful init and validate! Looks like that closes out this issue, and I appreciate all your help! As an aside, do you participate in any Slack communities? I'm glarizza on the Puppet community slack if you ever get on there.

Heads up that the new DSL will be supported in version 1.4.0, which supports directly passing in variables from the params and env maps as values to parameters in the library methods (i.e. you can do terraform.init(dir: "${env.WORKSPACE}/terraform") then).