Chef deploy_resource private repo, ssh deploy keys and ssh_wrapper - ruby-on-rails

I'm having loads of trouble getting my Chef recipe to clone a private repo. Well, I had it working yesterday but after 'cheffin' my Vagrant box half a dozen times, I've broken it. I'm a Chef newbie as you may guess.
Following the deploy_resource guide here, I've created my deploy.rb recipe (shortened):
deploy_branch "/var/www/html/ps" do
repo git#github.com:simonmorley/private-v2.git
ssh_wrapper "/tmp/.ssh/chef_ssh_deploy_wrapper.sh"
branch "rails4"
migrate false
environment "RAILS_ENV" => node[:ps][:rails_env]
purge_before_symlink %w{conf data log tmp public/system public/assets}
create_dirs_before_symlink []
symlinks( # the arrow is sort of reversed:
"conf" => "conf", # current/conf -> shared/conf
"data" => "data", # current/data -> shared/data
"log" => "log", # current/log -> shared/log
"tmp" => "tmp", # current/tmp -> shared/tmp
"system" => "public/system", # current/public/system -> shared/system
"assets" => "public/assets" # current/public/assets -> shared/assets
)
scm_provider Chef::Provider::Git # is the default, for svn: Chef::Provider::Subversion
notifies :restart, "service[ps]"
notifies :restart, "service[nginx]"
end
In defaults, I have the following to create the dirs etc.
directory "/tmp/.ssh" do
action :create
owner node[:base][:username]
group node[:base][:username]
recursive true
end
template "/tmp/.ssh/chef_ssh_deploy_wrapper.sh" do
source "chef_ssh_deploy_wrapper.sh.erb"
owner node[:base][:username]
mode 0770
end
# Put SSH private key to be used with SSH wrapper
template "/tmp/.ssh/id_deploy" do
source "id_rsa.pub.erb"
owner node[:base][:username]
mode 0600
end
And in the wrapper:
#!/bin/sh
exec ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "/tmp/.ssh/id_deploy" "$#"
And I have created a public key and uploaded this to github.
When I deploy the recipe, it gives me an error:
deploy_branch[/var/www/html/ps] action deployEnter passphrase for key '/tmp/.ssh/id_deploy':
Obvs I don't have a password set... The private key must therefore be missing..
Just by chance, I removed the id_deploy key from the recipe, deleted the folders and ran it again. Low and behold, it started working... The reason being that the id_rsa.pub && id_rsa files were in /root/.ssh from when I manually generated them to test.
I don't understand what I'm doing wrong here. My questions are therefore:
Do I need a private and public key on each node I deploy to? The docs don't mention this.
Should this not be deploying as non-root user? I have set a user in my roles file..
Why is the ssh_wrapper not doing what it's supposed to

It took a good couple of days to figure this out properly.
Just to clarify, this is what I did to fix it. I do not know if it's correct, but it works for me.
Generate a set of public and private keys following this tutorial.
Add the public key to the Github repo that you want to clone.
Create a template in my default recipe which includes both the public and private keys. See below.
Created the relevant templates for the pub and private keys.
Created the chef_ssh_deploy_wrapper.sh.erb file (see below)
Created a deploy.rb recipe (see below)
Uploaded and added the recipes to my role. Ran chef-client.
Hey presto! Sit back with a beer and watch your repo. smartly cloned into your dir.
The templates are as follows:
Create the directories and templates:
template "/tmp/.ssh/chef_ssh_deploy_wrapper.sh" do
source "chef_ssh_deploy_wrapper.sh.erb"
owner node[:base][:username]
mode 0770
end
template "/home/#{node[:base][:username]}/.ssh/id_rsa.pub" do
source "id_rsa.pub.erb"
owner node[:base][:username]
mode 0600
end
template "/home/#{node[:base][:username]}/.ssh/id_rsa" do
source "id_rsa.erb"
owner node[:base][:username]
mode 0600
end
Create an ssh wrapper chef_ssh_deploy_wrapper.erb
#!/bin/sh
exec ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "/home/#{node[:base][:username]}/.ssh/id_rsa" "$#"
(Make sure you use the private key here or it will fail)
Finally the deploy.rb recipe:
deploy_branch node[:my_app][:deploy_to] do
repo node[:base][:repository]
ssh_wrapper "/tmp/.ssh/chef_ssh_deploy_wrapper.sh"
branch "rails4"
user node[:base][:username]
group node[:base][:username]
rollback_on_error true
migrate false
environment "RAILS_ENV" => node[:my_app][:environment]
purge_before_symlink %w{conf data log tmp public/system public/assets}
create_dirs_before_symlink []
symlinks(
"config" => "config",
"data" => "data",
"log" => "log",
"tmp" => "tmp",
"system" => "public/system",
"assets" => "public/assets"
)
scm_provider Chef::Provider::Git # is the default, for svn: Chef::Provider::Subversion
before_restart do
system("su #{node[:base][:username]} -c 'cd #{node[:my_app][:deploy_to]}/current && /usr/bin/bundle install'") or raise "bundle install failed"
system("su #{node[:base][:username]} -c 'RAILS_ENV=production /usr/local/bin/rake assets:precompile'")
end
notifies :restart, "service[my_app]"
notifies :restart, "service[nginx]"
end
The before restart has since been replaced as we were initially compiling ruby from source but decided to use rvm in the end. Much easier for multi-user installations.
NB: I'm deploying as an sudo user, if you're doing so as root (avoid this), use the /root/.ssh path instead.
I took much inspiration from this article.
Good luck, I hope this helps someone.

Your question doesn't have a link to to the deploy_resource source, so I can't be sure if this will apply, but if it uses a git resource underneath, the following might be helpful...
As described in this answer to a similar question, you can avoid creating extra script files to go with each SSH key by adding the SSH command as an "external transport" part of the repository URL:
git "/path/to/destination" do
repository "ext::ssh -i /path/to/.ssh/deployment_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no git#github.com %S /my_name/some_repo.git"
branch "master"
...
end

Related

Using local copy of a framework in podfile if exists

I have a framework and a project where I'm using my framework in. I'm trying to use the framework that is locally built during development. Is there a way to do something like:
if my-local-library-path exists, do this:
pod 'MyLibraryName', :path => "my-local-library-path"
else, do this:
pod 'MyLibraryName', git: 'https://github.com/mygithuburl', tag: '0.0.1'
Since a Podfile is actually Ruby code, let's check if the file exists:
if File.exist?("complete-path-to-a-library-file")
pod 'MyLibraryName', :path => "my-local-library-path"
else
pod 'MyLibraryName', git: 'https://github.com/mygithuburl', tag: '0.0.1'
end
I can offer the solution I use for rather long time. There is script wrapper around cocoapods command that simplify choosing installation source between local folder or remote repo. Here it is at Github. Copied here, but newer version may be available in repo
#!/usr/bin/env bash
# Assume default values
# By default install pods from remote
LOCAL=0
# By default don't delete Pods folder and Podfile.lock
DELETE=0
# By default do pod install with verbose flag and tell it to grab latest specs from the podspec repo (otherwise we can end up with different states on
# different people's machines)
COMMAND="pod install --verbose"
# By default do not tell cocoapods to grab latest specs from the podspec repo (otherwise we can end up with different states on different people's machines)
# Designed as separate operation and disabled by default because it is rather long operation and cocoapods itself remove this command from 'pod install' by default
REPO_UPDATE=0
# By default do not show help message
SHOW_HELP=0
# Parse parameters
while [[ $# > 0 ]]
do
key="$1"
case $key in
-r|--remove)
DELETE=1
;;
-l|--local)
LOCAL=1
;;
-c|--command)
COMMAND="$2"
shift # past argument
;;
-u|--update)
REPO_UPDATE=1
;;
-h|--help)
SHOW_HELP=1
;;
*)
# unknown option
;;
esac
shift # past argument or value
done
if [ $SHOW_HELP != 0 ] ; then
echo "podrun script designed as a solution to implement easy installation of pods choosing source from remote or local path."
echo
echo "podrun script can run pre- and post-run scripts. To run pre-run script create 'pre-run.sh' file at the same directory as podrun script. \
To run post-run script create 'post-run.sh' file at the same directory as podrun script. No parameters can be passed to them. \
To pass parameters you can run another script from 'pre-run.sh' and 'post-run.sh' with parameters, described in them."
echo
echo "Usage:"
echo "podrun [-c cocoapods_command|-l|-r|-d|-u|-h]"
echo
echo "-с (--command) Specifies command to be evaluated by Cocoapods. If omitted, 'pod install --verbose' command runs by the default."
echo
echo "-l (--local) Makes Cocoapods to install pods from local paths. This is done by installing DC_INSTALL_LOCALLY environment variable to 'true' value.\
To achieve using this check in podfile value of its variable. If omitted, DC_INSTALL_LOCALLY will be set to 'false' value. This means, that pods will be installed from the remote."
echo
echo "-r (--remove) Deletes Podfile.lock file and Pods folder from the current path. By default these files won't be deleted (if flag is omitted)."
echo
echo "-u (--update) Makes cocoapods to update specs repos before installing pods (this operation may be rather long)."
echo
echo "-h (--help) Shows help (this message)."
exit 0
fi
# Print out parameters for debug case
echo DELETE = "${DELETE}"
echo LOCAL = "${LOCAL}"
echo COMMAND = "${COMMAND}"
echo REPO_UPDATE = "${REPO_UPDATE}"
# Check if we have Podfile in our working folder
# (a kind of protection, that we won't remove unexpected Pods folder)
# If we are in the wrong folder, we'll get error later from Cocoapods nevertheless
# this is preventive
PODFILE="Podfile"
if [ -f "$PODFILE" ] ; then
echo "$PODFILE found."
else
echo "$PODFILE not found. It seems, that you're in a wrong directory. Aborting..."
exit 1
fi
# Set temporary environment variable that allows us to choose podfile variant
if [ $LOCAL != 0 ] ; then
export DC_INSTALL_LOCALLY=true
else
export DC_INSTALL_LOCALLY=false
fi
# Delete Pods folder and Podfile.lock file, if necessary
if [ $DELETE != 0 ] ; then
rm -rf Pods
rm -f Podfile.lock
fi
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Run pre-run.sh script
echo "Start pre-run script"
pushd $DIR
sh pre-run.sh
popd
# Run update of repos to grab latest specs from the podspec repo (otherwise we can end up with different states on
# different people's machines)
if [ $REPO_UPDATE != 0 ] ; then
pod repo update
fi
# Run command
${COMMAND}
if [ $? -ne 0 ]; then
exit 1
fi
# Run post-run.sh script
echo "Start post-run script"
pushd $DIR
sh post-run.sh
popd
# Unset temporary environment variable that allows us to choose podfile variant
unset DC_INSTALL_LOCALLY
Steps to use:
Download script or clone repository with it from Github. Add script to the folder with your podfile or place somewhere else and add path to it to your PATH variable.
Edit or create your podfile with the next format:
if "#{ENV['DC_INSTALL_LOCALLY']}" == "true"
puts "Using local pods"
pod "SomePod", :path => "<SomePod_local_path>"
else
puts "Using remote pods"
pod 'SomePod'
end
Open terminal with podfile folder and run podrun -l to install pods locally or podrun to install from remote.
Run podrun -h for help.

.gitignore not ignoring files

Here is my .gitignore file:
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config.
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
# Ignore all logfiles and tempfiles.
/log/*
!/log/.keep
/tmp
# Ignore application configuration
/config/application.yml
/config/application.yml.bak
*.bak
Now, my repository is at https://github.com/rmohan80/learn-rails
Why would my latest commit -- "add configuration for email" add Readme.rdoc.bak but ignore .gitignore.bak
Any clues?
The star character does do match files beginning with a period.
You can add .*.bak to ignore them in your case or you can change the glob option in your shell :
# capture dot file
shopt -s dotglob
# do git stuff here
# stop capturing dot file
shopt -u dotglob
A similar problem solved here : https://stackoverflow.com/a/19365350
You have to checkout the HEAD, so that your repository looks unmodified. Then run the following:
$ echo '*.*.bak' >> .gitignore
To exclude files that are formatted like README.md.bak.
And run
$ echo '**/*.bak' >> .gitignore
to exclude files that are formatted like README.bak anywhere in the tree below the current directory.
Having .bak.bak files is something you don't want.

git error while deploying through chef

i am trying to deploy rails app through chef code. recipe contains code
deploy_revision "testapp" do
repo "REPO_URL"
ssh_wrapper "/home/ubuntu/.ssh/chef_ssh_deploy_wrapper.sh"
environment({
"RAILS_ENV" => "staging"
})
deploy_to "/home/ubuntu/testapp"
branch "master"
user "ubuntu"
group "ubuntu"
scm_provider Chef::Provider::Git
end
and sshwrapper file contains
exec ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no github.com -i "/home/ubuntu/.ssh/id_rsa" "$#"
when i ran bootstrap
im getting error as follows
STDERR: Warning: Permanently added 'github.com,192.30.252.129' (RSA) to the list of known hosts.
192.168.1.32 Permission denied (publickey).
192.168.1.32 fatal: Could not read from remote repository.
192.168.1.32 Please make sure you have the correct access rights
192.168.1.32 and the repository exists.
192.168.1.32 ---- End output of git ls-remote "REPO_URL" master* ----
so , unable to figure it out the reason. Any help would be appreciated.

Rename project in Ruby on Rails 4.0.2

I'm aware of the Rename plugin for rails (https://github.com/get/Rename), but does anyone know of a way to easily rename a project in Rails 4.0.2 seeing as plugins are deprecated beginning with Rails 4?
Just consider you have used
rails new blog
This will create a blog application. Now if you want to rename the folder blog, just use
$mv blog blog_new
This will just rename the folder and the application will run with no issues as external folder name changes will not affect the application. Else you need to change each file as specified by srt32 but i don't see any specific reason to change project name from inside.
Assuming your app name is my_app you can run something like grep -r 'my_app' . from the root of your project and find all the places where the app name is referenced. It shouldn't be that bad to go update them. The list of places should look something like:
config/application.rb
config/environment.rb
config/environments/development.rb
config/environments/test.rb
config/environments/production.rb
config/initializers/secret_token.rb
config/routes.rb
Rakefile
Enter following commands
$ rails new ProjectToRename
$ cd ProjectToRename
$ grep -ri 'project_?to_?rename'
Finally done.
You'll need to rename the top-level directory yourself:
$ cd ..
$ mv ProjectToRename SomeNewName
I've written the following script to do just that. You can see it also at https://gist.github.com/danielpclark/8dfcdd7ac63149323bbc
#!/usr/bin/ruby
# Rename Rails Project (File: rename_rails)
# Copyright 6ft Dan(TM) / MIT License
# Check the config/application.rb for capital usage in project name by model OldProjectName
# Usage: rename_rails OldProjectName NewAwesomeName
# Replace string instances of project name
`grep -lR #{ARGV[0]} | xargs sed -i 's/#{ARGV[0]}/#{ARGV[1]}/g'`
`grep -lR #{ARGV[0].downcase} | xargs sed -i 's/#{ARGV[0].downcase}/#{ARGV[1].downcase}/g'`
# Rename Rails directory if it exists
if File.directory?(ARGV[0])
`mv #{ARGV[0]} #{ARGV[1]}`
drc = ARGV[1]
elsif File.directory?(ARGV[0].downcase)
`mv #{ARGV[0].downcase} #{ARGV[1]}`
drc = ARGV[1]
end
# Delete temporary files (helps prevent errors)
drc ||= ''
if ['cache','pids','sessions','sockets'].all? {
|direc| File.directory?(File.join(drc,'tmp', direc)) }
FileUtils.rm_rf(File.join(drc,'tmp'))
end
And I've created a howto video on YouTube. http://youtu.be/dDw2RmczcDA

What tools do you use to avoid accidently pushing private information to a github repo on a rails project?

Are there any tools you use to scrub your project before pushing to a public github repo. How do you maintain your private settings, while pushing your source code to a public repo? What is the best practice?
.gitignore file is your friend.
I don't keep database.yml in git. I write it in a cap setup task. For email addresses and other things, I read them at app initialize from a file in the file-system. Again, not under source code management and written to the shared directory during cap setup.
Here's a sample:
namespace :deploy do
task :start do ; end
task :stop do ; end
task :setup do
run <<-CMD
mkdir -p -m 775 #{release_path} #{shared_path}/system #{shared_path}/media &&
mkdir -p -m 777 #{shared_path}/log &&
mkdir -p -m 777 #{shared_path}/pids &&
mkdir -p #{deploy_to}/#{shared_dir}/config
CMD
end
require 'erb'
after deploy:setup do
db_config = ERB.new <<-EOF
production:
adapter: mysql2
database: my_fine_database
host: 127.0.0.1
username: database_user
password: database_password
EOF
email_config = ERB.new <<-EOF
---
:user_name: me#mydomain.com
:password: verysecret
:port: 25
:address: mydomain.com
:domain: mydomain.com
:authentication: :login
EOF
put db_config.result, "#{shared_path}/config/database.yml"
put email_config.result, "#{shared_path}/config/creds.yml"
end
and in my environment.rb, I put:
credentials = File.join(Rails.root, 'config/creds.yml')
ActionMailer::Base.smtp_settings = YAML.load(File.open(credentials)) if File.exists?(credentials)
What other sensitive information might you be storing?
Sometimes you don't want to gitignore an entire file - maybe you'd prefer to just scrub out a line or two of sensitive data. I've written lucido specifically for this purpose.
lucido (pronounced loo-CHEE-dough) is a simple script designed to ... strip and restore sensitive data with ease. Within a git repository, lucido prevents you from committing your sensitive data, and automatically restores it for you after any merges.

Resources