buckknife 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|