buckknife 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/HISTORY.md +8 -0
- data/README.md +65 -0
- data/Rakefile +27 -0
- data/buckknife.gemspec +31 -0
- data/lib/buckknife.rb +44 -0
- data/lib/buckknife/command.rb +33 -0
- data/lib/buckknife/commands/add_role.rb +13 -0
- data/lib/buckknife/commands/base.rb +24 -0
- data/lib/buckknife/commands/bootstrap.rb +28 -0
- data/lib/buckknife/commands/capistrano.rb +46 -0
- data/lib/buckknife/commands/create.rb +42 -0
- data/lib/buckknife/commands/euca_create.rb +60 -0
- data/lib/buckknife/commands/rackspace_create.rb +58 -0
- data/lib/buckknife/commands/run_client.rb +49 -0
- data/lib/buckknife/data_accessor.rb +29 -0
- data/lib/buckknife/environment.rb +16 -0
- data/lib/buckknife/helper.rb +39 -0
- data/lib/buckknife/knife_helper.rb +22 -0
- data/lib/buckknife/node.rb +30 -0
- data/lib/buckknife/project.rb +80 -0
- data/lib/buckknife/templates/deploy_environments.rb.erb +85 -0
- data/lib/buckknife/templates/project.json.erb +75 -0
- data/lib/buckknife/version.rb +3 -0
- data/lib/chef/knife/project_add_role.rb +24 -0
- data/lib/chef/knife/project_bootstrap.rb +24 -0
- data/lib/chef/knife/project_capistrano.rb +19 -0
- data/lib/chef/knife/project_create.rb +53 -0
- data/lib/chef/knife/project_info.rb +13 -0
- data/lib/chef/knife/project_list.rb +17 -0
- data/lib/chef/knife/project_new.rb +28 -0
- data/lib/chef/knife/project_run_client.rb +30 -0
- data/lib/chef/knife/project_show.rb +34 -0
- data/spec/buckknife/commands/add_role_spec.rb +14 -0
- data/spec/buckknife/commands/bootstrap_spec.rb +22 -0
- data/spec/buckknife/commands/capistrano_spec.rb +13 -0
- data/spec/buckknife/commands/create_spec.rb +48 -0
- data/spec/buckknife/commands/run_client_spec.rb +15 -0
- data/spec/buckknife/environment_spec.rb +14 -0
- data/spec/buckknife/helper_spec.rb +11 -0
- data/spec/buckknife/node_spec.rb +18 -0
- data/spec/buckknife/project_spec.rb +49 -0
- data/spec/data_bags/projects/myapp.json +139 -0
- data/spec/knife.rb +4 -0
- data/spec/knife/project_bootstrap_spec.rb +0 -0
- data/spec/knife/project_info_spec.rb +17 -0
- data/spec/knife/project_list_spec.rb +18 -0
- data/spec/spec_helper.rb +27 -0
- metadata +251 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module BuckKnife
|
2
|
+
module Commands
|
3
|
+
class RackspaceCreate < Create
|
4
|
+
include Base
|
5
|
+
|
6
|
+
# --bootstrap-version VERSION The version of Chef to install
|
7
|
+
# -N, --node-name NAME The Chef node name for your new node
|
8
|
+
# -s, --server-url URL Chef Server URL
|
9
|
+
# -k, --key KEY API Client Key
|
10
|
+
# --color Use colored output
|
11
|
+
# -c, --config CONFIG The configuration file to use
|
12
|
+
# --defaults Accept default values for all questions
|
13
|
+
# -d, --distro DISTRO Bootstrap a distro using a template; default is 'ubuntu10.04-gems'
|
14
|
+
# -e, --editor EDITOR Set the editor to use for interactive commands
|
15
|
+
# -E, --environment ENVIRONMENT Set the Chef environment
|
16
|
+
# -f, --flavor FLAVOR The flavor of server; default is 2 (512 MB)
|
17
|
+
# -F, --format FORMAT Which format to use for output
|
18
|
+
# -I, --image IMAGE The image of the server
|
19
|
+
# --no-color Don't use colors in the output
|
20
|
+
# -n, --no-editor Do not open EDITOR, just accept the data as is
|
21
|
+
# -u, --user USER API Client Username
|
22
|
+
# --prerelease Install the pre-release chef gems
|
23
|
+
# --print-after Show the data after a destructive operation
|
24
|
+
# --rackspace-api-auth-url URL Your rackspace API auth url; default is 'auth.api.rackspacecloud.com'
|
25
|
+
# -K, --rackspace-api-key KEY Your rackspace API key
|
26
|
+
# -A USERNAME Your rackspace API username
|
27
|
+
# --rackspace-api-username
|
28
|
+
# -r, --run-list RUN_LIST Comma separated list of roles/recipes to apply
|
29
|
+
# -S, --server-name NAME The server name
|
30
|
+
# -P, --ssh-password PASSWORD The ssh password
|
31
|
+
# -x, --ssh-user USERNAME The ssh username; default is 'root'
|
32
|
+
# --template-file TEMPLATE Full path to location of template to use
|
33
|
+
# --sudo Execute the bootstrap via sudo
|
34
|
+
# -V, --verbose More verbose output. Use twice for max verbosity
|
35
|
+
# -v, --version Show chef version
|
36
|
+
# -y, --yes Say yes to all prompts for confirmation
|
37
|
+
# -h, --help Show this message
|
38
|
+
|
39
|
+
def command
|
40
|
+
super.tap do |c|
|
41
|
+
c.opt '-S', node.name
|
42
|
+
c.opt '-K', project.rs_api_key
|
43
|
+
c.opt '-A', project.rs_api_username
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def image_id
|
50
|
+
node.image_id || '49'
|
51
|
+
end
|
52
|
+
|
53
|
+
def flavor
|
54
|
+
BuckKnife::Helper.rackspace_flavors[ node.size ].to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module BuckKnife
|
2
|
+
module Commands
|
3
|
+
# options is a hash with one or more of the following keys specified:
|
4
|
+
# :project => Project Name eg. myapp
|
5
|
+
# :environment => Environment Name eg. staging
|
6
|
+
# :node => Node Name eg. myapp-staging-app1
|
7
|
+
# :ssh_user => (optional) ssh user to connect with
|
8
|
+
# :ssh_password => (optional) ssh user's password
|
9
|
+
class RunClient
|
10
|
+
include Base
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@project = BuckKnife::Project.find( options[:project] )
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def command
|
18
|
+
super.tap do |c|
|
19
|
+
c.arg 'ssh', query, 'sudo chef-client'
|
20
|
+
c.opt '--attribute', 'ipaddress'
|
21
|
+
c.opt '-x', ssh_username
|
22
|
+
c.opt '-P', ssh_password
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def query
|
29
|
+
result = []
|
30
|
+
result << "project:#{@options[:project]}" if @options[:project]
|
31
|
+
result << "environment:#{@options[:environment]}" if @options[:environment]
|
32
|
+
result << "name:#{@options[:node]}" if @options[:node]
|
33
|
+
result.join(' AND ')
|
34
|
+
end
|
35
|
+
|
36
|
+
def node
|
37
|
+
@project.node(@options[:node]) || @project.nodes.first
|
38
|
+
end
|
39
|
+
|
40
|
+
def ssh_username
|
41
|
+
node.ssh_username
|
42
|
+
end
|
43
|
+
|
44
|
+
def ssh_password
|
45
|
+
node.ssh_password
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module BuckKnife
|
2
|
+
module DataAccessor
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
|
6
|
+
def _read_data(key)
|
7
|
+
@data[key.to_s]
|
8
|
+
end
|
9
|
+
|
10
|
+
def _write_data(key, val)
|
11
|
+
@data[key.to_s] = val
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def data_accessor(*names)
|
17
|
+
names.each do |name|
|
18
|
+
define_method(name) do
|
19
|
+
_read_data(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
define_method("#{name}=") do |val|
|
23
|
+
_write_data(name, val)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module BuckKnife::Helper
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def parse_node_ip(node, interface)
|
5
|
+
network = node['network']
|
6
|
+
interfaces = network['interfaces']
|
7
|
+
iface = interfaces[interface]
|
8
|
+
addresses = iface['addresses']
|
9
|
+
|
10
|
+
return addresses.keys.select {|key| addresses[key]['family'] == 'inet'}.first
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_project_data_bag(project)
|
14
|
+
project_data_bag_file = File.expand_path File.join( BuckKnife.project_root, "#{project}.json" )
|
15
|
+
project_data = JSON.parse File.read(project_data_bag_file)
|
16
|
+
end
|
17
|
+
|
18
|
+
def combine_roles_and_recipes(roles=[], recipes=[], separator=' ')
|
19
|
+
role_array = roles.map { |role| "role[#{role}]" }
|
20
|
+
recipe_array = recipes.map { |recipe| "recipe[#{recipe}]" }
|
21
|
+
roles_and_recipes = role_array.concat(recipe_array).join(separator)
|
22
|
+
|
23
|
+
roles_and_recipes
|
24
|
+
end
|
25
|
+
|
26
|
+
def rackspace_flavors
|
27
|
+
{
|
28
|
+
'256M' => 1,
|
29
|
+
'512M' => 2,
|
30
|
+
'1G' => 3,
|
31
|
+
'2G' => 4,
|
32
|
+
'4G' => 5,
|
33
|
+
'8G' => 6,
|
34
|
+
'16G' => 7
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module BuckKnife
|
2
|
+
module KnifeHelper
|
3
|
+
def project_from_arg_or_ask
|
4
|
+
project_name = @name_args[0]
|
5
|
+
|
6
|
+
if project_name.nil?
|
7
|
+
show_usage
|
8
|
+
ui.fatal("You must specify a project name")
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
project = Project.find(project_name)
|
14
|
+
rescue BuckKnife::UnknownProjectError
|
15
|
+
ui.fatal("A project data file '#{project_name}.json' was not found in #{Project.root}")
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
return project
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module BuckKnife
|
2
|
+
class Node
|
3
|
+
include DataAccessor
|
4
|
+
attr_accessor :name
|
5
|
+
|
6
|
+
def initialize(name, data)
|
7
|
+
@name = name
|
8
|
+
@data = data
|
9
|
+
end
|
10
|
+
|
11
|
+
data_accessor :image_id, :size, :flavor, :environment, :address, :group
|
12
|
+
data_accessor :ssh_username, :ssh_password
|
13
|
+
|
14
|
+
def roles
|
15
|
+
@data["roles"].split(' ')
|
16
|
+
end
|
17
|
+
|
18
|
+
def recipes
|
19
|
+
@data["recipes"].split(' ')
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_list
|
23
|
+
BuckKnife::Helper.combine_roles_and_recipes(roles, recipes)
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_list_items
|
27
|
+
run_list.split(' ')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
module BuckKnife
|
5
|
+
class Project
|
6
|
+
include DataAccessor
|
7
|
+
include ActiveModel::Validations
|
8
|
+
|
9
|
+
data_accessor :repository, :distro, :base_run_list, :provider
|
10
|
+
data_accessor :ssh_username, :identity_file
|
11
|
+
data_accessor :rs_api_key, :rs_api_username
|
12
|
+
|
13
|
+
validates_presence_of :provider, :distro, :base_run_list
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def all_names
|
17
|
+
Dir["#{root}/*.json"].map { |f| File.basename(f).split(".").first }
|
18
|
+
end
|
19
|
+
|
20
|
+
def all
|
21
|
+
Dir["#{root}/*.json"].map { |f| from_file(f) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def find(name)
|
25
|
+
from_file root.join("#{name}.json")
|
26
|
+
end
|
27
|
+
|
28
|
+
def from_file(path)
|
29
|
+
raise UnknownProjectError unless File.exist?(path)
|
30
|
+
new( JSON.parse( File.read(path) ) )
|
31
|
+
end
|
32
|
+
|
33
|
+
def root
|
34
|
+
@root ||= begin
|
35
|
+
unless path = Chef::Config[:knife][:project_dir]
|
36
|
+
raise UnknownProjectRootError, "You must define knife[:project_dir] in your knife.rb"
|
37
|
+
end
|
38
|
+
|
39
|
+
Pathname.new path
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def root=(path)
|
44
|
+
@root = Pathname.new(path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(data)
|
49
|
+
@data = data
|
50
|
+
end
|
51
|
+
|
52
|
+
def name
|
53
|
+
@data["id"]
|
54
|
+
end
|
55
|
+
|
56
|
+
def node_names
|
57
|
+
@data["nodes"].keys.sort
|
58
|
+
end
|
59
|
+
|
60
|
+
def nodes
|
61
|
+
@data["nodes"].map { |name, data| Node.new(name, data) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def node(name)
|
65
|
+
nodes.find { |n| n.name == name }
|
66
|
+
end
|
67
|
+
|
68
|
+
def environment_names
|
69
|
+
@data["environments"].keys.sort
|
70
|
+
end
|
71
|
+
|
72
|
+
def environments
|
73
|
+
@data["environments"].map do |name, data|
|
74
|
+
Environment.new( name, data, nodes.select { |n| n.environment == name } )
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# This file is automatically generated by BuckKnife.
|
2
|
+
#
|
3
|
+
# DO NOT MODIFY THIS FILE
|
4
|
+
#
|
5
|
+
# Place your custom variables, tasks and overrides in the deploy.rb
|
6
|
+
# file. To add this file to your Rails application copy it the the
|
7
|
+
# config directory as config/deploy_environments.rb
|
8
|
+
#
|
9
|
+
# Then load it at the top of your deploy.rb file with the following
|
10
|
+
# statement:
|
11
|
+
#
|
12
|
+
# load 'config/deploy_environments'
|
13
|
+
|
14
|
+
|
15
|
+
require "bundler/capistrano"
|
16
|
+
require 'highline/import'
|
17
|
+
|
18
|
+
|
19
|
+
default_run_options[:pty] = true
|
20
|
+
|
21
|
+
set :repository, "<%= project.repository %>"
|
22
|
+
set :scm, :git
|
23
|
+
set :app_user, "deploy"
|
24
|
+
set :user, "<%= project.nodes.first.ssh_username %>"
|
25
|
+
set :password, "<%= project.nodes.first.ssh_password %>"
|
26
|
+
set :use_sudo, true
|
27
|
+
|
28
|
+
set :checkout, "export"
|
29
|
+
set :deploy_via, "copy"
|
30
|
+
set :copy_strategy, :export
|
31
|
+
set :copy_exclude, ".git/*"
|
32
|
+
set :copy_compression, :zip
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
<% environments.each do |environment| %>
|
37
|
+
|
38
|
+
desc "Deploy to <%= environment[:name] %> (usage: cap <%= environment[:name] %> deploy)"
|
39
|
+
task :<%= environment[:name] %> do
|
40
|
+
set :domain, "<%= environment[:domain_name] %>"
|
41
|
+
set :application, "<%= project.name %>"
|
42
|
+
set :rails_env, "<%= environment[:name] %>"
|
43
|
+
set :deploy_to, "/var/www/<%= environment[:domain_name] %>"
|
44
|
+
|
45
|
+
<% environment[:app_servers].each do |app_server| %>
|
46
|
+
|
47
|
+
role :app, "<%= app_server.address %>" # <%= app_server.name %>
|
48
|
+
<% end %>
|
49
|
+
<% environment[:job_servers].each do |job_server| %>
|
50
|
+
|
51
|
+
role :job, "<%= job_server.address %>" # <%= job_server.name %>
|
52
|
+
<% end %>
|
53
|
+
|
54
|
+
role :db, "<%= environment[:primary_db].address %>", :primary => true # <%= environment[:primary_db].name %>
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
<% end %>
|
59
|
+
|
60
|
+
namespace :deploy do
|
61
|
+
task :start do ; end
|
62
|
+
task :stop do ; end
|
63
|
+
|
64
|
+
task :restart, :roles => :app, :except => { :no_release => true } do
|
65
|
+
run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
|
66
|
+
end
|
67
|
+
|
68
|
+
task :chown_project, :roles => [:app, :job] do
|
69
|
+
sudo "chmod 777 #{deploy_to}/releases"
|
70
|
+
sudo "chown -R #{app_user}:sysadmin #{deploy_to}"
|
71
|
+
end
|
72
|
+
|
73
|
+
task :symlink_database_config, :roles => [:app, :job] do
|
74
|
+
run "rm #{release_path}/config/database.yml"
|
75
|
+
run "ln -s #{deploy_to}/shared/database.yml #{release_path}/config/database.yml"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
after 'deploy:setup', 'deploy:chown_project'
|
82
|
+
after 'deploy', 'deploy:chown_project'
|
83
|
+
after 'deploy:finalize_update', 'deploy:symlink_database_config'
|
84
|
+
|
85
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
{
|
2
|
+
"id": "<%= project_name %>",
|
3
|
+
"domain": "<%= project_name %>.com",
|
4
|
+
"repository": "FIXME",
|
5
|
+
"provider": "rackspace",
|
6
|
+
"public_interface": "eth0",
|
7
|
+
"internal_interface": "eth1",
|
8
|
+
"distro": "FIXME",
|
9
|
+
"base_run_list": "monitor_client",
|
10
|
+
|
11
|
+
"environments": {
|
12
|
+
"production": {
|
13
|
+
"domain_name": "origin.<%= project_name %>.com",
|
14
|
+
"www_virtual_ip": "FIXME",
|
15
|
+
"database_user": "FIXME",
|
16
|
+
"database_password": "FIXME"
|
17
|
+
},
|
18
|
+
"staging": {
|
19
|
+
"domain_name": "staging.<%= project_name %>.com",
|
20
|
+
"www_virtual_ip": "127.0.0.1",
|
21
|
+
"database_user": "FIXME",
|
22
|
+
"database_password": "FIXME"
|
23
|
+
},
|
24
|
+
"dev": {
|
25
|
+
"domain_name": "dev.<%= project_name %>.com",
|
26
|
+
"www_virtual_ip": "127.0.0.1",
|
27
|
+
"database_user": "FIXME",
|
28
|
+
"database_password": "FIXME"
|
29
|
+
}
|
30
|
+
},
|
31
|
+
|
32
|
+
"nodes": {
|
33
|
+
"myapp-monitor1": {
|
34
|
+
"group": "monitor",
|
35
|
+
"size": "2G",
|
36
|
+
"environment": "production",
|
37
|
+
"roles": "",
|
38
|
+
"recipes": "",
|
39
|
+
"address": "127.0.0.1",
|
40
|
+
"ssh_username": "FIXME",
|
41
|
+
"ssh_password": "FIXME"
|
42
|
+
},
|
43
|
+
|
44
|
+
"myapp-dev-app1": {
|
45
|
+
"group": "app",
|
46
|
+
"size": "2G",
|
47
|
+
"environment": "dev",
|
48
|
+
"roles": "app_heartbeat_pair rails_app rails_nginx_app",
|
49
|
+
"recipes": "",
|
50
|
+
"address": "127.0.0.1",
|
51
|
+
"ssh_username": "FIXME",
|
52
|
+
"ssh_password": "FIXME"
|
53
|
+
},
|
54
|
+
"myapp-dev-app2": {
|
55
|
+
"group": "app",
|
56
|
+
"size": "2G",
|
57
|
+
"environment": "dev",
|
58
|
+
"roles": "app_heartbeat_pair rails_app rails_nginx_app",
|
59
|
+
"recipes": "",
|
60
|
+
"address": "127.0.0.1",
|
61
|
+
"ssh_username": "username",
|
62
|
+
"ssh_password": "password"
|
63
|
+
},
|
64
|
+
"myapp-dev-job1": {
|
65
|
+
"group": "job",
|
66
|
+
"size": "2G",
|
67
|
+
"environment": "dev",
|
68
|
+
"roles": "rails_app rails_job",
|
69
|
+
"recipes": "",
|
70
|
+
"address": "127.0.0.1",
|
71
|
+
"ssh_username": "username",
|
72
|
+
"ssh_password": "password"
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|