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.
Related
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.
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
I'm interested in gathering the key value for any network_interfaces_*_state:up attribute's running on the host with the chef-client being run on. So any network interface with a state 'up' attribute.
I have a template containing a configuration file, in which I need to gather the active network devices, using the above chef attribute. I've tried writing a few things within the default recipe file, such as:
template '/etc/foo.conf' do
....
variables ({
netdev: search(node, 'network_interfaces_*_state:up').each {r |r| puts "#{r['network']['interfaces'].select { |i,j| j['state'] == 'up' }.keys } " }
})
end
So there are two things that are obviously wrong.
Running this as knife exec -E "......" returns the interface name for state:up on all nodes. I only want it from the current node that the chef-client is being run on.
chef-client is returning an undefined method 'search' for Chef::Resource::Template, tracing back to the 'netdev' variable as posted above.
I'm unfamiliar with Ruby and using Chef at this level, and was really hoping that I could get help with understanding two things. How do I pull a attribute value from a local host, and how the heck can I write this into a recipe/cookbook?
So what you need is the first 'up' interface, assuming the loopback interface should be avoided, this should do:
template '/etc/foo.conf' do
....
variables ({
netdev: node['network']['interfaces'].select { |i,j| j['state'] == 'up' && i != 'lo' }.keys[0]
})
end
The main idea is to filter the interfaces hash on the interface state and name, keep the keys and take the first one of the resulting array.
Previous answer kept for information.
Attributes are indexed and flattened so you may search for just state:up but may find other attributes named state.
Using the flattened version you could do:
knife node search 'network_interface_*_state:up' -a network.interfaces
This is derived from the examples of nested fields in the documentation linked above.
In case you wish to get each interface up for each node you can play with the search and a little of ruby with knife exec like this :
knife exec -E "nodes.search('network_interfaces_*_state:up').each { |n| puts \"#{n} #{n['network']['interfaces'].select { |i,j| j['state'] == 'up' }.keys } \" }"
node[xxxxxxx] ["eth1", "eth2", "eth3", "usb0"]
node[yyyyyyy] ["docker0"]
node[zzzzzzz] ["eth1", "eth2", "eth3", "usb0"]
The idea is to search for nodes with up interfaces and for each filtering the interfaces whose property (j in select block as they are a hash within a hash) state is up and then keep only the keys of the resulting filtered hash which are the interfaces with state up. (side note my examples above were done with state:down to limit the results)
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.
I'm writing a chef recipe which simply creates a database config file, but I'm stumped simply access the attributes. I have a few PHP applications being deployed to each instance, and OpsWorks uses the same recipes for everyone, so I have a few different settings in the attributes file.
attributes/database-settings.rb
# API
default[:api][:path] = 'app/config/database.php';
default[:api][:host] = 'test';
default[:api][:database] = 'test';
default[:api][:username] = 'test';
default[:api][:password] = 'test';
recipes/database-settings.rb
Chef::Log.info("Database settings!");
node[:deploy].each do |application, deploy|
if node.has_key?(application)
Chef::Log.info("Application: #{application}");
path = node["api"]["path"]; # ERROR HAPPENING HERE
Chef::Log.info("Path: #{path}");
template path do
source "database.erb"
mode 0440
variables({
:host => node["api"]["host"],
:database => node["api"]["database"],
:username => node["api"]["username"],
:password => node["api"]["password"]
})
end
end
end
The error I'm getting is no implicit conversion of String into Integer. I've tried creating and accessing the settings in every way I can think of, such as...
node[:api][:path] # no implicit conversion of Symbol into Integer
node['api']['path'] # no implicit conversion of String into Integer
node[:api].path # undefined method `path' for #<Chef::Node::ImmutableArray:0x007fa4a71086e8>
node[application][:path] # no implicit conversion of Symbol into Integer
I'm sure there's something very obvious I'm doing wrong here, but I've tried everything I can think of an I just can't seem to find any way of getting this to work?! Ideally I'd like to use a variable where I can "api", but using an if/else wouldn't be too terrible for 3 apps...
That is a common error seen when you try to access an object thinking it is a hash, but is actually an array. In fact, from one of your errors, it can be read that node["api"] is a Chef::Node::ImmutableArray.
Ok so the problem wasn't really that I was accessing the config wrongly, it was that the different attribute files were all being merged into a single config and I didn't realise this.
I had these config files...
attributes/database_settings.rb
default[:api][:path] = 'app/config/database.php';
default[:api][:username] = 'example';
attributes/writable_directories.rb
default[:api] = ['public/uploads', 'storage/cache'];
When I tried to access default[:api][:path] I was actually accessing the array of directories when seemed to override the database settings attributes. Moving these into default[:directories][:api] and default[:database][:api][:path] etc fixed this.
Note that you will also get this error if you accidentally enter a space between "node" and the items indexing it:
node[:foo][:bar]
will work, while
node [:foo][:bar]
will throw this exception. It can be hard to spot.