capistrano 3 (ruby) variable scope - ruby

I have some problems with a capistrano 3 script regarding the scope of variables. I read the variables from hiera like this:
deployment = hiera("deployment")
deployment.each_pair{|k,v|
binding.eval("#{k}='#{v}'")
}
My hiera file looks like this:
---
deployment:
webserver_group: 'www-data'
app_config_environment: 'prod'
application: 'site_manager'
translator_deploymentLockPath: '/tmp/translation_lockfile.lock'
If I try to access the variables in a task I receive an error message like this:
Exception while executing on host xxxx: undefined local variable or
method `translator_deploymentLockPath' for
Here is how a task looks like:
desc "creates a new lockfile"
task :create_lock do
on roles(:app), in: :parallel do
execute :touch, "#{translator_deploymentLockPath}"
end
end
If I declare the variables global (like this: binding.eval("$#{k}='#{v}'") ) I can read them in the tasks using $translator_deploymentLockPath instead of #{translator_deploymentLockPath} but this is not a solution for me because I have a lot of code to modify.
What am I missing? How can I read the variables from hiera (using eyaml backend) and declare them so I can use them in my tasks?

Related

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.

Why cant I access a variable outside a Capistrano task

I have want to set this deferred variable in Capistrano which depends on some variable I set during calling the task
set(:installation_dir) do
if target == "staging"
"/some/path"
else
"/some/other/path"
end
end
task :foo do
p "INSTALLATION_DIR >>> #{installation_dir}"
end
If running the task this error happens
Hector:monitoring-agent robertj$ cap foo -s target=development
/Users/robertj/.rvm/gems/ruby-2.0.0-p0#pf/gems/capistrano-2.15.4/lib/capistrano/configuration/variables.rb:122:in
`method_missing_with_variables': undefined local variable or method `target' for
#<Capistrano::Configuration:0x007fd6a22f9100> (NameError)
This is making me mad. Why doesnt Capistrano 2.x have a simple way to access variables where ever I call.
Looks like, fetch do what you want
p "INSTALLATION_DIR >>> #{fetch(:installation_dir)}"
When you run it like that, you're setting an environment variable. To use it in your Capistrano script you need to set :target, ENV['target'].

Opsworks Custom Chef Recipies

I have successfully deployed my application using AWS OpsWorks, now I am trying to implement a custom Chef Cookbook that will allow me to set the bash environment variables. I have setup the Git repo the cookbook is being updated with OpsWorks. I generated the cookbook using the knife command on my dev box which is really just the directory structure with a recipes/default.rb file containing a few lines of code.
When I try to do something like the following I seem to keep getting errors
node[:deploy].each do |application, deploy|
deploy = node[:deploy][application]
command "ls -la"
end
(Note: ls -la is just for testing i know this will not set the environment variables)
I get the following error: ERROR: Caught exception during execution of custom recipe: xyz-enviroment: NoMethodError - undefined method command' for #<Chef::Recipe:0x7feb59200c00> - /opt/aws/opsworks/releases/20130328224322_109/vendor/bundle/ruby/1.8/gems/chef-0.9.15.5/bin/../lib/chef/mixin/recipe_definition_dsl_core.rb:56:in method_missing
Also if I try something like
execute "setting up the enviroment" do
# TODO: Add code that does something here
end
I get the following error:
execute[setting up the enviroment] (/opt/aws/opsworks/current/site-cookbooks/xyz-enviroment/recipes/default.rb:18:in `from_file') had an error:
No such file or directory - setting up the enviroment
I'm new to Chef so I'm sure there is something simple I'm doing wrong I just haven't been able to figure it out. Thanks in advance for the help.
I already solved my issue before seeing the responses below, they may have worked to solve the issue but I don't have the time to go back and try them now.
My solution was to use a Chef template to create an initializer file to set the variables when rails boots up the application.
# deafult.rb
node[:deploy].each do |application, deploy|
deploy = node[:deploy][application]
execute "restart Rails app #{application}" do
cwd deploy[:current_path]
command node[:opsworks][:rails_stack][:restart_command]
action :nothing
end
template "#{deploy[:deploy_to]}/current/config/initializers/dev_enviroment.rb" do
source "dev_enviroment.erb"
cookbook 'dev-enviroment'
group deploy[:group]
owner deploy[:user]
variables(:dev_env => deploy[:dev_env])
notifies :run, resources(:execute => "restart Rails app #{application}")
only_if do
File.exists?("#{deploy[:deploy_to]}") && File.exists?("#{deploy[:deploy_to]}/current/config/")
end
end
end
dev_enviroment.erb
ENV['VAR1'] = "<%= #dev_env[:VAR1] %>"
ENV['VAR2'] = "<%= #dev_env[:VAR2] %>"
The custom Chef JSON used in the Opsworks stack layer:
{
"deploy": {
"myapp": {
"dev_env": {
"VAR1": "INFO1",
"VAR2": "INFO2",
}
}
}
}
You didn't specify what command to run, so it's actually trying to run setting up the environment, which isn't a valid command.
Try instead specifying the command attribute inside the block:
execute "setting up the enviroment" do
command "/path/to/command --flags"
end
Alternatively, set the resource name to the command itself:
execute "/path/to/command --flags" do
# TODO: Add code that does something here
end
Your second question was correctly answered by clb. As for your first, 'command' is not a valid chef resource, you want something like:
node[:deploy].each do |application, deploy|
deploy = node[:deploy][application]
execute "running a command for #{application}" do
command "ls -la"
end
end
OpsWorks has a new feature, where you can add Environmentvariables on the App
You can access them via
node[:deploy]['appshortname'][:environment_variables][:variable_name]
( see http://docs.aws.amazon.com/opsworks/latest/userguide/attributes-json-deploy.html#attributes-json-deploy-app-environment )
So you can directly set them for your chef context like this:
node[:deploy]['magento']['environment_variables'].each {|key, value| ENV[key]=value }
And you can "dump" that into a shell script for example like this:
file "#{release_path}/environment.sh" do
content ENV.reduce("#!/bin/bash\n") { |a, e| a + "export #{e[0]}=\"#{e[1]}\"\n" }
mode '0775'
end
We do something similar, and solved it by adding our environment variables to the json config file in Opsworks dashboard. In chef, we write the file on deploy with this template: https://github.com/octocall/opsworks-cookbooks/blob/34e60267a4d3b5b9cf84e91829cc80c98c26f8ed/deploy/definitions/opsworks_rails.rb#L26, and then we symlink it using the "symlink_before_migrate" property of the json file, like this:
{
"deploy": {
"APPNAME": {
"symlink_before_migrate": {
"config/.env": ".env"
}
}
}
}

Loading variables from a separate file for Rake

I am trying to get into Ruby / Rake. I thought it would be a good idea to separate some configuration out of the Rakefile in a file called configuration. This file has the following content:
email="nobody#nowhere.com"
password="somepassword"
proxy_server="someproxy.com:8080"
puts "config loaded"
Then in my Rakefile I load the configuration file by
load 'configuration'
and use the variables defined later on, e.g.:
task :dummy do
sh = "echo #{proxy_server}"
end
But then I get an error stating that the variable is not defined:
rake aborted!
undefined local variable or method `proxy_server' for #<Object:0xb783595c>
How can I access the configuration variables defined in the configuration file?
Visibility: variables from configuration file are not visible in the script that has executed it. You need to establish a common context, for example using global variables (even if globals are evil:-)) like:
$proxy_server="someproxy.com:8080"
and then
task :dummy do
sh = "echo #{$proxy_server}"
end
But since rake files are Ruby themselves why use a separate configuration file in the first place? If you need to change the config you can as well edit rake file.

Capistrano Checking for undefined variable in Task

In Capistrano using the Multi-stage extension I have two environments: prod and testing.
I need a few variables in testing.rb that are not needed in prod.rb and I want some of my tasks to be able to check if the variable is defined and use it if it is, but ignore it if it is not set.
So, in testing.rb I would have something like:
set :foo, 'bar'
prod.rb wouldn't have any reference to :foo since it doesn't need it. In one of my tasks, I would like to do something like:
if defined?(foo)
# do something with foo
else
# do something without foo
end
But I keep getting the error:
undefined local variable or method 'foo'
Is there a way to test for undefined global variables in the task? Or do I have to do something like:
set :foo, ''
In all my environments that don't need the :foo variable?
Try using exists?(:foo) instead of defined?(foo), as recommended in the Capistrano docs.

Resources