Puppet - requiring a defined resource with parameters - deployment

I'm very new to Puppet, and can't seem to find the answer to this question. I have a defined Puppet resource that takes a few arguments:
class xy::xy {
include apache:regular_apache
define setup($pkg_name, $xy_version, $pas_ver) {
file { '/etc/xy':
ensure => present,
notify => Service['apache'],
}
}
I'm trying to require this custom resource for another resource in another file.
class soft::buy {
include xy::xy
$xt_requires = [Xy::Xy::Setup["{'xt_buy': pkg_name => 'xt_buy_v01',
xy_version => '1.0.1',
pas_version => '2.1.4'}"]]
package { 'buy.xt':
ensure => $::buy_xt_version,
provider => 'xt',
require => $xt_requires,
}
}
The error that I get is this: Syntax error at 'require'; expected '}'
From reading the Puppet docs, it seems like I'm missing a comma or colon somewhere, but I've tried a variety of things. I was wondering how to properly require a custom defined resource with parameters for another resource? Thanks!

The syntax error can be fixed by the following code snippet.
package { 'buy.xt':
ensure => $::buy_xt_version,
provider => 'xt',
require => $xt_requires
}
[EDIT: The original code defines $xt_requires, not $requires]
You are defining the parameter require (which defines which resource needs to be handled first).
This is different from the language statement require (which is including a class and adding a dependency on the required class).
However, in the require-paramter, you cannot specify the parameters for the requirement, just it's presence. Fully correct would be:
xy::xy::setup {'xt_buy':
pkg_name => 'xt_buy_v01',
xy_version => '1.0.1',
pas_version => '2.1.4'
}
package { 'buy.xt':
ensure => $::buy_xt_version,
provider => 'xt',
require => Xy::Xy::Setup['xt_buy']
}

Related

Perl5 - Moose - Attribute accessors' names

I'd like to ask you for an advice regarding attribute accessors' naming.
I started to develop a project that is supposed to have quite a ramified hierarchy of classes, for example, the SomeFramework class, a bunch of classes like SomeFramework::Logger and, let's say, classes similar to SomeFramework::SomeSubsystem::SomeComponent::SomeAPI classes.
My goal is to design the most efficient communication between all these classes. I'll explain how I'm doing it now, so maybe you would like to share some opinions on how to make it better.
When I initialize the SomeFramework class, I have an object reference which I use from my application.
my $someframework = SomeFramework->new(parameter => 'value');
The SomeFramework class has some attributes, such as logger, configuration, etc, here are some examples of their definitions:
has 'logger' => (
is => 'ro',
isa => 'SomeFramework::Logger',
reader => 'get_logger',
writer => '_set_logger',
builder => '_build_logger',
lazy => 1
);
sub _build_logger {
my $self = shift;
SomeFramework::Logger->new(someframework => $self);
}
I'm passing the reference to the parent object to the child object, because I need the child to have access to the parent and its methods & accessors. So in the SomeFramework::Logger I have such attribute:
has 'someframework' => (
is => 'ro',
isa => 'SomeFramework',
reader => 'get_someframework',
writer => '_set_someframework',
required => 1
);
It lets me to have access to any object from within the SomeFramework::Logger class, usually it looks something like that:
my $configuration =
$self->
get_someframework->
get_configuration->
get_blah_blah;
To extrapolate it, let's look into the SomeFramework::SomeSubsystem::SomeComponent::SomeAPI class. This class has its own "parent" attribute (let's call it somecomponent) which is supposed to have a reference to a SomeFramework::SomeSubsystem::SomeComponent object as the value. The SomeFramework::SomeSubsystem::SomeComponent class has the attribute for its own parent attribute (we can call it somesubsystem) which is supposed to contain a reference to a SomeFramework::SomeSubsystem object. And, finally, this class has the attribute for its own parent too (someframework), so it contains the reference to a SomeFramework object.
It all makes it possible to have something like that inside of the SomeFramework::SomeSubsystem::SomeComponent::SomeAPI class:
my $configuration =
$self->
get_someframework->
get_somesubsystem->
get_somecomponent->
get_configuration->
get_blah_blah;
The first thing I'd like to know: is it a good practice? I hope, it is, but maybe you would advice me to go some more smooth way?
The second question is a bit more complicated (as for me), but I hope you'll help me with it. :) I like canonical names of accessors recommended by D.Conway in his "Perl Best Practices", but I'd like to do something like that:
my $configuration = $self->sc->ss->sf->conf->blah_blah;
Surely I can name all readers in this laconical manner:
has 'some_framework' => (
is => 'ro',
isa => 'SomeFramework',
reader => 'sf',
writer => '_set_someframework',
required => 1
);
But I don't like the idea of managing without the "standard" accessors names. :(
Also I can use MooseX::Aliases, it works fine for something like that:
has 'some_framework' => (
is => 'ro',
isa => 'SomeFramework',
reader => 'get_someframework',
writer => '_set_someframework',
required => 1,
alias => 'sf'
);
It looks fine, but there's an issue with attributes which names do NOT needed to be shortened. For example:
has 'api' => (
is => 'ro',
isa => 'SomeFramework::SomeSubsystem::SomeComponent::API',
reader => '_get_api',
writer => '_set_api',
required => 1,
alias => 'api'
);
In this case Moose throws an exception: Conflicting init_args: (api, api) at constructor. :( As I understand, MooseX::Aliases tries to create an attribute with the same value of the init_args parameter, so it fails. By the way, sometimes it happens, but sometimes it works fine, I haven't discovered when exactly it doesn't work.
Maybe I should have something like that:
has 'api' => (
is => 'ro',
isa => 'SomeFramework::SomeSubsystem::SomeComponent::API',
reader => '_get_api',
writer => '_set_api',
required => 1,
handles => {
api => 'return_self' # It's supposed to have some method that only
# returns the reference to its own object
}
);
? But it doesn't seem to be the best option too, because it helps me only if the attribute contains a reference some object for which I can define the return_self method. If the attribute contains a reference to some "foreign" object or some other value (e.g., a hash), it won't be possible to call that method. :(
Ugh... Sorry for such a long rant! I hope, you have managed to read to here. :)
I'll be very happy to get to know what do you thing and what would you suggest to do. Feel free to share any your ideas on this topic, any fresh ideas will be very appreciated!
Updated on 25.10.2015
As for the bigger question, let me see if I understood. There are an Apple and a Banana. The Fridge has both of them inside. But you want the Apple to know about the Fridge, and the Worm should know about the Apple, so that it can go from Worm up to Apple up to Fridge and turn the $fridge->light off when it wants to sleep. Is that correct? Sounds like a horrible idea that breaks all kinds of design patterns
Well, to be frank, I didn't think it's horrible. As for me, it's quite good when it's possible to have access from some class to some other class within the same framework. Why not? For example, let's imagine we have some class for the jobs-queue runner (let's call it SomeFramework::JobsQueue::Executor) and some class for jobs. Is it really bad to do something like:
package SomeFramework::JobsQueue::Executor;
use Moose;
use MooseX::Params::Validate;
has queue {
isa => 'SomeFramework::JobsQueue',
required => 1,
reader => 'get_queue',
writer => '_set_queue'
}
# This attribute is being set by the framework when the framework
# creates the SomeFramework::JobsQueue::Executor-based object
sub execute {
my($self, $job, $options) = validated_hash(
\#_,
job => { isa => 'SomeFramework::JobsQueue::Job' },
options => { isa => 'HashRef' }
);
my $queue = $self->get_queue;
$queue->mark_as_running($job->get_id);
$job->execute(options => $options);
$queue->mark_as_completed($job->get_id);
}
? So, our queue-runner object is aware about the queue object it "belongs" to, so it can call some methods of this queue object.
Or let's look at much more simple example:
package SomeFramework::SomeSubsystem;
use Moose;
has 'some_framework' => {
isa => 'SomeFramework',
required => 1,
reader => 'get_some_framework',
writer => '_set_some_framework'
}
sub some_method {
my $self = shift;
$self->get_some_framework->get_logger->log_trace("Hello, world!");
}
So, our object knows how to call methods of the framework's object that has initialized that object, moreover it can call some methods of the framework's object and even some methods of other objects initialized and stored by the framework's object.
If it's really bad, would you be so kind as to help me to understand why? Thank you!

How do I create a Catalyst::Model::DBIC::Schema from a DBIC::Schema?

Total lack of documentation makes this difficult..
Essentially, I have
package MyApp::Schema;
sub new_schema {
__PACKAGE__->connect(...)
}
Then I have
package MyCatApp::Model::MyApp;
use Moose;
extends 'Catalyst::Model::DBIC::Schema';
## what here;
_PACKAGE__->make_immutable;
How do I make this work? If I have this in my config...
<Model::MyApp>
schema_class MyApp::Schema
traits Caching
user_defined_schema_accessor foo
</Model::MyApp>
I get the following error:
BEGIN failed--compilation aborted at MyCatapp.psgi line 4, line 1.
Error while loading MyCatapp.psgi: Couldn't instantiate component "Either ->config->{connect_info} must be defined for MyCatApp::Model::MyApp or MyApp::Schema must have connect info defined on it.
But adding sub connect_info {} in MyApp::Schema does not change anything. That error is generated on line 480 of this file.
That method seems to be looking at $schema_class->storage->connect_info which I believe is an method on an instantiated object, and not a function in the package. However, if I try to set
__PACKAGE__->config('schema_class', MyApp::Schema->new_schema)
I then get...
Error while loading : Couldn't instantiate component "MyCatApp::Model::MyApp", "Attribute (schema_class) does not pass the type constraint because: Validation failed for 'Catalyst::Model::DBIC::Schema::Types::SchemaClass' with value MyApp::Schema=HASH(0xb4a5ff0) at /usr/local/lib/perl/5.18.2/Moose/Exception.pm line 37
So, how do I go about doing this...
So.. I'm not sure this is right so I won't mark it as accepted but I was pretty damn close.. This line is dead on..
__PACKAGE__->config('schema_class', MyApp::Schema->new_schema)
And the code is in place for that to work, as per the link above on line 480 of Catalyst::Model::DBIC::Schema. However, the type is wrong..
The type of schema_class is SchemaClass. And, SchemaClass is defined here
subtype SchemaClass,
as LoadableClass,
where { $_->isa('DBIx::Class::Schema') };
Now, SchemaClass is a subtype of a MooseX::Types::LoadableClass which essentially just takes a package name and requires it..
The problem is LoadableClass doesn't take an instance of DBIC::Schema. Anyway.. I was just changed it too..
has schema_class => (
is => 'ro',
isa => 'Object',
required => 1
);
Now I can stuff an instantiated schema into schema_class and pass the conditional if($schema_class->storage && $schema_class->storage->connect_info).
I filed a bug on this here
You could instantiate the connection info in your MyApp::Schema by in it doing:
__PACKAGE__->connection(#your_connection_details);
This will set up a Storage on the class itself, not a schema object. This will then pass the Catalyst::Model::DBIC::Schema check and work successfully. I think this achieves what you're after without changing the underlying classes (though might depend on the details not included).
From the documentation you referenced, your MyCatApp::Model::MyApp would look like this:
__PACKAGE__->config(
schema_class => 'MyApp::Schema',
connect_info => {
dsn => 'dbi:Pg:dbname=mypgdb',
user => 'postgres',
password => '',
pg_enable_utf8 => 1,
on_connect_do => [
'some SQL statement',
'another SQL statement',
],
}
);
And in your controller, you would:
my $db = $c->model('MyApp');

Upgrading to Catalyst::Plugin::Cache from Catalyst::Plugin::Cache::FileCache

I am writing a build script to build a Catalyst app and apparently it's an older app since it uses Catalyst::Plugin::Cache::FileCache instead of the newer Catalyst::Plugin::Cache which supports a FileCache option. When doing a build through Makefile.PL I can't use cpanm because Catalyst::Plugin::Cache::FileCache is no longer retrievable. :-(
Finding the way to do that is what is proving problematic for me though.
I suppose I could make changes to the app to use Catalyst::Plugin::Cache but I'm not finding the documentation clear, or any easy way to do it.
Currently the app uses this....
__PACKAGE__->config( name => 'Stats', cache =>{storage=>'./tmp'} );
http://search.cpan.org/~mramberg/Catalyst-Plugin-Cache-FileCache-0.7/lib/Catalyst/Plugin/Cache/FileCache.pm is the documentation for the deprecated module.
The new documentation for Catalyst::Plugin::Cache is here..... http://metacpan.org/pod/Catalyst::Plugin::Cache
In one of my models, I have this....
__PACKAGE__->config(
schema_class => 'Schema::STATS',
connect_info => [
'dbi:ODBC:DSN=....;driver=...;Server=...;database=...;RB_CONFIG=...;',
'USER',
'PASS',
{limit_dialect=>'GenericSubQ',
on_connect_do => ['set dateformat ymd'],
cursor_class => 'DBIx::Class::Cursor::Cached'
}
],
);
sub COMPONENT {
my $self = shift->NEXT::COMPONENT(#_);
$self->schema->default_resultset_attributes({ cache_object => Cache::FileCache->
new({ namespace => 'Schema::STATS' }), cache_for=>3600});
return $self;
}
I'm looking to make as few changes as possible right now to this app but is anyone aware of any easier way to make the couple of changes I need to switch to the newer Catalyst::Plugin::Cache?
Worse comes to worse I could always just package the older Catalyst::Plugin::Cache::FileCache source and install it but I was hoping there would just be an easier way to to use the new one.
Many thanks!
Janie
__PACKAGE__->config(
'Plugin::Cache' => {
backends => {
default => {
class => "Cache::FileCache",
cache_root => "./tmp",
namespace => "Pick Something",
}
}
);
and removing Plugin::Cache::FileCache and adding Plugin::Cache should get you about 90% of the way there. $c->cache will keep working as you expect, and it will be a vanilla Cache::FileCache object.

Moose: injecting parameters into construction

I have a filename attribute that has attached validation. Now when the filename isn't specified during construction I want to read it from a configuration file.
subtype 'filename'
=> as 'Str'
=> where { -r $_ }
=> message { "$_ is not a readable file" };
has 'students_file' => ( is => 'rw', isa => 'filename', default => '' );
has 'teachers_file' => ( is => 'rw', isa => 'filename', default => '' );
The issue is that BUILD seems to be run after the validation occurs. All examples of BUILDARGS seem to handle a different way of constructing the object.
So where should I read the configuration and set the attribute?
Give teachers_file and students_file builder methods (or inline default subs) that set them from the config file. The builder will only run if those attributes aren't provided as keys to the constructor (unless you use init_arg => undef to prevent them from being set in the constructor).
If your config is an attribute of its own, with a builder that reads the config file, then you have an ordering problem between config and teachers_file and students_file. You can resolve this by making teachers_file and students_file lazy attributes, which ensures that they won't try to be constructed before the config attribute. However, you may want to make sure that the "foo is not a readable file" error is thrown as early as possible during construction, instead of the first time the attribute is used. You can work around that by adding
sub BUILD {
my $self = shift;
$self->teachers_file;
$self->students_file;
}
which ensures that those attributes are read once (and constructed) before the constructor returns.

are there iterators and loops in puppet?

When I define(?) a resource e.g. to ensure dir structure, are there any loops available?
Like that:
for X in [app1,app2] do:
file { '/opt/app/' + X:
ensure => directory,
owner => 'root',
group => 'root',
mode => '0644',
}
I have tens of directories and I am really tired with declaring it in puppet.. It would take 15 LOC of bash.
Any ideas?
Older versions of the puppet language have no support for loops.
But you can use an array instead of a simple string for the title and declare several resources at the same time with the same params:
$b = '/opt/app'
file { [ "$b/app1", "$b/app2" ]:
ensure => directory,
owner => 'root',
group => 'root',
mode => 0644,
}
You can also declare many resources of the same type with different params by ending each resource with a ;, which is a bit more compact than repeating the file and the {s and }s:
file {
[ "$b/app1", "$b/app2" ]:
ensure => directory,
owner => 'root',
group => 'root',
mode => 0755;
[ "$b/app1/secret", "$b/app2/secret" ]:
ensure => directory,
owner => 'root',
group => 'root',
mode => 0700;
}
In the specific case of files, you can set up a source and use recursion:
file { "/opt/app":
source => "puppet:///appsmodule/appsdir",
recurse => true;
}
(that would require having a source of that directory structure for puppet to use as the source)
You can define a new resource type to reuse a portion of the param multiple times:
define foo {
file {
"/tmp/app/${title}":
ensure => directory,
owner => 'root',
mode => 0755;
"/tmp/otherapp/${title}":
ensure => link,
target => "/tmp/app/${title}",
require => File["/tmp/app/${title}"]
}
}
foo { ["app1", "app2", "app3", "app4"]: }
Starting with Puppet 2.6, there's a Ruby DSL available that has all the looping functionality you could ask for: http://www.puppetlabs.com/blog/ruby-dsl/ (I've never used it, however). In Puppet 3.2, they introduced some experimental loops, however those features may change or go away in later releases.
As of version 3.2 there are lambdas
You must set parser = future in puppet.conf.
$a = [1,2,3]
each($a) |$value| { notice $value }
Another option for declaring multiple defined types is create_resources. Pass it a hash of hashes:
create_resources(file, {
'/tmp/test1' => {
ensure => directory,
owner => 'root',
group => 'root',
mode => '0644',
},
'/tmp/test2' => {
ensure => directory,
owner => 'www-data',
group => 'www-data',
mode => '0755',
},
})
As of Puppet 4 (and the "future parser" of late versions of Puppet 3) the Puppet DSL has iteration functions similar in form and function to some of the methods of Ruby arrays and hashes:
each - evaluates a block of code (formally, a lambda) for each element of an array or hash
filter - applies a lambda to each element of an array or hash and returns an array or hash of those for which the lambda evaluated to true
map - applies a lambda to each element of an array or hash, and returns an array of the results
reduce - applies a lambda to each element of an array or hash to build up a single result, which it returns
There is no indexed for loop along the lines of C's or Java's, but you can combine array sectioning with any of the functions above to achieve iteration over a subset of a data structure. There is no indefinite iteration along the lines of a C or Java while loop.
Of course, you can still use the resource-centric approaches described in other answers, and sometimes one of those is indeed the best available approach. You cannot any longer use Ruby DSL, however; it is removed altogether from Puppet 4. Among the iteration functions, the ability to define custom functions, the ascension of data-centric approaches into favor, and all Puppet's historic standard features, Ruby DSL seems not much missed.
Yes. "Ruby DSL" could help, just use file extension ".rb" instead of ".pp" in manifest and you can define puppet "type" like this:
define 'myapps::structure', :applist do
#applist.each do |app|
file( #name+'/'+app,
:ensure => directory,
:owner => 'root',
:group => 'root',
:mode => '0644')
end
end
Classes and nodes also can be defined in similar way. Notice however that this feature is deprecated since release 3