Executing bash script from puppet fails - bash

I have been trying to copy and execute a shell script residing in puppet master machine to my puppet agent
This is my code
[root#ip-****** manifests]# cat site.pp
class mymodule::myklass{
file {'my_bash_script':
ensure => 'file',
source => '/etc/puppet/modules/mymodule/my_bash_script.sh',
path => '/home/ec2-user/my_bash_script.sh',
owner => 'root',
mode => '755',
notify => Exec['run_my_script'],
}
exec { 'run_my_script':
command => '/home/ec2-user/my_bash_script.sh',
}
}
include mymodule::myklass
my script:
[root#ip-********* mymodule]# cat my_bash_script.sh
#!/bin/sh
mv /usr/bin/node /usr/bin/bnode
ln -s /usr/local/bin/node /usr/bin/node
mv /usr/bin/npm /usr/bin/bnpm
ln -s /usr/local/bin/npm /usr/bin/npm
I am getting the following error:
[root#ip-*********** /]# puppet agent -t
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for ip*****************8
Info: Applying configuration version '1472235841'
Error: /Stage[main]/Mymodule::Myklass/File[my_bash_script]: Could not evaluate: Could not retrieve information from environment production source(s) puppet:///modules/mymodule/my_bash_script.sh
Notice: /Stage[main]/Mymodule::Myklass/Exec[run_my_script]: Dependency File[my_bash_script] has failures: true
Warning: /Stage[main]/Mymodule::Myklass/Exec[run_my_script]: Skipping because of failed dependencies
Notice: Finished catalog run in 0.08 seconds
Can anyone please help me resolve this error?

You have to use the puppet module URI to source your file resources if they are located in your module/files directories:
file {'my_bash_script':
ensure => 'file',
source => 'puppet:///modules/mymodule/my_bash_script.sh',
path => '/home/ec2-user/my_bash_script.sh',
owner => 'root',
mode => '755',
notify => Exec['run_my_script'],
}
Note the documentation here: https://docs.puppet.com/puppet/latest/reference/types/file.html#file-attribute-source
If it still fails with that error, then that means the file is missing from your $modulepath/mymodule/files/my_bash_script.sh so you need to place it there.
Furthermore, your bash script could be converted to intrinsic Puppet DSL, and it is odd that you are including a class inside of itself at the end.

Related

Run cmd commands with open source puppet

Can someone please help me with the open source puppet?
I want to provide a jar file to a windows client and execute the .jar file with the command line.
The .jar file is actually an update for an application which is running as a service.
I am poorly familiar with the puppet language but would guess something like this to execute the jar file:
exec { 'jar_execution':
command => 'cmd.exe /c java -jar foo.jar',
}
Should this be part of the manifest which could look like this?
service { 'fooservice':
name => foo_service,
ensure => running,
enable => true,
}
file { 'foo.jar':
path => 'C:/foo/temp/foo.jar',
ensure => file,
source => "puppet:///modules/foo/foo.jar",
}
exec { 'jar_execution':
command => 'cmd.exe /c java -jar C:/foor/temp/foo.jar',
}
And how does the agent actually run this command?
There are few architectural considerations:
Use archive resource (from archive module) instead of: file with your .jar in your module's .git history.
Have all artifacts (e.g. binary files) served by another service like Apache Archiva, Nexus or Artifactory or even Puppet Server itself. If the .jar is not developed by your company, you may want to use the authoritative source with maybe some internal caching.
Name your resources in a way that are global to your whole infrastructure, otherwise you may have events that notify themselves and produce undesired outcome.
Order of resources in puppet doesn't matter. You want to order the resources the way it makes sense to you and use the before, after, require, notify to ensure dependency.
I would recommend having binaries files outside of a module, as binaries are not supposed to be versioned. You would probably have another service that can serve files, or even your puppet infrastructure can provide those packages, in the similar way it provides the puppet-agent itself, regardless if you use Puppet OSS or Puppet Enterprise.
archive { 'C:/foo/temp/foo.jar':
source => https://location/to/your/.jar,
}
exec { 'C:/foo/temp/foo.jar': # notice in resource name recommended to use / not \
command => 'cmd.exe /c java -jar C:/foor/temp/foo.jar',
refreshonly => true # this one executes exec only if the file was re-downloaded (maybe it's signature has changed), or the file was removed from disk and was re-downloaded.
onlyif => 'test -f C:\foo\temp\foo.jar' # or some command that guards you to not run this .jar every time you run puppet
subscribe => Archive['C:/foo/temp/foo.jar'],
notify => Service['foo_service'] # most probably you need to restart the service after you ran the .jar, otherwise you wouldn't have added in your question.
}
service { 'foo_service':
ensure => running,
enable => true,
}
I notice that in your example you don't need to remove your .jar after being executed. In case you need that, another exec command can remove the file downloaded in the same

Chef::Exceptions::CookbookNotFound: Cookbook sudo not found and Chef-Solo “undefined method `[]' for nil:NilClass”

I'm trying to make a cookbook that has some dependencies, but it doesn't work
/recipe
ls
default.rb
Recipe:
include_recipe 'sudo::default'
include_recipe 'user'
def will_create_user?(username, user)
return false if node['etc']['passwd'].key?(username)
return true if user['action'] == 'create' || !user.key?('action')
false
end
node['authorization']['sudo']['groups'].each do |group|
group group
end
node['vms']['users'].each do |username, user|
send_notification = will_create_user? username, user
user_account username do
comment user['comment'] if user['comment']
ssh_keys user['ssh_keys']
groups user['groups'] if user['groups']
if send_notification
notifies :run, "execute[reset_password_#{username}]", :delayed
end
end
execute "reset_password_#{username}" do
command "passwd -d #{username} && chage -d 0 #{username}"
action :nothing
end
end
Metadata.rb
...
version '0.1.0'
chef_version '>= 14.0'
depends "sudo"
depends "user"
Berksfile.lock
DEPENDENCIES
vms-users
path: .
metadata: true
GRAPH
sudo (5.4.5)
user (0.7.0)
vms-users (0.1.0)
sudo (>= 0.0.0)
user (>= 0.0.0)
Attributes/default.rb
{
"vms": {
"users": {
'magrini' => {
'comment' => 'Bruna Magrini',
'groups' => ['sysadmin'],
'ssh_keys' => ['chave ssh'],
},
}
}
}
I'm executing using chef-client --local-mode default.rb
Error: Chef::Exceptions::CookbookNotFound: Cookbook sudo not found
Recipe `sudo::default` is not in the run_list, and cookbook 'sudo'
is not a dependency of any cookbook in the run_list. To load this recipe,
first add a dependency on cookbook 'sudo' in the cookbook you're
including it from in that cookbook's metadata.
Running handlers:
[2019-12-19T20:42:12+00:00] ERROR: Running exception handlers
Running handlers complete
[2019-12-19T20:42:12+00:00] ERROR: Exception handlers complete
Chef Infra Client failed. 0 resources updated in 01 seconds
[2019-12-19T20:42:12+00:00] FATAL: Stacktrace dumped to /home/chef-repo/.chef/local-mode-cache/cache/chef-stacktrace.out
[2019-12-19T20:42:12+00:00] FATAL: Please provide the contents of the stacktrace.out file if you file a bug report
[2019-12-19T20:42:12+00:00] FATAL: Chef::Exceptions::CookbookNotFound: Cookbook sudo not found. If you're loading sudo from another cookbook, make sure you configure the dependency in your metadata
i have a feeling that you are confusing something, since the title of your question mentions chef-solo when you are really using chef-zero (--local-mode).
you should definitely favor chef-zero over chef-solo (but i won't get into the reasons why).
in both cases (using chef-zero or chef-solo), you will have to download all the cookbooks and make verify chef-client knows where are the cookbooks located.
if you ase using chef-zero, here are some references:
Local mode does not require a configuration file, instead it will look for a directory named /cookbooks and will set chef_repo_path to be just above that. (Local mode will honor the settings in a configuration file, if desired.) If the client.rb file is not found and no configuration file is specified, local mode will search for a config.rb file.
client.rb settings:
chef_repo_path: The path to the chef-repo containing cookbooks and other files, such as environments or data bags, when running Chef Infra Client in local mode.
cookbook_path: The sub-directory for Chef Infra Client cookbooks. This value can be a string or an array of file system locations, processed in the specified order. The last cookbook is considered to override local modifications.
since i see that you are using berkshelf, you can use vendor sub-command to download all the cookbooks dependencies and place them in the same directory. then, have a custom configuration for chef-client, that sets the value of cookbook_path to the same directory which you used in conjunction with berks vendor, and finally execute chef-client.

Puppet - unable to execute ONLY ONCE ordered chain of Exec commands after notification

TLDR:
I can't configure ordered chain of Puppet "Exec" commands to run ONLY ONCE.
Details:
I want to use Vagrant and Puppet modules to setup VM with installed Redmine and some sample data loaded into it.
I'm using https://forge.puppetlabs.com/johanek/redmine and it works great - Redmine is installed and it works.
My goal:
Now I want to load sample data into Redmine using REST API:
Create 1 test project
Import 2 issues into this project
I want to run 2 simple "Exec", one after another and ONLY ONCE, but I can't achieve this, hence the question.
My current effort:
I've tried to subscribe to one of latest steps in redmine installation
subscribe => [Exec['rails_migrations']]
and then import data, but the first step "create-project1" always notifies second step "import-issues", so it creates duplicated data.
And if run vagrant provision few times, the "import-issues" creates duplicates of this issues.
Here is my code:
exec {'create-project1':
subscribe => [Exec['rails_migrations']],
path => ['/usr/bin', '/usr/sbin', '/bin'],
creates => "$redmine_install_dir/.data_loaded",
command => "curl WHICH_CREATES_PROJECT && touch $redmine_install_dir/.data_loaded",
notify => [Exec['import-issues']],
} ->
exec {'import-issues':
path => ['/usr/bin', '/usr/sbin', '/bin'],
command => "curl WHICH_IMPORTS_ISSUES",
refreshonly => true,
}
Question:
How to configure those Exec commands to run in chain and ONLY ONCE?
Im also thinking about extending this chain to 5 commands in near future, so keep that in mind.
you were almost there with 'ONLY ONCE' - Puppet has onlyif properties that you can include in your exec block to test if a file already exists or not.
you could then do something like
exec {'create-project1':
subscribe => [Exec['rails_migrations']],
path => ['/usr/bin', '/usr/sbin', '/bin'],
onlyif => "test ! -f $redmine_install_dir/.data_loaded"
command => "curl WHICH_CREATES_PROJECT && touch $redmine_install_dir/.data_loaded",
notify => [Exec['import-issues']],
which test on the existence of the $redmine_install_dir/.data_loaded- you should be able to play a bit with that to achieve what you want

Puppet does not honor 'require' modules

I have created a module to add a user as follows:
user { 'test':
ensure => 'present',
comment => 'Test User',
home => '/home/test',
shell => '/bin/bash',
managehome => 'true',
gid => 'postgres',
groups => 'users',
password => '$1$PIp.c9J6$gdAyd76OhBk7n9asda80wm0',
require => [ Package['rubygem-ruby-shadow'], Class['postgres'] ],
}
It requires the class postgres as I need its primary group to be assigned to postgres, and the rubygem-ruby-shadow dependency is for the password setup.
My problem is puppet does not honor these requirements. Puppet will always execute the useradd module first before rubygem-ruby-shadow, and this causes the password setup to fail. I have also tried to include rubygem-ruby-shadow in the same class as useradd but to no avail.
The output upon running puppet agent -t:
linux-z14x:~ # puppet agent -t
info: Caching catalog for linux-z14x.playground.local
info: /User[test]: Provider useradd does not support features manages_passwords; not managing attribute password
info: Applying configuration version '1425978163'
notice: /Stage[main]/Users/Package[rubygem-ruby-shadow]/ensure: created
notice: /User[test]/ensure: created
notice: Finished catalog run in 78.19 seconds
Running it the second time:
linux-z14x:~ # puppet agent -t
info: Caching catalog for linux-z14x.playground.local
info: Applying configuration version '1425978163'
notice: /Stage[main]/Users/User[test]/password: changed password
notice: Finished catalog run in 74.79 seconds
My rubygem-ruby-shadow class:
package { 'rubygem-ruby-shadow':
ensure => 'installed',
require => Class['myrepo'],
provider => 'zypper',
}
How do I get rubygem-ruby-shadow module to run first before the useradd?
Puppet master version is 3.7.4-1 (on CentOS) and puppet client is 2.6.12-0.10.1 (on SLES 11 SP2).
Thanks.
This is unfortunate. The provider detects the absence of ruby-shadow during agent initialization, and does not update its capabilities during the transaction.
This may be limitation of Puppet that might be fixed in a more recent version (what are you using?)
I do try and make sure to provide ruby-shadow along with Puppet itself everywhere, to avoid this very issue.

Make chef cookbook recipe only run once

So I use the following recipe:
include_recipe "build-essential"
node_packages = value_for_platform(
[ "debian", "ubuntu" ] => { "default" => [ "libssl-dev" ] },
[ "amazon", "centos", "fedora", "centos" ] => { "default" => [ "openssl-devel" ] },
"default" => [ "libssl-dev" ]
)
node_packages.each do |node_package|
package node_package do
action :install
end
end
bash "install-node" do
cwd Chef::Config[:file_cache_path]
code <<-EOH
tar -xzf node-v#{node["nodejs"]["version"]}.tar.gz
(cd node-v#{node["nodejs"]["version"]} && ./configure --prefix=#{node["nodejs"]["dir"]} && make && make install)
EOH
action :nothing
not_if "#{node["nodejs"]["dir"]}/bin/node --version 2>&1 | grep #{node["nodejs"]["version"]}"
end
remote_file "#{Chef::Config[:file_cache_path]}/node-v#{node["nodejs"]["version"]}.tar.gz" do
source node["nodejs"]["url"]
checksum node["nodejs"]["checksum"]
notifies :run, resources(:bash => "install-node"), :immediately
end
It successfully installed nodejs on my Vagrant VM but on restart it's getting executed again. How do I prevent this? I'm not that good in reading ruby code.
To make the remote_file resource idempotent (i.e. to not download a file already present again) you have to correctly specify the checksum of the file. You do this in your code using the node["nodejs"]["checksum"] attribute. However, this only works, if the checksum is correctly specified as the SHA256 hash of the downloaded file, no other algorithm (esp. not MD5) is supported.
If the checksum is not correct, your recipe will still work. However, on the next run, Chef will notice that the checksum of the existing file is different from the one you specified and will download the file again, thus notify the install node ressource and do the whole compile stuff.
With chef, it's important that recipes be idempotent. That means that they should be able to run over and over again without changing the outcome. Chef expects to be able to run all the recipes on a node periodically, and that should be ok.
Do you have a way of knowing which resource within that recipe is causing you problems? The remote_file one is the only one I'm suspicious of being non-idempotent, but I'm not sure offhand.
Looking at the Chef wiki, I find this:
Deprecated Behavior In Chef 0.8.x and earlier, Remote File is also
used to fetch files from the files/ directory in a cookbook. This
behavior is now provided by #Cookbook File, and use of Remote File for
this purpose is deprecated (though still valid) in Chef 0.9.0 and
later.
Anyway, the way chef tends to work, it will look to see if whatever "#{Chef::Config[:file_cache_path]}/node-v#{node["nodejs"]["version"]}.tar.gz" resolves to exists, and if it does, it should skip that resource. Is it possible that install-node deletes that file when it's finished installing? If so, chef will re-fetch it every time.
You can run a recipe only once overriding the run-list with -o modifier.
sudo chef-client -o "recipe[cookbook::recipe]"
-o RunlistItem,RunlistItem..., Replace current run list with specified items
--override-runlist
In my experience remote_file always runs when executing chef-client, even if the target file already exists. I'm not sure why (haven't dug into the Chef code to find the exact cause of the bug), though.
You can always write a not_if or only_if to control the execution of the remote_file resource, but usually it's harmless to just let it run every time.
The rest of your code looks like it's already idempotent, so there's no harm in running the client repeatedly.
There's an action you can specify for remote_file that will make it run conditionally:
remote_file 'target' do
source 'wherever'
action :create_if_missing
end
See the docs.
If you want to test whether your recipe is idempotent, you may be interested in ToASTER, a framework for systematic testing of Chef scripts.
http://cloud-toaster.github.io/
Chef recipes are executed with different configurations in isolated container environments (Docker VMs), and ToASTER reports various metrics such as system state changes, convergence properties, and idempotence issues.

Resources