Puppet: Found 1 dependency cycle - puppet

I am getting this error when applying my Puppet manifest:
Error: Could not apply complete catalog: Found 1 dependency cycle:
(Exec[pip install requirements] => File[change venv permissions] => File[enforce MinGW compiler] => Exec[pip install requirements])
Try the '--graph' option and opening the resulting '.dot' file in OmniGraffle or GraphViz
Here is my Puppet manifest (the relevant part) and I don't see any dependency cycle there. Any ideas?
exec {'create virtualenv':
command => "$install_dir/Scripts/virtualenv.exe venv",
cwd => $project_dir,
require => Exec['install virtualenv'],
}
file { "fix Mingw32CCompiler":
path => "C:/Python27/Lib/distutils/cygwinccompiler.py",
content => template($cygwinc_template),
ensure => present,
require => Exec['create virtualenv'],
}
file { "enforce MinGW compiler":
path => "$project_dir/venv/Lib/distutils/distutils.cfg",
owner => $user,
content => $mingw,
ensure => present,
require => File['fix Mingw32CCompiler'],
}
exec {'pip install requirements':
timeout => 1200,
command => "$project_dir/venv/Scripts/pip.exe install -r $project_dir/requirements.txt",
require => File['enforce MinGW compiler'],
}
file {'change venv permissions':
path => "$project_dir/venv",
recurse => true,
owner => $user,
mode => 0770,
require => Exec['pip install requirements'],
}

In puppet files have an implicit require for any parent directories that are declared.
Effectively:
File['change venv permissions'] -> File['enforce MinGW compiler']
So the parent requires the exec, the exec requires the child, and the child requires the parent, creating a loop.

What was your last change (that's probably the moment you added the cycle).
Try the suggestion to generate the graph. Post the generated dot file as gist so that we can investigate further.
Take a look at Debugging cycle or missing dependency.

Related

Puppet: how many times an exec block with creates run?

I have the below two exec resources and would like the exec resource run the script to run whenever the file /var/lib/my-file is not present. I'm wondering what would happen if the file never gets created. Would the exec resource check if file exists run forever in a loop until it gets created?
exec { 'run the script':
command => "python my-script.py",
path => '/bin:/usr/bin:/usr/local/bin',
timeout => 900,
subscribe => File["my-settings.yaml"],
refreshonly => true,
}
exec { 'check if file exists':
command => 'true',
path => '/bin:/usr/bin:/usr/local/bin',
creates => '/var/lib/my-file',
notify => Exec['run the script']
}
A resource is only applied once per catalog application, which occurs once per catalog compilation per node. You can verify this for yourself by trying it out.
If the Python script fails to create the file, the resource would simply be applied again during the next catalog application. Otherwise, idempotence prevails and the resource is not applied because the file already exists.
Additionally, you should simplify your resources into:
exec { 'run the script':
command => 'python my-script.py',
path => '/bin:/usr/bin:/usr/local/bin',
timeout => 900,
creates => '/var/lib/my-file',
subscribe => File["my-settings.yaml"],
refreshonly => true,
}
This is functionally the same as what you have in your question and is more efficient and easier to read.

File paths must be fully qualified error in puppet

I was trying to create a new file in my module, but every time I am getting an error for my file resource, saying:
File paths must be fully qualified, not '/the/path/that/I/have/given'.
What are the possible reasons for that error?
class fresh_start {
file { 'source_file.rb':
ensure => 'file',
source => 'puppet:///modules/fresh_start/source_file.rb',
path => '/etc/puppetlabs/code/environments/production/modules/fresh_start/destination_file.rb',
owner => 'root',
group => 'root',
mode => '0755', # Use 0700 if it is sensitive
notify => Exec['run_my_ruby']
}
exec { 'run_my_ruby':
command => 'ruby etc/puppetlabs/code/environments/production/modules/fresh_start/source_file.rb > /etc/puppetlabs/code/environments/production/modules/fresh_start/output.txt',
refreshonly => true,
}
}
I have also tried to put the file path in a variable and use that variable as the path attribute's value, but I got the same error.
This happens when you attempt to run code with Unix paths on Windows. Check out the Puppet source code here and here.
Using a debugger, we can see that Unix-style paths are rejected on a Windows platform:
[1] pry(main)> slash = '[\\\\/]'
=> "[\\\\/]"
[2] pry(main)> label = '[^\\\\/]+'
=> "[^\\\\/]+"
[3] pry(main)> AbsolutePathWindows = %r!^(?:(?:[A-Z]:#{slash})|(?:#{slash}#{slash}#{label}#{slash}#{label})|(?:#{slash}#{slash}\?#{slash}#{label}))!io
=> /^(?:(?:[A-Z]:[\\\/])|(?:[\\\/][\\\/][^\\\/]+[\\\/][^\\\/]+)|(?:[\\\/][\\\/]\?[\\\/][^\\\/]+))/i
[4] pry(main)> path = '/foo/bar'
=> "/foo/bar"
[5] pry(main)> path =~ AbsolutePathWindows
=> nil

puppet code deleted a file, instead of replacing

I am having issues with puppet modules, and this modules should replace /etc/ssh/sshd_config file based on Redhat version. So the issue is, after applying the code, puppet deleted the file, instead of replacing it.
someone please suggest any wrong with my code.
here is the my puppet manifest file;
class os_vul_ssh {
case $::operatingsystemmajrelease {
'6':{$sshconfigfile = 'sshd_config.rhel6'}
'7':{$sshconfigfile = 'sshd_config.rhel7'}
}
package { "openssh-server":
ensure => installed,
}
service { 'sshd':
ensure => "running",
enable => true,
require => Package["openssh-server"],
}
file { "/etc/ssh/sshd_config":
owner => root,
group => root,
mode => '0644',
source => "puppet:///modules/os_vul/${::sshconfigfile}",
require => Package["openssh-server"],
notify => Service["sshd"],
}
}
file { "/etc/ssh/sshd_config":
ensure => file, <----- this is missing
owner => root,
group => root,
mode => '0644',
source => "puppet:///modules/os_vul/${::sshconfigfile}",
require => Package["openssh-server"],
notify => Service["sshd"],
}
Might be more going on here, but this is the first issue that jumps out at me.
By the way, you can cleanup your code with this:
file { "/etc/ssh/sshd_config":
ensure => file,
owner => root,
group => root,
mode => '0644',
source => "puppet:///modules/os_vul/sshd_config.rhel${::operatingsystemmajrelease}",
require => Package["openssh-server"],
notify => Service["sshd"],
}
and if you are using Facter 3 then consider changing your fact to:
$facts['operatingsystemmajrelease']
and note that your sshconfigfile is a local variable and should be included in your file resource as a local variable $sshconfigfile and not global $::sshconfigfile.

How to use ExtUtils::MakeMaker to install data in a custom directory

I need to install my main script (as executable) in /usr/bin (depends on the INSTALL_BASE). That part is done.
The next one is to install into the directory /usr/share/project_name/data options.txt, this one is hard for me, see my codes:
Manifest file:
data/options.txt
script/joel-perl
Makefile.PL
Makefile.PL
use ExtUtils::MakeMaker;
WriteMakefile (
NAME => 'joelperl',
VERSION_FROM => 'script/joel-perl',
PREREQ_PM => {
'Switch' => 0
},
EXE_FILES => ['script/joel-perl'],
PM => {
'data/options.txt' => '$(INSTALL_BASE)/share/project_name/data'
}
);
When running:
perl Makefile.PL INSTALL_BASE="/usr"
make
I get "ERROR: Can't create '/usr/share/project_name'", so my question is:
How to add/copy files to an especific place, like in my case?
See File::ShareDir::Install module:
use ExtUtils::MakeMaker;
use File::ShareDir::Install;
install_share 'data';
WriteMakefile(...);
package MY;
use File::ShareDir::Install qw(postamble);

Run custom services on startup with puppet

I'm migrating our old process of doing our linux configurations to be managed by puppet but I'm having issues trying to figure out how to do this. We add some custom scripts to the init.d folder on our systems to manage some processes, and then these need this command executed on them to launch on startup:
update-rc.d $file defaults
So what I'm doing with puppet is that I have all these scripts residing a directory, and I copy them over to init.d. I then want to call 'exec' on each of these files with the former command and use the file name as an argument. This is what I have so far:
#copy init files
file { '/etc/init.d/':
ensure => 'directory',
recurse => 'remote',
source => ["puppet:///files/init_files/"],
mode => 755,
notify => Exec[echo],
}
exec { "echo":
command => "update-rc.d $file defaults",
cwd => "/tmp", #directory to execute from
path => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:",
refreshonly => true
}
This will copy all the files and it calls exec when something is added/updated, but what I can't figure out is how to pass the name of the file as an argument into the exec command. It seems I'm really close but I just can't find anything to help with what I need to do. Is this the right way to try and achieve this?
Thanks.
Your probably not going to accomplish that if your using ensure => 'directory'. You will want to declare the file resource for each individual init script. And the exec isn't the way to go to enable a service. Use the service resource.
file { '/etc/init.d':
ensure => 'directory',
mode => '0755'
}
file { '/etc/init.d/init_script_1':
ensure => 'present',
owner => 'root',
group => 'root',
mode => '0755',
notify => Service['init_script_1']
}
file { '/etc/init.d/init_script_2':
ensure => 'present',
owner => 'root',
group => 'root',
mode => '0755',
notify => Service['init_script_2']
}
service { 'init_script_1':
ensure => running,
enable => true
}
service { 'init_script_2':
ensure => running,
enable => true
}
Hope this helps.

Resources