Sftp in Chef doesn't work - windows

I'm trying to download a remote file over sftp, and I wrote this:
remote_file 'C:/adnet/mercury_package/Adnet.EASS.zip' do
source 'sftp://myusername:mypassword#packages.domain.com:22/package/Adnet.EASS.zip'
action :create
end
and the output is:
SocketError
-----------
getaddrinfo: No such host is known.
I tried to remove username and password from the URI, and put them in remote_user and remote_password, but then it complains about not having the userinfo:
"no userinfo provided in the sftp URI"
The stacktrace is here

The problem was in single quotes around the source. It works like this:
remote_file 'C:/adnet/mercury_package/Adnet.EASS.zip' do
source "sftp://myusername:mypassword#packages.domain.com:22/package/Adnet.EASS.zip"
action :create
end

Related

How can I loop through bash routine in Chef?

I have a bash script in Chef that fetches the time through NTP protocol from 3 instances running NTP server. The code at present is
if not node.run_list.roles.include?("ntp_server")
bash "ntpdate" do
code <<-EOH
/usr/sbin/ntpdate -s 10.204.255.15 10.204.251.41 10.204.251.21
EOH
end
end
This has been working just fine. However, I am supposed to automate the task such as if one of the instances is replaced, there is no manual intervention required to update the IP in the code above.
To achieve that, I have been successfully able to fetch the instances running the ntp_server role.
ntp_servers = search(:node, 'role:ntp_server')
Having done that, I am unable to add those IP's to the bash subroutine in Chef shown above in the code.
Can someone let me know how am I supposed to achieve that?
You shouldn't use bash block and call ntpdate with each chef run. ntpd should take care of clock being in sync, Chef has cookbook for this.
You could move IP addresses to the node and use join in code.
...
code "/usr/sbin/ntpdate -s #{node["ntp_ipaddresses"].join(" ")}"
...
Please, use ntp cookbook.
I managed to solve what I had posted in the question. The way I did it was using a Template and then the bash script. The recipe code now looks
ntp_servers = search(:node, 'role:ntp_server')
if not node.run_list.roles.include?("ntp_server")
template "/usr/local/bin/ntpdate.sh" do
source "ntpdate.sh.erb"
owner "root"
group "root"
mode 0644
variables(
:ntp_servers => ntp_servers
)
end
bash "ntpdate" do
user "root"
code <<-EOH
bash /usr/local/bin/ntpdate.sh
EOH
end
end
Having done this, I created a template in chef with the following configuration
#!/bin/bash
/usr/sbin/ntpdate -s <% #ntp_servers.each do |ntp_server| -%> <%= ntp_server['ipaddress'] %> <% end -%>
This way I could not dynamically add the ip addresses of the servers belonging to role ntp_server

Have two resources and append one to another in the Chef remote_file

I would like to copy http://seapower/spring.txt and http://seapower/has_sprung.txt and append second one to the first one in a new file named src_filepath.txt:
remote_file 'src_filepath.txt' do
source 'http://seapower/spring.txt', 'http://seapower/has_sprung.txt'
checksum node['nginx']['foo123']['checksum']
owner 'root'
group 'root'
mode '0755'
end
It doesn't work and just copy the first file to src_filepath.txt
Something like this is probably a good place to start and then tweak however you like:
cache1 = "#{Chef::Config[:file_cache_path]}/content1"
cache2 = "#{Chef::Config[:file_cache_path]}/content2"
# this will not redownload if cache1 exists and has not been updated
remote_file cache1 do
source "http://source.url/content1"
end
# this will not redownload if cache1 exists and has not been updated
remote_file cache2 do
source "http://source.url/content2"
end
# this will not update the file if the contents has not changed
file "/my/combined/file" do
content lazy { IO.read(cache1) + IO.read(cache2) }
end
This is not something Chef supports directly. You could use multiple remote_file resources and either a ruby_block or execute plus cat to implement the concat.
remote_file does not support concatenation, so you would not be able to implement this using that resource directly, however you could piece together the desired result using the file resource and Net::HTTP like so:
file_path = '/path/to/your_whole_file'
unless File.exist?(file_path) &&
Digest::SHA256.hexdigest(File.read(file_path)) == 'your_file_checksum'
file file_path do
content(
Net::HTTP.get(URI('http://source.url/content1')) +
Net::HTTP.get(URI('http://source.url/content2'))
)
owner 'root'
group 'root'
mode '0755'
end
end
The reason for the Digest::SHA256 call at the beginning is to prevent Chef from trying to download both files during every Chef run. Note that you may have to require the net/http and digest gems at the top of your recipe for this to work.
Also, because it's against best practices to put Ruby code directly into your recipes, you may want to wrap the above code in a simple custom resource.

Chef Recipe Compile Error

Does anyone know why the following code results the error: undefined method 'tar' for "riak-1.4.2":String
remote_file "/vagrant/usr/src/#{node.default['riak']['version'].tar.gz}" do
source "#{node.default['riak']['url']}"
mode 0755
notifies :run, "bash[extract_riak]", :immediately
end
bash "extract_riak" do
code <<-EOH
# Following is the line which causes the error.
/bin/tar xzf /vagrant/usr/src/#{node.default['riak']['version']}.tar.gz -C /vagrant/usr/src/#{node.default['riak']['version']}
EOH
notifies :run, "bash[make_riak]", :immediately
end
This line is raising the error:
remote_file "/vagrant/usr/src/#{node.default['riak']['version'].tar.gz}"
The .tar.gz should be outside the brackets, like so:
remote_file "/vagrant/usr/src/#{node.default['riak']['version']}.tar.gz"
Everything between the brackets is executed as ruby code and the result takes it's place in the string. node.default['riak']['version'].tar.gz is a chain of function calls, including calling a non-existent tar and gz function at the end. These are part of the filename, and should go outside the brackets.
As a side note, you probably want to use node[:attribute] to get attributes, and only use node.default[:attribute] to set attributes.
I recommend the ark cookbook as better choice for handling archives.
The following example recipe:
include_recipe "ark"
ark "riak" do
url "http://s3.amazonaws.com/downloads.basho.com/riak/1.4/1.4.2/riak-1.4.2.tar.gz"
version "1.4.2"
end
will install riak under the "/usr/local/riak-1.4.2" directory.
Finally, there is a riak cookbook available as well, which reportedly will also install from source.
Instead of:
#{node.default['riak']['version']}.tar.gz
you want:
#{node.default['riak']['version'].tar.gz}

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"
}
}
}
}

Checking a URL for hostname

I am trying to evaluate user-submitted urls to find out whether they contain valid hostnames (formatting) and if so, extract the hostname. Know of any libraries/methods that could help?
Example:
user_input = "www.google.com"
if user_input.has_valid_host?
hostname = user_input.get_hostname #=> "google.com"
url = "http://" + #hostname #=> "http://google.com"
else
puts "Invalid URL"
end
This example is very simple but I need the url checked against all valid domain extensions and the hostname extracted from any string (assuming that it's present)
I don't know ruby, but I wouldn't think of this as a ruby question.
I would use regex to split out the hostname as you suggest.
Then I would do a system call to the nslookup routine.
On a Windows system from the command prompt it is nslookup.
C:\Users\xyz>nslookup www.google.com
Server: UnKnown
Address: 192.168.237.2
Name: www.l.google.com
Address: 173.194.73.99
Aliases: www.google.com.localdomain
From Ruby you should do an API call instead of using the command line, but both will eventually interface to the DNS service on the local machine.
See: Is there a good DNS server library in ruby?

Resources