gaptool-server 0.5.19 → 0.6.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|