ironfan 3.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.gitignore +51 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +130 -0
  4. data/Gemfile +26 -0
  5. data/LICENSE.md +201 -0
  6. data/README.md +328 -0
  7. data/Rakefile +104 -0
  8. data/TODO.md +16 -0
  9. data/VERSION +1 -0
  10. data/chefignore +41 -0
  11. data/cluster_chef-knife.gemspec +123 -0
  12. data/cluster_chef.gemspec +111 -0
  13. data/config/client.rb +59 -0
  14. data/config/proxy.pac +12 -0
  15. data/config/ubuntu10.04-ironfan.erb +157 -0
  16. data/config/ubuntu11.10-ironfan.erb +145 -0
  17. data/ironfan.gemspec +121 -0
  18. data/lib/chef/knife/bootstrap/ubuntu10.04-ironfan.erb +157 -0
  19. data/lib/chef/knife/bootstrap/ubuntu11.10-ironfan.erb +145 -0
  20. data/lib/chef/knife/cluster_bootstrap.rb +74 -0
  21. data/lib/chef/knife/cluster_kick.rb +94 -0
  22. data/lib/chef/knife/cluster_kill.rb +73 -0
  23. data/lib/chef/knife/cluster_launch.rb +164 -0
  24. data/lib/chef/knife/cluster_list.rb +50 -0
  25. data/lib/chef/knife/cluster_proxy.rb +126 -0
  26. data/lib/chef/knife/cluster_show.rb +61 -0
  27. data/lib/chef/knife/cluster_ssh.rb +141 -0
  28. data/lib/chef/knife/cluster_start.rb +40 -0
  29. data/lib/chef/knife/cluster_stop.rb +43 -0
  30. data/lib/chef/knife/cluster_sync.rb +77 -0
  31. data/lib/chef/knife/generic_command.rb +66 -0
  32. data/lib/chef/knife/knife_common.rb +195 -0
  33. data/lib/ironfan.rb +143 -0
  34. data/lib/ironfan/chef_layer.rb +299 -0
  35. data/lib/ironfan/cloud.rb +412 -0
  36. data/lib/ironfan/cluster.rb +118 -0
  37. data/lib/ironfan/compute.rb +153 -0
  38. data/lib/ironfan/deprecated.rb +33 -0
  39. data/lib/ironfan/discovery.rb +177 -0
  40. data/lib/ironfan/dsl_object.rb +124 -0
  41. data/lib/ironfan/facet.rb +144 -0
  42. data/lib/ironfan/fog_layer.rb +150 -0
  43. data/lib/ironfan/private_key.rb +130 -0
  44. data/lib/ironfan/role_implications.rb +58 -0
  45. data/lib/ironfan/security_group.rb +119 -0
  46. data/lib/ironfan/server.rb +281 -0
  47. data/lib/ironfan/server_slice.rb +260 -0
  48. data/lib/ironfan/volume.rb +157 -0
  49. data/spec/ironfan/cluster_spec.rb +13 -0
  50. data/spec/ironfan/facet_spec.rb +69 -0
  51. data/spec/ironfan/server_slice_spec.rb +19 -0
  52. data/spec/ironfan/server_spec.rb +112 -0
  53. data/spec/ironfan_spec.rb +193 -0
  54. data/spec/spec_helper.rb +50 -0
  55. data/spec/spec_helper/dummy_chef.rb +25 -0
  56. data/spec/test_config.rb +20 -0
  57. data/tasks/chef_config.rake +38 -0
  58. data/tasks/jeweler_use_alt_branch.rake +53 -0
  59. metadata +217 -0
@@ -0,0 +1,50 @@
1
+ #
2
+ # Author:: Philip (flip) Kromer (<flip@infochimps.com>)
3
+ # Copyright:: Copyright (c) 2011 Infochimps, Inc
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__)+"/knife_common.rb")
20
+
21
+ class Chef
22
+ class Knife
23
+ class ClusterList < Knife
24
+ include Ironfan::KnifeCommon
25
+
26
+ deps do
27
+ require 'formatador'
28
+ end
29
+
30
+ banner "knife cluster list (options)"
31
+
32
+ def run
33
+ load_ironfan
34
+ configure_dry_run
35
+
36
+ hash = Ironfan.cluster_filenames
37
+
38
+ table = []
39
+ hash.keys.sort.each do |key|
40
+ table.push( { :cluster => key, :path => hash[key] } )
41
+ end
42
+
43
+ ui.info "Cluster Path: #{ Ironfan.cluster_path.join ", " }"
44
+
45
+ Formatador.display_compact_table(table, [:cluster,:path])
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,126 @@
1
+ #
2
+ # Author:: Philip (flip) Kromer (flip@infochimps.com)
3
+ # Copyright:: Copyright (c) 2011 Infochimps, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__)+"/knife_common.rb")
20
+ require File.expand_path(File.dirname(__FILE__)+"/cluster_ssh.rb")
21
+ require File.expand_path(File.dirname(__FILE__)+"/generic_command.rb")
22
+
23
+ class Chef
24
+ class Knife
25
+ #
26
+ # Runs the ssh command to open a SOCKS proxy to the given host, and writes a
27
+ # PAC (automatic proxy config) file to
28
+ # /tmp/ironfan_proxy-YOURNAME.pac. Only the first host is used, even if
29
+ # multiple match.
30
+ #
31
+ # Why not use Net::Ssh directly? The SOCKS proxy support was pretty
32
+ # bad. Though ugly, exec'ing the command works.
33
+ #
34
+ class ClusterProxy < Ironfan::Script
35
+
36
+ import_banner_and_options(Chef::Knife::ClusterSsh, :except => [:concurrency, ])
37
+ banner 'knife cluster proxy "CLUSTER [FACET [INDEXES]]" (options) - Runs the ssh command to open a SOCKS proxy to the given host, and writes a PAC (automatic proxy config) file to /tmp/ironfan_proxy-YOURNAME.pac. Only the first host is used, even if multiple match.'
38
+
39
+ option :background,
40
+ :long => "--[no-]background",
41
+ :description => "Requests ssh to go to background after setting up the proxy",
42
+ :boolean => true,
43
+ :default => true
44
+
45
+ option :socks_port,
46
+ :long => '--socks-port',
47
+ :short => '-D',
48
+ :description => 'Port to listen on for SOCKS5 proxy',
49
+ :default => '6666'
50
+
51
+ def relevant?(server)
52
+ server.sshable?
53
+ end
54
+
55
+ def perform_execution(target)
56
+ svr = target.first
57
+ cmd = command_for_target(svr)
58
+
59
+ dump_proxy_pac
60
+ exec(*cmd)
61
+ end
62
+
63
+ def command_for_target(svr)
64
+ config[:attribute] ||= Chef::Config[:knife][:ssh_address_attribute] || "fqdn"
65
+ config[:ssh_user] ||= Chef::Config[:knife][:ssh_user]
66
+ config[:identity_file] ||= svr.cloud.ssh_identity_file
67
+ config[:host_key_verify] ||= Chef::Config[:knife][:host_key_verify] || (not config[:no_host_key_verify]) # pre-vs-post 0.10.4
68
+
69
+ if (svr.cloud.public_ip) then address = svr.cloud.public_ip ; end
70
+ if (not address) && (svr.chef_node) then address = format_for_display( svr.chef_node )[config[:attribute]] ; end
71
+ if (not address) && (svr.fog_server) then address = svr.fog_server.public_ip_address ; end
72
+
73
+ cmd = [ 'ssh', '-N' ]
74
+ cmd += [ '-D', config[:socks_port].to_s ]
75
+ cmd += [ '-p', config[:port].to_s ] if config[:port].present?
76
+ cmd << '-f' if config[:background]
77
+ cmd << "-#{'v' * config[:verbosity].to_i}" if (config[:verbosity].to_i > 0)
78
+ cmd += %w[ -o StrictHostKeyChecking=no ] if config[:host_key_verify]
79
+ cmd += %w[ -o ConnectTimeout=10 -o ServerAliveInterval=60 -o ControlPath=none ]
80
+ cmd += [ '-i', File.expand_path(config[:identity_file]) ] if config[:identity_file].present?
81
+ cmd << (config[:ssh_user] ? "#{config[:ssh_user]}@#{address}" : address)
82
+
83
+ Chef::Log.debug("Cluster proxy config: #{config.inspect}")
84
+ Chef::Log.debug("Cluster proxy command: #{cmd.inspect}")
85
+ ui.info(["SOCKS Proxy on",
86
+ "local port", ui.color(config[:socks_port], :cyan),
87
+ "for", ui.color(svr.name, :cyan),
88
+ "(#{address})"
89
+ ].join(" "))
90
+
91
+ cmd
92
+ end
93
+
94
+ #
95
+ # Write a .pac (automatic proxy configuration) file
96
+ # to /etc/ironfan_proxy-YOURNAME.pac
97
+ #
98
+ def dump_proxy_pac
99
+ pac_filename = File.expand_path(File.join('/tmp', "ironfan_proxy-#{ENV['USER']}.pac"))
100
+ ui.info("point your browser at PAC (automatic proxy config file) file://#{pac_filename}")
101
+ File.open(pac_filename, 'w') do |f|
102
+ f.print proxy_pac_contents
103
+ end
104
+ end
105
+
106
+ EC2_PROXY_PATTERNS = [ "*ec2*.amazonaws.com", "*ec2.internal*", "*compute-*.amazonaws.com", "*compute-*.internal*", "*domu*.internal*", "10.*",]
107
+
108
+ def proxy_pac_contents
109
+ proxy_patterns = EC2_PROXY_PATTERNS
110
+ proxy_patterns += Array(Chef::Config[:cluster_proxy_patterns])
111
+ rules = proxy_patterns.compact.map{|str| "(shExpMatch(host, %-28s))" % %Q{"#{str}"} }
112
+ %Q{function FindProxyForURL(url, host) {
113
+ if (#{rules.join(" ||\n ")}
114
+ ) {
115
+ return "SOCKS5 localhost:#{config[:socks_port]}";
116
+ }
117
+ return "DIRECT";
118
+ }\n}
119
+ end
120
+
121
+
122
+
123
+
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,61 @@
1
+ #
2
+ # Author:: Philip (flip) Kromer (<flip@infochimps.com>)
3
+ # Copyright:: Copyright (c) 2011 Infochimps, Inc
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__)+"/knife_common.rb")
20
+
21
+ class Chef
22
+ class Knife
23
+ class ClusterShow < Knife
24
+ include Ironfan::KnifeCommon
25
+ deps do
26
+ Ironfan::KnifeCommon.load_deps
27
+ end
28
+
29
+ banner "knife cluster show CLUSTER_NAME [FACET_NAME [INDEXES]] (options)"
30
+ option :cloud,
31
+ :long => "--[no-]cloud",
32
+ :description => "Look up machines on AWS cloud (default is yes, look up machines; use --no-cloud to skip)",
33
+ :default => true,
34
+ :boolean => true
35
+
36
+ def run
37
+ load_ironfan
38
+ die(banner) if @name_args.empty?
39
+ configure_dry_run
40
+
41
+ # Load the cluster/facet/slice/whatever
42
+ target = get_slice(* @name_args)
43
+
44
+ #
45
+ # Dump entire contents of objects if -VV flag given
46
+ #
47
+ if config[:verbosity] >= 2
48
+ target.each do |svr|
49
+ Chef::Log.debug( "Server #{svr.name}: #{JSON.pretty_generate(svr.to_hash)}" )
50
+ Chef::Log.debug( "- cloud: #{JSON.pretty_generate(svr.cloud.to_hash)}" )
51
+ Chef::Log.debug( "- fog: #{JSON.pretty_generate(svr.fog_launch_description)}" )
52
+ end
53
+ end
54
+
55
+ # Display same
56
+ display(target)
57
+
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,141 @@
1
+ #
2
+ # Author:: Chris Howe (<chris@infochimps.com>)
3
+ # Copyright:: Copyright (c) 2011 Infochimps, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__)+"/knife_common.rb")
20
+ require 'chef/knife/ssh'
21
+
22
+ class Chef
23
+ class Knife
24
+ class ClusterSsh < Chef::Knife::Ssh
25
+ include Ironfan::KnifeCommon
26
+
27
+ deps do
28
+ Chef::Knife::Ssh.load_deps
29
+ Ironfan::KnifeCommon.load_deps
30
+ end
31
+
32
+ banner 'knife cluster ssh "CLUSTER [FACET [INDEXES]]" COMMAND (options)'
33
+ Chef::Knife::Ssh.options.each do |name, hsh|
34
+ next if name == :attribute
35
+ option name, hsh
36
+ end
37
+
38
+ option :attribute,
39
+ :short => "-a ATTR",
40
+ :long => "--attribute ATTR",
41
+ :description => "The attribute to use for opening the connection - default is fqdn (ec2 users may prefer cloud.public_hostname)"
42
+
43
+ def configure_session
44
+ predicate = @name_args[0].split(/[\s\-]+/,3).map(&:strip)
45
+
46
+ target = get_slice(*predicate).select(&:sshable?)
47
+
48
+ display(target) if config[:verbose]
49
+
50
+ config[:attribute] ||= Chef::Config[:knife][:ssh_address_attribute] || "fqdn"
51
+ config[:ssh_user] ||= Chef::Config[:knife][:ssh_user]
52
+ config[:identity_file] ||= target.ssh_identity_file
53
+
54
+ @action_nodes = target.chef_nodes
55
+ addresses = target.servers.map do |svr|
56
+ if (svr.cloud.public_ip) then address = svr.cloud.public_ip ; end
57
+ if (not address) && (svr.fog_server) then address = svr.fog_server.public_ip_address ; end
58
+ if (not address) && (svr.chef_node) then address = format_for_display( svr.chef_node )[config[:attribute]] ; end
59
+ address
60
+ end.compact
61
+
62
+ (ui.fatal("No nodes returned from search!"); exit 10) if addresses.nil? || addresses.length == 0
63
+
64
+ session_from_list(addresses)
65
+ end
66
+
67
+ #
68
+ # Override the one in Chef::Knife::Ssh to allow an err flag (prints in red
69
+ # if non-null)
70
+ #
71
+ def print_data(host, data, err=nil)
72
+ if data =~ /\n/
73
+ data.split(/\n/).each { |d| print_data(host, d, err) }
74
+ else
75
+ padding = @longest - host.length
76
+ str = ui.color(host, :cyan) + (" " * (padding + 1)) + (err ? ui.color(data, :red) : data)
77
+ ui.msg(str)
78
+ end
79
+ end
80
+
81
+ #
82
+ # Override the one in Chef::Knife::Ssh to give a big red warning if the
83
+ # process executes with badness
84
+ #
85
+ def ssh_command(command, subsession=nil)
86
+ subsession ||= session
87
+ command = fixup_sudo(command)
88
+ notifications = []
89
+ subsession.open_channel do |ch|
90
+ ch.request_pty
91
+ ch.exec command do |ch, success|
92
+ raise ArgumentError, "Cannot execute #{command}" unless success
93
+ # note: you can't do the stderr calback because requesting a pty
94
+ # squashes stderr and stdout together
95
+ ch.on_data do |ichannel, data|
96
+ print_data(ichannel[:host], data)
97
+ if data =~ /^knife sudo password: /
98
+ ichannel.send_data("#{get_password}\n")
99
+ end
100
+ end
101
+ ch.on_request "exit-status" do |ichannel, data|
102
+ exit_status = data.read_long
103
+ if exit_status != 0
104
+ command_snippet = (command.length < 70) ? command : (command[0..45] + ' ... ' + command[-19..-1])
105
+ notifications << [ichannel[:host], "'#{command_snippet.gsub(/[\r\n]+/, "; ")}' terminated with error status #{exit_status}", :err]
106
+ end
107
+ end
108
+ end
109
+ end
110
+ session.loop
111
+ notifications.each{|args| print_data(*args) }
112
+ end
113
+
114
+ def cssh
115
+ exec "cssh "+session.servers_for.map{|server| server.user ? "#{server.user}@#{server.host}" : server.host}.join(" ")
116
+ end
117
+
118
+ def run
119
+ load_ironfan
120
+ die(banner) if @name_args.empty?
121
+ extend Chef::Mixin::Command
122
+
123
+ @longest = 0
124
+ configure_session
125
+
126
+ case @name_args[1]
127
+ when "interactive" then interactive
128
+ when "screen" then screen
129
+ when "tmux" then tmux
130
+ when "macterm" then macterm
131
+ when "cssh" then cssh
132
+ else
133
+ ssh_command(@name_args[1..-1].join(" "))
134
+ end
135
+
136
+ session.close
137
+ end
138
+
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,40 @@
1
+ #
2
+ # Author:: Philip (flip) Kromer (<flip@infochimps.com>)
3
+ # Copyright:: Copyright (c) 2011 Infochimps, Inc
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require File.expand_path(File.dirname(__FILE__)+"/knife_common.rb")
20
+ require File.expand_path(File.dirname(__FILE__)+"/generic_command.rb")
21
+
22
+ class Chef
23
+ class Knife
24
+ class ClusterStart < Ironfan::Script
25
+ import_banner_and_options(Ironfan::Script)
26
+
27
+ def relevant?(server)
28
+ server.startable?
29
+ end
30
+
31
+ def perform_execution(target)
32
+ section("Starting machines")
33
+ super(target)
34
+ section("Announcing Chef nodes as started")
35
+ target.send(:delegate_to_servers, :announce_as_started)
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # Author:: Philip (flip) Kromer (<flip@infochimps.com>)
3
+ # Copyright:: Copyright (c) 2011 Infochimps, Inc
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ require File.expand_path(File.dirname(__FILE__)+"/generic_command.rb")
19
+
20
+ class Chef
21
+ class Knife
22
+ class ClusterStop < Ironfan::Script
23
+ import_banner_and_options(Ironfan::Script)
24
+
25
+ def relevant?(server)
26
+ server.running?
27
+ end
28
+
29
+ def perform_execution(target)
30
+ section("Stopping machines")
31
+ super(target)
32
+ section("Announcing Chef nodes as stopped")
33
+ target.send(:delegate_to_servers, :announce_as_stopped)
34
+ end
35
+
36
+ def confirm_execution(target)
37
+ ui.info " Unless these nodes are backed by EBS volumes, this will result in loss of all data"
38
+ ui.info " not saved elsewhere. Even if they are EBS backed, there may still be some data loss."
39
+ confirm_or_exit("Are you absolutely certain that you want to perform this action? (Type 'Yes' to confirm) ", 'Yes')
40
+ end
41
+ end
42
+ end
43
+ end