Class: Sumo

Inherits:
Object
  • Object
show all
Defined in:
lib/sumo.rb

Instance Method Summary collapse

Instance Method Details

#bootstrap_chef(hostname) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/sumo.rb', line 98

def bootstrap_chef(hostname)
	commands = [
		'apt-get update',
		'apt-get autoremove -y',
		'apt-get install -y ruby ruby-dev rubygems git-core',
		'gem sources -a http://gems.opscode.com',
		'gem install chef ohai --no-rdoc --no-ri',
		"git clone #{config['cookbooks_url']}",
	]
	ssh(hostname, commands)
end

#configObject



149
150
151
# File 'lib/sumo.rb', line 149

def config
	@config ||= read_config
end

#create_keypairObject



167
168
169
170
171
# File 'lib/sumo.rb', line 167

def create_keypair
	keypair = ec2.create_keypair(:key_name => "sumo").keyMaterial
	File.open(keypair_file, 'w') { |f| f.write keypair }
	File.chmod 0600, keypair_file
end

#create_security_groupObject



173
174
175
176
# File 'lib/sumo.rb', line 173

def create_security_group
	ec2.create_security_group(:group_name => 'sumo', :group_description => 'Sumo')
rescue EC2::InvalidGroupDuplicate
end

#ec2Object



189
190
191
# File 'lib/sumo.rb', line 189

def ec2
	@ec2 ||= EC2::Base.new(:access_key_id => config['access_id'], :secret_access_key => config['access_secret'])
end

#fetch_listObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/sumo.rb', line 28

def fetch_list
	result = ec2.describe_instances
	return [] unless result.reservationSet

	instances = []
	result.reservationSet.item.each do |r|
		r.instancesSet.item.each do |item|
			instances << {
				:instance_id => item.instanceId,
				:status => item.instanceState.name,
				:hostname => item.dnsName
			}
		end
	end
	instances
end

#fetch_resources(hostname) ⇒ Object



132
133
134
135
136
137
# File 'lib/sumo.rb', line 132

def fetch_resources(hostname)
	cmd = "ssh -i #{keypair_file} root@#{hostname} 'cat /root/resources' 2>&1"
	out = IO.popen(cmd, 'r') { |pipe| pipe.read }
	abort "failed to read resources, output:\n#{out}" unless $?.success?
	parse_resources(out, hostname)
end

#find(id_or_hostname) ⇒ Object



45
46
47
48
49
50
51
52
53
# File 'lib/sumo.rb', line 45

def find(id_or_hostname)
	return unless id_or_hostname
	id_or_hostname = id_or_hostname.strip.downcase
	list.detect do |inst|
		inst[:hostname] == id_or_hostname or
		inst[:instance_id] == id_or_hostname or
		inst[:instance_id].gsub(/^i-/, '') == id_or_hostname
	end
end

#instance_info(instance_id) ⇒ Object



67
68
69
70
71
# File 'lib/sumo.rb', line 67

def instance_info(instance_id)
	fetch_list.detect do |inst|
		inst[:instance_id] == instance_id
	end
end

#keypair_fileObject



163
164
165
# File 'lib/sumo.rb', line 163

def keypair_file
	"#{sumo_dir}/keypair.pem"
end

#launchObject



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/sumo.rb', line 6

def launch
	ami = config['ami']
	raise "No AMI selected" unless ami

	create_keypair unless File.exists? keypair_file

	create_security_group
	open_firewall(22)

	result = ec2.run_instances(
		:image_id => ami,
		:instance_type => config['instance_size'] || 'm1.small',
		:key_name => 'sumo',
		:group_id => [ 'sumo' ]
	)
	result.instancesSet.item[0].instanceId
end

#listObject



24
25
26
# File 'lib/sumo.rb', line 24

def list
	@list ||= fetch_list
end

#list_by_status(status) ⇒ Object



63
64
65
# File 'lib/sumo.rb', line 63

def list_by_status(status)
	list.select { |i| i[:status] == status }
end

#open_firewall(port) ⇒ Object



178
179
180
181
182
183
184
185
186
187
# File 'lib/sumo.rb', line 178

def open_firewall(port)
	ec2.authorize_security_group_ingress(
		:group_name => 'sumo',
		:ip_protocol => 'tcp',
		:from_port => port,
		:to_port => port,
		:cidr_ip => '0.0.0.0/0'
	)
rescue EC2::InvalidPermissionDuplicate
end

#parse_resources(raw, hostname) ⇒ Object



139
140
141
142
143
# File 'lib/sumo.rb', line 139

def parse_resources(raw, hostname)
	raw.split("\n").map do |line|
		line.gsub(/localhost/, hostname)
	end
end

#pendingObject



59
60
61
# File 'lib/sumo.rb', line 59

def pending
	list_by_status('pending')
end

#read_configObject



157
158
159
160
161
# File 'lib/sumo.rb', line 157

def read_config
	YAML.load File.read("#{sumo_dir}/config.yml")
rescue Errno::ENOENT
	raise "Sumo is not configured, please fill in ~/.sumo/config.yml"
end

#resources(hostname) ⇒ Object



127
128
129
130
# File 'lib/sumo.rb', line 127

def resources(hostname)
	@resources ||= {}
	@resources[hostname] ||= fetch_resources(hostname)
end

#runningObject



55
56
57
# File 'lib/sumo.rb', line 55

def running
	list_by_status('running')
end

#setup_role(hostname, role) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/sumo.rb', line 110

def setup_role(hostname, role)
	commands = [
		"cd chef-cookbooks",
		"/var/lib/gems/1.8/bin/chef-solo -c config.json -j roles/#{role}.json"
	]
	ssh(hostname, commands)
end

#ssh(hostname, cmds) ⇒ Object



118
119
120
121
122
123
124
125
# File 'lib/sumo.rb', line 118

def ssh(hostname, cmds)
	IO.popen("ssh -i #{keypair_file} root@#{hostname} > ~/.sumo/ssh.log 2>&1", "w") do |pipe|
		pipe.puts cmds.join(' && ')
	end
	unless $?.success?
		abort "failed\nCheck ~/.sumo/ssh.log for the output"
	end
end

#sumo_dirObject



153
154
155
# File 'lib/sumo.rb', line 153

def sumo_dir
	"#{ENV['HOME']}/.sumo"
end

#terminate(instance_id) ⇒ Object



145
146
147
# File 'lib/sumo.rb', line 145

def terminate(instance_id)
	ec2.terminate_instances(:instance_id => [ instance_id ])
end

#wait_for_hostname(instance_id) ⇒ Object

Raises:

  • (ArgumentError)


73
74
75
76
77
78
79
80
81
82
83
# File 'lib/sumo.rb', line 73

def wait_for_hostname(instance_id)
	raise ArgumentError unless instance_id and instance_id.match(/^i-/)
	loop do
		if inst = instance_info(instance_id)
			if hostname = inst[:hostname]
				return hostname
			end
		end
		sleep 1
	end
end

#wait_for_ssh(hostname) ⇒ Object

Raises:

  • (ArgumentError)


85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/sumo.rb', line 85

def wait_for_ssh(hostname)
	raise ArgumentError unless hostname
	loop do
		begin
			Timeout::timeout(4) do
				TCPSocket.new(hostname, 22)
				return
			end
		rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
		end
	end
end