Jenkins pipeline - change class name of loaded scripts - jenkins-pipeline

I have a Jenkins Pipeline project which loads several Groovy scripts. When I run this pipeline, Jenkins names these scripts' classes Script1, Script2, and so on. These names are displayed when replaying a build. They also appear on exception stack traces. I find this confusing, especially when there is more than a couple of scripts.
Is there any way of setting these names from the pipeline or -preferably- from within the scripts themselves? So far I tried manipulating the scripts' metaClass:
this.metaClass.name = 'Foo' //fails, doesn't find metaClass property
this.class.metaClass.name = 'Foo' //doesn't fail but has no apparent effect
this.class.metaClass.simpleName = 'Foo' //idem
this.class.metaClass.canonicalName = 'Foo' //idem
NOTE: I am well aware of Jenkins shared libraries. This question is meant to focus on loaded scripts alone.

No, there is currently no way to change the generated class name for a loaded script.
The name generation comes from the load step implementation class LoadStepExecution.
String clazz = execution.getNextScriptName(step.getPath());
In CpsFlowExecution, the script name is generated from the calling generateScriptName() on the shell which is a CpsGroovyShell. This invocationand removes the .groovy suffix.
public String getNextScriptName(String path) {
return shell.generateScriptName().replaceFirst("[.]groovy$", "");
}
The CpsGroovyShell generates the class name, which is where the Script1.groovy, Script2.groovy, etc. get created from
#Override
protected synchronized String generateScriptName() {
if (execution!=null)
return "Script" + (execution.loadedScripts.size()+1) + ".groovy";
else
return super.generateScriptName();
}

Maybe this will be of some help to some users. By chance I managed to force the name of the script class when explicitly creating a class inside the script, like:
class MyOwnScriptClass {
def someClassMember() {
}
}
return new MyOwnScriptClass()
After loading that file it shows me MyOwnScriptClass as class name - for the object which is returned by that script - not the script itself. However for my purposes this is sufficient.

Related

Terraform: Need to pass a bash script to 3 different Launch templates's userdata

I am trying to pass to 3 AWS launch template userdata a bash script. This script calls other scripts from Github depending on a specific variable. Since each launch template must call different scripts what is the best way to accomplish it. I am currently trying to configure a data source template_file but I canĀ“t find a way to do what I need.
This is a piece of the bash script where I put a variable that need to change its value depending on which launch template is being built every time:
#------------------------------------------------------------------------------------------
# Define here scripts (separated with 1 space) that will be executed on first run:
AMI_SCRIPTS="ami_base_lynis.sh ${ami_script}"
#------------------------------------------------------------------------------------------
download_and_run_scripts
This is the template file data source:
data "template_file" "AMIs"{
template = "${file("../AMIs/s1_aws_userdata.sh")}"
vars = {
ami = var.dci_appserver_ami
}
}
And this is the user data attribute:
user_data_base64 = base64encode(data.template_file.AMIs.rendered)
This is not working for me as it will replace the variable has the same value for all 3 launch templates. How can I assign each time a different value?
The syntax you used for user_data_base64 tells me that you're using Terraform v0.12 or later, so you should no longer use template_file as shown in the template_file documentation:
In Terraform 0.12 and later, the templatefile function offers a built-in mechanism for rendering a template from a file. Use that function instead, unless you are using Terraform 0.11 or earlier.
Because of that, I'm going to answer using the templatefile function instead.
Inside each of your launch template resource blocks, you can call templatefile with different values for the template variables in order to get a different result each time:
resource "aws_launch_template" "example1" {
# ...
user_data = base64encode(templatefile("${path.module}/../AMIs/s1_aws_userdata.sh", {
ami = var.dci_appserver_ami
ami_script = "script1.sh"
}))
}
resource "aws_launch_template" "example2" {
# ...
user_data = base64encode(templatefile("${path.module}/../AMIs/s1_aws_userdata.sh", {
ami = var.dci_appserver_ami
ami_script = "script2.sh"
}))
}
resource "aws_launch_template" "example3" {
# ...
user_data = base64encode(templatefile("${path.module}/../AMIs/s1_aws_userdata.sh", {
ami = var.dci_appserver_ami
ami_script = "script3.sh"
}))
}
You could in principle factor out constructing the templates into a local value if you want to do it more systematically, but since your question didn't include any indication that you are doing anything special with the launch templates here I've just written the simplest possible approach, where each launch template has its own template-rendering expression.

Terraform lambda_function_association for CloudFront as optional list within a module

we have our static stack (CloudFront, S3, ..) defined as a configurable module for different projects. Now some of them need edge lambdas and I wanted to make them configurable (and optional(!)), too.
We are using the module as following:
module "static" {
..
lambda_function_associations = [
{
event_type = "viewer-request"
lambda_arn = "${aws_lambda_function.onex_lambda_viewer_req.qualified_arn}"
},
{
event_type = "viewer-response"
lambda_arn = "${aws_lambda_function.onex_lambda_viewer_res.qualified_arn}"
},
]
..
}
and the default cache behaviour of CloudFront is defined as the following:
default_cache_behavior {
..
lambda_function_association = ["${var.lambda_function_associations}"]
..
}
and our variable within the module:
variable "lambda_function_associations" {
type = "list"
default = []
}
Applying this stack I get:
Error: module.static.aws_cloudfront_distribution.web: "default_cache_behavior.0.lambda_function_association.0.event_type": required field is not set
Error: module.static.aws_cloudfront_distribution.web: "default_cache_behavior.0.lambda_function_association.0.lambda_arn": required field is not set
Is there no way to make them work optionally? I really dont want to duplicate the whole stack when adding an edge lambda.
Apparently something like this works for lb_health_check configuration blocks:
https://github.com/hashicorp/terraform/issues/17292#issuecomment-393984861
Thanks in advance!
I recently stumbled upon the same issue. This is caused by a terraform limitation, which prevents us from passing dynamic values to a nested block inside a module.
The only workaround I found was duplicating the resource declaration and creating one of the resources based on a condition in the count variable (pass a static variable here, e.g. associate_lambda_function).
You can find more details and an example in this gitlab snippet

How to create local variables at runtime for any keyword test in TestComplete

I need to create a set of local variables at the beginning of a Keyword test and then use them later while executing the Test.
Is there any possibility to create local variables dynamically as like project variables which can be created dynamically.
Project.variables.<variable_name> = "project_variable_value"
in the similar fashion can we create any variable associated to any keyword test
Keywordtests.<generic_keyword_test_name>.variables.<variable_name> = "local_variable_value"
Sure, you can do this. Please see this example:
function Test11()
{
if (KeywordTests.Test1.Variables.VariableExists("MyVariable") == false) {
KeywordTests.Test1.Variables.AddVariable("MyVariable", "String");
}
KeywordTests.Test1.Variables.MyVariable = "test value";
Log.Message(KeywordTests.Test1.Variables.MyVariable);
}
Information on the AddVariable method can be found in the AddVariable Method help topic.

puppet variables overriding fails

I am a newbie on puppet and met some issues as subject, googled for some time but failed with an matched answer. Mys issues is as:
I defined global variables $puppetserver in /etc/puppet/manifests/site.pp as below:
case $domain {
/domain2/:{
$puppetserver = "puppetserver2"
include migrate
}
default:{
$puppetserver = "puppetserver3"
}
}
in node definition of the servers in domain2 in manifests/labs/domain2/nodes.pp
node 'server1.domain2.com' {
$puppetserver = "puppetserver3"
}
the migrate module is used for puppet migration, got from internet as below:
in /etc/puppet/modules/migrate/manifests/config.pp
class migrate::config {
if $puppetserver == undef {
fail('You must define the targeted Puppet master to perform the migration')
}
augeas { 'puppet.conf.migrate':
context => '/files/etc/puppet/puppet.conf/main',
changes => [
"set server $puppetserver",
]
}
}
Since the node 'server1.domain2.com' can match the domain2 setting in site.pp, so it applies the migrate module,what I expected is: it should get the 'puppetserver3' for $puppetserver defined in node block and then be updated in '/etc/puppet/puppet.conf' by the Augeas, but actual result is : it use 'puppetserver2' which was defined in site.pp. I cannot figure out why overriding is not working. Can you kindly help to check what's wrong?
And as a test:
When I tried to move the 'include migrate' module from site.pp to node 'server1.domain2.com' {} block of nodes.pp, it can work as expected.
it seems some order when puppet applying manifests, but what I got is that local scope variables will always overrides the variables, is that correct?
Thanks a lot for your kindly help.
When you include a class at top scope as you do, no node block is in scope during its evaluation. That's a good reason to avoid such shenanigans.
Put the include statement inside the node block, use an ENC to designate the class for inclusion, or maybe use hiera_include() inside your node block to include it indirectly. Alternatively, use hiera at top scope to set the correct value for the $puppetserver variable, and thereby take variable shadowing out of the picture.

Does anyone know how to stop T4 (tt) templates from regerating every single file? Is there a way to flag certain files?

So the tt templates will regenerate every file whenever you save. Now, great, it generates files. However, I am making partial classes to extend other classes, but I only need the files that dont already exist for me generated. The ones that exist, I'd like to preserve. So far, I am finding not one solid solution googling the globe...
In my code below, the exception for finding existing files doesnt matter, because the template will start by deleting all files first. Then it regenerates.
It there a method like "onsave" that I can override?
// BEGIN CODE TO GENERATE EXTENSIONS
<#
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
{
string fileName = entity.Name + ".Extension.cs";
string filePath = this.Host.TemplateFile.Substring(0,this.Host.TemplateFile.LastIndexOf(#"\"));
filePath = filePath + #"\Extensions\" + fileName;
if((File.Exists(filePath) && PreserveExistingExtensions == false) || !File.Exists(filePath))
{
fileManager.StartNewFile(fileName);
BeginNamespace(namespaceName, code);
bool entityHasNullableFKs = entity.NavigationProperties.Any(np => np.GetDependentProperties().Any(p=>ef.IsNullable(p)));
#>
<#=Accessibility.ForType(entity)#>
<#=code.SpaceAfter(code.AbstractOption(entity))#>partial class
<#=code.Escape(entity)#><#=code.StringBefore(" : ", code.Escape(entity.BaseType))#>
{
}
<#
EndNamespace(namespaceName);
}
}
fileManager.Process();
#>
I do something similar (partial classes) where I have one that is always generated, but the custom one will only be generated if it doesn't exist. This second one is created as starting class for customizations. I'll output two files like so:
MyClass.generated.cs
MyClass.cs
MyClass.cs will never be recreated, unless it doesn't exist. MyClass.generated.cs will always be recreated.
I use the T4toolbox to do this, Oleg Sych has actually made this quite easy.
You can check out some sample T4 Templates I built here. Specifically have a look at this one, it's a good example for generated partial classes where one needs to be created every time, and one is only created if it doesn't exist.
The main thing to look at is this line in the code:
var requestBaseMessageCustom = new MessageTemplate(rootNamespace, serviceName + "Request");
requestBaseMessageCustom.Output.File = "Messages/" + serviceName + "Request.cs";
requestBaseMessageCustom.Output.PreserveExistingFile = true;
requestBaseMessageCustom.Render();
Notice the property called PreserveExistingFile, that's the key.

Resources