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 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