Notice: A non well formed numeric value encountered in /home/srv33763/domains/emilwypych.com/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_formatter.class.php on line 118
Notice: A non well formed numeric value encountered in /home/srv33763/domains/emilwypych.com/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_formatter.class.php on line 119
Notice: A non well formed numeric value encountered in /home/srv33763/domains/emilwypych.com/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_formatter.class.php on line 118
Notice: A non well formed numeric value encountered in /home/srv33763/domains/emilwypych.com/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_formatter.class.php on line 119
Notice: A non well formed numeric value encountered in /home/srv33763/domains/emilwypych.com/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_formatter.class.php on line 118
Notice: A non well formed numeric value encountered in /home/srv33763/domains/emilwypych.com/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_formatter.class.php on line 119
Notice: A non well formed numeric value encountered in /home/srv33763/domains/emilwypych.com/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_formatter.class.php on line 118
Notice: A non well formed numeric value encountered in /home/srv33763/domains/emilwypych.com/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_formatter.class.php on line 119
Working with the Jenkins DSL configuration is quite easy. But we need to remember about best practices and do some work for ourselves. Just to simplify out live. One of these things is creating good code. We should extract repeating code blocks and use them in different places. Let’s assume, that we want to create granular permissions – Alice has permissions only for Job A, but Bob has permissions for both, Job A and Job B. As you see, we have now two jobs and two users. But we don’t know when we will have to add more users. And I have not mentioned what set of permissions our users should have yet. But now we have enough examples. User and set of permissions – let them be our perfect examples of how to create and use constant in Jenkins DSL.
Some theory (and practice)
Ok, so we have the following users:
- Alice Fortesting
- Bob Fortesting
And for that particular example, let assume that we need only two sets of permissions:
Full Job permissions:
- build
- cancel
- discover
- read
- workspace
Read Job Permissions:
- discover
- read
And now imagine, that you have about 50 jobs, which you can’t place inside one loop (too many differences) and you need to use the array with the permissions in each of them. Not encouraging, right? Unfortunately, it’s not the worst thing which can happen… What if there will be a day when you need to CHANGE values? Terrible idea…
Your first interface!
Luckily, we can use a constant in Jenkins DSL! Sound great, right? Yeah, but how we can do that? First thing comes to my mind is a new class with the variables. I bet that it’s one of the first things in your minds too, right? It’s not a bad idea, but also it’s not the best idea. Because, you know, in that class there will be no methods, just only the constants. Moreover, all of them should be public, not private (finally, they should be accessible in all files). So why not to use an interface?
Let’s create a simple interface with the data presented above.
1 2 3 4 5 6 7 8 9 |
package com.example.Dictionaries interface PermissionsDictionary { public final static def FULL_JOB_PERMISSIONS = [ 'hudson.model.Item.Build', 'hudson.model.Item.Cancel', 'hudson.model.Item.Discover', 'hudson.model.Item.Read', 'hudson.model.Item.Workspace' ] public final static def READ_JOB_PERMISSIONS = [ 'hudson.model.Item.Discover', 'hudson.model.Item.Read' ] } |
As you can see, we just create a simple package (up to you what name it will have) and the whole interface creation is not hard. I’m sure that some of you are mad now. 🙂 Why? Because I used “public final static” with regard to a variable defined in the interface. On the other hand, some of you think why did I do that. And what are those magic words? We should start from the beginning. 🙂
What are the public, final and static magic words?
Dear Developers – please forgive me this paragraph. I’m not a developer and I know that some persons that will look for help here, also won’t be developers. I had been a SysOps before I started to walk along the DevOps path, and I know that some topics can be hard to understand for admins and I know that my knowledge in this area is not quite good. If you don’t want to read about modifiers – please go ahead to the next topic. 🙂 But if you want to check whether or not I made a mistake – feel free to leave the feedback in the comment. 🙂
I’ll try to describe these three modifiers in simple words. All objects like classes and their members can use modifiers like public, protected, private, static. These modifiers indicate what and how can use objects. For example, public modifiers indicate that the object can be used anywhere in the code, by all other objects. Private modifier limits method access just only to the same class. In theory of course. Static modifier means that variable is common for the class, not instance of class and method can be accessed without the need for instance creation. And finally, final modifiers indicates that the value of our variable cannot be changed – it’s the final value.
Constants should be accessible from anywhere by each method and class, cannot be changed and cannot be a part of the instance, but the whole class. So we need to use public, final and static modifiers. I hope it’s clear. If you want to deep into this topic, please take a look at the bottom of this article – you will find some sources, mainly the Groovy or Java documentation, where you can expand your knowledge.
I’m sure that Groovy/Java Developers are completely mad since they saw the code with my interface. 🙂 And I’m also sure, that the rest of you absolutely don’t know why! And the reason is simple – I used too many modifiers. We need:
- public
- static
- final
but to be honest, we don’t need to write them down. 🙂 Not all. Static and final modifiers are necessary, but we really don’t need to use “public” in our code, because each method and class in Groovy is public by default. Thanks to that we can write more concise code. Also, we can omit (and even we should) def. According to the Groovy Style Guide:
“When using def in Groovy, the actual type holder is Object (so you can assign any object to variables defined with def, and return any kind of object if a method is declared returning def).”
Also, THIS QUESTION has a fantastic answer what actually def does in the Groovy code and how the code behaves.
How about the better code?
So the final version of our interface is:
1 2 3 4 5 6 7 8 9 |
package com.example.Dictionaries interface PermissionsDictionary { final static FULL_JOB_PERMISSIONS = [ 'hudson.model.Item.Build', 'hudson.model.Item.Cancel', 'hudson.model.Item.Discover', 'hudson.model.Item.Read', 'hudson.model.Item.Workspace' ] final static READ_JOB_PERMISSIONS = [ 'hudson.model.Item.Discover', 'hudson.model.Item.Read' ] } |
But how to use it?
Ok, so we have an interface, but… what now? We’ve just created a constant, now we can use it anywhere we want. As we have constant with the job permissions, we can use it (obviously) for setting up permissions. So take a look at this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import com.example.Dictionaries job("Example job") { steps { shell(""" echo Some text """.stripIndent().trim() ) } configure { it / 'properties' / 'hudson.security.AuthorizationMatrixProperty' { inheritanceStrategy(class: 'org.jenkinsci.plugins.matrixauth.inheritance.InheritGlobalStrategy') for (permissionType in PermissionsDictionary.FULL_JOB_PERMISSIONS) { permission("${permissionType}:USERNAME") } } } } |
More about used loop you can read in my article Jenkins DSL Authorization Matrix loop.
As the result we should receive an XML similar to this one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!-- 1. Example job --> <project> <actions></actions> <description></description> <keepDependencies>false</keepDependencies> <properties> <hudson.security.AuthorizationMatrixProperty> <inheritanceStrategy class='org.jenkinsci.plugins.matrixauth.inheritance.InheritGlobalStrategy'></inheritanceStrategy> <permission>hudson.model.Item.Build:USERNAME</permission> <permission>hudson.model.Item.Cancel:USERNAME</permission> <permission>hudson.model.Item.Discover:USERNAME</permission> <permission>hudson.model.Item.Read:USERNAME</permission> <permission>hudson.model.Item.Workspace:USERNAME</permission> </hudson.security.AuthorizationMatrixProperty> </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> <hudson.tasks.Shell> <command>echo Some text</command> </hudson.tasks.Shell> </builders> <publishers></publishers> <buildWrappers></buildWrappers> </project> |
Of course, it’s not the most beautiful way, but please remember – it’s just an example. 🙂
I know how to create constant in Jenkins DSL – what now?
Now you can use it in your job for simplifying your work and making more clean and understandable code! I know, that many of IT guys start to use such things like Jenkins Job DSL, but they have never worked as a Developer, so some topics are very difficult for them to understand. I’m also not a Developer, so my code needs improvements and I know it. Every day I try to improve myself and at the same time, I would like to share my knowledge.
But if you see any mistake I made in this text, please let me know, so I can fix it. Each improvement is also welcome. 🙂
Hi
where did you put your file with constants?
I tried next structure and it fails with script: 2: unable to resolve class com.example.constants
dsl_scripts/
├── a
│ └── folders.groovy
├── features
│ └── Cloud_Core
│ └── puck_cableco11_CCU_bat_code_vars.groovy
├── src
│ ├── test
│ │ └── groovy
│ │ └── JobScriptsSpec.groovy
│ └── example
│ └── constants
│ └── markers.groovy