buckknife 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +4 -0
  3. data/HISTORY.md +8 -0
  4. data/README.md +65 -0
  5. data/Rakefile +27 -0
  6. data/buckknife.gemspec +31 -0
  7. data/lib/buckknife.rb +44 -0
  8. data/lib/buckknife/command.rb +33 -0
  9. data/lib/buckknife/commands/add_role.rb +13 -0
  10. data/lib/buckknife/commands/base.rb +24 -0
  11. data/lib/buckknife/commands/bootstrap.rb +28 -0
  12. data/lib/buckknife/commands/capistrano.rb +46 -0
  13. data/lib/buckknife/commands/create.rb +42 -0
  14. data/lib/buckknife/commands/euca_create.rb +60 -0
  15. data/lib/buckknife/commands/rackspace_create.rb +58 -0
  16. data/lib/buckknife/commands/run_client.rb +49 -0
  17. data/lib/buckknife/data_accessor.rb +29 -0
  18. data/lib/buckknife/environment.rb +16 -0
  19. data/lib/buckknife/helper.rb +39 -0
  20. data/lib/buckknife/knife_helper.rb +22 -0
  21. data/lib/buckknife/node.rb +30 -0
  22. data/lib/buckknife/project.rb +80 -0
  23. data/lib/buckknife/templates/deploy_environments.rb.erb +85 -0
  24. data/lib/buckknife/templates/project.json.erb +75 -0
  25. data/lib/buckknife/version.rb +3 -0
  26. data/lib/chef/knife/project_add_role.rb +24 -0
  27. data/lib/chef/knife/project_bootstrap.rb +24 -0
  28. data/lib/chef/knife/project_capistrano.rb +19 -0
  29. data/lib/chef/knife/project_create.rb +53 -0
  30. data/lib/chef/knife/project_info.rb +13 -0
  31. data/lib/chef/knife/project_list.rb +17 -0
  32. data/lib/chef/knife/project_new.rb +28 -0
  33. data/lib/chef/knife/project_run_client.rb +30 -0
  34. data/lib/chef/knife/project_show.rb +34 -0
  35. data/spec/buckknife/commands/add_role_spec.rb +14 -0
  36. data/spec/buckknife/commands/bootstrap_spec.rb +22 -0
  37. data/spec/buckknife/commands/capistrano_spec.rb +13 -0
  38. data/spec/buckknife/commands/create_spec.rb +48 -0
  39. data/spec/buckknife/commands/run_client_spec.rb +15 -0
  40. data/spec/buckknife/environment_spec.rb +14 -0
  41. data/spec/buckknife/helper_spec.rb +11 -0
  42. data/spec/buckknife/node_spec.rb +18 -0
  43. data/spec/buckknife/project_spec.rb +49 -0
  44. data/spec/data_bags/projects/myapp.json +139 -0
  45. data/spec/knife.rb +4 -0
  46. data/spec/knife/project_bootstrap_spec.rb +0 -0
  47. data/spec/knife/project_info_spec.rb +17 -0
  48. data/spec/knife/project_list_spec.rb +18 -0
  49. data/spec/spec_helper.rb +27 -0
  50. 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,16 @@
1
+ module BuckKnife
2
+ class Environment
3
+ attr_reader :name, :nodes
4
+
5
+ def initialize(name, data, nodes)
6
+ @data = data
7
+ @name = name
8
+ @nodes = nodes
9
+ end
10
+
11
+ def domain_name
12
+ @data['domain_name']
13
+ end
14
+
15
+ end
16
+ 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
+ }