I have a problem with my DSL job generator:
Processing DSL script seed.groovy
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'com.dabsquared.gitlabjenkins.connection.GitLabApiTokenImpl#4046fd60' with class 'com.dabsquared.gitlabjenkins.connection.GitLabApiTokenImpl' to class 'com.dabsquared.gitlabjenkins.connection.GitLabApiTokenImpl'
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnSAM(DefaultTypeTransformation.java:405)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnNumber(DefaultTypeTransformation.java:319)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType(DefaultTypeTransformation.java:232)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.java:603)
at com.blue.devops.generator.Generator$_closure1.doCall(Generator.groovy:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
I am running a DSL job generator that access gitswarm API, gets the information about available projects and their branches and creates Jenkins jobs.
Interaction with gitswarm api is done via Client class - my gitswarm API client.
Generator.groovy:
class Generator {
SystemCredentialsProvider systemCredentialsProvider = (SystemCredentialsProvider) Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0]
CredentialsStore credentialsStore = (CredentialsStore) systemCredentialsProvider.getStore()
Domain domain = Domain.global()
List<Credentials> credentials = credentialsStore.getCredentials(domain)
Client client
Generator(String tokenId="gitswarm_token") {
credentials.each { Credentials credentials ->
if(credentials.getId() == tokenId) {
GitLabApiTokenImpl token = (GitLabApiTokenImpl) credentials //line 28
client = new Client(apiToken: token.getApiToken().getPlainText())
}
}
}
boolean generateJobs(DslFactory factory) {
ArrayList<Project> supportedProjects = client.getSupportedProjects()
supportedProjects.each { supportedProject ->
handleProject(supportedProject, factory)
}
return true
}
}
Jenkins is running this DSL seed job:
import com.blue.devops.generator.Generator
new Generator().generateJobs(this)
Please help
The Job DSL classloader does not support access to classes provided by other plugins. So you can't import a class from another plugin and can't cast an object to that class.
But Groovy is a dynamic language. You don't need to cast an object to a specific class to be able to access it's members. Groovy will figure that out at runtime. The following example show how to get the API token with using any classes from plugins.
def systemCredentialsProvider = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0]
def credentials = systemCredentialsProvider.credentials
def gitlabCredentials = credentials.find { it.id == 'test' }
def apiToken = gitlabCredentials.apiToken.plainText
Related
I'm not creating a new job.
I want to access a Jenkins secret string binding from inside a job DSL script. I haven't been able to find examples of this.
If I have a secret string binding in Jenkins named "my-secret-string" how do I get the value of that in a DSL script? I want the DSL to make REST calls and other things using secrets I have securely stored in Jenkins.
I cant use credentials('<idCredentials>') because I'm not creating a new job or anything, I want to use those secret values in the DSL script itself.
I don't understand the scenario. You are not creating a new job but you are still inside a job? What does that mean? I understood that you defined a credential - secret text in Jenkinks and you want to access it from a job? This is a standard scenario:
withCredentials([string(credentialsId: 'my-secret-string', variable: 'mySecretStringVar')]){
println mySecretStringVar
}
From Jenkins Console or groovy script epending on where credentials are located:
def getFolderCredsScript(def pipelineFolder, def credId){
def credentialsStore =
jenkins.model.Jenkins.instance.getAllItems(com.cloudbees.hudson.plugins.folder.Folder.class).findAll{it.name.equals(pipelineFolder)}
.each{
com.cloudbees.hudson.plugins.folder.AbstractFolder<?> folderAbs = com.cloudbees.hudson.plugins.folder.AbstractFolder.class.cast(it)
com.cloudbees.hudson.plugins.folder.properties.FolderCredentialsProvider.FolderCredentialsProperty property = folderAbs.getProperties().get(com.cloudbees.hudson.plugins.folder.properties.FolderCredentialsProvider.FolderCredentialsProperty.class)
if(property != null){
for (cred in property.getCredentials()){
if ( cred.id == credId ) {
return "${cred.username}:${cred.password}"
}
}
}
}
}
def getGlobalCredsScript(def credId){
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class, Jenkins.instance, null, null);
for (cred in creds) {
if (cred.id == credId){
return "${cred.username}:${cred.password}"
}
}
}
I found this question when trying to figure out how to set authenticationToken in my jenkins DSL. You can't use withCredential or a credentials call since it only accepts a string. The answer I found is to wrap the build/seed file. It can use withCredential and you pass in the credential as a string like this:
Jenkinsfile.build
withCredentials([
string(credentialsId: 'deploy-trigger-token', variable: 'TRIGGER_TOKEN'),
]) {
jobDsl targets: ".jenkins/deploy_${env.INSTANCE}_svc.dsl",
ignoreMissingFiles: true,
additionalParameters: [
trigger_token: env.TRIGGER_TOKEN
]
}
Then in your dsl file:
pipelineJob("Deploy Service") {
...
authenticationToken (trigger_token)
...
}
So to answer your question, you are correct you can't directly access the credential in your dsl, instead you do it in the seed build file which passes it in as a additionalParameters variable.
I'm creating a Grails (3.3.9) plugin to hold shared back-end code for some internal applications. For some reason, when I run the plugin to test it, my service is not being injected into my controller.
I've started with the default web-plugin profile, created a single domain class called Entry, and run generate-all to create a controller, service, and views. When I try to run my plugin as an application and view a single domain instance, I get the following error:
Cannot invoke method get() on null object. Stacktrace follows:
java.lang.reflect.InvocationTargetException: null
at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:211)
at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:188)
at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException: Cannot invoke method get() on null object
at com.mycompany.internal.EntryController.show(EntryController.groovy:18)
... 14 common frames omitted
The stacktrace takes me to line 18 in my controller:
def show(Long id) {
respond entryService.get(id)
}
This suggests to me that entryService is null.
My domain class looks like this:
class Entry {
String entryCode
String description
}
The controller is as follows:
class EntryController {
EntryService entryService
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond entryService.list(params), model:[entryCount: entryService.count()]
}
def show(Long id) {
respond entryService.get(id)
}
//snipped for brevity
And the service looks like this:
#Service(Entry)
interface EntryService {
Entry get(Serializable id)
//snipped for brevity
}
Based on the Grails plugin documentation, I would expect to be able to run the plugin standalone like any other application, and in normal applications, defining the service as an interface works fine. If I install this plugin to my local maven cache and use it in an application, it works exactly as I would expect; I am able to hit the controller's show endpoint and get back a result from my database.
At one point I tried implementing the service as a class rather than having it be an interface, but then I receive this error:
URI
/entry/index
Class
java.lang.IllegalStateException
Message
null
Caused by
Either class [com.mycompany.internal.Entry] is not a domain class or GORM has not been initialized correctly or has already been shutdown. Ensure GORM is loaded and configured correctly before calling any methods on a GORM entity.
What am I missing about how to set up and run a Grails plugin correctly?
Finally found an answer by digging into the GORM issue: class Authority is not a domain class or GORM has not been initialized correctly or has already been shutdown
Apparently the root of the problem was the need to add compile "org.grails.plugins:hibernate5" to my dependencies block in build.gradle. The weird thing is that the web-plugin profile provides the create-domain-class command, so I would think GORM and Hibernate support would be included by default, but I'm apparently misunderstanding some aspects of how plugins are supposed to work.
Well I am new to Groovy/Grails. I have written a Groovy script that uses RESTClient to make HTTP POST request to JIRA server. The POST request sends a JQL query and receives the result in JSON format. Here's the full code:
import groovyx.net.http.RESTClient;
import groovyx.net.http.HttpResponseDecorator;
import org.apache.http.HttpRequest;
import org.apache.http.protocol.HttpContext;
import org.apache.http.HttpRequestInterceptor;
import groovy.json.JsonSlurper;
import static groovyx.net.http.Method.*
import static groovyx.net.http.ContentType.*
#Grab(value = 'org.codehaus.groovy:groovy-all:2.1.6',
initClass = false)
#Grapes([
#Grab(group = 'org.codehaus.groovy.modules.http-builder',
module = 'http-builder', version = '0.5.2'),
#GrabExclude('org.codehaus.groovy:groovy')
])
// connect to JIRA
def jiraApiUrl = 'http://my-jira.com/rest/api/2/'
def jiraClient = new RESTClient(jiraApiUrl);
// authentication
def basic = 'Basic ' + 'username:password'.bytes.encodeBase64().toString()
jiraClient.client.addRequestInterceptor (
new HttpRequestInterceptor() {
void process(HttpRequest httpRequest,
HttpContext httpContext) {
httpRequest.addHeader('Authorization', basic)
}
})
// http post method
def uriPath = 'search'
def param = [maxResults : 1, jql : '<jql-query>']
def Issues = jiraClient.post(requestContentType : JSON, path : uriPath, body : param)
def slurpedIssues = new JsonSlurper().parseText(Issues.data.toString())
println Issues.data.total
I need to migrate this script to a Grails app. Any suggestions as to how to do the same?
Define dependencies in BuildConfig (except the groovy dependency)
copy script contents to a Service
Possible extension:
use the grails rest plugin or grails rest-client-builder plugin instead of http-builder
Putting the logic into Service object will give you the ability to do dependency injection, which is native to grails services.
Also, you should consider using AsyncHTTPBuilder if your app has many users trying to make requests.
I strongly believe that the service response will be directly rendered to JSON
//your controller
class AbcController{
//your action
def save() {
render(abcService.save(params) as JSON)//your service response now been rendered to JSON
}
}
//your service class class AbcService {
def save(params){
....
return something
}
}
I've deployed my grails application on weblogic server. The front end is grails and backend is using spring and hibernate (not using gorm).
I am seeing the following error when I try to access a static dummy page:
<Dec 11, 2011 5:15:02 AM EST> <Error> <HTTP> <BEA-101362> <[ServletContext#450252357[app: spec-version:2.5]] could not deserialize the request scoped attribute with name: "userService"
java.io.NotSerializableException: org.codehaus.groovy.grails.commons.spring.ReloadAwareAutowireCapableBeanFactory
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
at org.springframework.transaction.interceptor.TransactionInterceptor.writeObject(TransactionInterceptor.java:186)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Truncated. see log file for complete stacktrace
><[ServletContext#450252357[app: spec-version:2.5]] could not deserialize the request scoped attribute with name: "referenceDataService"
java.io.NotSerializableException: org.codehaus.groovy.grails.commons.spring.ReloadAwareAutowireCapableBeanFactory
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
at org.springframework.transaction.interceptor.TransactionInterceptor.writeObject(TransactionInterceptor.java:186)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Truncated. see log file for complete stacktrace
>
Here are the action & controller classes:
class UserAdminController {
def messageSource
def userService
def referenceDataService
def manageUsers = {
render(view: "manageUsers")
}
def showCreateUserAdmin = {
//code goes here
}
}
The reason for writing a dummy action is that the user shouldn't call the gsp page using manageUsers.gsp.
How can I fix this issue?
I'm creating a grails service that will interact with a 3rd party REST API via a Java library. The Java library requires credentials for the REST API by means of a url, username and password.
I'd like to store these credentials in configuration/Config.groovy, make them available to a service and ensure that credentials are available to the service before it requires them.
I appreciate that grailsApplication.config is available to controllers and that through a method of a service the relevant config values can be provided to the service, such as this:
package example
class ExampleController {
def exampleService
def index = { }
def process = {
exampleService.setCredentials(grailsApplication.config.apiCredentials)
exampleService.relevantMethod()
}
}
package example
import com.example.ExampleApiClient;
class ExampleService {
def credentials
def setCredentials(credentials) {
this.credentials = credentials
}
def relevantMethod() {
def client = new ExampleApiClient(
credentials.baseUrl,
credentials.username,
credentials.password
)
return client.action();
}
}
I feel this approach is slightly flawed as it depends on a controller calling setCredentials(). Having the credentials made available to the service automagically would be more robust.
Is either of these two options viable (I currently not familiar enough with grails):
Inject grailsApplication.config.apiCredentials into the service in the controller when the service is created?
Provide some form of contructor on the service that allows the credentials to be passed in to the service at instantiation time?
Having the credentials injected into the service is ideal. How could this be done?
The grailsApplication object is available within services, allowing this:
package example
import com.example.ExampleApiClient;
class ExampleService {
def grailsApplication
def relevantMethod() {
def client = new ExampleApiClient(
grailsApplication.config.apiCredentials.baseUrl
grailsApplication.config.apiCredentials.username,
grailsApplication.config.apiCredentials.password
)
return client.action();
}
}
Even though grailsApplication can be injected in services, I think services should not have to deal with configuration because it's harder to test and breaks the Single Responsibility principle. Spring, on the other side, can handle configuration and instantiation in a more robust way. Grails have a dedicated section in its docs.
To make your example work using Spring, you should register your service as a bean in resources.groovy
// Resources.groovy
import com.example.ExampleApiClient
beans {
// Defines your bean, with constructor params
exampleApiClient ExampleApiClient, 'baseUrl', 'username', 'password'
}
Then you will be able to inject the dependency into your service
class ExampleService {
def exampleApiClient
def relevantMethod(){
exampleApiClient.action()
}
}
In addition, in your Config.groovyfile, you can override any bean property using the Grails convention over configuration syntax: beans.<beanName>.<property>:
// Config.groovy
...
beans.exampleApiClient.baseUrl = 'http://example.org'
Both Config.groovy and resources.groovy supports different environment configuration.
For contexts where you can't inject the grailsApplication bean (service is not one of those, as described by Jon Cram), for example a helper class located in src/groovy, you can access it using the Holders class:
def MyController {
def myAction() {
render grailsApplication == grails.util.Holders.grailsApplication
}
}
The best options are (as from grails docs):
1 - Using Spring #Value annotation
import org.springframework.beans.factory.annotation.Value
class WidgetService {
int area
#Value('${widget.width}')
int width
def someServiceMethod() {
// this method may use the width property...
}
}
2 - Having your class implement GrailsConfigurationAware
import grails.config.Config
import grails.core.support.GrailsConfigurationAware
class WidgetService implements GrailsConfigurationAware {
int area
def someServiceMethod() {
// this method may use the area property...
}
#Override
void setConfiguration(Config co) {
int width = co.getProperty('widget.width', Integer, 10)
int height = co.getProperty('widget.height', Integer, 10)
area = width * height
}
}