gaptool-server 0.5.19 → 0.6.0.beta1
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/Gemfile +3 -0
- data/Rakefile +43 -0
- data/VERSION +1 -1
- data/bin/gaptool-server +7 -29
- data/bin/gaptool-shell +12 -0
- data/bin/gaptool_server_migrate_0.5-0.6 +96 -0
- data/config.ru +3 -12
- data/lib/app.rb +27 -10
- data/lib/exceptions.rb +35 -0
- data/lib/helpers/data.rb +257 -0
- data/lib/helpers/ec2.rb +80 -0
- data/lib/helpers/init.rb +2 -4
- data/lib/helpers/redis.rb +19 -0
- data/lib/helpers/rehash.rb +31 -56
- data/lib/routes.rb +172 -0
- data/lib/views/init.erb +6 -8
- data/tasks/app.rb +28 -0
- data/tasks/docker.rb +84 -0
- data/tasks/gem.rb +27 -0
- data/tasks/user.rb +32 -0
- data/test/api_test.rb +256 -0
- data/test/base_test.rb +16 -0
- data/test/data_test.rb +118 -0
- data/test/test_helper.rb +25 -0
- metadata +127 -14
- data/lib/helpers/gaptool-base.rb +0 -38
- data/lib/public/css/common.css +0 -4
- data/lib/routes/init.rb +0 -3
- data/lib/routes/main.rb +0 -206
- data/lib/routes/rehash.rb +0 -6
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
# docker links support
|
4
|
+
unless ENV['REDIS_PORT_6379_TCP_ADDR'].nil?
|
5
|
+
ENV['REDIS_HOST'] = ENV['REDIS_PORT_6379_TCP_ADDR']
|
6
|
+
ENV['REDIS_PORT'] = ENV['REDIS_PORT_6379_TCP_PORT']
|
7
|
+
ENV.delete('REDIS_PASS')
|
8
|
+
end
|
9
|
+
|
10
|
+
ENV['REDIS_HOST'] = 'localhost' unless ENV['REDIS_HOST']
|
11
|
+
ENV['REDIS_PORT'] = '6379' unless ENV['REDIS_PORT']
|
12
|
+
ENV['REDIS_PASS'] = nil unless ENV['REDIS_PASS']
|
13
|
+
ENV['REDIS_DB'] = '0' unless ENV['REDIS_DB']
|
14
|
+
|
15
|
+
$redis = Redis.new(:host => ENV['REDIS_HOST'],
|
16
|
+
:port => ENV['REDIS_PORT'].to_i,
|
17
|
+
:password => ENV['REDIS_PASS'],
|
18
|
+
:db => ENV['REDIS_DB'].to_i)
|
19
|
+
|
data/lib/helpers/rehash.rb
CHANGED
@@ -1,63 +1,38 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
role = $redis.hget(app,'role')
|
7
|
-
unless role == 'nil'
|
8
|
-
@roles << role
|
1
|
+
module Gaptool
|
2
|
+
module EC2
|
3
|
+
def self.rehash()
|
4
|
+
servers.each do |inst|
|
5
|
+
rmserver inst
|
9
6
|
end
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
@layout[role] = apps
|
23
|
-
end
|
24
|
-
|
25
|
-
$redis.keys("host:*").each do |rediskey|
|
26
|
-
$redis.del rediskey
|
27
|
-
end
|
28
|
-
|
29
|
-
$redis.keys("instance:*").each do |rediskey|
|
30
|
-
$redis.del rediskey
|
31
|
-
end
|
32
|
-
|
33
|
-
@zones = $redis.hgetall('amis').keys
|
34
|
-
@zones.each do |zone|
|
35
|
-
@ec2 = AWS::EC2.new(:access_key_id => $redis.hget('config', 'aws_id'), :secret_access_key => $redis.hget('config', 'aws_secret'), :ec2_endpoint => "ec2.#{zone}.amazonaws.com")
|
36
|
-
@instances = []
|
37
|
-
@ec2.instances.each do |instance|
|
38
|
-
if instance.tags['gaptool'] == 'yes' && instance.status == :running
|
39
|
-
@instances << instance
|
7
|
+
layout = roles()
|
8
|
+
puts layout
|
9
|
+
zones.each do |zone|
|
10
|
+
@ec2 = AWS::EC2.new(:access_key_id => $redis.hget('config', 'aws_id'),
|
11
|
+
:secret_access_key => $redis.hget('config', 'aws_secret'),
|
12
|
+
:ec2_endpoint => "ec2.#{zone}.amazonaws.com")
|
13
|
+
ilist = []
|
14
|
+
@ec2.instances.each do |instance|
|
15
|
+
if instance.tags['gaptool'] == 'yes' && instance.status == :running
|
16
|
+
ilist << instance
|
17
|
+
end
|
40
18
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
}
|
56
|
-
@host.keys.each do |key|
|
57
|
-
$redis.hset(@rediskey, key, @host[key])
|
19
|
+
ilist.each do |instance|
|
20
|
+
puts " - #{instance.tags['Name']}"
|
21
|
+
role, environment, iid = instance.tags['Name'].split('-')
|
22
|
+
data = {
|
23
|
+
"zone"=> instance.availability_zone,
|
24
|
+
"itype"=> instance.instance_type,
|
25
|
+
"role"=> role,
|
26
|
+
"environment"=> environment,
|
27
|
+
"hostname"=> instance.public_dns_name,
|
28
|
+
"apps" => layout[role].to_s,
|
29
|
+
"instance"=> instance.instance_id
|
30
|
+
}
|
31
|
+
puts data
|
32
|
+
addserver(data['instance'], data, nil)
|
58
33
|
end
|
59
34
|
end
|
35
|
+
return {"action" => "complete"}
|
60
36
|
end
|
61
|
-
return {"action" => "complete"}
|
62
37
|
end
|
63
38
|
end
|
data/lib/routes.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'securerandom'
|
3
|
+
require 'set'
|
4
|
+
require_relative 'exceptions'
|
5
|
+
class GaptoolServer < Sinatra::Application
|
6
|
+
|
7
|
+
def require_parameters(required_keys, data)
|
8
|
+
data = data.delete_if { |k,v| v.nil? }
|
9
|
+
required_keys = [*required_keys].to_set unless required_keys.is_a? Set
|
10
|
+
keys = data.keys.to_set
|
11
|
+
unless keys >= required_keys
|
12
|
+
raise BadRequest, "Missing required_parameters: #{(required_keys - keys).to_a.join(" ")}"
|
13
|
+
end
|
14
|
+
data
|
15
|
+
end
|
16
|
+
|
17
|
+
get '/' do
|
18
|
+
"You must be lost. Read the instructions."
|
19
|
+
end
|
20
|
+
|
21
|
+
get '/ping' do
|
22
|
+
"PONG"
|
23
|
+
end
|
24
|
+
|
25
|
+
post '/init' do
|
26
|
+
data = require_parameters(%w(role environment zone itype),
|
27
|
+
JSON.parse(request.body.read))
|
28
|
+
|
29
|
+
secret = SecureRandom.hex(12)
|
30
|
+
security_group = data['security_group'] || Gaptool::Data::get_role_data(data['role'])["security_group"]
|
31
|
+
sgid = Gaptool::EC2::get_or_create_securitygroup(data['role'], data['environment'], data['zone'], security_group)
|
32
|
+
image_id = data['ami'] || Gaptool::Data::get_ami_for_role(data['role'], data['zone'].chop)
|
33
|
+
data['terminable'] = data['terminable'].nil? ? true : !!data['terminable']
|
34
|
+
|
35
|
+
id = Gaptool::EC2::create_ec2_instance(
|
36
|
+
{
|
37
|
+
:image_id => image_id,
|
38
|
+
:availability_zone => data['zone'],
|
39
|
+
:instance_type => data['itype'],
|
40
|
+
:key_name => "gaptool",
|
41
|
+
:security_group_ids => sgid,
|
42
|
+
:user_data => "#!/bin/bash\ncurl --silent -H 'X-GAPTOOL-USER: #{env['HTTP_X_GAPTOOL_USER']}' -H 'X-GAPTOOL-KEY: #{env['HTTP_X_GAPTOOL_KEY']}' #{$redis.hget('config', 'url')}/register -X PUT --data '#{data.to_json}' | bash"
|
43
|
+
}, {
|
44
|
+
role: data['role'],
|
45
|
+
env: data['environment'],
|
46
|
+
zone: data['zone']
|
47
|
+
}
|
48
|
+
)
|
49
|
+
# Add host tag
|
50
|
+
Gaptool::Data::addserver(id, data, secret)
|
51
|
+
json({instance: id,
|
52
|
+
ami: image_id,
|
53
|
+
role: data['role'],
|
54
|
+
environment: data['environment'],
|
55
|
+
secret: secret,
|
56
|
+
terminable: data['terminable'],
|
57
|
+
security_group: sgid})
|
58
|
+
end
|
59
|
+
|
60
|
+
post '/terminate' do
|
61
|
+
data = require_parameters("id",
|
62
|
+
JSON.parse(request.body.read))
|
63
|
+
host_data = Gaptool::Data::get_server_data data['id']
|
64
|
+
raise NotFound, "No such instance: #{data['id']}" if host_data.nil?
|
65
|
+
raise Conflict, "Instance #{data['id']} cannot be terminated" if host_data['terminable'] == false
|
66
|
+
|
67
|
+
Gaptool::EC2::terminate_ec2_instance(data['zone'], data['id'])
|
68
|
+
Gaptool::Data::rmserver(data['id'])
|
69
|
+
json data['id'] => {'status' => 'terminated'}
|
70
|
+
end
|
71
|
+
|
72
|
+
put '/register' do
|
73
|
+
data = JSON.parse request.body.read
|
74
|
+
data = require_parameters(%w(role zone environment secret), data)
|
75
|
+
instance_id = Gaptool::Data::register_server data['role'], data['environment'], data['secret']
|
76
|
+
|
77
|
+
raise Forbidden, "Can't register instance: wrong secret or missing role/environment" unless instance_id
|
78
|
+
|
79
|
+
hostname = Gaptool::EC2::get_ec2_instance_data(data['zone'].chop, instance_id)[:hostname]
|
80
|
+
host_data = Gaptool::Data::get_server_data instance_id, initkey: true, force_runlist: true
|
81
|
+
|
82
|
+
chef_repo = host_data['chef_repo']
|
83
|
+
chef_branch = host_data['chef_branch']
|
84
|
+
# FIXME: remove init key from redis
|
85
|
+
initkey = host_data['init_key']
|
86
|
+
run_list = host_data['chef_runlist'].to_json
|
87
|
+
|
88
|
+
jdata = {
|
89
|
+
'hostname' => hostname,
|
90
|
+
'recipe' => 'init',
|
91
|
+
'number' => instance_id,
|
92
|
+
'instance' => instance_id,
|
93
|
+
'run_list' => run_list,
|
94
|
+
'role' => data['role'],
|
95
|
+
'environment' => data['environment'],
|
96
|
+
'chefrepo' => chef_repo,
|
97
|
+
'chefbranch' => chef_branch,
|
98
|
+
'identity' => initkey,
|
99
|
+
'appuser' => Gaptool::Data::get_config('appuser'),
|
100
|
+
'apps' => host_data['apps']
|
101
|
+
}.to_json
|
102
|
+
|
103
|
+
erb :init, locals: {
|
104
|
+
initkey: initkey,
|
105
|
+
chef_branch: chef_branch,
|
106
|
+
chef_repo: chef_repo,
|
107
|
+
json: jdata
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
get '/hosts' do
|
112
|
+
servers = Gaptool::Data::servers.map do |inst|
|
113
|
+
Gaptool::Data::get_server_data inst
|
114
|
+
end
|
115
|
+
json servers
|
116
|
+
end
|
117
|
+
|
118
|
+
get '/apps' do
|
119
|
+
out = {}
|
120
|
+
Gaptool::Data.apps.each do |app|
|
121
|
+
out[app] = Gaptool::Data::get_app_data(app)
|
122
|
+
end
|
123
|
+
json out
|
124
|
+
end
|
125
|
+
|
126
|
+
get '/hosts/:role' do
|
127
|
+
servers = Gaptool::Data::servers_in_role(params[:role]).map do |inst|
|
128
|
+
Gaptool::Data::get_server_data inst
|
129
|
+
end
|
130
|
+
json servers
|
131
|
+
end
|
132
|
+
|
133
|
+
get '/instance/:id' do
|
134
|
+
json Gaptool::Data::get_server_data(params[:id])
|
135
|
+
end
|
136
|
+
|
137
|
+
get '/hosts/:role/:environment' do
|
138
|
+
if params[:role] == 'ALL'
|
139
|
+
list = Gaptool::Data::servers_in_env params[:environment]
|
140
|
+
else
|
141
|
+
list = Gaptool::Data::servers_in_role_env params[:role], params[:environment]
|
142
|
+
end
|
143
|
+
servers = list.map do |inst|
|
144
|
+
Gaptool::Data::get_server_data inst
|
145
|
+
end
|
146
|
+
|
147
|
+
json servers
|
148
|
+
end
|
149
|
+
|
150
|
+
get '/host/:role/:environment/:instance' do
|
151
|
+
json Gaptool::Data::get_server_data params[:instance]
|
152
|
+
end
|
153
|
+
|
154
|
+
get '/ssh/:role/:environment/:instance' do
|
155
|
+
data = Gaptool::Data::get_server_data params[:instance]
|
156
|
+
host = data['hostname']
|
157
|
+
key, pubkey = Gaptool::EC2::putkey(host)
|
158
|
+
json hostname: host, key: key, pubkey: pubkey
|
159
|
+
end
|
160
|
+
|
161
|
+
get '/version' do
|
162
|
+
version = File.read(File.realpath(
|
163
|
+
File.join(File.dirname(__FILE__), "..", 'VERSION')
|
164
|
+
)).strip
|
165
|
+
json server_version: version, api: {v0: "/"}
|
166
|
+
end
|
167
|
+
|
168
|
+
post '/rehash' do
|
169
|
+
json Gaptool::Rehash::rehash()
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
data/lib/views/init.erb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
apt-get update
|
2
2
|
apt-get install -ymq zsh git libssl-dev ruby1.9.1-full build-essential
|
3
|
-
|
4
|
-
bash install.sh -v <%= @chef_version %>
|
5
|
-
|
3
|
+
gem install --bindir /usr/local/bin --no-ri --no-rdoc chef
|
6
4
|
cat << 'EOFKEY' > /root/.ssh/id_rsa
|
7
|
-
<%=
|
5
|
+
<%= initkey %>
|
8
6
|
EOFKEY
|
9
7
|
chmod 600 /root/.ssh/id_rsa
|
10
8
|
echo 'StrictHostKeyChecking no' > /root/.ssh/config
|
11
|
-
git clone -b <%=
|
12
|
-
echo '<%=
|
13
|
-
chef-solo -c /root/ops/cookbooks/init.rb -j /root/init.json
|
14
|
-
(rm /root/.ssh/id_rsa;
|
9
|
+
git clone -b <%= chef_branch %> <%= chef_repo %> /root/ops
|
10
|
+
echo '<%= json %>' > /root/init.json
|
11
|
+
chef-solo -c /root/ops/cookbooks/init.rb -j /root/init.json && \
|
12
|
+
(rm /root/.ssh/id_rsa; userdel -r ubuntu; rm -rf /root/.ssh; rm -rf /root/ops)
|
data/tasks/app.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
namespace :app do
|
2
|
+
desc "Add an application"
|
3
|
+
task :add, [:app, :role] do |t, args|
|
4
|
+
abort("Missing parameters") if args[:app].nil? || args[:app].empty? || args[:role].nil? || args[:role].empty?
|
5
|
+
ex = DH.get_app_data(args[:app])
|
6
|
+
unless ex.nil?
|
7
|
+
puts "Updating application #{args[:app]}" unless ex.nil?
|
8
|
+
DH.remove_app(args[:app])
|
9
|
+
end
|
10
|
+
DH.add_app(args[:app], args[:role])
|
11
|
+
puts "Added app #{args[:app]} in role #{args[:role]}"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Remove an application"
|
15
|
+
task :delete, [:app] do |t, args|
|
16
|
+
DH.remove_app(args[:app])
|
17
|
+
puts "Removed app #{args[:app]}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "List apps"
|
22
|
+
task :app do
|
23
|
+
puts "Apps:"
|
24
|
+
DH.apps.each do |app|
|
25
|
+
info = DH.get_app_data(app)
|
26
|
+
puts " * #{app} => #{info["role"]}"
|
27
|
+
end
|
28
|
+
end
|
data/tasks/docker.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
def printimages
|
2
|
+
puts %x[docker images | grep '^gild/gaptool']
|
3
|
+
end
|
4
|
+
|
5
|
+
unless File.exists?('/.dockerenv')
|
6
|
+
namespace :docker do
|
7
|
+
namespace :build do
|
8
|
+
task :image do
|
9
|
+
sys(%w(./scripts/build_docker_images.sh))
|
10
|
+
printimages
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Build the release image"
|
14
|
+
task :release do
|
15
|
+
sys(%w(./scripts/build_docker_images.sh -t release))
|
16
|
+
printimages
|
17
|
+
end
|
18
|
+
|
19
|
+
task :all => [:image]
|
20
|
+
end
|
21
|
+
|
22
|
+
namespace :push do
|
23
|
+
task :release do
|
24
|
+
sys(%w(docker push gild/gaptool:release))
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Push all tags to the Docker Hub'
|
28
|
+
task :all do
|
29
|
+
sys(%w(docker push gild/gaptool))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'Build the docker image'
|
34
|
+
task :build => 'build:image'
|
35
|
+
|
36
|
+
desc "Push the release image to the Docker Hub"
|
37
|
+
task :push => 'push:release'
|
38
|
+
|
39
|
+
task :up => [:build, :recreate]
|
40
|
+
|
41
|
+
desc "Run tests w/ docker"
|
42
|
+
task :test => :build do
|
43
|
+
sys(%w(fig run --rm gaptool rake test))
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Stop docker containers"
|
47
|
+
task :stop do
|
48
|
+
sys(%w(fig stop))
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "Start docker containers"
|
52
|
+
task :start do
|
53
|
+
sys(%w(fig start))
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "Restart docker containers"
|
57
|
+
task :restart => [:stop, :start]
|
58
|
+
|
59
|
+
desc "Stop and remove docker containers (alias 'rm')"
|
60
|
+
task :remove => :stop do
|
61
|
+
sys(%w(fig rm --force))
|
62
|
+
end
|
63
|
+
|
64
|
+
task :rm => :remove
|
65
|
+
|
66
|
+
desc "Recreate docker containers without building"
|
67
|
+
task :recreate do
|
68
|
+
sys(%w(fig up -d))
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Run a command in the docker container"
|
72
|
+
task :run do
|
73
|
+
exit sys(%W(fig run --rm gaptool #{ARGV[1..-1].shelljoin}))
|
74
|
+
end
|
75
|
+
|
76
|
+
desc "Run a rake task inside the docker container"
|
77
|
+
task :rake do
|
78
|
+
exit sys(%W(fig run --rm gaptool rake #{ARGV[1..-1].shelljoin}))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "Bring up docker containers"
|
83
|
+
task :docker => 'docker:up'
|
84
|
+
end
|
data/tasks/gem.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
namespace :gem do
|
2
|
+
|
3
|
+
desc "Build the gem"
|
4
|
+
task :build => :clean do
|
5
|
+
sys(%w(gem build gaptool-server.gemspec))
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Clean built file"
|
9
|
+
task :clean do
|
10
|
+
sys(%w(rm -vf *.gem))
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Bump the version"
|
14
|
+
task :bump do
|
15
|
+
version = File.read('VERSION').strip
|
16
|
+
nver = version.next
|
17
|
+
f = File.open('VERSION', 'w')
|
18
|
+
f.write(nver)
|
19
|
+
f.close
|
20
|
+
puts "Bumped #{version} => #{nver}"
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Push"
|
24
|
+
task :push => :build do
|
25
|
+
sys(%w(gem push gaptool-server*.gem))
|
26
|
+
end
|
27
|
+
end
|
data/tasks/user.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
namespace :user do
|
2
|
+
desc "Add a new user. rake user:create <username>"
|
3
|
+
task :create, [:username] do |t, args|
|
4
|
+
puts DH.useradd(args[:username])[:key]
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Rename a user. rake user:rename <oldname> <newname>"
|
8
|
+
task :rename, [:oldname, :newname] do |t, args|
|
9
|
+
user = DH.user(args[:oldname])
|
10
|
+
abort("Unknown user #{args[:oldname]}") if user.nil?
|
11
|
+
DH.userdel(user[:username])
|
12
|
+
DH.useradd(args[:newname], user[:key])
|
13
|
+
puts "User #{args[:oldname]} renamed to #{args[:newname]}"
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Delete a user. rake user:delete <username>"
|
17
|
+
task :delete, [:username] do |t, args|
|
18
|
+
DH.userdel(args[:username])
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Set user key. rake user:setkey <username> <key>"
|
22
|
+
task :setkey, [:username, :key] do |t, args|
|
23
|
+
user = DH.user(args[:username])
|
24
|
+
abort("Unknown user #{args[:username]}") if user.nil?
|
25
|
+
puts DH.useradd(args[:username], args[:key])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "List users"
|
30
|
+
task :user do
|
31
|
+
puts DH.users.keys.sort.join("\n")
|
32
|
+
end
|