Import list from one groovy file to another - jenkins

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()

Related

Jenkins spock: Mocking http-request-plugin's httpRequest

Somewhere in my shared library I got a helper class like this:
class Helper {
def script
Helper(script) {
this.script = script
}
void sendTemplate(String webhook, String template, Map<String, String> values, TemplateMapper mapper) {
def body = mapper.map(template, values)
def resp = script.httpRequest(contentType: 'APPLICATION_JSON', httpMode: 'POST',
requestBody: body, url: webhook)
if (resp.status != 200) {
throw new UnableToNotifyException()
}
}
}
I'm trying to test said class like so:
class HelperSpec extends JenkinsPipelineSpecification {
def helper
def setup() {
helper = new Helper(this)
}
def "a test"() {
setup:
def webhook = 'aWebhook'
def template = '%replaceMe'
def values = ['%replaceMe': 'hello world!']
def mapper = new SimpleTemplateMapper()
getPipelineMock('httpRequest')(_) >> [status: 200]
when:
helper.sendTemplate(webhook, template, values, mapper)
then:
1 * getPipelineMock('httpRequest')(_)
}
}
I'm using gradle and my build.gradle file has
testImplementation 'org.jenkins-ci.plugins:http_request:1.10#jar'
Other steps' tests run perfectly but with this one I always get
java.lang.IllegalStateException: There is no pipeline step mock for [httpRequest].
1. Is the name correct?
2. Does the pipeline step have a descriptor with that name?
3. Does that step come from a plugin? If so, is that plugin listed as a dependency in your pom.xml?
4. If not, you may need to call explicitlyMockPipelineStep('httpRequest') in your test's setup: block.
And when I use explicitlyMockPipelineStep('httpRequest') I get a null pointer exception, because, I presume, the default mock returns a null.
Is there anything I'm missing in the test to get it working? Thanks in advance!!!

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
}
}

Jenkinsfile - JsonSlurper returning a string instead of a map

So I'm trying out a CI build that has a config.json file embedded.
config.json
{
"some_collection": [
{ "foo": "bar" }
]
}
My Jenkinsfile:
import groovy.json.JsonSlurper
node {
bootstrap()
test()
}
def bootstrap() {
stage('bootstrap') {
git([url: "git#github.com:my-user/my-jenkinsfile-repo.git"])
}
}
def test() {
def config = getConfig()
echo "${config}"
echo "${config.class}"
}
#NonCPS
def getConfig() {
new JsonSlurper().parseText(readFile("./config.json")))
}
The echo of the config object shows the json as it is in the file, and the config.class says is a plain old string. I'm expecting the code about to return a Map.
I've tried JsonSlurper and JsonSluperClassic, I've also tried about every way I can think of to restructure the code to be more explicit and I'm running out of ideas.
EDIT: I've tried adding some strong typing:
def getConfig() {
JsonSlurper parser = new groovy.json.JsonSlurper()
def json = readFile("./config.json")
Map parsedJson = parser.parseText(json)
return parsedJson
}
This still causes config.class to return as a String
Handling JSON in Jenkins with the default libraries is a mess. Just use the readJSON step from the Pipeline Utility Steps Plugin. https://github.com/jenkinsci/pipeline-utility-steps-plugin/blob/master/docs/STEPS.md
Yyou can use new File(filename) to get the content of the config.json and pass it to parse() method.
You can just use:
def getConfig() {
def pjson = new groovy.json.JsonSlurper().parse(new File('./config.json'))
assert pjson instanceof Map
pjson
}

Jenkins pipeline MissingMethodException: No signature of method:

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

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