In the Grails configuration how do I access variables in other environments? - grails

If I have a list environment variable in grails in one environment, how do I modify it on the cascade to other environments?
For instance, in my quartz configuration I added the following,
quartz {
// other variables remove for clarity
whiteListedJobs = [ Job1.class, Job2.class, Job3.class ]
}
The quartz is not specific to any single environment. But in my environment specific configurations I'd like to override this list to include more jobs for certain environments.
Something like,
environments {
development {
quartz {
whiteListedJobs = whiteListedJobs + Job4.class
}
}
}
But unfortunately this doesn't work. My code that tries to read the quartz config variable as a list throws Cannot cast object '{}' with class 'groovy.util.ConfigObject' to class 'java.util.List', which suggests to me that it isn't working.
What is the correct way to do this?

The problem is that the groovy ConfigSlurper doesn't provide a way to read a config entry at a different level during the parsing process. However, it is parsed as a Groovy script, so you can use local variables to avoid completely rewriting entries:
def defaultJobs = [ Job1.class, Job2.class, Job3. class ]
quartz {
whiteListedJobs = defaultJobs
}
environments {
development {
quartz {
whiteListedJobs = defaultJobs + Job4.class
}
}
}

Related

Providing different values in Jenkins dsl configure block to create different jobs

I need my builds to timeout at a specific time (deadline) but currently Jenkins dsl only support the "absolute" strategy. So I tried to write the configure block but couldn't create jobs with different deadline values.
def settings = [
[
jobname: 'job1',
ddl: '13:10:00'
],
[
jobname: 'job2',
ddl: '14:05:00'
]
]
for (i in settings) {
job(i.jobname) {
configure {
it / buildWrappers << 'hudson.plugins.build__timeout.BuildTimeoutWrapper' {
strategy(class:'hudson.plugins.build_timeout.impl.DeadlineTimeOutStrategy') {
deadlineTime(i.ddl)
deadlineToleranceInMinutes(1)
}
}
}
steps {
// some stuff to do here
}
}
}
The above script gives me two jobs with the same deadline time(14:05:00):
<project>
<actions></actions>
<description></description>
<keepDependencies>false</keepDependencies>
<properties></properties>
<scm class='hudson.scm.NullSCM'></scm>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers></triggers>
<concurrentBuild>false</concurrentBuild>
<builders></builders>
<publishers></publishers>
<buildWrappers>
<hudson.plugins.build__timeout.BuildTimeoutWrapper>
<strategy class='hudson.plugins.build_timeout.impl.DeadlineTimeOutStrategy'>
<deadlineTime>14:05:00</deadlineTime>
<deadlineToleranceInMinutes>1</deadlineToleranceInMinutes>
</strategy>
</hudson.plugins.build__timeout.BuildTimeoutWrapper>
</buildWrappers>
</project>
I found this question but still couldn't get it to work.
You can use the Automatic Generated API
The generated DSL is only supported when running in Jenkins, e.g. it is
not available when running from the command line or in the Playground.
Use The Configure Block to generate custom config elements when not
running in Jenkins.
The generated DSL will not work for all plugins, e.g. if a plugin does
not use the #DataBoundConstructor and #DataBoundSetter annotations to
declare parameters. In that case The Configure Block can be used to
generate the config XML.
Fortunately the Timeout plugin support DataBoundConstructors
#DataBoundConstructor
public DeadlineTimeOutStrategy(String deadlineTime, int deadlineToleranceInMinutes) {
this.deadlineTime = deadlineTime;
this.deadlineToleranceInMinutes = deadlineToleranceInMinutes <= MINIMUM_DEADLINE_TOLERANCE_IN_MINUTES ? MINIMUM_DEADLINE_TOLERANCE_IN_MINUTES
: deadlineToleranceInMinutes;
}
So you should be able to do something like
def settings = [
[
jobname: 'job1',
ddl: '13:10:00'
],
[
jobname: 'job2',
ddl: '14:05:00'
]
]
for (i in settings) {
job(i.jobname) {
wrappers {
buildTimeoutWrapper {
strategy {
deadlineTimeOutStrategy {
deadlineTime(i.ddl)
deadlineToleranceInMinutes(1)
}
}
timeoutEnvVar('WHAT_IS_THIS_FOR')
}
}
steps {
// some stuff to do here
}
}
}
There is an extra layer in BuildTimeoutWrapper which houses the different strategies
When using nested classes you need to set the first letter of the class to lowercase
EDIT
You can see this in your own Jenkins install by using the 'Job DSL API Reference' link in a jobs page
http://<your jenkins>/plugin/job-dsl/api-viewer/index.html#method/javaposse.jobdsl.dsl.helpers.wrapper.WrapperContext.buildTimeoutWrapper
I saw very similar behaviour to this in a Jenkins DSL groovy script.
I was looping over a List of Maps in a for each, and I also have a configure closure like your example.
The behaviour I saw was that the Map object in the configure closure seemed to be the same for all iterations of the for each loop. Similar to how you are seeing the same deadline time.
I was actually referencing the same value in the Map both inside and outside the configure closure and the DSL was outputting different values. Outside the configure closure was as expected, but inside was the same value for all iterations.
My solution was just to use a variable to reference the Map value and use that both inside and outside the configure closure, when I did that, the value was consistent.
For your example (just adding a deadlineValue variable, and setting it outside the configure closure):
for (i in settings) {
def deadlineValue = i.ddl
job(i.jobname) {
configure {
it / buildWrappers << 'hudson.plugins.build__timeout.BuildTimeoutWrapper' {
strategy(class:'hudson.plugins.build_timeout.impl.DeadlineTimeOutStrategy') {
deadlineTime(deadlineValue)
deadlineToleranceInMinutes(1)
}
}
}
steps {
// some stuff to do here
}
}
}
I would not expect this to make a difference, but it worked for me.
However I agree as per the the other solution it is better to use buildTimeoutWrapper, so you can avoid using the configure block.
See: <Your Jenkins URL>/plugin/job-dsl/api-viewer/index.html#path/javaposse.jobdsl.dsl.DslFactory.job-wrappers-buildTimeoutWrapper-strategy-deadlineTimeOutStrategy for more details, obviously you'll need the Build Timeout plugin installed.
For my example I still needed the configure closure for the MultiJob plugin where some parameters were still not configurable via the DSL api.

Reusing Grails variables inside Config.groovy

In my Config.groovy I have:
// Lots of other stuff up here...
environments {
development {
myapp.port = 7500
}
production {
myapp.port = 7600
}
}
fizz {
buzz {
foo = "Port #${myapp.port}"
}
}
When I run my app via grails -Dgrails.env=development run-app, my web app spins up without errors, but then at runtime I see that the value of fizz.buzz.foo is "Port #[:]". I would expect it to be "Port #7500".
Why isn't Grails seeing my var?
You could probably get away with this if myapp.port were not in an environments block but that's a side effect of the way Config.groovy is processed rather than being intentional. And if you were to override myapp.port in an external config file then fizz.buzz.foo would still end up with the value from Config.groovy, not the override from the external.
You could make it a late-binding GString using a closure to pull the value from grails.util.Holders.config when fizz.buzz.foo is referenced rather than when it is defined:
foo = "Port #${-> Holders.config.myapp.port}"
This is different from "Port #${Holders.config.myapp.port}" which would attempt to access the config at the point where Config.groovy is being parsed.
If the value you're defining here is one that will ultimately end up defining a property of a Spring bean (for example many of the spring-security-core plugin configuration options become bean properties) then you may be able to do
foo = 'Port #${myapp.port}'
with single rather than double quotes. This causes the resulting config entry to contain the literal string ${myapp.port}, which will be resolved against the config by the Spring property placeholder mechanism when it is used as a bean property value.
Another way is to simply use variables within your config file like this:
def appPort = 7500
environments {
production {
appPort = 7600
myapp.port = appPort
}
}
fizz {
buzz {
foo = "Port #$appPort"
}
}
And also, you don't need to send the -Dgrails.environment=development when you execute the run-app, it's the default one.

Adding an extra config property to a grail target

In my config.groovy my dev environment is...
development {
...
grails.resources.mappers.yuicssminify.disable = true
grails.resources.mappers.yuijsminify.disable = true
grails.resources.mappers.hashandcache.disable = true
grails.resources.mappers.zip.disable = true
}
When I run grails dev war from jenkins I want an extra property in some cases. This property is:
grails.plugin.jbossas.removeLog4jJars = false
I cannot alter the development environment in the Config.groovy. Is there any way I can pass in this extra property?
I think the cleanest solution would be a new environment for your jenkins:
jenkins {
grails.plugin.jbossas.removeLog4jJars = false
}
If this is not an option (like mentioned in the comments) it is also possible to move this configuration value into a system specific property file:
grails.config.locations = ["file:/home/user/my-config.properties"]
Within my-config.properties on the jenkins system you can do:
grails.plugin.jbossas.removeLog4jJars = false
The my-config.properties files of other systems stay empty (I think they don't even need to exist).
If this is also not an option you can do something (not so nice) like this:
development {
if (MyUtils.isJenkins()) {
grails.plugin.jbossas.removeLog4jJars = false
}
...
}
Within MyUtils.isJenkins() you have to determine on which system your application is running. For example:
class MyUtils {
public static boolean isJenkins() {
def env = System.getenv()
return env[IS_JENKINS]
}
}
This example checks if the system variable IS_JENKINS is set (you can add other ways to determine on which system you are running like)

window/Linux Specific filesystem properties in Grails

I want to add linux based or windows based system properties in Grails as my app needs to run in the both. I know that we can add grails.config.locations location specified in Config. groovy.
But I need the if and esle condition for the file to be picked.
the problem is config.grrovy has userHome grailsHome appName appVersion
I would need something like osName.
Either I can go ahead with syetm.properties or if soembody can tell me how these (only) properties are availble in Config.groovy (through DefaultGrailsApplication or otherwise. that woyuld be great.
Also, somewjhat moer elegant would be if where I need those properties I make my service as user-defined-spring-bean. Would that be right and feasible approach?If yes, some example
You could do something like this in your Config.groovy:
environments {
development {
if (System.properties["os.name"] == "Linux") {
grails.config.locations = [ "file:$basedir/grails-app/conf/linux.properties" ]
} else {
grails.config.locations = [ "file:$basedir/grails-app/conf/windows.properties" ]
}
}
...
}
Alternatively, for a service based approach, you could bundle up all the OS specific behavior into implementations of service interface. For example:
// OsPrinterService.groovy
interface OsPrinterService {
void printOs();
}
// LinuxOsPrinterService.groovy
class LinuxOsPrinterService implements OsPrinterService {
void printOs() { println "Linux" }
}
// WindowsOsPrinterService.groovy
class WindowsOsPrinterService implements OsPrinterService {
void printOs() { println "Windows" }
}
Then instantiate the correct one in grails-app/conf/spring/resources.groovy like so:
beans = {
if (System.properties["os.name"] == "Linux") {
osPrinterService(LinuxOsPrinterService) {}
} else {
osPrinterService(WindowsOsPrinterService) {}
}
}
Then the correct service will be automatically injected into your objects by spring.
Create a custom enviorenment for both Windows and Linux. Something like the following should work if placed in config.groovy
environments {
productionWindows {
filePath=c:\path
}
productionLinux {
filePath=/var/dir
}
}
You should then be able to use the grails config object to get the value of filePath reguardless of weather your on Windows or Linux. For more details on this see section 3.2 of
http://www.grails.org/doc/1.0.x/guide/3.%20Configuration.html If you wanted to create a war file to run on Linux you would execute the following command.
grails -Dgrails.env=productionLinux war
And then to get a file path you stored in config.groovy for the specific environment your running in.
def fileToOpen=Conf.config.filePath
fileToOpen will contain the value you assigned to filePath in your config.groovy based on the environment your currently running as, so when running with productionLinux as the environment it will contain the value /var/dir

Configuration of Grails plugin

I'm developing my first Grails plugin. It has to access a webservice. The Plugin will obviously need the webservice url. What is the best way to configure this without hardcoding it into the Groovy classes? It would be nice with different config for different environments.
You might want to Keep It Simple(tm). You may define the URL directly in Config.groovy -including per-environment settings- and access it from your plugin as needed using grailsApplication.config (in most cases) or a ConfigurationHolder.config object (See further details in the manual).
As an added bonus that setting may also be defined in standard Java property files or on other configuration files specified in grails.config.locations.
e.g. in Config.groovy
// This will be the default value...
myPlugin.url=http://somewhe.re/test/endpoint
environments {
production {
// ...except when running in production mode
myPlugin.url=http://somewhe.re/for-real/endpoint
}
}
later, in a service provided by your plugin
import org.codehaus.groovy.grails.commons.ConfigurationHolder
class MyPluginService {
def url = ConfigurationHolder.config.myPlugin.url
// ...
}
If its only a small (read: one item) config option, it might just be easier to slurp in a properties file. If there are some number of configuration options, and some of them should be dynamic, i would suggest doing what the Acegi Security plugin does - add a file to /grails-app/conf/plugin_name_config.groovy perhaps.
added bonus is that the user can execute groovy code to compute their configuration options (much better over using properties files), as well as being able to do different environments with ease.
check out http://groovy.codehaus.org/ConfigSlurper , which is what grails internally use to slurp configs like config.groovy.
//e.g. in /grails-app/conf/MyWebServicePluginConfig.groovy
somePluginName {
production {
property1 = "some string"
}
test {
property1 = "another"
}
}
//in your myWebServicePlugin.groovy file, perhaps in the doWithSpring closure
GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader())
ConfigObject config
try {
config = new ConfigSlurper().parse(classLoader.loadClass('MyWebServicePluginConfig'))
} catch (Exception e) {/*??handle or what? use default here?*/}
assert config.test.property1.equals("another") == true

Resources