Puppet: Passing parameters through classes - class

This is a follow on to my earlier question about parameterized classes. Following on that example a little further, I want to be able to pass running or stopped into the service, but when I add the service to a box, I don't use "include poodle::service", I use "include poodle" which does all of the other stuff Poodle requires to be installed.
So, can I pass the variable along to the service class like this:
# SITE.PP
node 'tweedle.example.com' {
include basicstuff
include poodle
}
node 'beetle.example.com' {
include basicstuff
class { 'poodle':
$ensure => 'stopped'
}
}
## POODLE MODULE, manifests/init.pp
class poodle ( $ensure = 'running' ) {
class {'poodle::install': }
class {'poodle::config': }
class {'poodle::service':
ensure => $ensure
}
Class ['poodle::install'] -> Class ['poodle::config'] ~> Class ['poodle::service']
}
...
class poodle::service ( $ensure ) {
service {'poodle':
ensure => $ensure,
enable => true,
restart => "/etc/init.d/poodle stop && sleep 5 && /etc/init.d/poodle start",
subscribe => File['/opt/poodle/poodle.py'],
}
}
Or should I put the parameter directly on the service class and the explicitly call both the Poodle class and Poodle's service class like this:
# SITE.PP
node 'tweedle.example.com' {
include basicstuff
include poodle
}
node 'beetle.example.com' {
include basicstuff
include poodle
class { 'poodle::service':
$ensure => 'stopped'
}
}
## POODLE MODULE, manifests/init.pp
class poodle {
class {'poodle::install': }
class {'poodle::config': }
class {'poodle::service':
ensure => $ensure
}
Class ['poodle::install'] -> Class ['poodle::config'] ~> Class ['poodle::service']
}
...
class poodle::service ( $ensure = 'running') {
service {'poodle':
ensure => $ensure,
enable => true,
restart => "/etc/init.d/poodle stop && sleep 5 && /etc/init.d/poodle start",
subscribe => File['/opt/poodle/poodle.py'],
}
}
Or is adding the parameter to the service class and including ONLY that enough, because the service class has dependencies, like this:
# SITE.PP
node 'tweedle.example.com' {
include basicstuff
include poodle
}
node 'beetle.example.com' {
include basicstuff
class { 'poodle::service':
$ensure => 'stopped'
}
}
## POODLE MODULE, manifests/init.pp
class poodle {
class {'poodle::install': }
class {'poodle::config': }
class {'poodle::service':
ensure => $ensure
}
Class ['poodle::install'] -> Class ['poodle::config'] ~> Class ['poodle::service']
}
...
class poodle::service ( $ensure = 'running') {
service {'poodle':
ensure => $ensure,
enable => true,
restart => "/etc/init.d/poodle stop && sleep 5 && /etc/init.d/poodle start",
subscribe => File['/opt/poodle/poodle.py'],
}
}
What is the right course and best practice here? Thanks in advance!

Generally, you don't want people to have to understand the internal structure of your module to use it.
I certainly wouldn't require them to include both poodle and poodle:service.
Modules usually follow one of two structures:
Single entry point via init.pp for simple services with few/no params and no separate roles
(client/server) or "multiples" (like a database server could have multiple db's configured in
it via puppet)
Multiple entry points via subclasses and defined types that handle separate roles and
multiples
Based on what you've described, I would place the parameter in the main class and pass it through to the service subclass.

node 'beetle.example.com' {
class { 'poodle::service':
ensure => 'stopped'
}
}
works.
But I disagree saying "you don't want people to have to understand the internal structure of your module" in that point that you want the flexibility when creating vhosts to define what port, protocol, what backend - proxy + and what modules etc. should be used.
I don't see a way for my projects how to enable such flexibility by just writing include apache, nginx or such.

Related

Puppet - restart a service after a each loop

I'm writing a Puppet module to enforce a configuration and restart the service if needed. I'd like to do something like that:
class app_cfg {
$app_files = ['file1', 'file2', 'file3']
$app_files.each |String $file| {
exec {"sed -i 's/bar/foo/g' $file":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
}
} ~>
service { 'app':
ensure => running,
enable => true,
}
}
I could add "notify" inside the exec but I don't want to restart my service for each file, and it looks like the notify chaining arrow doesn't work after an each loop. Do you guys have any idea how to make it work?
Thanks a lot :)
I found a workaround by inserting a for loop inside the exec resource instead of using each:
class app_cfg {
service { 'app':
ensure => running,
enable => true,
}
exec {"for file in file1 file2 file3; do sed -i 's/bar/foo/g' \$file; done":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
provider => shell,
notify => Service['app']
}
}
I'm still pretty curious about the first solution! Did you ever manage to do something like that?
Thanks!
I could add "notify" inside the exec but I don't want to restart my
service for each file, and it looks like the notify chaining arrow
doesn't work after an each loop.
It is important to understand that the code in a Puppet manifest describes only how to build a catalog that in turn describes the desired configuration of your server. The catalog lists the classes and resources declared, their parameters and properties, and the relationships among them. The logic within a manifest does not map to actions performed when applying the resulting catalog; it is reflected only in which classes and resources are catalogued, and what their parameters, properties, and relationships are.
Thus, when you use one of the chaining arrows in a manifest, you are instructing the catalog builder to record a relationship between the two resources in the catalog, not an explicit instruction to apply or refresh a resource. No declared resource will be applied at more than once or refreshed more times than it is applied. It will be perfectly fine, therefore, to put an appropriate chain expression inside your loop, but what you cannot do is put the Service declaration itself inside the loop, because that would produce multiple declarations of it. So you might do any of these:
Declare the service (once), and use a resource reference to it in your chain expression inside the loop:
class app_cfg {
service { 'app':
ensure => running,
enable => true,
}
['file1', 'file2', 'file3'].each |String $file| {
exec {"sed -i 's/bar/foo/g' $file":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
} ~> Service['App']
}
}
As above, but use the appropriate resource metaparameter instead of a chain expression:
class app_cfg {
service { 'app':
ensure => running,
enable => true,
}
['file1', 'file2', 'file3'].each |String $file| {
exec {"sed -i 's/bar/foo/g' $file":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
notify => Service['app'],
}
}
}
Or declare the relationships with the help of a collector:
class app_cfg {
['file1', 'file2', 'file3'].each |String $file| {
exec {"sed -i 's/bar/foo/g' $file":
path => ['/bin'],
onlyif => 'grep "bar" $file 2>/dev/null',
cwd => '/opt/app/config/',
tag => 'app_notifier',
}
}
Exec<|tag == 'app_notifier|>
~> service { 'app':
ensure => running,
enable => true,
}
}

mojolicious helper storing an elasticsearch connection

i'm experimenting with elasticsearch within mojolicious.
I'm reasonably new at both.
I wanted to create a helper to store the ES connection and I was hoping to pass the helper configuration relating to ES (for example the node info, trace_on file etc).
If I write the following very simple helper, it works;
has elasticsearch => sub {
return Search::Elasticsearch->new( nodes => '192.168.56.21:9200', trace_to => ['File','/tmp/elasticsearch.log'] );
};
and then in startup
$self->helper(es => sub { $self->app->elasticsearch() });
however if I try to extend that to take config - like the following -
it fails. I get an error "cannot find index on package" when the application calls $self->es->index
has elasticsearch => sub {
my $config = shift;
my $params->{nodes} = '192.168.56.21:' . $config->{port};
$params->{trace_to} = $config->{trace_to} if $config->{trace_to};
my $es = Search::Elasticsearch->new( $params );
return $es;
};
and in startup
$self->helper(es => sub { $self->app->elasticsearch($self->config->{es}) });
I assume I'm simply misunderstanding helpers or config or both - can someone enlighten me?
Just fyi, in a separate controller file I use the helper as follows;
$self->es->index(
index => $self->_create_index_name($index),
type => 'crawl_data',
id => $esid,
body => {
content => encode_json $data,
}
);
that works fine if I create the helper using the simple (1st) form above.
I hope this is sufficient info? please let me know if anything else is required?
First of all, has and helper are not the same. has is a lazily built instance attribute. The only argument to an attribute constructor is the instance. For an app, it would look like:
package MyApp;
has elasticsearch => sub {
my $app = shift;
Search::ElasticSearch->new($app->config->{es});
};
sub startup {
my $app = shift;
...
}
This instance is then persistent for the life of the application after first use. I'm not sure if S::ES has any reconnect-on-drop logic, so you might need to think about it a permanent object is really what you want.
In contrast a helper is just a method, available to the app, all controllers and all templates (in the latter case, as a function). The first argument to a helper is a controller instance, whether the current one or a new one, depending on context. Therefore you need to build your helper like:
has (elasticsearch => sub {
my ($c, $config) = #_;
$config ||= $c->app->config->{es};
Search::ElasticSearch->new($config);
});
This mechanism will build the instance on demand and can accept pass-in arguments, perhaps for optional configuration override as I have shown in that example.
I hope this answers your questions.

Perl Validation::Class messages

I've write a custom class inherited from Validation::Class to define all mixins I need
Then whenever i want to use these mixins in any class, I just inherited from the custom class.
Its works perfectly but I want to customize all error messages. Example:
message required => '%s is required';
So I don't have to write it in each validation field like this.
field username => {
required => 1,
messages{
required => '%s is required'
}
};
But it doesn't seems to work, it's still displaying original Validation::Class error message.
Can I override all messages I need in my costume class?
My custom class looks like this:
#Validation.pm
package Validation;
use Validation::Class;
use File::Spec ;
BEGIN {
my ($volume, $directory, $file) = File::Spec->splitpath(__FILE__);
push ( #INC,$directory );
}
our #ISA = qw(Validation::Class);
# hook
build sub {
my ($self, #args) = #_; # on instantiation
};
# I have tried to but the message in here , but it doesn't work
#message required => '%s is reuqired';
# data filter template
mixin filter => {
filters => [qw/trim strip/],
};
# data validation template
mixin basic => {
max_length => 255,
messages => {
max_length => 'Invalid %s',
}
};
This is the child class inherited from my Validation class
#Person.pm
package person;
# Inherit my custom class instead of Validation::Class
use Validation;
our #ISA = qw(Validation);
load role => 'Validation';
# I want to have this message in all inherited classes
# here is my problem .. this line will override required message with my custom message
# but I have many classes like this class
# is there anyway to delete this line and to write it in the parent class
# so every child can use this message without duplicating it in them
message required => '%s is reuqired';
# data validation rules
field username => {
mixin => 'filter',
mixin => 'basic',
required => 1
};
I found the solution for the messages
I have added a new mixin in the parent class (my custom Validation class) like this
mixin my_messages => {
messages => {
required => '%s is reuqired',
pattern => 'Ivalid character found in %s',
}
};
You can add as many messages as you can.
Then use the mixin inside all fields in any child class.
example
field username => {
default => '',
mixin => 'my_messages',
};
Note that my_messages mixin must be the last rule in the field.

Sinatra and "controllers" behaviour

Sinatra from Box does't allow separate action to file? Like this:
index.php
get '/' and other
user.php
get '/user/show/'
post '/user/new/' and other
How to say sinatra use user.php for '/user/*' request, and index.php for '/'.
And how looks application with many get post in one file written in sinatra? (one huge ass?)
After reading a lot, exist some solution:
1.
class Get < Sinatra::Base
get('/') { 'GET!' }
end
class Post < Sinatra::Base
post('/') { 'POST!' }
end
class Routes < Sinatra::Base
get('/') { Get.call(env) }
post('/') { Post.call(env) }
end
run Routes
2.
class Foo < Sinatra::Base
get('/foo') { 'foo' }
end
class Bar < Sinatra::Base
get('/bar') { 'bar' }
end
Routes = Rack::Mount::RouteSet.new do |set|
set.add_route Foo, :path_info => %r{^/foo$}
set.add_route Bar, :path_info => %r{^/bar$}
end
run Routes

Puppet parameterized classes and changing parameters

I am trying to understand Puppet parameterized classes. I have a parameterized class defined this way:
class defaults(
$no_samba = 'FALSE'
)
{
if ($no_samba =~ /TRUE/) {
notify { "will not install samba": } ;
} else {
# install samba here
}
# More server install tasks here...
}
Furthermore, I define a basenode as follows:
node basenode
{
class {'defaults':
no_samba => 'FALSE',
}
}
and then I instantiate a server:
node myserver1 inherits basenode {
Class['defaults'] { no_samba => 'TRUE' }
}
However, this does not work. The myserver1 node does not show the notify message indicating that samba will not be installed.
Here's my non-typo answer - I think you're running into http://projects.puppetlabs.com/issues/7890
Here's a code sample where I tweaked your code to get the effect you're looking for, based on the rewritten example in the ticket:
class defaults(
$no_samba = 'FALSE'
)
{
notify {"no_samba_hack" :
message => "$no_samba";
}
if ($no_samba =~ /TRUE/) {
notify { "will not install samba": }
} else {
# install samba here
}
# More server install tasks here...
}
class basenode($no_samba="FALSE") {
class {defaults: no_samba => $no_samba}
}
node yourserver {
class { 'basenode' : no_samba => 'TRUE'}
}
When I run that with 'puppet apply sample.pp' with puppet 2.7.11 on Ubuntu 12.04, I get the following output:
notice: will not install samba
notice: /Stage[main]/Defaults/Notify[will not install samba]/message: defined 'message' as 'will not install samba'
notice: TRUE
notice: /Stage[main]/Defaults/Notify[no_samba_hack]/message: defined 'message' as 'TRUE'
notice: Finished catalog run in 0.05 seconds
Was samba installed on myserver1, and/or did any of the other server install tasks get triggered ? If only the notify message wasn't printed, then it may really be a problem with notify type versus the notice function.
Notify should look like "notify{"i have curly brackets and a trailing colon":}
Notice is called like a function: notice("i use parenthesis")
Try changing 'notify' to 'notice' and see if it works. You may also want to check the puppet syntax with 'puppet parser validate default.pp' (assuming your default class is in default.pp)
I believe it has to do with scope. It looks like you're creating the 'default' class in the base node and then setting a resource default for the 'default' class after the fact in something that inherits that basenode.
http://docs.puppetlabs.com/guides/language_guide.html
"Defaults are not global — they only affect the current scope and scopes below the current one."
Here is simple example:
class apache-setup {
class { 'apache':
mpm_module => 'prefork',
}
}
include apache-setup
Or:
class { '::mysql::server':
config_file => '/etc/my.cnf',
root_password => 'root', # Sets MySQL root password.
override_options => {
'mysqld' => {
'max_connections' => '512',
'max_allowed_packet' => '256M',
'log' => 'ON',
'log_slow_queries' => 'ON',
'general_log' => 'ON',
'wait_timeout' => '28800',
}
}
}