How can I access Groovy globals from inside a class? - jenkins

We have a script in our Jenkins shared pipeline code which works along these lines, in src/Utils.groovy:
boolean isBuildNumberEven() {
int number = env.BUILD_NUMBER as int
(number % 2) == 0
}
This script is then used like this from one of the scripts in vars/
Utils utils = new Utils()
if (utils.isBuildNumberEven()) {
// Do something differently
}
The key point is that env apparently can be used with no problem.
Now I want to clean up the code and explicitly declare the class, so I change src/Utils.groovy like so:
class Utils {
boolean isBuildNumberEven() {
int number = env.BUILD_NUMBER as int
(number % 2) == 0
}
}
Now, I get an error about env being accessed as a global while it is not a global.
The Groovy compiler itself obviously knows how to convert a script with no class into a class, but I haven't quite figured out how to do it myself.
Is there a trick to allow me to access those global variables from the class, other than the obvious one of passing them all in via the constructor? Is there some global class somewhere which contains them all perhaps?
(Note that although I have used env for this example, it is not the only one I need to access. So yes, I already converted these to use System.getenv, but then immediately hit some other variable we were using. So there's no easy way out.)

Related

How can I get a custom python type and avoid importing a python module every time a C function is called

I am writing some functions for a C extension module for python and need to import a module I wrote directly in python for access to a custom python type. I use PyImport_ImportModule() in the body of my C function, then PyObject_GetAttrString() on the module to get the custom python type. This executes every time the C function is called and seems like it's not very efficient and may not be best practice. I'm looking for a way to have access to the python custom type as a PyObject* or PyTypeObject* in my source code for efficiency and I may need the type in more than one C function also.
Right now the function looks something like
static PyObject* foo(PyObject* self, PyObject* args)
{
PyObject* myPythonModule = PyImport_ImportModule("my.python.module");
if (!myPythonModule)
return NULL;
PyObject* myPythonType = PyObject_GetAttrString(myPythonModule, "MyPythonType");
if (!myPythonType) {
Py_DECREF(myPythonModule);
return NULL;
}
/* more code to create and return a MyPythonType instance */
}
To avoid retrieving myPythonType every function call I tried adding a global variable to hold the object at the top of my C file
static PyObject* myPythonType;
and initialized it in the module init function similar to the old function body
PyMODINIT_FUNC
PyInit_mymodule(void)
{
/* more initializing here */
PyObject* myPythonModule = PyImport_ImportModule("my.python.module");
if (!myPythonModule) {
/* clean-up code here */
return NULL;
}
// set the static global variable here
myPythonType = PyObject_GetAttrString(myPythonModule, "MyPythonType");
Py_DECREF(myPythonModule);
if (!myPythonType) {
/* clean-up code here */
return NULL;
/* finish initializing module */
}
which worked, however I am unsure how to Py_DECREF the global variable whenever the module is finished being used. Is there a way to do that or even a better way to solve this whole problem I am overlooking?
First, just calling import each time probably isn't as bad as you think - Python does internally keep a list of imported modules, so the second time you call it on the same module the cost is much lower. So this might be an acceptable solution.
Second, the global variable approach should work, but you're right that it doesn't get cleaned up. This is rarely a problem because modules are rarely unloaded (and most extension modules don't really support it), but it isn't great. It also won't work with isolated sub-interpreters (which isn't much of a concern now, but may become more more popular in future).
The most robust way to do it needs multi-phase initialization of your module. To quickly summarise what you should do:
You should define a module state struct containing this type of information,
Your module spec should contain the size of the module state struct,
You need to initialize this struct within the Py_mod_exec slot.
You need to create an m_free function (and ideally the other GC functions) to correctly decref your state during de-initialization.
Within a global module function, self will be your module object, and so you can get the state with PyModule_GetState(self)

Possible to create Graal native function callable from C without isolate?

I'd like to create a library, written in Java, callable from C, with simple method signatures:
int addThree(int in) {
return in + 3;
}
I know it's possible to do this with GraalVM if you do a little dance and create an Isolate in your C program and pass it in as the first parameter in every function call. There is good sample code here.
The problem is that the system I'm writing for, Postgres, can load C libraries and call functions in them, but I would have to create a wrapper function in C that would wrap every function I wanted to expose. This really limits the value of being able to slap something together in Java and use it in Postgres directly. I'd have to do something like this:
int myPublicAddThreeFunction(int in) {
graal_isolatethread_t *thread = NULL;
if (graal_create_isolate(NULL, NULL, &thread) != 0) {
fprintf(stderr, "error on isolate creation or attach\n");
return 1;
}
return SomeClassName_addThree_big_random_string_here(thread, in);
}
Is there a way, in Java alone, to expose a simple C function? I'm thinking I could create the isolate in a static method that gets loaded once on startup, somehow set it as the current isolate, and have the Java method just use it. Haven't been able to figure it out, though.
Also, it would be real nice not to have to append a big random string to every function name.

Using an enum loaded from another Groovy file (Jenkins Pipeline issue)

I have the following Groovy script as part of a Jenkins pipeline
permissions.groovy
enum PermissionType {
ANONYMOUS,
AUTHENTICATED
}
def get_job_permissions(PermissionType permission) {
...
}
return this
I load this file into another Groovy file as part of my Jenkins pipeline and call get_job_permissions passing through one of the enums as a parameter.
pipeline.groovy
def job_permissions = load 'permissions.groovy'
job_permissions.get_job_permissions(job_permissions.PermissionType.AUTHENTICATED)
Jenkins fails on this with the following error (I've verified that in this case 'Script3' is the call to get_job_permissions with the enum parameter).
groovy.lang.MissingPropertyException: No such property: PermissionType for class: Script3
I know the script load and call is correct, as I can change the signature of get_job_permissions to the following, pass through a random string in pipeline.groovy and the call goes through correctly.
def get_job_permissions(def permission) {
...
}
If I don't change the signature, and still pass through a random string, Jenkins fails the build as it can't find the method it thinks I'm calling (which is true, it's not there, it's expecting a PermissionType type).
I've tried a number of different things to expose PermissionType to the calling script
Adding #Field (not legal Groovy)
Changing the enum definition to public def PermissionType (not legal Groovy)
Removing and adding public to the enum definition
Changing the case (though I believe enums need to start with an upper case character?)
None of these solutions allow me to reference the enum type from the calling script, and I understand this is because I'm trying to access a type by referencing it through an instance of the script.
But if I can't do it this way, what is the best way to do it?
Thanks
I managed to get something to work - I certainly know it's probably not the right, or even good, way to do it, but it unblocked me and gave me what I needed.
Rather than define the enum in a script as you normally would
enum PermissionType {
ANONYMOUS,
AUTHENTICATED
}
I created a class containing the enum with member variables initialised to the values within the enum.
permissions.groovy
public class PermissionTypes {
public enum Values {
ANONYMOUS,
AUTHENTICATED
}
public final PermissionTypes.Values ANONYMOUS = PermissionTypes.Values.ANONYMOUS
public final PermissionTypes.Values AUTHENTICATED = PermissionTypes.Values.AUTHENTICATED
}
#Field final PermissionTypes Permissions = new PermissionTypes()
I can then expose an instance of that class in the script, load it as normal and I finally get access to the enum values.
pipeline.groovy
def job_permissions = load 'permissions.groovy'
job_permissions.get_job_permissions(job_permissions.Permissions.AUTHENTICATED)
I think we can all agree this is slightly bonkers, but it gave me what I needed.
Only issues I have with this (which I can live with for now)
You can only load the file ones in a script otherwise you get a duplicate class exception
You can't use the type in an external method, only the values - OK for me since any methods taking in the type are local to the class definition
Would still love to know the right way to do this :)
I ran into this problem recently and found a different solution that looks less hacky.
enum PermissionType {
ANONYMOUS,
AUTHENTICATED
}
def get_job_permissions(PermissionType permission) {
...
}
// Do this before you return out to make the enum available as well
this.PermissionType = PermissionType
return this
I prefer to use:
MyEnumClass.groovy:
package cl.mypackage.utils
class MyEnumClass {
static enum MyEnum {
FOO, BAR, QWERTY
}
}
How to use:
import cl.mypackage.utils.MyEnumClass
def in_some_place() {
def fooEnum = MyEnumClass.MyEnum.FOO
}
Regards

Why we don't need to provide initial value for local variables?

While I was learning suddenly I wondered myself:
why do we have to provide initial values for global(even beyond a class scope) variable but we do not have to do same step with local variables like this? Is there any reason?
if importRequired {
let deleteObjectCount: Int
}
It is allowed, because deleteObjectCount is never been used in your code. And - and this is the difference to global variables - this fact can be checked by the compiler.
You could even do something like:
let importRequired = true
if importRequired {
let deleteObjectCount: Int
deleteObjectCount = 5
print (deleteObjectCount)
}
(e.g. kind-of modify a constant let variable) because the compiler checks that the constant is written only once, and this is done before reading it's value.
In contrast, global variables must be initialized directly, because otherwise the compiler cannot guarantee that they have been so before being initialized (because the could be accessed from anywhere in your program).

Maximum recursion getting shared service

I have defined two classes (Environment and ConfigurationReader). Both are registered as shared dependencies.
The Environment class tries get the current environment, but for this, needs read a configuration file via ConfigurationReader.
The sequence diagram is:
The classes are:
class Environment
{
...
public function resolve()
{
$config = DI::getDefault()->getCfg();
$config->getValue('pepe', 'db_name');
}
...
}
class ConfigurationReader
{
...
public function getValue($aConfig, $aKey)
{
$path = $this->getFile($aConfig);
}
protected function getFile($aConfig)
{
$env = DI::getDefault()->getEnv();
$path = 'config/' . $env->getShortName() . '/' . $aConfig . '.yml';
return $path;
}
...
}
And are registered and created in the index.php:
...
$di = new FactoryDefault();
$di->setShared('env', function() use ($di) {
$env = new Services\Environment($di);
$env->resolve();
return $env;
});
$di->setShared('cfg', function() use ($di) {
return new Services\ConfigurationReader($di);
});
$di->getShared('cfg');
$di->getShared('env');
...
So, PHP crash at $config = DI::getDefault()->getCfg(); and says:
PHP Fatal error: Maximum recursion depth exceeded
Any ideas ?
A couple remarks
You're passing the di to the constructor, but end up getting it statically (DI::getDefault())
regarding the infinite loop, it's because cfg needs env who needs cfg who needs env etc.....
To have the framework automatically injecting the DI into your service you should either implement InjectionAwareInterface (https://docs.phalconphp.com/en/latest/reference/di.html#automatic-injecting-of-the-di-itself) or
extend the Component class (If you need event management too, use Plugin instead of Component ). Have a look at this discussion : https://forum.phalconphp.com/discussion/383/plugin-vs-component-what-s-the-difference-
Regarding your use case, you don't give enough context for an exhaustive answer, but I think you could simplify it as:
ConfigService: Unless you use configs from different env namespaces, you should pass the value of $env->getShortName() value to the service constructor (without getting it from the env service). In our apps the env is determined by nginx based on the domain name or other parameters and passed as an environment variable to php. Also, if you don't have hundreds of config files, and your app heavily relies on them, you should read and parse them once on instantiation and store the configs in the service (as associative array, config objects, or whatever you prefer). Add some cache layer to avoid wasting resource parsing all your files on each request. Phalcons provide The Config component to do so. It comes with file adapters (only ini and associative array format but you could easily implement your own yml adapter). If most of your app config relies on configurable values, that will probably be the first component you want to instantiate (or at least declare in the di). It shouldn't dependencies to other services.
EnvService: You can access your config values by calling the config service (If you have it to extend Component, you can do something like $this->cfg->getValue($key)).

Resources