toquen 2.0.2 → 2.0.3
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/README.md +0 -7
- data/Rakefile +4 -4
- data/lib/toquen.rb +7 -22
- data/lib/toquen/aws.rb +62 -69
- data/lib/toquen/bootstrapper.rb +1 -1
- data/lib/toquen/capistrano.rb +66 -66
- data/lib/toquen/details_table.rb +9 -10
- data/lib/toquen/local_writer.rb +10 -10
- data/lib/toquen/stunning.rb +20 -22
- data/lib/toquen/templates/deploy.rb +1 -1
- data/lib/toquen/version.rb +1 -1
- data/toquen.gemspec +15 -14
- metadata +16 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a28059029b69454a045b609eb532ee6ae6c915b5
|
4
|
+
data.tar.gz: 90b6749d1f029af3c84a5a0bc8d73d4412e0a266
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dab2f916dea92ea6ea56bb0b70abd5b6429c84ba8d542529b79638b66bde3499cb4ea5459bd48b13657db7ac38b86a7390222828a5f6782dad63a0625561eb03
|
7
|
+
data.tar.gz: 3734a36573be44c2d0c6cea9883b9fff820a812b76058bdc75608c0f6c0bebc45db82c79eee3c09336e68cb334aa36a2432032ae00f130f7cea6de65d1970193
|
data/README.md
CHANGED
@@ -96,13 +96,6 @@ cap update_nodes
|
|
96
96
|
This will update the local node information cache as well as the capistrano stages.
|
97
97
|
|
98
98
|
## Additional Configuration
|
99
|
-
If you want to use a different tag name (or you like commas as a delimiter) you can specify your own role extractor/setter by placing the following in either your Capfile or config/deploy.rb:
|
100
|
-
|
101
|
-
```ruby
|
102
|
-
# these are the default - replace with your own
|
103
|
-
Toquen.config.aws_roles_extractor = lambda { |inst| (inst.tags["MyRoles"] || "").split(",") }
|
104
|
-
Toquen.config.aws_roles_setter = lambda { |ec2, inst, roles| ec2.tags.create(inst, 'Roles', :value => roles.sort.join(' ')) }
|
105
|
-
```
|
106
99
|
|
107
100
|
By default, instance information is only pulled out of the default region (us-east-1), but you can specify mutiple alternative regions:
|
108
101
|
|
data/Rakefile
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
2
|
require 'rdoc/task'
|
3
3
|
|
4
|
-
RDoc::Task.new(
|
5
|
-
rdoc.title =
|
4
|
+
RDoc::Task.new('doc') do |rdoc|
|
5
|
+
rdoc.title = 'Toquen: Capistrano + AWS + Chef-Solo'
|
6
6
|
rdoc.rdoc_dir = 'docs'
|
7
7
|
rdoc.rdoc_files.include('README.md')
|
8
8
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
9
|
-
|
9
|
+
end
|
data/lib/toquen.rb
CHANGED
@@ -1,22 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
|
9
|
-
module Toquen
|
10
|
-
class Config
|
11
|
-
attr_accessor :aws_roles_extractor, :aws_roles_setter
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@aws_roles_extractor = lambda { |inst| (inst.tags["Roles"] || "").split }
|
15
|
-
@aws_roles_setter = lambda { |ec2, inst, roles| ec2.tags.create(inst, 'Roles', :value => roles.sort.join(' ')) }
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.config
|
20
|
-
@config ||= Config.new
|
21
|
-
end
|
22
|
-
end
|
1
|
+
require 'toquen/version'
|
2
|
+
require 'toquen/stunning'
|
3
|
+
require 'toquen/aws'
|
4
|
+
require 'toquen/local_writer'
|
5
|
+
require 'toquen/bootstrapper'
|
6
|
+
require 'toquen/capistrano'
|
7
|
+
require 'toquen/details_table'
|
data/lib/toquen/aws.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require 'aws'
|
1
|
+
require 'aws-sdk'
|
2
2
|
|
3
|
+
# Top level module namespace
|
3
4
|
module Toquen
|
4
5
|
def self.servers_with_role(role)
|
5
6
|
Toquen::AWSProxy.new.server_details.select do |details|
|
@@ -7,109 +8,101 @@ module Toquen
|
|
7
8
|
end
|
8
9
|
end
|
9
10
|
|
11
|
+
# Class to handle all interaction with AWS
|
10
12
|
class AWSProxy
|
11
13
|
attr_reader :regions
|
12
14
|
|
13
15
|
def initialize
|
14
|
-
@key_id = fetch(:aws_access_key_id)
|
15
|
-
@key = fetch(:aws_secret_access_key)
|
16
16
|
@regions = fetch(:aws_regions, ['us-east-1'])
|
17
|
-
|
17
|
+
key = fetch(:aws_access_key_id)
|
18
|
+
key_id = fetch(:aws_secret_access_key)
|
19
|
+
creds = Aws::Credentials.new(key, key_id)
|
20
|
+
Aws.config.update(credentials: creds) if creds.set?
|
18
21
|
end
|
19
22
|
|
20
|
-
def server_details
|
21
|
-
|
23
|
+
def server_details(running = true, regions = nil)
|
24
|
+
each_instance(running, regions) { |inst| extract_details(inst) }
|
22
25
|
end
|
23
26
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
def each_instance(running = true, regions = nil)
|
28
|
+
filters = []
|
29
|
+
filters << { name: 'instance-state-name', values: ['running'] } if running
|
30
|
+
|
31
|
+
results = []
|
32
|
+
(regions || @regions).each do |region|
|
33
|
+
resource = Aws::EC2::Resource.new(region: region)
|
34
|
+
results += resource.instances.map { |i| yield(i) }
|
35
|
+
end
|
36
|
+
results
|
28
37
|
end
|
29
38
|
|
30
39
|
def add_role(ivips, role)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
unless roles.include? role
|
38
|
-
roles << role
|
39
|
-
ec2.tags.create(i, 'Roles', :value => roles.uniq.sort.join(' '))
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
40
|
+
each_instance do |i|
|
41
|
+
roles = extract_details(i)[:roles]
|
42
|
+
next unless !roles.include?(role) && ivips.include?(i.public_ip_address)
|
43
|
+
roles << role
|
44
|
+
tag = { key: 'Roles', value: roles.uniq.sort.join(' ') }
|
45
|
+
i.create_tags(tags: [tag])
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
46
49
|
def remove_role(ivips, role)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
if roles.include? role
|
54
|
-
roles = roles.reject { |r| r == role }
|
55
|
-
Toquen.config.aws_roles_setter.call(ec2, i, roles.uniq)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
50
|
+
each_instance do |i|
|
51
|
+
roles = extract_details(i)[:roles]
|
52
|
+
next unless roles.include?(role) && ivips.include?(i.public_ip_address)
|
53
|
+
roles.reject! { |r| r == role }
|
54
|
+
tag = { key: 'Roles', value: roles.uniq.sort.join(' ') }
|
55
|
+
i.create_tags(tags: [tag])
|
59
56
|
end
|
60
57
|
end
|
61
58
|
|
62
59
|
def get_security_groups(ids)
|
63
|
-
result = []
|
64
60
|
@regions.map do |region|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
ectwo.security_groups.each { |sg| result << sg if ids.include? sg.id }
|
69
|
-
end
|
70
|
-
end
|
71
|
-
result
|
61
|
+
sgs = Aws::EC2::Resource.new(region: region).security_groups
|
62
|
+
sgs.select { |sg| ids.include? sg.group_id }
|
63
|
+
end.flatten
|
72
64
|
end
|
73
65
|
|
74
66
|
def authorize_ingress(secgroup, protocol, port, ip)
|
75
67
|
# test if exists first
|
76
|
-
return false
|
77
|
-
|
78
|
-
|
68
|
+
return false unless secgroup.ip_permissions.to_a.select do |p|
|
69
|
+
port_match = ((p.from_port)..(p.to_port)).cover? port
|
70
|
+
ip_match = p.ip_ranges.map(&:cidr_ip).include?(ip)
|
71
|
+
p.ip_protocol == protocol && port_match && ip_match
|
72
|
+
end.empty?
|
79
73
|
|
80
|
-
secgroup.authorize_ingress(protocol, port,
|
74
|
+
secgroup.authorize_ingress(ip_protocol: protocol, from_port: port,
|
75
|
+
to_port: port, cidr_ip: ip)
|
81
76
|
true
|
82
77
|
end
|
83
78
|
|
84
79
|
def revoke_ingress(secgroup, protocol, port, ip)
|
85
80
|
# test if exists first
|
86
|
-
return false
|
87
|
-
|
88
|
-
|
81
|
+
return false if secgroup.ip_permissions.to_a.select do |p|
|
82
|
+
port_match = ((p.from_port)..(p.to_port)).cover? port
|
83
|
+
ip_match = p.ip_ranges.map(&:cidr_ip).include?(ip)
|
84
|
+
p.ip_protocol == protocol && port_match && ip_match
|
85
|
+
end.empty?
|
89
86
|
|
90
|
-
secgroup.revoke_ingress(protocol, port,
|
87
|
+
secgroup.revoke_ingress(ip_protocol: protocol, from_port: port,
|
88
|
+
to_port: port, cidr_ip: ip)
|
91
89
|
true
|
92
90
|
end
|
93
91
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
:environment => i.tags["Environment"] || nil
|
109
|
-
}
|
110
|
-
end
|
111
|
-
end
|
92
|
+
def extract_details(instance)
|
93
|
+
tags = instance.tags.each_with_object({}) { |t, h| h[t.key] = t.value }
|
94
|
+
{
|
95
|
+
id: tags['Name'],
|
96
|
+
name: tags['Name'],
|
97
|
+
type: instance.instance_type,
|
98
|
+
environment: tags['Environment'],
|
99
|
+
internal_ip: instance.private_ip_address,
|
100
|
+
external_ip: instance.public_ip_address,
|
101
|
+
external_dns: instance.public_dns_name,
|
102
|
+
internal_dns: instance.private_dns_name,
|
103
|
+
roles: tags.fetch('Roles', '').split,
|
104
|
+
security_groups: instance.security_groups.map(&:group_id)
|
105
|
+
}
|
112
106
|
end
|
113
|
-
|
114
107
|
end
|
115
108
|
end
|
data/lib/toquen/bootstrapper.rb
CHANGED
@@ -6,7 +6,7 @@ module Toquen
|
|
6
6
|
# host is available via the binding
|
7
7
|
hosttype = fetch(:hosttype, 'ubuntu')
|
8
8
|
path = File.expand_path("../templates/#{hosttype}_bootstrap.erb", __FILE__)
|
9
|
-
raise "Bootstrap process for #{hosttype} does not exist!" unless File.
|
9
|
+
raise "Bootstrap process for #{hosttype} does not exist!" unless File.exist?(path)
|
10
10
|
user = fetch(:ssh_options)[:user]
|
11
11
|
StringIO.new ERB.new(File.read(path)).result(binding)
|
12
12
|
end
|
data/lib/toquen/capistrano.rb
CHANGED
@@ -5,14 +5,14 @@ require 'json'
|
|
5
5
|
|
6
6
|
set :chef_upload_location, -> { "/home/#{fetch(:ssh_options)[:user]}" }
|
7
7
|
|
8
|
-
desc
|
8
|
+
desc 'update local cache of nodes and roles'
|
9
9
|
task :update_nodes do
|
10
10
|
load Pathname.new fetch(:deploy_config_path, 'config/deploy.rb')
|
11
11
|
roles = Hash.new([])
|
12
12
|
servers = []
|
13
13
|
|
14
14
|
aws = Toquen::AWSProxy.new
|
15
|
-
aws.server_details.each do |details|
|
15
|
+
aws.server_details.reject { |d| d[:roles].empty? }.each do |details|
|
16
16
|
details[:roles].each { |role| roles[role] += [details] }
|
17
17
|
roles['all'] += [details]
|
18
18
|
Toquen::LocalWriter.create_node details
|
@@ -26,7 +26,7 @@ task :update_nodes do
|
|
26
26
|
Toquen::LocalWriter.superfluous_check!(servers, roles.keys)
|
27
27
|
end
|
28
28
|
|
29
|
-
desc
|
29
|
+
desc 'SSH into a specific server'
|
30
30
|
task :ssh do
|
31
31
|
hosts = []
|
32
32
|
on roles(:all) do |host|
|
@@ -34,10 +34,10 @@ task :ssh do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
run_locally do
|
37
|
-
if hosts.
|
38
|
-
warn
|
37
|
+
if hosts.empty?
|
38
|
+
warn 'No server matched that role'
|
39
39
|
elsif hosts.length > 1
|
40
|
-
warn
|
40
|
+
warn 'More than one server matched that role'
|
41
41
|
else
|
42
42
|
keys = fetch(:ssh_options)[:keys]
|
43
43
|
keyoptions = keys.map { |key| "-i #{key}" }.join(' ')
|
@@ -48,11 +48,11 @@ task :ssh do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
desc
|
51
|
+
desc 'send up apps.json config file'
|
52
52
|
task :update_appconfig do
|
53
|
-
if File.
|
53
|
+
if File.exist?('config/apps.json')
|
54
54
|
apps = JSON.parse(File.read('config/apps.json'))
|
55
|
-
config = {
|
55
|
+
config = { '_description' => 'Dropped off by Toquen/Chef.', 'servers' => [] }.merge(apps['default'] || {})
|
56
56
|
Dir.glob("#{fetch(:chef_nodes_path)}/*.json") do |fname|
|
57
57
|
open(fname, 'r') { |f| config['servers'] << JSON.parse(f.read) }
|
58
58
|
end
|
@@ -67,18 +67,18 @@ task :update_appconfig do
|
|
67
67
|
appconfig.merge!(apps[host.properties.environment.to_s] || {})
|
68
68
|
end
|
69
69
|
debug "Uploading app config file to #{dest}"
|
70
|
-
upload! StringIO.new(JSON.pretty_generate(appconfig)),
|
70
|
+
upload! StringIO.new(JSON.pretty_generate(appconfig)), '/tmp/apps.json'
|
71
71
|
sudo "mv /tmp/apps.json #{dest}"
|
72
72
|
sudo "chmod 755 #{dest}"
|
73
73
|
end
|
74
74
|
else
|
75
|
-
run_locally
|
76
|
-
error
|
77
|
-
|
75
|
+
run_locally do
|
76
|
+
error 'No config/apps.json file found.'
|
77
|
+
end
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
-
desc
|
81
|
+
desc 'bootstrap a server so that it can run chef'
|
82
82
|
task :bootstrap do
|
83
83
|
on roles(:all), in: :parallel do |host|
|
84
84
|
info "Bootstrapping #{host}..."
|
@@ -88,24 +88,24 @@ task :bootstrap do
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
desc
|
91
|
+
desc 'Update cookbooks/data bags/roles on server'
|
92
92
|
task :update_kitchen do
|
93
93
|
kitchen = "#{fetch(:chef_upload_location)}/kitchen"
|
94
|
-
lkitchen =
|
94
|
+
lkitchen = '/tmp/toquen/kitchen'
|
95
95
|
user = fetch(:ssh_options)[:user]
|
96
96
|
keys = fetch(:ssh_options)[:keys]
|
97
97
|
|
98
98
|
run_locally do
|
99
|
-
info
|
100
|
-
execute :rm,
|
101
|
-
execute :mkdir,
|
102
|
-
%
|
99
|
+
info 'Building kitchen locally...'
|
100
|
+
execute :rm, '-rf', lkitchen
|
101
|
+
execute :mkdir, '-p', lkitchen
|
102
|
+
%w(cookbooks data_bags roles environments nodes).each do |dname|
|
103
103
|
source = File.expand_path fetch("chef_#{dname}_path".intern)
|
104
|
-
execute :ln,
|
104
|
+
execute :ln, '-s', source, File.join(lkitchen, dname)
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
-
open("#{lkitchen}/chef_config.rb", 'w')
|
108
|
+
open("#{lkitchen}/chef_config.rb", 'w') do |f|
|
109
109
|
f.write("cookbook_path '#{kitchen}/cookbooks'\n")
|
110
110
|
f.write("role_path '#{kitchen}/roles'\n")
|
111
111
|
f.write("data_bag_path '#{kitchen}/data_bags'\n")
|
@@ -114,7 +114,7 @@ task :update_kitchen do
|
|
114
114
|
f.write("log_level :#{fetch(:chef_log_level)}\n")
|
115
115
|
f.write("cache_path '/tmp/chef_cache'\n")
|
116
116
|
f.write("local_mode 'true'\n")
|
117
|
-
|
117
|
+
end
|
118
118
|
|
119
119
|
on roles(:all), in: :parallel do |host|
|
120
120
|
sudo "chown -R #{user} #{fetch(:chef_upload_location)}"
|
@@ -126,7 +126,7 @@ task :update_kitchen do
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
|
-
desc
|
129
|
+
desc 'Run chef for servers'
|
130
130
|
task :cook do
|
131
131
|
on roles(:all), in: :parallel do |host|
|
132
132
|
info "Chef is now cooking on #{host}..."
|
@@ -138,11 +138,11 @@ task :cook do
|
|
138
138
|
end
|
139
139
|
before :cook, :update_kitchen
|
140
140
|
|
141
|
-
desc
|
142
|
-
task :add_role, :role do |
|
141
|
+
desc 'Add given role to machines'
|
142
|
+
task :add_role, :role do |_t, args|
|
143
143
|
run_locally do
|
144
|
-
if args[:role].nil?
|
145
|
-
error
|
144
|
+
if args[:role].nil? || args[:role].empty?
|
145
|
+
error 'You must give the role to add'
|
146
146
|
else
|
147
147
|
aws = Toquen::AWSProxy.new
|
148
148
|
aws.add_role roles(:all), args[:role]
|
@@ -150,11 +150,11 @@ task :add_role, :role do |t, args|
|
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
|
-
desc
|
154
|
-
task :remove_role, :role do |
|
153
|
+
desc 'Remove given role from machines'
|
154
|
+
task :remove_role, :role do |_t, args|
|
155
155
|
run_locally do
|
156
|
-
if args[:role].nil?
|
157
|
-
error
|
156
|
+
if args[:role].nil? || args[:role].empty?
|
157
|
+
error 'You must give the role to remove'
|
158
158
|
else
|
159
159
|
aws = Toquen::AWSProxy.new
|
160
160
|
aws.remove_role roles(:all), args[:role]
|
@@ -162,102 +162,102 @@ task :remove_role, :role do |t, args|
|
|
162
162
|
end
|
163
163
|
end
|
164
164
|
|
165
|
-
desc
|
166
|
-
task :open_port, :port do |
|
165
|
+
desc 'Open a port of ingress to the current machine'
|
166
|
+
task :open_port, :port do |_t, args|
|
167
167
|
port = (args[:port] || 22).to_i
|
168
168
|
run_locally do
|
169
169
|
ivip = StunClient.get_ip
|
170
170
|
if ivip.nil?
|
171
|
-
error
|
171
|
+
error 'Could not fetch internet visible IP of this host.'
|
172
172
|
return
|
173
173
|
end
|
174
174
|
|
175
175
|
ivip = "#{ivip}/32"
|
176
176
|
aws = Toquen::AWSProxy.new
|
177
177
|
aws.get_security_groups(fetch(:filter)[:secgroups]).each do |sg|
|
178
|
-
if aws.authorize_ingress sg,
|
179
|
-
info "Opened port tcp:#{port} on security group '#{sg.
|
178
|
+
if aws.authorize_ingress sg, 'tcp', port, ivip
|
179
|
+
info "Opened port tcp:#{port} on security group '#{sg.group_name}' (#{sg.id}) to #{ivip}"
|
180
180
|
else
|
181
|
-
warn "Port tcp:#{port} in security group '#{sg.
|
181
|
+
warn "Port tcp:#{port} in security group '#{sg.group_name}' (#{sg.id}) already open to #{ivip}"
|
182
182
|
end
|
183
183
|
end
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
-
desc
|
188
|
-
task :close_port, :port do |
|
187
|
+
desc 'Close a port of ingress to the current machine'
|
188
|
+
task :close_port, :port do |_t, args|
|
189
189
|
port = (args[:port] || 22).to_i
|
190
190
|
run_locally do
|
191
191
|
ivip = StunClient.get_ip
|
192
192
|
if ivip.nil?
|
193
|
-
error
|
193
|
+
error 'Could not fetch internet visible IP of this host.'
|
194
194
|
return
|
195
195
|
end
|
196
196
|
|
197
197
|
ivip = "#{ivip}/32"
|
198
198
|
aws = Toquen::AWSProxy.new
|
199
199
|
aws.get_security_groups(fetch(:filter)[:secgroups]).each do |sg|
|
200
|
-
if aws.revoke_ingress sg,
|
201
|
-
info "Closed port tcp:#{port} on security group '#{sg.
|
200
|
+
if aws.revoke_ingress sg, 'tcp', port, ivip
|
201
|
+
info "Closed port tcp:#{port} on security group '#{sg.group_name}' (#{sg.id}) to #{ivip}"
|
202
202
|
else
|
203
|
-
warn "Port tcp:#{port} in security group '#{sg.
|
203
|
+
warn "Port tcp:#{port} in security group '#{sg.group_name}' (#{sg.id}) already closed to #{ivip}"
|
204
204
|
end
|
205
205
|
end
|
206
206
|
end
|
207
207
|
end
|
208
208
|
|
209
|
-
desc
|
209
|
+
desc 'Open SSH ingress to current machine'
|
210
210
|
task :open_ssh do
|
211
|
-
invoke
|
211
|
+
invoke 'open_port', '22'
|
212
212
|
end
|
213
213
|
|
214
|
-
desc
|
214
|
+
desc 'Close SSH ingress to current machine'
|
215
215
|
task :close_ssh do
|
216
|
-
invoke
|
216
|
+
invoke 'close_port', '22'
|
217
217
|
end
|
218
218
|
|
219
|
-
desc
|
219
|
+
desc 'install toquen capistrano setup to current directory'
|
220
220
|
task :toquen_install do
|
221
|
-
unless Dir.
|
222
|
-
puts
|
221
|
+
unless Dir.exist?('config')
|
222
|
+
puts 'Creating config directory...'
|
223
223
|
Dir.mkdir('config')
|
224
224
|
end
|
225
|
-
unless Dir.
|
226
|
-
puts
|
225
|
+
unless Dir.exist?('config/deploy')
|
226
|
+
puts 'Creating config/deploy directory...'
|
227
227
|
Dir.mkdir('config/deploy')
|
228
228
|
end
|
229
|
-
|
230
|
-
puts
|
231
|
-
FileUtils.cp File.expand_path(
|
229
|
+
unless File.exist?('config/deploy.rb')
|
230
|
+
puts 'Initializing config/deploy.rb configuration file...'
|
231
|
+
FileUtils.cp File.expand_path('../templates/deploy.rb', __FILE__), 'config/deploy.rb'
|
232
232
|
end
|
233
|
-
gipath = File.expand_path(
|
234
|
-
if
|
235
|
-
puts
|
233
|
+
gipath = File.expand_path('../templates/gitignore', __FILE__)
|
234
|
+
if !File.exist?('.gitignore')
|
235
|
+
puts 'Initializing .gitignore file...'
|
236
236
|
FileUtils.cp gipath, '.gitignore'
|
237
237
|
else
|
238
|
-
puts
|
238
|
+
puts 'You already have a .gitignore, consider adding these files to it:'
|
239
239
|
puts File.read(gipath)
|
240
240
|
end
|
241
241
|
end
|
242
242
|
|
243
|
-
desc
|
243
|
+
desc 'Show all information about EC2 instances'
|
244
244
|
task :details do
|
245
245
|
filter_roles = Set.new fetch(:filter)[:roles]
|
246
246
|
aws = Toquen::AWSProxy.new
|
247
247
|
aws.regions.each do |region|
|
248
|
-
instances = aws.
|
249
|
-
instance_roles = instance[:roles] + [
|
248
|
+
instances = aws.server_details(true, [region]).reject do |instance|
|
249
|
+
instance_roles = instance[:roles] + ['all', "server-#{instance[:name]}"]
|
250
250
|
(filter_roles.intersection instance_roles.to_set).empty?
|
251
251
|
end
|
252
252
|
Toquen::DetailsTable.new(instances, region).output unless instances.empty?
|
253
253
|
end
|
254
254
|
end
|
255
|
-
|
255
|
+
|
256
256
|
module Capistrano
|
257
257
|
module TaskEnhancements
|
258
|
-
|
258
|
+
alias original_default_tasks default_tasks
|
259
259
|
def default_tasks
|
260
|
-
original_default_tasks + %w
|
260
|
+
original_default_tasks + %w(toquen_install update_nodes)
|
261
261
|
end
|
262
262
|
end
|
263
263
|
end
|
data/lib/toquen/details_table.rb
CHANGED
@@ -3,7 +3,6 @@ require 'term/ansicolor'
|
|
3
3
|
|
4
4
|
module Toquen
|
5
5
|
class DetailsTable
|
6
|
-
|
7
6
|
def initialize(instances, region)
|
8
7
|
@instances = instances
|
9
8
|
@region = region
|
@@ -12,14 +11,14 @@ module Toquen
|
|
12
11
|
|
13
12
|
def output
|
14
13
|
table = Terminal::Table.new(
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
14
|
+
style: {
|
15
|
+
border_x: '',
|
16
|
+
border_i: '',
|
17
|
+
border_y: ''
|
19
18
|
}
|
20
19
|
)
|
21
20
|
table.title = @color.bold { "Instances in #{@region}" }
|
22
|
-
header =
|
21
|
+
header = %w(Name Roles Env Public Private Type)
|
23
22
|
table.add_row header.map { |h| @color.underline @color.bold h }
|
24
23
|
@instances.each do |instance|
|
25
24
|
table.add_row instance_to_row(instance)
|
@@ -30,10 +29,10 @@ module Toquen
|
|
30
29
|
def instance_to_row(instance)
|
31
30
|
[
|
32
31
|
@color.green { instance[:name] },
|
33
|
-
@color.yellow { instance[:roles].join(
|
34
|
-
@color.magenta { instance[:environment] ||
|
35
|
-
instance[:external_dns].nil? ?
|
36
|
-
instance[:internal_dns].nil? ?
|
32
|
+
@color.yellow { instance[:roles].join(',') },
|
33
|
+
@color.magenta { instance[:environment] || '' },
|
34
|
+
instance[:external_dns].nil? ? '(N/A)' : @color.cyan(instance[:external_dns]) + " (#{instance[:external_ip]})",
|
35
|
+
instance[:internal_dns].nil? ? '(N/A)' : @color.cyan(instance[:internal_dns]) + " (#{instance[:internal_ip]})",
|
37
36
|
@color.red { instance[:type] }
|
38
37
|
]
|
39
38
|
end
|
data/lib/toquen/local_writer.rb
CHANGED
@@ -3,7 +3,7 @@ module Toquen
|
|
3
3
|
def self.create_node(details)
|
4
4
|
FileUtils.mkdir_p fetch(:chef_nodes_path)
|
5
5
|
path = File.join(fetch(:chef_nodes_path), "#{details[:name]}.json")
|
6
|
-
existing = File.
|
6
|
+
existing = File.exist?(path) ? JSON.parse(File.read(path)) : {}
|
7
7
|
open(path, 'w') do |f|
|
8
8
|
node = {
|
9
9
|
'name' => details[:name],
|
@@ -21,14 +21,14 @@ module Toquen
|
|
21
21
|
f.write("# This file will be overwritten by toquen! Don't put anything here.\n")
|
22
22
|
f.write("set :stage, '#{name}'.intern\n")
|
23
23
|
secgroups = []
|
24
|
-
servers.each
|
25
|
-
rstring = (details[:roles] + [
|
24
|
+
servers.each do |details|
|
25
|
+
rstring = (details[:roles] + ['all', "server-#{details[:name]}"]).join(' ')
|
26
26
|
f.write("server '#{details[:external_ip]}', ")
|
27
27
|
f.write("roles: %w{#{rstring}}, ")
|
28
28
|
f.write("environment: \"#{details[:environment]}\", ") unless details[:environment].nil?
|
29
29
|
f.write("awsname: '#{details[:name]}'\n")
|
30
30
|
secgroups += details[:security_groups]
|
31
|
-
|
31
|
+
end
|
32
32
|
secstring = secgroups.uniq.join(' ')
|
33
33
|
f.write("set :filter, roles: %w{#{name}}, secgroups: %w{#{secstring}}\n")
|
34
34
|
end
|
@@ -37,18 +37,18 @@ module Toquen
|
|
37
37
|
def self.superfluous_check!(servers, roles)
|
38
38
|
# check for superflous stages / data bag items and warn if found
|
39
39
|
run_locally do
|
40
|
-
Dir["#{fetch(:chef_data_bags_path)}/servers/*.json"].each
|
41
|
-
unless servers.include? File.basename(path,
|
40
|
+
Dir["#{fetch(:chef_data_bags_path)}/servers/*.json"].each do |path|
|
41
|
+
unless servers.include? File.basename(path, '.json')
|
42
42
|
warn "Data bag item #{path} does not represent an active server. You should delete it."
|
43
43
|
end
|
44
|
-
|
44
|
+
end
|
45
45
|
|
46
46
|
stages = roles + servers.map { |n| "server-#{n}" }
|
47
|
-
Dir[
|
48
|
-
unless stages.include? File.basename(path,
|
47
|
+
Dir['config/deploy/*.rb'].each do |path|
|
48
|
+
unless stages.include? File.basename(path, '.rb')
|
49
49
|
warn "Stage #{path} does not represent an active server. You should delete it."
|
50
50
|
end
|
51
|
-
|
51
|
+
end
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
data/lib/toquen/stunning.rb
CHANGED
@@ -8,37 +8,35 @@ class StunClient
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def get_ip
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
type, length = data.unpack("nn")
|
11
|
+
Timeout.timeout(0.5) do
|
12
|
+
socket = UDPSocket.new
|
13
|
+
data = [0x0001, 0].pack('nn') + Random.new.bytes(16)
|
14
|
+
socket.send(data, 0, @host, @port)
|
15
|
+
data, = socket.recvfrom(1000)
|
16
|
+
type, length = data.unpack('nn')
|
18
17
|
|
19
|
-
|
20
|
-
|
18
|
+
# if not a message binding response
|
19
|
+
return nil unless type == 0x0101
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
data = data[4+length..-1]
|
21
|
+
data = data[20..-1]
|
22
|
+
until data.empty?
|
23
|
+
type, length = data.unpack('nn')
|
24
|
+
# if attr type is ATTR_MAPPED_ADDRESS, return it
|
25
|
+
if type == 0x0001
|
26
|
+
values = data[4...4 + length].unpack('CCnCCCC')
|
27
|
+
return values[3..-1] * '.'
|
31
28
|
end
|
29
|
+
data = data[4 + length..-1]
|
30
|
+
end
|
32
31
|
|
33
|
-
return nil
|
34
|
-
}
|
35
|
-
rescue Timeout::Error
|
36
32
|
return nil
|
37
33
|
end
|
34
|
+
rescue Timeout::Error
|
35
|
+
return nil
|
38
36
|
end
|
39
37
|
|
40
38
|
def self.get_ip
|
41
|
-
servers = [[
|
39
|
+
servers = [['stun.l.google.com', 19_302], ['stun.ekiga.net', 3478], ['stunserver.org', 3478]]
|
42
40
|
servers.each do |host, port|
|
43
41
|
ip = StunClient.new(host, port).get_ip
|
44
42
|
return ip unless ip.nil?
|
@@ -7,7 +7,7 @@ set :aws_secret_access_key, nil
|
|
7
7
|
# Set the location of your SSH key. You can give a list of files, but
|
8
8
|
# the first key given will be the one used to upload your chef files to
|
9
9
|
# each server.
|
10
|
-
set :ssh_options,
|
10
|
+
set :ssh_options, keys: ['./mykey.pem'], user: 'ubuntu'
|
11
11
|
|
12
12
|
# Set the location of your cookbooks/data bags/roles/nodes for Chef
|
13
13
|
set :chef_cookbooks_path, 'kitchen/cookbooks'
|
data/lib/toquen/version.rb
CHANGED
data/toquen.gemspec
CHANGED
@@ -4,22 +4,23 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'toquen/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
|
-
gem.name =
|
7
|
+
gem.name = 'toquen'
|
8
8
|
gem.version = Toquen::VERSION
|
9
|
-
gem.authors = [
|
10
|
-
gem.email = [
|
11
|
-
gem.description =
|
12
|
-
gem.summary =
|
13
|
-
gem.homepage =
|
9
|
+
gem.authors = ['Brian Muller']
|
10
|
+
gem.email = ['bamuller@gmail.com']
|
11
|
+
gem.description = 'Toquen: Capistrano + AWS + Chef-Solo'
|
12
|
+
gem.summary = 'Toquen: Joins Capistrano + AWS + Chef-Solo into small devops ease'
|
13
|
+
gem.homepage = 'https://github.com/bmuller/toquen'
|
14
14
|
gem.licenses = ['MIT']
|
15
|
-
|
16
|
-
gem.files = `git ls-files`.split(
|
17
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
-
gem.require_paths = [
|
19
|
+
gem.require_paths = ['lib']
|
20
20
|
gem.add_dependency('capistrano', '~> 3.0', '>= 3.0.1')
|
21
|
-
gem.add_dependency('aws-sdk', '~>
|
22
|
-
gem.add_dependency('terminal-table', '~> 1.6'
|
23
|
-
gem.add_dependency('term-ansicolor', '~> 1.3'
|
24
|
-
gem.add_development_dependency('rdoc', '~> 4.2'
|
21
|
+
gem.add_dependency('aws-sdk', '~> 2')
|
22
|
+
gem.add_dependency('terminal-table', '~> 1.6')
|
23
|
+
gem.add_dependency('term-ansicolor', '~> 1.3')
|
24
|
+
gem.add_development_dependency('rdoc', '~> 4.2')
|
25
|
+
gem.add_development_dependency('rubocop')
|
25
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toquen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Muller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capistrano
|
@@ -36,14 +36,14 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '2'
|
40
40
|
type: :runtime
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
46
|
+
version: '2'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: terminal-table
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -51,9 +51,6 @@ dependencies:
|
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '1.6'
|
54
|
-
- - ">="
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: 1.6.0
|
57
54
|
type: :runtime
|
58
55
|
prerelease: false
|
59
56
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -61,9 +58,6 @@ dependencies:
|
|
61
58
|
- - "~>"
|
62
59
|
- !ruby/object:Gem::Version
|
63
60
|
version: '1.6'
|
64
|
-
- - ">="
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: 1.6.0
|
67
61
|
- !ruby/object:Gem::Dependency
|
68
62
|
name: term-ansicolor
|
69
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -71,9 +65,6 @@ dependencies:
|
|
71
65
|
- - "~>"
|
72
66
|
- !ruby/object:Gem::Version
|
73
67
|
version: '1.3'
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: 1.3.2
|
77
68
|
type: :runtime
|
78
69
|
prerelease: false
|
79
70
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -81,9 +72,6 @@ dependencies:
|
|
81
72
|
- - "~>"
|
82
73
|
- !ruby/object:Gem::Version
|
83
74
|
version: '1.3'
|
84
|
-
- - ">="
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
version: 1.3.2
|
87
75
|
- !ruby/object:Gem::Dependency
|
88
76
|
name: rdoc
|
89
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -91,9 +79,6 @@ dependencies:
|
|
91
79
|
- - "~>"
|
92
80
|
- !ruby/object:Gem::Version
|
93
81
|
version: '4.2'
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: 4.2.2
|
97
82
|
type: :development
|
98
83
|
prerelease: false
|
99
84
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -101,9 +86,20 @@ dependencies:
|
|
101
86
|
- - "~>"
|
102
87
|
- !ruby/object:Gem::Version
|
103
88
|
version: '4.2'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rubocop
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
104
100
|
- - ">="
|
105
101
|
- !ruby/object:Gem::Version
|
106
|
-
version:
|
102
|
+
version: '0'
|
107
103
|
description: 'Toquen: Capistrano + AWS + Chef-Solo'
|
108
104
|
email:
|
109
105
|
- bamuller@gmail.com
|