sunzi 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sunzi.rb +9 -1
- data/lib/sunzi/cli.rb +4 -4
- data/lib/sunzi/cloud.rb +18 -0
- data/lib/sunzi/cloud/base.rb +67 -12
- data/lib/sunzi/cloud/digital_ocean.rb +34 -118
- data/lib/sunzi/cloud/linode.rb +46 -122
- data/lib/sunzi/dns.rb +19 -0
- data/lib/sunzi/dns/base.rb +7 -0
- data/lib/sunzi/dns/linode.rb +26 -0
- data/lib/sunzi/dns/route53.rb +25 -0
- data/lib/templates/create/install.sh +8 -8
- data/lib/templates/setup/linode/linode.yml +1 -1
- data/sunzi.gemspec +1 -1
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9fc8aafe77074cbb76a6fcf041b1647cc0a0634
|
4
|
+
data.tar.gz: f58cf2395b1361ffeb48336fc5b6e1e09f325555
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4f22aa9652eb1bca2951b42514aa307c6267bfdd708d7fbc52ecaadac7fde746685d122b967e0adb1ffbbcaaaab7d4eaa5c9b5e81b85bec0d17cf2dd885c59f
|
7
|
+
data.tar.gz: 6d78f2ba89d13ca900cdb6358e388c6197a1a954ae88112ec8eb424ab1fad2a1f483b5d4989c426d522a2f969ea10b8a9bb190093ff3407d84cbc4ff171d17ea
|
data/lib/sunzi.rb
CHANGED
@@ -4,13 +4,21 @@ require 'yaml'
|
|
4
4
|
|
5
5
|
module Sunzi
|
6
6
|
autoload :Cli, 'sunzi/cli'
|
7
|
+
autoload :Cloud, 'sunzi/cloud'
|
7
8
|
autoload :Dependency, 'sunzi/dependency'
|
9
|
+
autoload :DNS, 'sunzi/dns'
|
8
10
|
autoload :Logger, 'sunzi/logger'
|
9
11
|
autoload :Utility, 'sunzi/utility'
|
10
12
|
|
11
|
-
|
13
|
+
class Cloud
|
12
14
|
autoload :Base, 'sunzi/cloud/base'
|
13
15
|
autoload :Linode, 'sunzi/cloud/linode'
|
14
16
|
autoload :DigitalOcean, 'sunzi/cloud/digital_ocean'
|
15
17
|
end
|
18
|
+
|
19
|
+
class DNS
|
20
|
+
autoload :Base, 'sunzi/dns/base'
|
21
|
+
autoload :Linode, 'sunzi/dns/linode'
|
22
|
+
autoload :Route53, 'sunzi/dns/route53'
|
23
|
+
end
|
16
24
|
end
|
data/lib/sunzi/cli.rb
CHANGED
@@ -21,13 +21,13 @@ module Sunzi
|
|
21
21
|
end
|
22
22
|
|
23
23
|
desc 'setup [linode|digital_ocean]', 'Setup a new VM'
|
24
|
-
def setup(
|
25
|
-
Cloud
|
24
|
+
def setup(provider)
|
25
|
+
Sunzi::Cloud.new(self, provider).setup
|
26
26
|
end
|
27
27
|
|
28
28
|
desc 'teardown [linode|digital_ocean] [name]', 'Teardown an existing VM'
|
29
|
-
def teardown(
|
30
|
-
Cloud
|
29
|
+
def teardown(provider, name)
|
30
|
+
Sunzi::Cloud.new(self, provider).teardown(name)
|
31
31
|
end
|
32
32
|
|
33
33
|
desc 'version', 'Show version'
|
data/lib/sunzi/cloud.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Sunzi
|
2
|
+
class Cloud
|
3
|
+
def initialize(cli, provider)
|
4
|
+
@subject = case provider
|
5
|
+
when 'linode'
|
6
|
+
Sunzi::Cloud::Linode.new(cli, provider)
|
7
|
+
when 'digital_ocean'
|
8
|
+
Sunzi::Cloud::DigitalOcean.new(cli, provider)
|
9
|
+
else
|
10
|
+
abort_with "#{provider} is not valid!"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(sym, *args, &block)
|
15
|
+
@subject.send sym, *args, &block
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/sunzi/cloud/base.rb
CHANGED
@@ -1,29 +1,84 @@
|
|
1
1
|
Sunzi::Dependency.load('highline')
|
2
2
|
|
3
3
|
module Sunzi
|
4
|
-
|
4
|
+
class Cloud
|
5
5
|
class Base
|
6
6
|
include Sunzi::Utility
|
7
7
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
def initialize(cli, provider)
|
9
|
+
@provider = provider
|
10
|
+
@cli = cli
|
11
|
+
@ui = HighLine.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
unless File.exist? "#{@provider}/#{@provider}.yml"
|
16
|
+
@cli.empty_directory "#{@provider}/instances"
|
17
|
+
@cli.template "templates/setup/#{@provider}/#{@provider}.yml", "#{@provider}/#{@provider}.yml"
|
18
|
+
exit_with "Now go ahead and edit #{@provider}.yml, then run this command again!"
|
19
|
+
end
|
20
|
+
|
21
|
+
@config = YAML.load(File.read("#{@provider}/#{@provider}.yml"))
|
22
|
+
@dns = Sunzi::DNS.new(@config, @provider) if @config['dns']
|
23
|
+
|
24
|
+
if @config['fqdn']['zone'] == 'example.com'
|
25
|
+
abort_with "You must have your own settings in #{@provider}.yml"
|
16
26
|
end
|
27
|
+
|
28
|
+
# Ask environment and hostname
|
29
|
+
@env = ask("environment? (#{@config['environments'].join(' / ')}): ", String) {|q| q.in = @config['environments'] }.to_s
|
30
|
+
@host = ask('hostname? (only the first part of subdomain): ', String).to_s
|
31
|
+
|
32
|
+
abort_with '"label" field in linode.yml is no longer supported. rename it to "name".' if @config['label']
|
33
|
+
@fqdn = @config['fqdn'][@env].gsub(/%{host}/, @host)
|
34
|
+
@name = @config['name'][@env].gsub(/%{host}/, @host)
|
35
|
+
|
36
|
+
assign_api
|
37
|
+
@attributes = {}
|
38
|
+
do_setup
|
39
|
+
|
40
|
+
# Save instance info
|
41
|
+
@cli.create_file "#{@provider}/instances/#{@name}.yml", YAML.dump(@instance)
|
42
|
+
|
43
|
+
# Register IP to DNS
|
44
|
+
@dns.add(@fqdn, @public_ip) if @dns
|
17
45
|
end
|
18
46
|
|
19
|
-
def
|
20
|
-
@
|
21
|
-
|
47
|
+
def teardown(name)
|
48
|
+
unless File.exist?("#{@provider}/instances/#{name}.yml")
|
49
|
+
abort_with "#{name}.yml was not found in the instances directory."
|
50
|
+
end
|
51
|
+
|
52
|
+
@config = YAML.load(File.read("#{@provider}/#{@provider}.yml"))
|
53
|
+
@dns = Sunzi::DNS.new(@config, @provider) if @config['dns']
|
54
|
+
|
55
|
+
@instance = YAML.load(File.read("#{@provider}/instances/#{name}.yml"))
|
56
|
+
|
57
|
+
# Are you sure?
|
58
|
+
moveon = ask("Are you sure about deleting #{@instance[:fqdn]} permanently? (y/n) ", String) {|q| q.in = ['y','n']}
|
59
|
+
exit unless moveon == 'y'
|
60
|
+
|
61
|
+
# Run Linode / DigitalOcean specific tasks
|
62
|
+
assign_api
|
63
|
+
do_teardown
|
64
|
+
|
65
|
+
# Delete DNS record
|
66
|
+
@dns.delete(@instance[ip_key]) if @dns
|
67
|
+
|
68
|
+
# Remove the instance config file
|
69
|
+
@cli.remove_file "#{@provider}/instances/#{name}.yml"
|
70
|
+
|
71
|
+
say 'Done.'
|
22
72
|
end
|
23
73
|
|
24
74
|
def ask(question, answer_type, &details)
|
25
75
|
@ui.ask(@ui.color(question, :green, :bold), answer_type, &details)
|
26
76
|
end
|
77
|
+
|
78
|
+
def proceed?
|
79
|
+
moveon = ask("Are you ready to go ahead and create #{@fqdn}? (y/n) ", String) {|q| q.in = ['y','n']}
|
80
|
+
exit unless moveon == 'y'
|
81
|
+
end
|
27
82
|
end
|
28
83
|
end
|
29
84
|
end
|
@@ -1,160 +1,76 @@
|
|
1
1
|
Sunzi::Dependency.load('digital_ocean')
|
2
2
|
|
3
3
|
module Sunzi
|
4
|
-
|
4
|
+
class Cloud
|
5
5
|
class DigitalOcean < Base
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
@cli.template 'templates/setup/digital_ocean/digital_ocean.yml', 'digital_ocean/digital_ocean.yml'
|
10
|
-
exit_with 'Now go ahead and edit digital_ocean.yml, then run this command again!'
|
11
|
-
end
|
12
|
-
|
13
|
-
@config = YAML.load(File.read('digital_ocean/digital_ocean.yml'))
|
14
|
-
|
15
|
-
if @config['fqdn']['zone'] == 'example.com'
|
16
|
-
abort_with 'You must have your own settings in digital_ocean.yml'
|
17
|
-
end
|
18
|
-
|
19
|
-
# When route53 is specified for DNS, check if it's properly configured and if not, fail earlier.
|
20
|
-
setup_route53 if @config['dns'] == 'route53'
|
21
|
-
|
22
|
-
@api = ::DigitalOcean::API.new :api_key => @config['api_key'], :client_id => @config['client_id']
|
23
|
-
|
24
|
-
# Ask environment and hostname
|
25
|
-
@env = ask("environment? (#{@config['environments'].join(' / ')}): ", String) {|q| q.in = @config['environments'] }.to_s
|
26
|
-
@host = ask('hostname? (only the first part of subdomain): ', String).to_s
|
6
|
+
def do_setup
|
7
|
+
choose(:size, @api.sizes.list.sizes)
|
8
|
+
choose(:region, @api.regions.list.regions)
|
27
9
|
|
28
|
-
|
29
|
-
@name = @config['name'][@env].gsub(/%{host}/, @host)
|
30
|
-
|
31
|
-
# Choose a size
|
32
|
-
result = @api.sizes.list.sizes
|
33
|
-
result.each{|i| say "#{i.id}: #{i.name}" }
|
34
|
-
@size_id = ask('which size?: ', Integer) {|q| q.in = result.map(&:id); q.default = result.first.id }
|
35
|
-
@size_name = result.find{|i| i.id == @size_id }.name
|
36
|
-
|
37
|
-
# Choose a region
|
38
|
-
result = @api.regions.list.regions
|
39
|
-
result.each{|i| say "#{i.id}: #{i.name}" }
|
40
|
-
@region_id = ask('which region?: ', Integer) {|q| q.in = result.map(&:id); q.default = result.first.id }
|
41
|
-
@region_name = result.find{|i| i.id == @region_id }.name
|
42
|
-
|
43
|
-
# Choose a image
|
10
|
+
# Choose an image
|
44
11
|
result = @api.images.list({'filter' => 'global'}).images
|
45
12
|
if @config['distributions_filter']
|
46
13
|
result = result.select{|i| i.distribution.match Regexp.new(@config['distributions_filter'], Regexp::IGNORECASE) }
|
47
14
|
end
|
48
|
-
|
49
|
-
@image_id = ask('which image?: ', Integer) {|q| q.in = result.map(&:id); q.default = result.first.id }
|
50
|
-
@image_name = result.find{|i| i.id == @image_id }.name
|
15
|
+
choose(:image, result)
|
51
16
|
|
52
17
|
# Go ahead?
|
53
|
-
|
54
|
-
exit unless moveon == 'y'
|
18
|
+
proceed?
|
55
19
|
|
56
|
-
|
20
|
+
ssh_key_ids = @api.ssh_keys.list.ssh_keys.map(&:id).join(',')
|
57
21
|
|
58
22
|
# Create
|
59
23
|
say "creating a new droplets..."
|
60
24
|
result = @api.droplets.create(:name => @name,
|
61
|
-
:size_id
|
62
|
-
:image_id
|
63
|
-
:region_id
|
64
|
-
:ssh_key_ids =>
|
25
|
+
:size_id => @attributes[:size_id],
|
26
|
+
:image_id => @attributes[:image_id],
|
27
|
+
:region_id => @attributes[:region_id],
|
28
|
+
:ssh_key_ids => ssh_key_ids)
|
65
29
|
|
66
30
|
@droplet_id = result.droplet.id
|
67
31
|
say "Created a new droplet (id: #{@droplet_id}). Booting..."
|
68
32
|
|
69
|
-
# Boot
|
33
|
+
# Boot - we need this before getting public IP
|
70
34
|
while @api.droplets.show(@droplet_id).droplet.status.downcase != 'active'
|
71
|
-
sleep
|
35
|
+
sleep 3
|
72
36
|
end
|
73
37
|
|
74
38
|
@public_ip = @api.droplets.show(@droplet_id).droplet.ip_address
|
75
39
|
say "Done. ip address = #{@public_ip}"
|
76
40
|
|
77
|
-
|
78
|
-
case @config['dns']
|
79
|
-
when 'route53'
|
80
|
-
# Set the public IP to AWS Route 53
|
81
|
-
say "Setting the public IP to AWS Route 53..."
|
82
|
-
Route53::DNSRecord.new(@fqdn, "A", "300", [@public_ip], @route53_zone).create
|
83
|
-
when 'linode'
|
84
|
-
Sunzi::Dependency.load('linode') # FIXME: this should run at the beginning
|
85
|
-
linode = ::Linode.new(:api_key => @config['linode']['api_key'])
|
86
|
-
# Set the public IP to Linode DNS Manager
|
87
|
-
say "Setting the public IP to Linode DNS Manager..."
|
88
|
-
domainid = linode.domain.list.find{|i| i.domain == @config['fqdn']['zone'] }.domainid
|
89
|
-
linode.domain.resource.create(:DomainID => domainid, :Type => 'A', :Name => @fqdn, :Target => @public_ip)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Save the instance info
|
93
|
-
hash = {
|
41
|
+
@instance = {
|
94
42
|
:droplet_id => @droplet_id,
|
95
|
-
:env
|
43
|
+
:env => @env,
|
96
44
|
:host => @host,
|
97
45
|
:fqdn => @fqdn,
|
98
46
|
:name => @name,
|
99
47
|
:ip_address => @public_ip,
|
100
|
-
:size_id
|
101
|
-
:size_name
|
102
|
-
:region_id
|
103
|
-
:region_name
|
104
|
-
:image_id
|
105
|
-
:image_name
|
48
|
+
:size_id => @attributes[:size_id],
|
49
|
+
:size_name => @attributes[:size_name],
|
50
|
+
:region_id => @attributes[:region_id],
|
51
|
+
:region_name => @attributes[:region_name],
|
52
|
+
:image_id => @attributes[:image_id],
|
53
|
+
:image_name => @attributes[:image_name],
|
106
54
|
}
|
107
|
-
@cli.create_file "digital_ocean/instances/#{@name}.yml", YAML.dump(hash)
|
108
|
-
|
109
55
|
end
|
110
56
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
@config = YAML.load(File.read('digital_ocean/digital_ocean.yml'))
|
117
|
-
setup_route53 if @config['dns'] == 'route53'
|
118
|
-
|
119
|
-
@instance = YAML.load(File.read("digital_ocean/instances/#{name}.yml"))
|
120
|
-
@droplet_id = @instance[:droplet_id]
|
121
|
-
|
122
|
-
@api = ::DigitalOcean::API.new :api_key => @config['api_key'], :client_id => @config['client_id']
|
123
|
-
|
124
|
-
# Are you sure?
|
125
|
-
moveon = ask("Are you sure about deleting #{@instance[:fqdn]} permanently? (y/n) ", String) {|q| q.in = ['y','n']}
|
126
|
-
exit unless moveon == 'y'
|
57
|
+
def choose(key, result)
|
58
|
+
result.each{|i| say "#{i.id}: #{i.name}" }
|
59
|
+
@attributes[:"#{key}_id"] = ask("which #{key}?: ", Integer) {|q| q.in = result.map(&:id); q.default = result.first.id }
|
60
|
+
@attributes[:"#{key}_name"] = result.find{|i| i.id == @attributes[:"#{key}_id"] }.name
|
61
|
+
end
|
127
62
|
|
128
|
-
|
63
|
+
def do_teardown
|
129
64
|
say 'deleting droplet...'
|
130
|
-
@api.droplets.delete(@droplet_id)
|
131
|
-
|
132
|
-
# Delete DNS record
|
133
|
-
case @config['dns']
|
134
|
-
when 'route53'
|
135
|
-
say 'deleting the public IP from AWS Route 53...'
|
136
|
-
@record = @route53_zone.get_records.find{|i| i.values.first == @instance[:ip_address] }
|
137
|
-
@record.delete if @record
|
138
|
-
when 'linode'
|
139
|
-
Sunzi::Dependency.load('linode') # FIXME: this should run at the beginning
|
140
|
-
linode = ::Linode.new(:api_key => @config['linode']['api_key'])
|
141
|
-
say 'deleting the public IP from Linode DNS Manager...'
|
142
|
-
domainid = linode.domain.list.find{|i| i.domain == @config['fqdn']['zone'] }.domainid
|
143
|
-
resource = linode.domain.resource.list(:DomainID => domainid).find{|i| i.target == @instance[:ip_address] }
|
144
|
-
linode.domain.resource.delete(:DomainID => domainid, :ResourceID => resource.resourceid)
|
145
|
-
end
|
146
|
-
|
147
|
-
# Remove the instance config file
|
148
|
-
@cli.remove_file "digital_ocean/instances/#{name}.yml"
|
65
|
+
@api.droplets.delete(@instance[:droplet_id])
|
66
|
+
end
|
149
67
|
|
150
|
-
|
68
|
+
def assign_api
|
69
|
+
@api = ::DigitalOcean::API.new :api_key => @config['api_key'], :client_id => @config['client_id']
|
151
70
|
end
|
152
71
|
|
153
|
-
def
|
154
|
-
|
155
|
-
route53 = Route53::Connection.new(@config['route53']['key'], @config['route53']['secret'])
|
156
|
-
@route53_zone = route53.get_zones.find{|i| i.name.sub(/\.$/,'') == @config['fqdn']['zone'] }
|
157
|
-
abort_with "zone for #{@config['fqdn']['zone']} was not found on route53!" unless @route53_zone
|
72
|
+
def ip_key
|
73
|
+
:ip_address
|
158
74
|
end
|
159
75
|
end
|
160
76
|
end
|
data/lib/sunzi/cloud/linode.rb
CHANGED
@@ -1,79 +1,31 @@
|
|
1
1
|
Sunzi::Dependency.load('linode')
|
2
2
|
|
3
3
|
module Sunzi
|
4
|
-
|
4
|
+
class Cloud
|
5
5
|
class Linode < Base
|
6
|
-
def
|
7
|
-
# Only run for the first time
|
8
|
-
unless File.exist? 'linode/linode.yml'
|
9
|
-
@cli.empty_directory 'linode/instances'
|
10
|
-
@cli.template 'templates/setup/linode/linode.yml', 'linode/linode.yml'
|
11
|
-
exit_with 'Now go ahead and edit linode.yml, then run this command again!'
|
12
|
-
end
|
13
|
-
|
14
|
-
@config = YAML.load(File.read('linode/linode.yml'))
|
15
|
-
|
16
|
-
if @config['fqdn']['zone'] == 'example.com'
|
17
|
-
abort_with 'You must have your own settings in linode.yml'
|
18
|
-
end
|
19
|
-
|
20
|
-
# When route53 is specified for DNS, check if it's properly configured and if not, fail earlier.
|
21
|
-
setup_route53 if @config['dns'] == 'route53'
|
22
|
-
|
6
|
+
def do_setup
|
23
7
|
@sshkey = File.read(File.expand_path(@config['root_sshkey_path'])).chomp
|
24
8
|
if @sshkey.match(/\n/)
|
25
9
|
abort_with "RootSSHKey #{@sshkey.inspect} must not be multi-line! Check inside \"#{@config['root_sshkey_path']}\""
|
26
10
|
end
|
27
11
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@fqdn = @config['fqdn'][@env].gsub(/%{host}/, @host)
|
33
|
-
@label = @config['label'][@env].gsub(/%{host}/, @host)
|
34
|
-
@group = @config['group'][@env]
|
35
|
-
@api = ::Linode.new(:api_key => @config['api_key'])
|
36
|
-
|
37
|
-
# Choose a plan
|
38
|
-
result = @api.avail.linodeplans
|
39
|
-
result.each{|i| say "#{i.planid}: #{i.ram}MB, $#{i.price}" }
|
40
|
-
@planid = ask('which plan?: ', Integer) {|q| q.in = result.map(&:planid); q.default = result.first.planid }
|
41
|
-
@plan_label = result.find{|i| i.planid == @planid }.label
|
42
|
-
|
43
|
-
# Choose a datacenter
|
44
|
-
result = @api.avail.datacenters
|
45
|
-
result.each{|i| say "#{i.datacenterid}: #{i.location}" }
|
46
|
-
@datacenterid = ask('which datacenter?: ', Integer) {|q| q.in = result.map(&:datacenterid); q.default = result.first.datacenterid }
|
47
|
-
@datacenter_location = result.find{|i| i.datacenterid == @datacenterid }.location
|
48
|
-
|
49
|
-
# Choose a distribution
|
50
|
-
result = @api.avail.distributions
|
51
|
-
if @config['distributions_filter']
|
52
|
-
result = result.select{|i| i.label.match Regexp.new(@config['distributions_filter'], Regexp::IGNORECASE) }
|
53
|
-
end
|
54
|
-
result.each{|i| say "#{i.distributionid}: #{i.label}" }
|
55
|
-
@distributionid = ask('which distribution?: ', Integer) {|q| q.in = result.map(&:distributionid); q.default = result.first.distributionid }
|
56
|
-
@distribution_label = result.find{|i| i.distributionid == @distributionid }.label
|
57
|
-
|
58
|
-
# Choose a kernel
|
59
|
-
result = @api.avail.kernels
|
60
|
-
if @config['kernels_filter']
|
61
|
-
result = result.select{|i| i.label.match Regexp.new(@config['kernels_filter'], Regexp::IGNORECASE) }
|
62
|
-
end
|
63
|
-
result.each{|i| say "#{i.kernelid}: #{i.label}" }
|
64
|
-
@kernelid = ask('which kernel?: ', Integer) {|q| q.in = result.map(&:kernelid); q.default = result.first.kernelid }
|
65
|
-
@kernel_label = result.find{|i| i.kernelid == @kernelid }.label
|
12
|
+
choose(:plan, @api.avail.linodeplans)
|
13
|
+
choose(:datacenter, @api.avail.datacenters, :label_method => :location)
|
14
|
+
choose(:distribution, @api.avail.distributions, :filter => 'distributions_filter')
|
15
|
+
choose(:kernel, @api.avail.kernels, :filter => 'kernels_filter')
|
66
16
|
|
67
17
|
# Choose swap size
|
68
18
|
@swap_size = ask('swap size in MB? (default: 256MB): ', Integer) { |q| q.default = 256 }
|
69
19
|
|
70
20
|
# Go ahead?
|
71
|
-
|
72
|
-
exit unless moveon == 'y'
|
21
|
+
proceed?
|
73
22
|
|
74
23
|
# Create
|
75
24
|
say "creating a new linode..."
|
76
|
-
result = @api.linode.create(
|
25
|
+
result = @api.linode.create(
|
26
|
+
:DatacenterID => @attributes[:datacenterid],
|
27
|
+
:PlanID => @attributes[:planid],
|
28
|
+
:PaymentTerm => @config['payment_term'])
|
77
29
|
@linodeid = result.linodeid
|
78
30
|
say "created a new instance: linodeid = #{@linodeid}"
|
79
31
|
|
@@ -82,16 +34,17 @@ module Sunzi
|
|
82
34
|
|
83
35
|
# Update settings
|
84
36
|
say "Updating settings..."
|
85
|
-
|
37
|
+
@group = @config['group'][@env]
|
38
|
+
settings = { :LinodeID => @linodeid, :Label => @name, :lpm_displayGroup => @group }
|
86
39
|
settings.update(@config['settings']) if @config['settings']
|
87
|
-
|
40
|
+
@api.linode.update(settings)
|
88
41
|
|
89
42
|
# Create a root disk
|
90
43
|
say "Creating a root disk..."
|
91
44
|
result = @api.linode.disk.createfromdistribution(
|
92
45
|
:LinodeID => @linodeid,
|
93
|
-
:DistributionID => @distributionid,
|
94
|
-
:Label => "#{@distribution_label} Image",
|
46
|
+
:DistributionID => @attributes[:distributionid],
|
47
|
+
:Label => "#{@attributes[:distribution_label]} Image",
|
95
48
|
:Size => @totalhd - @swap_size,
|
96
49
|
:rootPass => @config['root_pass'],
|
97
50
|
:rootSSHKey => @sshkey
|
@@ -112,8 +65,8 @@ module Sunzi
|
|
112
65
|
say "Creating a config profile..."
|
113
66
|
result = @api.linode.config.create(
|
114
67
|
:LinodeID => @linodeid,
|
115
|
-
:KernelID => @kernelid,
|
116
|
-
:Label => "#{@distribution_label} Profile",
|
68
|
+
:KernelID => @attributes[:kernelid],
|
69
|
+
:Label => "#{@attributes[:distribution_label]} Profile",
|
117
70
|
:DiskList => [ @root_diskid, @swap_diskid ].join(',')
|
118
71
|
)
|
119
72
|
@config_id = result.configid
|
@@ -126,34 +79,20 @@ module Sunzi
|
|
126
79
|
result = @api.linode.ip.list(:LinodeID => @linodeid).find{|i| i.ispublic == 0 }
|
127
80
|
@private_ip = result.ipaddress
|
128
81
|
|
129
|
-
|
130
|
-
case @config['dns']
|
131
|
-
when 'linode'
|
132
|
-
# Set the public IP to Linode DNS Manager
|
133
|
-
say "Setting the public IP to Linode DNS Manager..."
|
134
|
-
@domainid = @api.domain.list.find{|i| i.domain == @config['fqdn']['zone'] }.domainid
|
135
|
-
@api.domain.resource.create(:DomainID => @domainid, :Type => 'A', :Name => @fqdn, :Target => @public_ip)
|
136
|
-
when 'route53'
|
137
|
-
# Set the public IP to AWS Route 53
|
138
|
-
say "Setting the public IP to AWS Route 53..."
|
139
|
-
Route53::DNSRecord.new(@fqdn, "A", "300", [@public_ip], @route53_zone).create
|
140
|
-
end
|
141
|
-
|
142
|
-
# Save the instance info
|
143
|
-
hash = {
|
82
|
+
@instance = {
|
144
83
|
:linode_id => @linodeid,
|
145
84
|
:env => @env,
|
146
85
|
:host => @host,
|
147
86
|
:fqdn => @fqdn,
|
148
|
-
:label => @
|
87
|
+
:label => @name,
|
149
88
|
:group => @group,
|
150
|
-
:plan_id =>
|
151
|
-
:datacenter_id =>
|
152
|
-
:datacenter_location => @datacenter_location,
|
153
|
-
:distribution_id =>
|
154
|
-
:distribution_label =>
|
155
|
-
:kernel_id =>
|
156
|
-
:kernel_label =>
|
89
|
+
:plan_id => @attributes[:planid],
|
90
|
+
:datacenter_id => @attributes[:datacenterid],
|
91
|
+
:datacenter_location => @attributes[:datacenter_location],
|
92
|
+
:distribution_id => @attributes[:distributionid],
|
93
|
+
:distribution_label => @attributes[:distribution_label],
|
94
|
+
:kernel_id => @attributes[:kernelid],
|
95
|
+
:kernel_label => @attributes[:kernel_label],
|
157
96
|
:swap_size => @swap_size,
|
158
97
|
:totalhd => @totalhd,
|
159
98
|
:root_diskid => @root_diskid,
|
@@ -162,27 +101,29 @@ module Sunzi
|
|
162
101
|
:public_ip => @public_ip,
|
163
102
|
:private_ip => @private_ip,
|
164
103
|
}
|
165
|
-
@cli.create_file "linode/instances/#{@label}.yml", YAML.dump(hash)
|
166
104
|
|
167
105
|
# Boot
|
168
106
|
say 'Done. Booting...'
|
169
107
|
@api.linode.boot(:LinodeID => @linodeid)
|
170
108
|
end
|
171
109
|
|
172
|
-
def
|
173
|
-
|
174
|
-
|
110
|
+
def choose(key, result, options = {})
|
111
|
+
label_method = options[:label_method] || :label
|
112
|
+
id = :"#{key}id"
|
113
|
+
label = :"#{key}_#{label_method}"
|
114
|
+
|
115
|
+
# Filters
|
116
|
+
if options[:filter] and @config[options[:filter]]
|
117
|
+
result = result.select{|i| i.label.match Regexp.new(@config[options[:filter]], Regexp::IGNORECASE) }
|
175
118
|
end
|
176
|
-
@config = YAML.load(File.read('linode/linode.yml'))
|
177
|
-
setup_route53 if @config['dns'] == 'route53'
|
178
119
|
|
179
|
-
|
180
|
-
@
|
181
|
-
@
|
120
|
+
result.each{|i| say "#{i.send(id)}: #{i.send(label_method)}" }
|
121
|
+
@attributes[id] = ask("which #{key}?: ", Integer) {|q| q.in = result.map(&id); q.default = result.first.send(id) }
|
122
|
+
@attributes[label] = result.find{|i| i.send(id) == @attributes[id] }.send(label_method)
|
123
|
+
end
|
182
124
|
|
183
|
-
|
184
|
-
|
185
|
-
exit unless moveon == 'y'
|
125
|
+
def do_teardown
|
126
|
+
@linode_id_hash = { :LinodeID => @instance[:linode_id] }
|
186
127
|
|
187
128
|
# Shutdown first or disk deletion will fail
|
188
129
|
say 'shutting down...'
|
@@ -193,31 +134,14 @@ module Sunzi
|
|
193
134
|
# Delete the instance
|
194
135
|
say 'deleting linode...'
|
195
136
|
@api.linode.delete(@linode_id_hash.merge(:skipChecks => 1))
|
137
|
+
end
|
196
138
|
|
197
|
-
|
198
|
-
|
199
|
-
when 'linode'
|
200
|
-
say 'deleting the public IP from Linode DNS Manager...'
|
201
|
-
@domainid = @api.domain.list.find{|i| i.domain == @config['fqdn']['zone'] }.domainid
|
202
|
-
@resource = @api.domain.resource.list(:DomainID => @domainid).find{|i| i.target == @instance[:public_ip] }
|
203
|
-
@api.domain.resource.delete(:DomainID => @domainid, :ResourceID => @resource.resourceid)
|
204
|
-
when 'route53'
|
205
|
-
say 'deleting the public IP from AWS Route 53...'
|
206
|
-
@record = @route53_zone.get_records.find{|i| i.values.first == @instance[:public_ip] }
|
207
|
-
@record.delete if @record
|
208
|
-
end
|
209
|
-
|
210
|
-
# Remove the instance config file
|
211
|
-
@cli.remove_file "linode/instances/#{name}.yml"
|
212
|
-
|
213
|
-
say 'Done.'
|
139
|
+
def assign_api
|
140
|
+
@api = ::Linode.new(:api_key => @config['api_key'])
|
214
141
|
end
|
215
142
|
|
216
|
-
def
|
217
|
-
|
218
|
-
route53 = Route53::Connection.new(@config['route53']['key'], @config['route53']['secret'])
|
219
|
-
@route53_zone = route53.get_zones.find{|i| i.name.sub(/\.$/,'') == @config['fqdn']['zone'] }
|
220
|
-
abort_with "zone for #{@config['fqdn']['zone']} was not found on route53!" unless @route53_zone
|
143
|
+
def ip_key
|
144
|
+
:public_ip
|
221
145
|
end
|
222
146
|
|
223
147
|
def wait_for(action)
|
data/lib/sunzi/dns.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sunzi
|
2
|
+
class DNS
|
3
|
+
def initialize(config, cloud)
|
4
|
+
dns = config['dns']
|
5
|
+
@subject = case dns
|
6
|
+
when 'linode'
|
7
|
+
Sunzi::DNS::Linode.new(config, cloud)
|
8
|
+
when 'route53'
|
9
|
+
Sunzi::DNS::Route53.new(config, cloud)
|
10
|
+
else
|
11
|
+
abort_with "DNS #{dns} is not valid!"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(sym, *args, &block)
|
16
|
+
@subject.send sym, *args, &block
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Sunzi::Dependency.load('linode')
|
2
|
+
|
3
|
+
module Sunzi
|
4
|
+
class DNS
|
5
|
+
class Linode < Base
|
6
|
+
def initialize(config, cloud)
|
7
|
+
@api = ::Linode.new(:api_key => (cloud == 'linode') ? config['api_key'] : config['linode']['api_key'])
|
8
|
+
zone = config['fqdn']['zone']
|
9
|
+
@domain = @api.domain.list.find{|i| i.domain == zone }
|
10
|
+
abort_with "zone for #{zone} was not found on Linode DNS!" unless @domain
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(fqdn, ip)
|
14
|
+
say 'adding the public IP to Linode DNS Manager...'
|
15
|
+
@api.domain.resource.create(:DomainID => @domain.domainid, :Type => 'A', :Name => fqdn, :Target => ip)
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(ip)
|
19
|
+
say 'deleting the public IP from Linode DNS Manager...'
|
20
|
+
resource = @api.domain.resource.list(:DomainID => @domain.domainid).find{|i| i.target == ip }
|
21
|
+
abort_with "ip address #{ip} was not found on Linode DNS!" unless resource
|
22
|
+
@api.domain.resource.delete(:DomainID => @domain.domainid, :ResourceID => resource.resourceid)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Sunzi::Dependency.load('route53')
|
2
|
+
|
3
|
+
module Sunzi
|
4
|
+
class DNS
|
5
|
+
class Route53 < Base
|
6
|
+
def initialize(config, cloud)
|
7
|
+
@api = ::Route53::Connection.new(config['route53']['key'], config['route53']['secret'])
|
8
|
+
zone = config['fqdn']['zone']
|
9
|
+
@route53_zone = @api.get_zones.find{|i| i.name.sub(/\.$/,'') == zone }
|
10
|
+
abort_with "zone for #{zone} was not found on Route 53!" unless @route53_zone
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(fqdn, ip)
|
14
|
+
say 'adding the public IP to Route 53...'
|
15
|
+
::Route53::DNSRecord.new(fqdn, "A", "300", [ip], @route53_zone).create
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(ip)
|
19
|
+
say 'deleting the public IP from Route 53...'
|
20
|
+
record = @route53_zone.get_records.find{|i| i.values.first == ip }
|
21
|
+
record.delete if record
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -11,12 +11,18 @@ export DEBIAN_FRONTEND=noninteractive
|
|
11
11
|
# Add Dotdeb repository. Recommended if you're using Debian. See http://www.dotdeb.org/about/
|
12
12
|
# source recipes/dotdeb.sh
|
13
13
|
|
14
|
-
# Update installed packages
|
14
|
+
# Update apt catalog and upgrade installed packages
|
15
15
|
sunzi.mute "apt-get update"
|
16
16
|
sunzi.mute "apt-get -y upgrade"
|
17
17
|
|
18
18
|
# Install packages
|
19
|
-
|
19
|
+
apt-get -y install git-core ntp curl
|
20
|
+
|
21
|
+
# Install sysstat, then configure if this is a new install.
|
22
|
+
if sunzi.install "sysstat"; then
|
23
|
+
sed -i 's/ENABLED="false"/ENABLED="true"/' /etc/default/sysstat
|
24
|
+
/etc/init.d/sysstat restart
|
25
|
+
fi
|
20
26
|
|
21
27
|
# Set RAILS_ENV
|
22
28
|
environment=$(cat attributes/environment)
|
@@ -41,9 +47,3 @@ if [[ "$(which ruby)" != /usr/local/rvm/rubies/ruby-$ruby_version* ]]; then
|
|
41
47
|
# Install Bundler
|
42
48
|
gem install bundler
|
43
49
|
fi
|
44
|
-
|
45
|
-
# Install sysstat, then configure if this is a new install.
|
46
|
-
if sunzi.install "sysstat"; then
|
47
|
-
sed -i 's/ENABLED="false"/ENABLED="true"/' /etc/default/sysstat
|
48
|
-
/etc/init.d/sysstat restart
|
49
|
-
fi
|
data/sunzi.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = 'sunzi'
|
5
|
-
spec.version = '1.1.
|
5
|
+
spec.version = '1.1.1' # retrieve this value by: Gem.loaded_specs['sunzi'].version.to_s
|
6
6
|
spec.authors = ['Kenn Ejima']
|
7
7
|
spec.email = ['kenn.ejima@gmail.com']
|
8
8
|
spec.homepage = 'http://github.com/kenn/sunzi'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sunzi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenn Ejima
|
@@ -67,10 +67,15 @@ files:
|
|
67
67
|
- bin/sunzi
|
68
68
|
- lib/sunzi.rb
|
69
69
|
- lib/sunzi/cli.rb
|
70
|
+
- lib/sunzi/cloud.rb
|
70
71
|
- lib/sunzi/cloud/base.rb
|
71
72
|
- lib/sunzi/cloud/digital_ocean.rb
|
72
73
|
- lib/sunzi/cloud/linode.rb
|
73
74
|
- lib/sunzi/dependency.rb
|
75
|
+
- lib/sunzi/dns.rb
|
76
|
+
- lib/sunzi/dns/base.rb
|
77
|
+
- lib/sunzi/dns/linode.rb
|
78
|
+
- lib/sunzi/dns/route53.rb
|
74
79
|
- lib/sunzi/logger.rb
|
75
80
|
- lib/sunzi/utility.rb
|
76
81
|
- lib/templates/create/.gitignore
|