CICD

How to Fix Jenkins “No such DSL method ‘node’ found among steps”

Had this error today,

Also:   org.jenkinsci.plugins.workflow.actions.ErrorAction$ErrorId: 417038a0-74ea-4878-a8b3-6a832a793150
java.lang.NoSuchMethodError: No such DSL method 'node' found among steps [archive, bat, build, catchError, checkout, container,
 containerLog, deleteDir, dir, echo, envVarsForTool, error, fileExists, getContext, git, input, isUnix, library, libraryResource, load,
 mail, milestone, node, parallel, podTemplate, powershell, properties, pwd, pwsh, readFile, readTrusted, resolveScm, retry, script, sh,
 sleep, stage, stash, step, timeout, tool, unarchive, unstable, unstash, validateDeclarativePipeline, waitForBuild, waitUntil, warnError,
 withContext, withCredentials, withEnv, wrap, writeFile, ws] or symbols [GitUsernamePassword, agent, all, allBranchesSame, allOf, always,
 any, anyOf, apiToken, apiTokenProperty, architecture, archiveArtifacts, artifactManager, assembla, authorInChangelog, batchFile,
 bitbucket, bitbucketServer, booleanParam, branch, browser, buildButton, buildDiscarder, buildDiscarders, buildRetention,
 buildSingleRevisionOnly, buildingTag, builtInNode, caseInsensitive, caseSensitive, certificate, cgit, changeRequest, changelog,
 changelogBase, changelogToBranch, changeset, checkoutOption, checkoutToSubdirectory, choice, choiceParam, cleanAfterCheckout,
 cleanBeforeCheckout, clock, cloneOption, command, computerRetentionCheckInterval, configMapVolume, consoleUrlProvider, containerEnvVar,
 containerLivenessProbe, containerTemplate, cps, credentials, cron, crumb, default, defaultDisplayUrlProvider, defaultFolderConfiguration, 
defaultView, demand, disableConcurrentBuilds, disableRestartFromStage, disableResume, discoverOtherRefs, discoverOtherRefsTrait, diskSpace, diskSpaceMonitor, downstream, dumb, durabilityHint, dynamicPVC, emptyDirVolume, emptyDirWorkspaceVolume, envVar, envVars, envVarsFilter, 
environment, equals, experimentalFlags, expression, file, fileParam, filePath, fingerprint, fingerprints, firstBuildChangelog, fisheye, 
frameOptions, freeStyle, freeStyleJob, fromScm, fromSource, genericEphemeralVolume, git, gitBranchDiscovery, gitHooks, gitLab, gitList, 
gitSCM, gitTagDiscovery, gitTool, gitUsernamePassword, gitWeb, gitblit, github, gitiles, gogs, headRegexFilter, headWildcardFilter, 
hostPathVolume, hostPathWorkspaceVolume, hyperlink, hyperlinkToModels, ignoreOnPush, inbound, installSource, isRestartedRun, jdk, jgit,
 jgitapache, jnlp, jobBuildDiscarder, jobName, kiln, kubeconfig, kubernetes, kubernetesAgent, label, lastDuration, lastFailure, 
lastGrantedAuthorities, lastStable, lastSuccess, legacy, legacySCM, lfs, list, local, localBranch, localBranchTrait, location, logRotator,
 loggedInUsersCanDoAnything, mailer, masterBuild, maven, maven3Mojos, mavenErrors, mavenGlobalConfig, mavenMojos, mavenWarnings, merge, 
modernSCM, multiBranchProjectDisplayNaming, multibranch, myView, namedBranchesDifferent, never, nfsVolume, nfsWorkspaceVolume, node, 
nodeProperties, none, nonresumable, not, onFailure, organizationFolder, override, overrideIndexTriggers, paneStatus, 
parallelsAlwaysFailFast, parameters, password, pattern, perBuildTag, permanent, persistentVolumeClaim, 
persistentVolumeClaimWorkspaceVolume, phabricator, pipeline, pipelineTriggers, plainText, plugin, podAnnotation, podEnvVar, podLabel, 
pollSCM, portMapping, preserveStashes, prism, projectNamingStrategy, proxy, pruneStaleBranch, pruneStaleTag, pruneTags, 
queueItemAuthenticator, quietPeriod, rateLimit, rateLimitBuilds, redmine, refSpecs, remoteName, resourceRoot, responseTime, 
retainOnlyVariables, rhodeCode, run, runParam, schedule, scmGit, scmRetryCount, scriptApproval, scriptApprovalLink, search, secretEnvVar, 
secretVolume, security, shell, simpleBuildDiscarder, skipDefaultCheckout, skipStagesAfterUnstable, slave, sourceRegexFilter, 
sourceWildcardFilter, sparseCheckout, sparseCheckoutPaths, sshUserPrivateKey, standard, status, string, stringParam, submodule, 
submoduleOption, suppressAutomaticTriggering, suppressFolderAutomaticTriggering, swapSpace, tag, teamFoundation, text, textParam, timezone, 
tmpSpace, toolLocation, triggeredBy, unsecured, untrusted, upstream, userIdentity, userSeed, usernameColonPassword, usernamePassword, 
viewgit, viewsTabBar, weather, zip] or globals [currentBuild, env, params, pipeline, scm]
	at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.DSL.invokeMethod(DSL.java:219)
	at PluginClassLoader for workflow-cps//org.jenkinsci.plugins.workflow.cps.CpsScript.invokeMethod(CpsScript.java:124)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1225)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:41)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
	at PluginClassLoader for script-security//org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:180)
	at PluginClassLoader for script-security//org.kohsuke.groovy.sandbox.GroovyInterceptor.onMethodCall(GroovyInterceptor.java:23)
	at PluginClassLoader for script-security//org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:163)

when building Jenkins Pipeline using Script from SCM. The weird thing is the same script is working perfectly well when being executed directly from Jenkins, without the SCM / Git part.

The rootcause is actually simple, we are storing the Jenkins pipeline in Git using UTF8 With BOM with a specific Windows encoding. Saving it as a UTF8 directy and push it to Git can solve this problem.

ps.
opening the log in notepad++, after choosing the option to show all character, will give this result.

As we can see from above image, there is a “hidden” character with the name of “ZWNBSP” that is invisible to naked eyes. However it makes Jenkins failed to parse the corresponding pipeline.

Using Secret Credential to Connect to Gitlab in Jenkins

Jenkins is a famous CICD tools that can orchestrate our build and deployment strategy, which can also connect with other CICD toolings such as sourcecode management, or security scanning tools.

But sometimes access to those toolings are limited therefore we need to provide some credentials, but dont want those credentials to be displayed in a plain text. This is where Jenkins Credentials fits into the picture.

We can leverage Jenkins Credentials to store credentials as a secret which can be call by our pipeline directly,

We can start by creating a “Username with password” and put our Gitlab username and password there, dont forget to set the ID for this credentials which is going to be called later from our pipeline.

We can call the saved credentials from pipeline by using “withCredentials” mechanism

node() {
    stage ('git clone') {
        sh "git config --global http.sslVerify false"
        withCredentials([usernamePassword(credentialsId: 'my-gitlab-credential', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
            sh "git clone https://\${USERNAME}:\${PASSWORD}@gitlab.company.com/app/my-repo.git source "
        }
    }
}

A successful pipeline would generate below logs,

Started by user developer
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/jobs/my-pipeline/workspace
[Pipeline] {
[Pipeline] stage
[Pipeline] { (git clone)
[Pipeline] sh
+ git config --global http.sslVerify false
[Pipeline] withCredentials
Masking supported pattern matches of $USERNAME or $PASSWORD
[Pipeline] {
[Pipeline] sh
+ git clone https://****:****@gitlab.company.com/app/my-repo.git source
Cloning into 'source'...
[Pipeline] }
[Pipeline] // withCredentials
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

And we can see from above logs that our pipeline is successfully executed.