sunzi 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|