Jenkins pipeline MissingMethodException: No signature of method: - jenkins

I wrote a function to insert inject a variable through the EnvInj-plugin. Following script I used:
import hudson.model.*
import static hudson.model.Cause.RemoteCause
#com.cloudbees.groovy.cps.NonCPS
def call(currentBuild) {
def ipaddress=""
for (CauseAction action : currentBuild.getActions(CauseAction.class)) {
for (Cause cause : action.getCauses()) {
if(cause instanceof RemoteCause){
ipaddress=cause.addr
break;
}
}
}
return ["ip":ipaddress]
}
When I put it the the folder $JENKINS_HOME/workflow-libs/vars as a global function, i get the following error:
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper.getActions() is applicable for argument types: (java.lang.Class) values: [class hudson.model.CauseAction]
I am completly new in groovy, so I don't know why it is not working. With the EnvInj-plugin it was fine. Can anyone help me?

You will probably need the rawbuild property of the currentBuild.
The following script should do it for you.
//$JENKINS_HOME/workflow-libs/vars/getIpAddr.groovy
#com.cloudbees.groovy.cps.NonCPS
def call() {
def addr = currentBuild.rawBuild.getActions(CauseAction.class)
.collect { actions ->
actions.causes.find { cause ->
cause instanceof hudson.model.Cause.RemoteCause
}
}
?.first()?.addr
[ ip: addr ]
}
if you use it like:
def addressInfo = getIpAddr()
def ip = addressInfo.ip
Note that it will be null if there are no RemoteCause actions
You might want to return only the addr instead of the hashmap [ ip: addr ], like so
//$JENKINS_HOME/workflow-libs/vars/getIpAddr.groovy
#com.cloudbees.groovy.cps.NonCPS
def call() {
currentBuild.rawBuild.getActions(CauseAction.class)
.collect { actions ->
actions.causes.find { cause ->
cause instanceof hudson.model.Cause.RemoteCause
}
}
?.first()?.addr
}
and then
def addressInfo = [ ip: getIpAdder() ]
Alos note that, depending on the security of your Jenkins, you might need to allow the running of extension methods in sandboxed scripts. You will notice a RejectedSandboxException
You can solve this by approving this through Manage Jenkins -> In-process Script Approval
Hope it works

Related

Jenkins/Groovy: How to call withCredentials() from a class constructor?

How do I call withCredentials() from a Groovy class constructor?
Why does this:
#Library('my-sandbox-libs#dev') sandbox_lib
pipeline {
agent any
stages {
stage( "1" ) {
steps {
script {
try {
def my_obj = new org.obj.Obj()
}
catch(Exception e) {
echo "Jenkinsfile: ${e.toString()}"
throw e
}
}
}
}
}
}
// src/org/obj/Obj.groovy
package org.obj
public class Obj {
def secret_
Obj() {
withCredentials([string(credentialsId: 'test_secret_text', variable: 'val')]) {
this.secret_ = val
}
}
}
...generate this error:
Jenkinsfile: groovy.lang.MissingMethodException: No signature of method: org.obj.Obj.string() is applicable for argument types: (java.util.LinkedHashMap) values: [[credentialsId:test_secret_text, variable:val]]
Possible solutions: toString(), toString(), print(java.io.PrintWriter), print(java.lang.Object), find(), split(groovy.lang.Closure)
?
Update:
Tried the following, in line with #daggett's answer:
// src/org/obj/Obj.groovy
package org.obj
public class Obj {
def secret_
Obj(pipeline) {
pipeline.withCredentials([string(credentialsId: 'test_secret_text',
variable: 'val')]) {
this.secret_ = val
}
}
}
#Library('my-sandbox-libs#dev') sandbox_lib
pipeline {
agent any
stages {
stage( "1" ) {
steps {
script { def my_obj = new org.obj.Obj(this) }
}
}
}
}
...which generated the error Posting build status of FAILED to bitbucket.company.comhudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: org.obj.Obj.string() is applicable for argument types: (java.util.LinkedHashMap) values: [[credentialsId:test_secret_text, variable:val]] Possible solutions: toString(), toString(), print(java.io.PrintWriter), print(java.lang.Object), find(), split(groovy.lang.Closure)
However, the following -- just an experiment -- runs fine:
// src/org/obj/Obj.groovy
package org.obj
public class Obj {
def secret_
Obj(pipeline) {
pipeline.echo "hello world"
}
}
Note: I also tried public class Obj implements Serializable {...}, which did not change the noted error.
I'm still having a hard time building a mental model of the Jenkins/Groovy language, but to me this appears as though there's something "special" about withCredentials() preventing it from being called from a class constructor.
In case it's relevant, I have Jenkins version 2.190.3, which has Groovy version 2.4.12.
this should help: https://www.jenkins.io/doc/book/pipeline/shared-libraries/#accessing-steps
the easiest way - to pass pipeline as argument into constructor
public class Obj {
def secret_
Obj(pipeline) {
pipeline.withCredentials(...
}
and call it like this
...
steps {
script {
def my_obj = new org.obj.Obj(this)
I solved it by doing the following:
Create a file in /vars which will do the withCredential.
File name for example: /vars/dockerCommands.groovy
(I called it docker.groovy before, but docker is an existing step already, so I advise to change it so it will not conflict.)
.
.
.
def login(String cred, String reg) {
withCredentials([
usernamePassword(credentialsId: cred ,usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASSWORD')
]){
docker login -u ${DOCKER_USER} -p ${DOCKER_PASSWORD} ${reg}"
}
}
.
.
In the class I called for this step.
.
.
public void login(String cred, String reg) {
this.steps.dockerCommands.login(cred, reg)
}
.
.
Note, steps is a private var which is equivalent to your pipeline var.
I create it in the constructor method of the class.

Import list from one groovy file to another

I have two groovy files.
First.groovy:
class First {
def getContent() {
content = ["one", "two"]
}
}
return this
And second.groovy:
{
node(label) {
def listClass = this.class.classLoader.parseClass("First.groovy")
def CONTENT=listClass.getContent()
}
I get error from jenkins when I run the job:
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: java.lang.Class.getContent() is applicable for argument types: () values: []
kindly advice on efficient way to import a list from one groovy file to another.
Jenkins offers the method load(filepath) for your case so you don't have to juggle with all the classloader methods.
You don't necessarely need the class in your First.groovy so simply remove it.
First.groovy
def getContent() {
def content = ["one", "two"]
return content
}
return this;
And call it like:
def first = load 'First.groovy'
def content = first.getContent()

Passing closures from a class in Groovy/Jenkins

I'm trying to create a JobGenerator class that will pass a build step down to the calling instance. I'm running into an issue where if I get this error when I try to run this:
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: org.jenkinsci.plugins.workflow.cps.CpsClosure2.build() is applicable for argument types: (java.util.LinkedHashMap) values: [[job:FooJob]]
class BuildGenerator implements Serializable {
static def generateJob() {
return [
"TestJob",
{ ->
build(
job: 'FooJob'
)
},
]
}
}
node(){
def tasks = [:]
def label
def task
stage("Build") {
def generator = new BuildGenerator()
tasks["Testing"] = generator.generateJob()[1]
parallel tasks
}
}
If I remove the generateJob function outside of the class then it works fine. What am I doing wrong with closures here? I'm new to groovy/jenkins world.
Well... This is the way Groovy/Jenkins pipeline work. build is available inside node as the rest of steps and functions. If you wish to access these you have to pass the CPS instance to the method, like this (or use constructor to pass the instance only once):
class BuildGenerator implements Serializable {
static def generateJob(script) {
return [
"TestJob",
{ ->
script.build(
job: 'FooJob'
)
},
]
}
}
node(){
def tasks = [:]
def label
def task
stage("Build") {
def generator = new BuildGenerator()
tasks["Testing"] = generator.generateJob(this)[1]
parallel tasks
}
}

groovy, script:this and jenkinsfile

Based on this:
println in "call" method of "vars/foo.groovy" works, but not in method in class
I am trying to get my head around printing to the console from classes created in a Jenkins pipeline using jenkins shared libraries. I have the following:
MyPipeline.groovy
node("test") {
stage("Test") {
def a = new A(script:this)
echo "Calling A.a()"
a.a()
}
}
A.groovy
class A {
Script script;
public void a() {
script.echo("Hello from A")
def b = new B(script)
echo "Calling B.b()"
b.b()
}
}
B.groovy
class B {
Script script;
public void b() {
script.echo("Hello from B")
}
}
When I run that I get:
"Hello from A"
but then I get the error from B:
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: samples.B(samples.MyPipeline)
How do I make it possible to print to console/build log in my classes also when delegating to other classes - like B in the above case?
As suggested by below answer I have now tried to update A.groovy to:
class A {
Script script;
public void a() {
script.echo("Hello from A")
def b = new B()
b.script = script
//def b = new B(script)
echo "Calling B.b()"
b.b()
}
}
But that just gives a new error:
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: samples.A.echo() is applicable for argument types: (java.lang.String) values: [Calling B.b()]
Possible solutions: each(groovy.lang.Closure), getAt(java.lang.String), wait(), a(), every(), grep()
As per groovy's Initializing beans with named parameters and the default constructor
Just call empty constructor and set the parameter script
def b = new B()
b.script = script
With a bean like:
class Server {
String name
Cluster cluster }
Instead of setting each setter in subsequent statements as follows:
def server = new Server()
server.name = "Obelix"
server.cluster = aCluster
Also replace the following echo
echo "Calling B.b()"
To using script.echo method:
script.echo("Calling B.b()")

MissingMethodException: No signature of method error after replacing method using groovy metaclass?

I have a simple domain
class Cat {
String name
static constraints = {
}
}
The controller is as follows:
class CatController {
def index() { }
def say(){
def cat = new Cat(name: "cc")
cat.save()
render "done"
}
}
and i have a test integration test as follows:
class CatTests extends GroovyTestCase{
CatController controller = new CatController()
#Test
void testSomething() {
Cat.metaClass.save = {
throw new Exception("Asdasd")
}
shouldFail(Exception){
controller.say()
}
GroovySystem.metaClassRegistry.removeMetaClass(Cat.class)
}
#Test
void testSomething2() {
def before_cat_count = Cat.count()
controller.say()
def after_cat_count = Cat.count()
assertEquals after_cat_count - before_cat_count, 1
}
}
I get the following error when running the test.
Failure: testSomething2(cat.CatTests)
| groovy.lang.MissingMethodException: No signature of method: cat.Cat.count() is applicable for argument types: () values: []
Possible solutions: count(), ident(), print(java.lang.Object), print(java.io.PrintWriter), getCount(), wait()
at cat.CatTests.testSomething2(CatTests.groovy:37)
| Completed 2 integration tests, 1 failed in 192ms
Now, my doubt and question is why is it not finding the count method on this line
def before_cat_count = Cat.count()
of testSomething2() method.
I have already removed the meta class at the end of method testSomething. So, i am wondering why is count() method not being found. I appreciate any help! Thanks!
Perhaps simply
GroovySystem.metaClassRegistry.removeMetaClass(Cat)
simply Cat, not Cat.class
Sourced from the web, I use a method for this type of cleanup.
def revokeMetaClassChanges(Class type, def instance = null) {
GroovySystem.metaClassRegistry.removeMetaClass(type)
if(instance) {
instance.metaClass = null
}
}
With invocations like:
cleanup:
revokeMetaClassChanges(FirstService, firstServiceInstance)
revokeMetaClassChanges(SecondService, null)
with much success.

Resources