Chef - using powershell_script from within ruby_block - ruby

Is it possible to use powershell_script from within a ruby_block?
I want to run the powershell script during the convergence phase and not the compilation phase.
The current code doesn't work:
ruby_block 'ruby block so that code is run during convergence phase and not compilation phase' do
block do
buildNumber = "123"
powershell_script 'run powershell script' do
environment ({'buildNumber' => buildNumber})
code "path/to/script/script.ps1"
end
end
action :run
end
I know you can use a guard on the powershell_script outside the ruby_block to make it run during convergence but I need the local variable buildNumber that is defined inside the ruby block.

the following code worked for me:
x = Chef::Resource::PowershellScript.new('unzipper script',run_context)
x.code 'D:/git/chef/cookbooks/java-8-upgrade/unzipper.ps1'
x.environment ({'buildNumber' => buildNumber})
x.run_action :run

In the simple case, you can probably use the powershell_out helper method. For more complex cases, make a custom resource and use the normal resource. Do not use manual resource invocation (Chef::Resource::Foo.new as we explicitly do not support this and know for a fact that it breaks things).

Related

action_class.class_eval method not working with execute resource's environment property

I have an interesting problem where I refactored a recipe by creating a Chef resource to handle some tasks I may need in other recipes. For instance, I've created the following action:
resource_name :my_command
action :run do
execute "Execute my command" do
environment ({"SETTINGS_FOLDER" => node['settings']['folder']})
command "#{command_exe} -some -params"
end
end
action_class.class_eval do
def command_exe
"#{node['command']['folder']}\\bin\\command.exe"
end
end
When I call my_command from a recipe it works as expected. However I have several other actions that this resource will implement that'll all use the same environment. So what I did was refactor the resource to look like this:
resource_name :command
action :run do
execute "Execute my command" do
environment env
command "#{command_exe} -some -params"
end
end
action_class.class_eval do
def command_exe
"#{node['command']['folder']}\\bin\\command.exe"
end
def env
{"SETTINGS_FOLDER" => node['settings']['folder']}
end
end
What happens now is, once chef-client executes the my_command resource it appears as though the SETTINGS_FOLDER environment variable on the machine winds up looking like this:
SETTINGS_FOLDER = ""C:\my\settings\folder""
Notice the doubled double-quotes? I'm not sure why this is happening, but it makes my command.exe very angry :(
The ['settings']['folder'] attribute is defined in the cookbook's attributes/default.rblike so:
default['settings']['folder'] = 'C:\\my\\settings\\folder'
My node is running chef-client 13.0.118
EDIT I think the doubled double-quotes was a red herring. I think the logger just represented the hash in that way. My new thought is that perhaps the env method is not being evaluated before being passed to the environment, but rather the function reference itself is being passed. Bear with me, Ruby isn't my first language...
The "env" method name might be a reserved word or is getting stomped later in the run. Try a different name for that method, perhaps?

Chef - get variable from ruby block

I try to get a variable from my ruby block, but Chef doesn't recognise my variable outside of this block. How can I retrieve any variable out of ruby block? Thanks in advance.
ruby_block 'fetch_path' do
block do
current_path = `sudo cat /var/chef/cache/revision`
new_path = current_path.to_s.split(',').last.split('"').drop(1).first
Chef::Log.info("### Your Current Directory: '#{new_path}' ###")
end
end
Chef::Log.info("### Your Current Directory: '#{new_path}' ###")
Within the block I can get a value, but, out of block - no.
There's two thing here.
First, your second Chef::Log.info will be run at compilation phase, at this time your ruby_block has not been converged. See here about it. You can prefix your logs with 1) and 2) to witch one runs first.
Second, there's a scoping problem, when you define a variable in a block, it is available only within this block.
In chef you can use node.run_state['variable'] as a global variable usable in all recipes, without an use case it's hard to showcase this.
Side note: you should not use the backticks `` construction to execute commands and prefer using shell_out from the recipe DSL.

Capistrano Ruby: Refactoring Reused String Values

I have a couple tasks within my deploy.rb file that execute within my theme folder.
I currently define them as follows:
namespace :deploy do
desc 'NPM Build Production'
task :npm_build_production do
on roles(:app) do
within "#{release_path}/web/app/themes/example" do
execute :npm, "install --silent --no-progress"
execute :npm, "run build:production"
end;
end
end
end
before 'deploy:updated', 'deploy:npm_build_production'
Everything is working correctly with this implementation, but since there are multiple instances where I use this path, I'd like to extract it to a symbol or variable. I am new to Ruby and am running into some issues.
I have tried using the code below, but for some reason the path ends up being wrong when executed in my tasks.
set :theme_path, -> { "#{release_path}/web/app/themes/example" }
These variables are set on different stages of implementation. for example the release_path variable is not defined in the setting variables section. Move your variable to your execution block for tasking and it will work . for example this snipped will work
namespace :deploy do
desc 'NPM Build Production'
task :npm_build_production do
on roles(:app) do
theme_path = "#{release_path}/web/app/themes/example"
within theme_path do
execute :npm, "install --silent --no-progress"
execute :npm, "run build:production"
end;
end
end
end
also you will be able to fetch some variables using this way while setting other variables like fetch(:varible_name) but I am not sure it is feasible for the release_path since it is a scoped variable
here is how you could do it with a function
def theme_path(release_path)
"#{release_path}/web/app/themes/example"
end
just call this function with your release_path variable and you will have the themepath wherever you want. inside your deployment logic.

Passing variables between chef resources

i would like to show you my use case and then discuss possible solutions:
Problem A:
i have 2 recipes, "a" and "b".. "a" installs some program on my file system (say at "/usr/local/bin/stuff.sh" and recipe "b" needs to run this and do something with the output.
so recipe "a" looks something like:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh"
(the script just echo(es) "stuff" to stdout)
and recipe "b" looks something like:
include_recipe "a"
var=`/usr/local/bin/stuff.sh`
(note the backquotes, var should contain stuff)
and now i need to do something with it, for instance create a user with this username. so at script "b" i add
user "#{node[:var]}"
As it happens, this doesn't work.. apparently chef runs everything that is not a resource and only then runs the resources so as soon as i run the script chef complains that it cannot compile because it first tries to run the "var=..." line at recipe "b" and fails because the "execute ..." at recipe a did not run yet and so the "stuff.sh" script does not exist yet.
Needless to say, this is extremely annoying as it breaks the "Chef runs everything in order from top to bottom" that i was promised when i started using it.
However, i am not very picky so i started looking for alternative solutions to this problem, so:
Problem B: i've run across the idea of "ruby_block". apparently, this is a resource so it will be evaluated along with the other resources. I said ok, then i'd like to create the script, get the output in a "ruby_block" and then pass it to "user". so recipe "b" now looks something like:
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`
end
end
user "#{node[:var]}"
However, as it turns out the variable (var) was not passed from "ruby_block" to "user" and it remains empty. No matter what juggling i've tried to do with it i failed (or maybe i just didn't find the correct juggling method)
To the chef/ruby masters around: How do i solve Problem A? How do i solve Problem B?
You have already solved problem A with the Ruby block.
Now you have to solve problem B with a similar approach:
ruby_block "create user" do
block do
user = Chef::Resource::User.new(node[:var], run_context)
user.shell '/bin/bash' # Set parameters using this syntax
user.run_action :create
user.run_action :manage # Run multiple actions (if needed) by declaring them sequentially
end
end
You could also solve problem A by creating the file during the compile phase:
execute "echo 'echo stuff' > /usr/local/bin/stuff.sh" do
action :nothing
end.run_action(:run)
If following this course of action, make sure that:
/usr/local/bin exist during Chef's compile phase;
Either:
stuff.sh is executable; OR
Execute it through a shell (e.g.: var=`sh /usr/local/bin/stuff.sh`
The modern way to do this is to use a custom resource:
in cookbooks/create_script/resources/create_script.rb
provides :create_script
unified_mode true
property :script_name, :name_property: true
action :run do
execute "creating #{script_name}" do
command "echo 'echo stuff' > #{script_name}"
not_if { File.exist?(script_name) }
end
end
Then in recipe code:
create_script "/usr/local/bin/stuff.sh"
For the second case as written I'd avoid the use of a node variable entirely:
script_location = "/usr/local/bin/stuff.sh"
create_script script_location
# note: the user resources takes a username not a file path so the example is a bit
# strange, but that is the way the question was asked.
user script_location
If you need to move it into an attribute and call it from different recipes then there's no need for ruby_blocks or lazy:
some cookbook's attributes/default.rb file (or a policyfile, etc):
default['script_location'] = "/usr/local/bin/stuff.sh"
in recipe code or other custom resources:
create_script node['script_location']
user node['script_location']
There's no need to lazy things or use ruby_block using this approach.
There are actually a few ways to solve the issue that you're having.
The first way is to avoid the scope issues you're having in the passed blocks and do something like ths.
include_recipe "a"
this = self
ruby_block "a_block" do
block do
this.user `/usr/local/bin/stuff.sh`
end
end
Assuming that you plan on only using this once, that would work great. But if you're legitimately needing to store a variable on the node for other uses you can rely on the lazy call inside ruby to do a little work around of the issue.
include_recipe "a"
ruby_block "a_block" do
block do
node.default[:var] = `/usr/local/bin/stuff.sh`.strip
end
end
user do
username lazy { "#{node[:var]}" }
end
You'll quickly notice with Chef that it has an override for all default assumptions for cases just like this.

How to define a function/action/... in chef that returns a value which can be used in e.g. not_if

I'm learning chef at the moment and I'm trying to write everything in a way that repeated provisioning doesn't break anything.
I have a server that is deployed on the machine and then there is some code loaded into it. The next time of provisioning I like to test first if the code has been loaded already. And I want to do it in a generic way because I use it in different recipes.
My idea would be to define a function/defintion/etc.. I can call the function which tests the condition and returns a value. My hopes would be that I can use this function/... in a not_if clause for other actions.
Is there a way to do this in chef with a defintion/action/provider/... or would I need to add some rubyish stuff somewhere?
Resources in Chef all have conditional execution.
The not_if and only_if statements can take a shell command as a string or a ruby block to determine if they should perform their action or not.
user "myuser" do
not_if "grep myuser /etc/password"
action :create
end
You might have a node attribute and use that as your conditional or call a ruby method that returns true or false.
template "/tmp/somefile" do
mode "0644"
source "somefile.erb"
not_if { node[:some_value] }
end
https://web.archive.org/web/20111120120013/http://wiki.opscode.com/display/chef/Resources#Resources-ConditionalExecution

Resources