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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a3f735ba4adee8e29059770569406897d42e9a7
4
- data.tar.gz: 3ec26e19e5ca10213f79726d0963e29b03672dc9
3
+ metadata.gz: e9fc8aafe77074cbb76a6fcf041b1647cc0a0634
4
+ data.tar.gz: f58cf2395b1361ffeb48336fc5b6e1e09f325555
5
5
  SHA512:
6
- metadata.gz: 361584e1a3104439e3c1e4ab735ce920b32fdbb052af1815d47f3c5c88c7ffb801cb2b007c7cd5edb53897848aec88dd67239921a1edac9d585d61119c0b49cc
7
- data.tar.gz: 564fa3ae5ac3f99ce117c79251a5249307f7e81a79dabbb5c49b973c3f3e1e9fdba766ae124377166600e8d000c9d435e6e5a946f420266cbea7475cf580a57d
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
- module Cloud
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(target)
25
- Cloud::Base.choose(self, target).setup
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(target, name)
30
- Cloud::Base.choose(self, target).teardown(name)
29
+ def teardown(provider, name)
30
+ Sunzi::Cloud.new(self, provider).teardown(name)
31
31
  end
32
32
 
33
33
  desc 'version', 'Show version'
@@ -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
@@ -1,29 +1,84 @@
1
1
  Sunzi::Dependency.load('highline')
2
2
 
3
3
  module Sunzi
4
- module Cloud
4
+ class Cloud
5
5
  class Base
6
6
  include Sunzi::Utility
7
7
 
8
- def self.choose(cli, target)
9
- case target
10
- when 'linode'
11
- Cloud::Linode.new(cli)
12
- when 'digital_ocean'
13
- Cloud::DigitalOcean.new(cli)
14
- else
15
- abort_with "#{target} is not valid!"
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 initialize(cli)
20
- @cli = cli
21
- @ui = HighLine.new
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
- module Cloud
4
+ class Cloud
5
5
  class DigitalOcean < Base
6
- def setup
7
- unless File.exist? 'digital_ocean/digital_ocean.yml'
8
- @cli.empty_directory 'digital_ocean/instances'
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
- @fqdn = @config['fqdn'][@env].gsub(/%{host}/, @host)
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
- result.each{|i| say "#{i.id}: #{i.name}" }
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
- moveon = ask("Are you ready to go ahead and create #{@fqdn}? (y/n) ", String) {|q| q.in = ['y','n']}
54
- exit unless moveon == 'y'
18
+ proceed?
55
19
 
56
- @ssh_key_ids = @api.ssh_keys.list.ssh_keys.map(&:id).join(',')
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 => @size_id,
62
- :image_id => @image_id,
63
- :region_id => @region_id,
64
- :ssh_key_ids => @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 5
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
- # Register IP to DNS
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 => @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 => @size_id,
101
- :size_name => @size_name,
102
- :region_id => @region_id,
103
- :region_name => @region_name,
104
- :image_id => @image_id,
105
- :image_name => @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 teardown(name)
112
- unless File.exist?("digital_ocean/instances/#{name}.yml")
113
- abort_with "#{name}.yml was not found in the instances directory."
114
- end
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
- # Delete the droplet
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
- say 'Done.'
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 setup_route53
154
- Sunzi::Dependency.load('route53')
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
@@ -1,79 +1,31 @@
1
1
  Sunzi::Dependency.load('linode')
2
2
 
3
3
  module Sunzi
4
- module Cloud
4
+ class Cloud
5
5
  class Linode < Base
6
- def setup
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
- # 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
- @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
- moveon = ask("Are you ready to go ahead and create #{@fqdn}? (y/n) ", String) {|q| q.in = ['y','n']}
72
- exit unless moveon == 'y'
21
+ proceed?
73
22
 
74
23
  # Create
75
24
  say "creating a new linode..."
76
- result = @api.linode.create(:DatacenterID => @datacenterid, :PlanID => @planid, :PaymentTerm => @config['payment_term'])
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
- settings = { :LinodeID => @linodeid, :Label => @label, :lpm_displayGroup => @group }
37
+ @group = @config['group'][@env]
38
+ settings = { :LinodeID => @linodeid, :Label => @name, :lpm_displayGroup => @group }
86
39
  settings.update(@config['settings']) if @config['settings']
87
- result = @api.linode.update(settings)
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
- # Register IP to DNS
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 => @label,
87
+ :label => @name,
149
88
  :group => @group,
150
- :plan_id => @planid,
151
- :datacenter_id => @datacenterid,
152
- :datacenter_location => @datacenter_location,
153
- :distribution_id => @distributionid,
154
- :distribution_label => @distribution_label,
155
- :kernel_id => @kernelid,
156
- :kernel_label => @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 teardown(name)
173
- unless File.exist?("linode/instances/#{name}.yml")
174
- abort_with "#{name}.yml was not found in the instances directory."
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
- @instance = YAML.load(File.read("linode/instances/#{name}.yml"))
180
- @linode_id_hash = { :LinodeID => @instance[:linode_id] }
181
- @api = ::Linode.new(:api_key => @config['api_key'])
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
- # Are you sure?
184
- moveon = ask("Are you sure about deleting #{@instance[:fqdn]} permanently? (y/n) ", String) {|q| q.in = ['y','n']}
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
- # Delete DNS record
198
- case @config['dns']
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 setup_route53
217
- Sunzi::Dependency.load('route53')
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,7 @@
1
+ module Sunzi
2
+ class DNS
3
+ class Base
4
+ include Sunzi::Utility
5
+ end
6
+ end
7
+ 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
- sunzi.mute "apt-get -y install git-core ntp curl"
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
@@ -14,7 +14,7 @@ fqdn:
14
14
  zone: example.com
15
15
  production: '%{host}.example.com'
16
16
  staging: '%{host}.staging.example.com'
17
- label:
17
+ name:
18
18
  production: 'example-%{host}'
19
19
  staging: 'example-staging-%{host}'
20
20
  group:
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.0' # retrieve this value by: Gem.loaded_specs['sunzi'].version.to_s
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.0
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