cluster 0.5.33
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +69 -0
- data/VERSION +1 -0
- data/bin/cluster +14 -0
- data/bin/ec2-consistent-snapshot +676 -0
- data/bin/periodic.sh +19 -0
- data/examples/cacerts.pem +19 -0
- data/examples/credentials.yml +24 -0
- data/examples/monitor.god +88 -0
- data/examples/users.sh +42 -0
- data/lib/cluster.rb +267 -0
- data/lib/cluster/cli.rb +206 -0
- data/lib/cluster/configuration.rb +52 -0
- data/lib/cluster/infrastructure.rb +160 -0
- data/lib/cluster/infrastructures/amazon.rb +568 -0
- data/lib/cluster/infrastructures/amazon_instance.rb +270 -0
- data/lib/cluster/infrastructures/amazon_release.rb +63 -0
- data/lib/cluster/instance.rb +97 -0
- data/lib/cluster/release.rb +30 -0
- data/lib/cluster/version.rb +25 -0
- data/lib/ext/array.rb +13 -0
- data/lib/ext/cluster_extensions.rb +13 -0
- metadata +206 -0
data/bin/periodic.sh
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# This will run periodically to do appropriate backups and cleanups.
|
3
|
+
# run as a crontab so the environment is minimal
|
4
|
+
|
5
|
+
export GEM_PATH=$(gem env gempath)
|
6
|
+
export CREDENTIALS=${HOME}/.cluster/credentials.yml
|
7
|
+
|
8
|
+
mysql_user=
|
9
|
+
mysql_host=$(cluster service -d database)
|
10
|
+
mysql_password=
|
11
|
+
aws_secret=""
|
12
|
+
aws_key=""
|
13
|
+
region=""
|
14
|
+
volume=""
|
15
|
+
mount=""
|
16
|
+
|
17
|
+
cluster period
|
18
|
+
|
19
|
+
sudo ec2-consistent-snapshot --aws-access-key-id ${aws_key} --aws-secret-access-key ${aws_secret} --mysql-user ${mysql_user} --mysql-password ${mysql_password} --mysql-host ${mysql_host} --xfs-filesystem ${mount} --region ${region} ${volume}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
|
3
|
+
FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
|
4
|
+
VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
|
5
|
+
biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
|
6
|
+
dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
|
7
|
+
MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
|
8
|
+
MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
|
9
|
+
A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
|
10
|
+
b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
|
11
|
+
cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
|
12
|
+
bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
|
13
|
+
VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
|
14
|
+
ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
|
15
|
+
uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
|
16
|
+
9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
|
17
|
+
hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
|
18
|
+
pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
|
19
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,24 @@
|
|
1
|
+
amazon:
|
2
|
+
user: 91*********
|
3
|
+
key: AKI**********
|
4
|
+
secret: U***************************
|
5
|
+
|
6
|
+
monitor:
|
7
|
+
email:
|
8
|
+
address: smtp.gmail.com
|
9
|
+
port: 587
|
10
|
+
domain: ingamersports.com
|
11
|
+
authentication: plain
|
12
|
+
user_name: dog@score345.com
|
13
|
+
password: *******
|
14
|
+
messages:
|
15
|
+
from: dog@ingamersports.com
|
16
|
+
contacts:
|
17
|
+
- name: alerts
|
18
|
+
email: alert@ingamersports.com
|
19
|
+
- name: panic
|
20
|
+
email: panic@ingamersports.com
|
21
|
+
group: emergency
|
22
|
+
- name: simon
|
23
|
+
email: sdeboer@ingamersports.com
|
24
|
+
group: person
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/bin/env god -c
|
2
|
+
require 'rubygems'
|
3
|
+
require 'tlsmail'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'cluster'
|
6
|
+
require 'cluster/infrastructures/amazon'
|
7
|
+
|
8
|
+
Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
|
9
|
+
|
10
|
+
CREDENTIALS = ENV['CREDENTIALS'] || File.join(ENV['HOME'], '.cluster', 'credentials.yml')
|
11
|
+
|
12
|
+
credentials_file = File.expand_path(CREDENTIALS)
|
13
|
+
|
14
|
+
unless File.exists?(credentials_file)
|
15
|
+
$stderr.puts "Cannot find credentials for cluster : #{credentials_file} !"
|
16
|
+
exit 2
|
17
|
+
end
|
18
|
+
MONITOR_PATH = File.join File.dirname(credentials_file), 'services.d'
|
19
|
+
|
20
|
+
Cluster::Configuration[:credentials_file] = credentials_file
|
21
|
+
CLUSTER = Cluster.new(Amazon.new)
|
22
|
+
|
23
|
+
class UserService < God::Behavior
|
24
|
+
def before_start
|
25
|
+
service = self.watch.name[/^(\w+)-/, 1]
|
26
|
+
users_file = File.join(MONITOR_PATH, service, 'users.sh')
|
27
|
+
unless File.exists? users_file
|
28
|
+
FileUtils.mkdir_p File.dirname users_file
|
29
|
+
CLUSTER.retrieve service, 'users.sh', users_file
|
30
|
+
end
|
31
|
+
|
32
|
+
system(users_file) if File.exists? users_file
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ProjectService < God::Behavior
|
37
|
+
def before_start
|
38
|
+
service = self.watch.name[/^(\w+)-/, 1]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
creds = YAML::load_file credentials_file
|
43
|
+
|
44
|
+
if creds.include? 'monitor'
|
45
|
+
moni = creds['monitor']
|
46
|
+
email = moni['email'].keys.inject({}) {|m, k|
|
47
|
+
m.merge k.to_sym => moni['email'][k]
|
48
|
+
}
|
49
|
+
God::Contacts::Email.server_settings = email
|
50
|
+
God::Contacts::Email.message_settings = {
|
51
|
+
:from => moni['messages']['from']
|
52
|
+
}
|
53
|
+
|
54
|
+
if moni.include? 'contacts'
|
55
|
+
for contact in moni['contacts']
|
56
|
+
God.contact(:email) {|c|
|
57
|
+
c.name = contact['name']
|
58
|
+
c.email = contact['email']
|
59
|
+
contact['group'] and (c.group = contact['group'])
|
60
|
+
}
|
61
|
+
end
|
62
|
+
else
|
63
|
+
God.contact(:email) {|c|
|
64
|
+
c.name = 'system notification'
|
65
|
+
c.email = moni['messages']['from']
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
CLUSTER.instance_state('configuring')
|
71
|
+
|
72
|
+
for service in CLUSTER.current('services')
|
73
|
+
monitor_file = File.join MONITOR_PATH, service, "config.god"
|
74
|
+
FileUtils.mkdir_p File.dirname(monitor_file)
|
75
|
+
begin
|
76
|
+
CLUSTER.retrieve service, 'config.god', monitor_file
|
77
|
+
rescue => err
|
78
|
+
applog(nil, :warn, "Service of #{service} does not appear to have a monitor configuration. Skipping!")
|
79
|
+
next
|
80
|
+
end
|
81
|
+
|
82
|
+
if File.readable? monitor_file
|
83
|
+
applog(nil, :info, "Loading monitor file #{monitor_file}")
|
84
|
+
load monitor_file
|
85
|
+
else
|
86
|
+
applog(nil, :warn, "Cannot find a monitor file for #{monitor_file}")
|
87
|
+
end
|
88
|
+
end
|
data/examples/users.sh
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
if ! id staging 2>/dev/null 1>&2; then
|
4
|
+
adduser --disabled-password --gecos 'staging' staging
|
5
|
+
mkdir -m 700 ~staging/.ssh
|
6
|
+
touch ~staging/.ssh/authorized_keys
|
7
|
+
chmod 600 ~staging/.ssh/authorized_keys
|
8
|
+
touch ~staging/.ssh/config
|
9
|
+
chmod 600 ~staging/.ssh/config
|
10
|
+
|
11
|
+
cat >>~staging/.ssh/authorized_keys <<END_OF_KEYS
|
12
|
+
ssh-dss AAAAB3NzaC1kc3MAAACBANcFLeySRpmVji+g9KcHBaedFE5SOLgkSQIKbeVjOwzGC75AIM5gY6gjOu0Kj4BYhlxUwOWkOEYgZwqu15qxtKPCnYQST3jqQnbfwN8UOC4y7XH/4G0gdOD9taFx4PpW0H+zvYs7smBb1qG8NnlUH1tyCGdNNmZLxn2b84R5pDcVAAAAFQDqV2zGPugaRR7gslhxyxrZnIpE9QAAAIAtEViFMBq8h4TXXbdeYR3EhR+zcUHzRz/yB3N7mdo8C1a5apHAnzwX7RPzvkskThU2Idj3ZxUyrTbd5WpBfk/OIwF+hvM3/Uw6XJLNaDSTd0t0HOJxE+SJuHwfOwNtI+J2IXwckUJ2laOTwjOc0YzIR1Gnf50gcz67R07H4qidtQAAAIANQsT7jT68TemlxBjjK22YDRxdRC5M2VuTAT6dDp9JL7KB6B1b3kU7EjiG9gk9oDUGWQiE5sEtHaFjgeViSwQhtaQ/Rqx8EmNG5W9lNgVcWtPmxqMJtOaaNggE+znE1xanY+fDtLqbAkbExZ8vypyUuMXvsgl7AiSSEMnpmX1Itw== simon@s345
|
13
|
+
ssh-dss AAAAB3NzaC1kc3MAAACBAIxHXUznZLryPsnK0YCa7W4HM3wSqpCV8MwIBYd4kl/qQzC2VlyX0mFhVFC3esLWyyWEAFcKyI3FlzI70Z4ppnukyL/2MonVTqto3RPoTQs8Ty3y10MYo+9UE65U6Rv2yHfx551vobJgHgVS7tmupCcemZYscgFCeUMgLOz+5tylAAAAFQC9NTxN10PfjndbLDaPYrpC3h8g9wAAAIBnq0vCE9FqjukltP85M5qiawS5sEXArB0HRP4MDpwk4xDdrpiEwa2qYSOiwN1brsxMGrVHe3HetPeApmZZV5vysV1sLp3CQAlGHoWJ3GjOR3lDYpTymlbZOzd62mKp7FxPVtNszbDiABJckTLUV95TM2OB2hn/mWqpzwJBh9GBrAAAAIAmFDbo3aCg3WcDFnBzI72aHBQ4532xEMrlASABJ9FwkNPCtr2VZRLR4vxo9Oyn21+z4SmFocJN0DcKgj1APeS8en/frAFG/UJr/4KdV/LAnTBz8qd6QnbxdUYxizVoYKI1Pl76axbPQCVe+dthfn0x19quOMc/ofHA7I0GGIojJw== nick@Sparta-MB.local
|
14
|
+
ssh-dss AAAAB3NzaC1kc3MAAACBAO/mj7W3wUuDAhvicx+C2MkAFJGFZvaPrBA+nbFbK240HDZcmS8sA9Ww3VwivKzQ4CwrpdMY3dPg0TM9H2USQvfMsis4bStDwLy4CFIvBEGoviVVdu8gBzVN+YGajhv7yZqJ9cpOSnUXUzCBh4ddhLtni4IQ2lEZyKdlUzmA77pZAAAAFQDSdiFXpCu3CrAm0zivwA68DcDj5wAAAIEA557V4w7vNgmZvkb3hnQui5IkVTpN+b2PKCzvA9clbJQvKBpivS7k5jpx+GeoAYA+pYNLppMf9zpyPLrJjJhJ3Y3qS3p5iFCed2aJRwxhd5lYIyEedZ2uW6tchVRPoXkT1/tmXBKUNnue8YHslhqXOy8nHyHd7dH7BXLblnQknRcAAACBANzpDp5oXEG7w40tCYd7dtJfDLT8cIXyA75yX2MtkHjpBylT+lP/c1P9pmPytEtBfoQCvwFKjVA7fVGTkF1QfMVzcJsB1wh6jUyS287JtbS4D2O/yEP6FioG5Qva8GdJ29Oy49+LIHgamc93ZaJPub+wvFYivNzQsomjdXtiNRNA olpc@xo-10-F8-44.localdomain
|
15
|
+
ssh-dss AAAAB3NzaC1kc3MAAACBAOC9i08N/CoZO9I4kXv8LFSkReglZw9gmOqK72E/IpijLZkd2oh0wyV98S+8lYZR0OPmK6B4vbPIarBsZiPfOe2EMGxsuYjwD3X3S2hIM/1zrvk+WMzf6UADxmCwWzC6bYa1aBH59ba9ojolsBuou6yOxm3boYmZYbnMCLUzm/eLAAAAFQCHNIOpZTKG0chczg+5T3WJ4Q5JAwAAAIA2/VIk8pST/ynuXuu82Z9ZsR9ZJHICYfYi2EqLA6qJUxSB8MjvF9BQGYgX+UCs96uFme8wPJMBU74c7mZlcHvtlgc+1rMQUTZhJ820b/eg2gcPKGYikWj8y3wT0TDoWemMWAaFprvgrPLhHYuVMUKKzI9VNTeKDD7NC1xMsd9pOgAAAIBT14VMwBZjaq0oJSDxzy7bgf5Tragy9yhs7ZgQ0QTpMTFrkO/YvbsCLeXbsRcq904EMcB5glOHXONppn5skwWwBCFtjpjL9WLaeCnhpCxFC+D89GyR7T3Jlvj/Wa2tkuCSFJLBc64kK+pTmDseRLBbhazJazn3oUrNuEFtazzZCw== simon@secondary
|
16
|
+
ssh-dss AAAAB3NzaC1kc3MAAACBALlZtmMckFufyVq+It10p4pC65NJCRdDpRrf1uIoVXvz1ZSlsm/f0jhatuNPpwnSQoRpvlWetiS5mzGJ3ze8aReqHHe9CZvDCpVy33cwyRhCZf7ndyGh1qa9ZA0YMHNVdVs03SpNr7Fzc3muGcKX3BiiOZz+6mtbaQW0X1p/VHpLAAAAFQC8AuBVqCRJKgBMHDHb0nxQAyHbRQAAAIEAgQJvHZ3Db3yVy99goeIJw/3lysVemaddIdPuJj/AdxRwkDeuZIxxtOUne/qjXr7hmu48EyXLVg/jsNivKarLI6CXZAk5JbxkgLN+8y0Ckzcd48KhSGpZsbTBtjV29UFH9aiMVOUTbL59603HNa3ucH2lCLhllBf+ameZZp+EFYMAAACARZwlCuPobEAk7wOxWzGQcaRvmSDSbIKexMeUBgZBZeYjp5Bxld6e7x/j3GBQEkzmxqf08zP6LpglDoNR3KuY2TEWhAS3H1KdT4GibNSNpY4y2Pl2K/GjUWC6wMmVcFFBCz/jJorttMSI1G+OVcy+Vdw42HyQ5Z2QtuoVA2MQ5IQ= staging@domU-12-31-39-0E-C8-82
|
17
|
+
END_OF_KEYS
|
18
|
+
cat >>~staging/.ssh/config <<END_OF_CONFIG
|
19
|
+
Host web
|
20
|
+
HostName domU-12-31-39-00-64-E4.compute-1.internal
|
21
|
+
User app
|
22
|
+
Host webu
|
23
|
+
HostName domU-12-31-39-00-64-E4.compute-1.internal
|
24
|
+
User ubuntu
|
25
|
+
Host *.compute-1.internal
|
26
|
+
ForwardAgent yes
|
27
|
+
END_OF_CONFIG
|
28
|
+
|
29
|
+
chown -R staging:staging ~staging/.ssh
|
30
|
+
fi
|
31
|
+
|
32
|
+
if ! id app 2>/dev/null 1>&2; then
|
33
|
+
adduser --disabled-password --gecos 'app' app
|
34
|
+
mkdir -m 700 ~app/.ssh
|
35
|
+
touch ~app/.ssh/authorized_keys
|
36
|
+
chmod 600 ~app/.ssh/authorized_keys
|
37
|
+
|
38
|
+
cat >>~app/.ssh/authorized_keys <<END_OF_KEYS
|
39
|
+
ssh-dss AAAAB3NzaC1kc3MAAACBALlZtmMckFufyVq+It10p4pC65NJCRdDpRrf1uIoVXvz1ZSlsm/f0jhatuNPpwnSQoRpvlWetiS5mzGJ3ze8aReqHHe9CZvDCpVy33cwyRhCZf7ndyGh1qa9ZA0YMHNVdVs03SpNr7Fzc3muGcKX3BiiOZz+6mtbaQW0X1p/VHpLAAAAFQC8AuBVqCRJKgBMHDHb0nxQAyHbRQAAAIEAgQJvHZ3Db3yVy99goeIJw/3lysVemaddIdPuJj/AdxRwkDeuZIxxtOUne/qjXr7hmu48EyXLVg/jsNivKarLI6CXZAk5JbxkgLN+8y0Ckzcd48KhSGpZsbTBtjV29UFH9aiMVOUTbL59603HNa3ucH2lCLhllBf+ameZZp+EFYMAAACARZwlCuPobEAk7wOxWzGQcaRvmSDSbIKexMeUBgZBZeYjp5Bxld6e7x/j3GBQEkzmxqf08zP6LpglDoNR3KuY2TEWhAS3H1KdT4GibNSNpY4y2Pl2K/GjUWC6wMmVcFFBCz/jJorttMSI1G+OVcy+Vdw42HyQ5Z2QtuoVA2MQ5IQ= staging@domU-12-31-39-0E-C8-82
|
40
|
+
END_OF_KEYS
|
41
|
+
chown -R app:app ~app/.ssh
|
42
|
+
fi
|
data/lib/cluster.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
%w(configuration version infrastructure instance release).each {|l| require File.join('cluster', l) }
|
2
|
+
|
3
|
+
class Cluster
|
4
|
+
def security(*groups)
|
5
|
+
groups << 'access' if groups.empty?
|
6
|
+
@sub.security(groups)
|
7
|
+
end
|
8
|
+
|
9
|
+
def cost(*sizes)
|
10
|
+
@sub.cost(sizes)
|
11
|
+
end
|
12
|
+
|
13
|
+
def period(*args)
|
14
|
+
@sub.period(args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def revoke(*ips)
|
18
|
+
if ips.empty?
|
19
|
+
current_ip = ''
|
20
|
+
require 'open-uri'
|
21
|
+
open('http://checkip.dyndns.com').each do |line|
|
22
|
+
current_ip = $1 and break if line =~ /IP Address: ([\d\.]+)</
|
23
|
+
end
|
24
|
+
ips << "#{current_ip}/32"
|
25
|
+
end
|
26
|
+
@sub.revoke(ips)
|
27
|
+
end
|
28
|
+
|
29
|
+
def authorize(*ips)
|
30
|
+
if ips.empty?
|
31
|
+
current_ip = ''
|
32
|
+
require 'open-uri'
|
33
|
+
open('http://checkip.dyndns.com').each do |line|
|
34
|
+
current_ip = $1 and break if line =~ /IP Address: ([\d\.]+)</
|
35
|
+
end
|
36
|
+
ips << "#{current_ip}/32"
|
37
|
+
end
|
38
|
+
@sub.authorize(ips)
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(infrastructure)
|
42
|
+
self.class.set_credentials_file
|
43
|
+
infrastructure.configure
|
44
|
+
@sub = infrastructure
|
45
|
+
end
|
46
|
+
|
47
|
+
def machine(groups = [])
|
48
|
+
res = machines(groups)
|
49
|
+
res.empty? ? nil : res.first
|
50
|
+
end
|
51
|
+
|
52
|
+
def machines(groups = [])
|
53
|
+
@sub.machines(groups)
|
54
|
+
end
|
55
|
+
alias :instances :machines
|
56
|
+
|
57
|
+
def service(roles)
|
58
|
+
res = services(roles)
|
59
|
+
res.empty? ? nil : res.first
|
60
|
+
end
|
61
|
+
|
62
|
+
def services(roles = [])
|
63
|
+
@sub.services(roles)
|
64
|
+
end
|
65
|
+
|
66
|
+
def labeled(name)
|
67
|
+
@sub.instances.select {|i| i.identified_by? name}
|
68
|
+
end
|
69
|
+
|
70
|
+
def release(*params)
|
71
|
+
klass = @sub.release_class
|
72
|
+
env, tag = params
|
73
|
+
if tag
|
74
|
+
klass.find(env, tag) or klass.create(:environment => env, :tag => tag)
|
75
|
+
elsif env
|
76
|
+
klass.current(env)
|
77
|
+
else
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_credentials
|
83
|
+
{'credentials' => nil}
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_file_store(name)
|
87
|
+
@sub.create_file_store(name)
|
88
|
+
end
|
89
|
+
|
90
|
+
def create_data_store(name)
|
91
|
+
@sub.create_data_store(name)
|
92
|
+
end
|
93
|
+
|
94
|
+
def start(machine_size, *services)
|
95
|
+
size = machine_size.to_s.strip.downcase
|
96
|
+
unless @sub.class.machine_sizes.include? size
|
97
|
+
msg = "#{Cluster::NAME} needs to have a size from: #{@sub.class.machine_sizes.join(', ')}"
|
98
|
+
puts msg
|
99
|
+
raise msg
|
100
|
+
end
|
101
|
+
# FIXME need a way to pass through a number argument
|
102
|
+
number ||= 1
|
103
|
+
|
104
|
+
services = services.map {|s|
|
105
|
+
s.split(',')
|
106
|
+
}.flatten
|
107
|
+
|
108
|
+
# This may not be necessary, but might as well make sure they are
|
109
|
+
# up to date any time that we are starting a new instance.
|
110
|
+
self.save_credentials
|
111
|
+
|
112
|
+
begin
|
113
|
+
number.times.map do |n|
|
114
|
+
@sub.new_instance(size, services)
|
115
|
+
end
|
116
|
+
rescue => err
|
117
|
+
puts "#{Cluster::NAME} cannot start new instance: #{err.message}\n\t#{err.backtrace.join("\n\t")}"
|
118
|
+
exit 2
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def update_image_file(filename)
|
123
|
+
input = File.open(filename)
|
124
|
+
@sub.save_images(input)
|
125
|
+
end
|
126
|
+
|
127
|
+
def current(*params)
|
128
|
+
unless @sub.in_cluster?
|
129
|
+
puts "#{Cluster::NAME} says that we aren't in the cluster?"
|
130
|
+
exit 2
|
131
|
+
end
|
132
|
+
|
133
|
+
unless params.length > 0
|
134
|
+
puts "#{Cluster::NAME} current needs to know what to do?\n\t* services\n\t* name (|| dns)\n\t* ip\n\t* enable\n\t* disable"
|
135
|
+
exit 2
|
136
|
+
end
|
137
|
+
|
138
|
+
cmd = params.shift
|
139
|
+
|
140
|
+
case cmd.downcase
|
141
|
+
when 'services'
|
142
|
+
@sub.current_instance.services
|
143
|
+
when /^(n|dn)/
|
144
|
+
@sub.current_instance.dns
|
145
|
+
when /^i/
|
146
|
+
@sub.current_instance.ip
|
147
|
+
when /^e/
|
148
|
+
@sub.alter_instances!(@sub.current_instance) {|i| i.enable *params }
|
149
|
+
when /^di/
|
150
|
+
@sub.alter_instances!(@sub.current_instance) {|i| i.disable *params }
|
151
|
+
when /^s/
|
152
|
+
@sub.alter_instances!(@sub.current_instance) {|i| i.set_state *params }
|
153
|
+
else
|
154
|
+
puts "${Cluster::NAME} current did not understand '#{params.join(' ')}'"
|
155
|
+
exit 1
|
156
|
+
end
|
157
|
+
end
|
158
|
+
alias :instance :current
|
159
|
+
|
160
|
+
def instance_state(state)
|
161
|
+
@sub.current_instance.set_state(state)
|
162
|
+
end
|
163
|
+
|
164
|
+
def store(service, key, filename = nil)
|
165
|
+
full_key = File.join service, key
|
166
|
+
file = if filename and File.readable? filename
|
167
|
+
File.open(filename, 'r')
|
168
|
+
elsif File.readable? full_key
|
169
|
+
File.open(full_key, 'r')
|
170
|
+
elsif File.readable? File.basename(full_key)
|
171
|
+
File.open(File.basename(full_key))
|
172
|
+
else
|
173
|
+
puts "#{Cluster::NAME} cannot open a file for storage"
|
174
|
+
exit 2
|
175
|
+
end
|
176
|
+
@sub.store full_key, file
|
177
|
+
end
|
178
|
+
|
179
|
+
def retrieve(service, key, output = nil)
|
180
|
+
res = @sub.retrieve File.join(service, key)
|
181
|
+
if output
|
182
|
+
begin
|
183
|
+
File.open(output, 'w') {|f|
|
184
|
+
f.write res
|
185
|
+
}
|
186
|
+
rescue => err
|
187
|
+
puts "#{Cluster::NAME} cannot open #{output} for writing."
|
188
|
+
exit 2
|
189
|
+
end
|
190
|
+
else
|
191
|
+
res
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def save_credentials(location = nil)
|
196
|
+
creds = if Cluster::Configuration.credentials?
|
197
|
+
File.open(Cluster::Configuration[:credentials_file])
|
198
|
+
else
|
199
|
+
cx = @sub.to_credentials.merge to_credentials
|
200
|
+
StringIO.new(cx.to_yaml)
|
201
|
+
end
|
202
|
+
@sub.save_credentials(creds, location)
|
203
|
+
end
|
204
|
+
|
205
|
+
def save_monitor(filename, key = nil)
|
206
|
+
file = open(filename)
|
207
|
+
unless file
|
208
|
+
puts "#{Cluster::NAME} cannot open file '#{filename}' for reading."
|
209
|
+
exit 2
|
210
|
+
end
|
211
|
+
|
212
|
+
key ||= File.basename(filename)
|
213
|
+
begin
|
214
|
+
@sub.save_monitor file, key
|
215
|
+
rescue => err
|
216
|
+
puts "#{Cluster::NAME} could not save monitor configuration: #{err.message}\n\t#{err.backtrace.join("\n\t")}"
|
217
|
+
exit 2
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def credentials_url
|
222
|
+
@sub.credentials_url
|
223
|
+
end
|
224
|
+
|
225
|
+
def fetch_credentials(url)
|
226
|
+
unless Cluster::Configuration.credentials?
|
227
|
+
puts "Need to know where to save the incoming credentials."
|
228
|
+
exit 2
|
229
|
+
end
|
230
|
+
|
231
|
+
out = File.open(Cluster::Configuration[:credentials_file], 'w')
|
232
|
+
open(url).each do |l| out.write(l); end
|
233
|
+
out.close
|
234
|
+
end
|
235
|
+
|
236
|
+
def fetch_monitor(output)
|
237
|
+
monitor = @sub.fetch_monitor
|
238
|
+
unless monitor
|
239
|
+
puts "#{Cluster::NAME} cannot find any monitor information."
|
240
|
+
exit 1
|
241
|
+
end
|
242
|
+
|
243
|
+
File.open(output, 'w') {|f|
|
244
|
+
f.write(monitor)
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
def gemurl
|
249
|
+
Cluster::LOCATION
|
250
|
+
end
|
251
|
+
|
252
|
+
def imageurl
|
253
|
+
Cluster::IMAGES
|
254
|
+
end
|
255
|
+
|
256
|
+
class << self
|
257
|
+
def set_credentials_file
|
258
|
+
unless Cluster::Configuration.credentials?
|
259
|
+
if ENV['CREDENTIALS'] and File.exist?(ENV['CREDENTIALS'])
|
260
|
+
Cluster::Configuration[:credentials_file] = ENV['CREDENTIALS']
|
261
|
+
elsif ENV['HOME'] and File.exist?(File.join(ENV['HOME'], '.cluster', 'credentials.yml'))
|
262
|
+
Cluster::Configuration[:credentials_file] = File.join(ENV['HOME'], '.cluster', 'credentials.yml')
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|