hap 0.0.2
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 +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +73 -0
- data/Rakefile +34 -0
- data/bin/hap +7 -0
- data/env.rb +14 -0
- data/hap.gemspec +30 -0
- data/lib/hap.rb +65 -0
- data/lib/hap/app.rb +66 -0
- data/lib/hap/cli.rb +111 -0
- data/lib/hap/constants.rb +23 -0
- data/lib/hap/generators.rb +15 -0
- data/lib/hap/generators/endpoint.rb +43 -0
- data/lib/hap/generators/gemfile.rb +28 -0
- data/lib/hap/generators/haproxy.rb +56 -0
- data/lib/hap/generators/install.rb +52 -0
- data/lib/hap/generators/procfile.rb +39 -0
- data/lib/hap/generators/templates/app/.ruby-gemset +1 -0
- data/lib/hap/generators/templates/app/.ruby-version +1 -0
- data/lib/hap/generators/templates/app/Gemfile +8 -0
- data/lib/hap/generators/templates/app/Rakefile +0 -0
- data/lib/hap/generators/templates/app/env.rb +10 -0
- data/lib/hap/generators/templates/app/server.rb +2 -0
- data/lib/hap/generators/templates/config/backend/Gemfile.erb +5 -0
- data/lib/hap/generators/templates/config/backend/Procfile.erb +3 -0
- data/lib/hap/generators/templates/config/frontend/Procfile.erb +8 -0
- data/lib/hap/generators/templates/config/frontend/haproxy.cfg.erb +41 -0
- data/lib/hap/generators/templates/endpoint.erb +12 -0
- data/lib/hap/helpers.rb +23 -0
- data/lib/hap/helpers/endpoint.rb +92 -0
- data/lib/hap/helpers/git.rb +41 -0
- data/lib/hap/helpers/heroku.rb +63 -0
- data/lib/hap/helpers/user_input.rb +29 -0
- data/lib/hap/version.rb +3 -0
- data/lib/kernel.rb +6 -0
- data/spec/cli_spec.rb +36 -0
- data/spec/fixtures/backend/Procfile +2 -0
- data/spec/fixtures/backend/Procfile.production +2 -0
- data/spec/fixtures/frontend/Procfile +3 -0
- data/spec/fixtures/frontend/Procfile.production +1 -0
- data/spec/fixtures/frontend/haproxy.cfg +31 -0
- data/spec/fixtures/frontend/haproxy.cfg.production +31 -0
- data/spec/fixtures/my_end_point.rb +7 -0
- data/spec/fixtures/namespace_my_end_point.rb +11 -0
- data/spec/generators/endpoint_spec.rb +29 -0
- data/spec/generators/haproxy_spec.rb +29 -0
- data/spec/generators/install_spec.rb +30 -0
- data/spec/generators/procfile_spec.rb +41 -0
- data/spec/support/minitest_helper.rb +18 -0
- metadata +225 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Hap
|
2
|
+
module Constants
|
3
|
+
|
4
|
+
DEPLOYMENT_DIR = "tmp/deploy"
|
5
|
+
RUNTIME_DIR = "tmp/server"
|
6
|
+
ENDPOINTS_DIR = "app/endpoints"
|
7
|
+
CONFIG_DIR = "config"
|
8
|
+
|
9
|
+
FRONT_END = "frontend"
|
10
|
+
BACK_END = "backend"
|
11
|
+
BUILDPACK_URL = 'https://github.com/kiafaldorius/haproxy-buildpack'
|
12
|
+
|
13
|
+
HAPROXY_BACKEND_SERVER_OPTIONS = 'inter 5000 fastinter 1000 fall 1 weight 50'
|
14
|
+
DEFAULT_PROCESS_TYPE = "web"
|
15
|
+
|
16
|
+
DEVELOPMENT_HOST = "localhost"
|
17
|
+
DEFAULT_TCP_PORT = 80
|
18
|
+
|
19
|
+
DEPLOYED_FRONTEND = "#{DEPLOYMENT_DIR}/#{FRONT_END}"
|
20
|
+
APP_DATA_FILE = "heroku.json"
|
21
|
+
REMOTE_REPO_NAME = "heroku"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Hap
|
2
|
+
module Generators
|
3
|
+
class Endpoint < Thor::Group
|
4
|
+
|
5
|
+
include Hap::Helpers
|
6
|
+
|
7
|
+
argument :name, required: true
|
8
|
+
class_option :remote, default: false
|
9
|
+
|
10
|
+
def self.source_root
|
11
|
+
File.dirname(__FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy_endpoint_template
|
15
|
+
template("templates/endpoint.erb", "#{Hap.app_root}/#{Hap::ENDPOINTS_DIR}/#{name}.rb")
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_config_file
|
19
|
+
create_file "#{Hap.app_root}/#{Hap::CONFIG_DIR}/#{name}.rb", ""
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_remote_app
|
23
|
+
create_app name if options[:remote]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def modules
|
29
|
+
@modules ||= name.split("/").tap{|a| a.pop }
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_module?
|
33
|
+
@has_module ||= name.include?("/")
|
34
|
+
end
|
35
|
+
|
36
|
+
def class_name
|
37
|
+
@class_name ||= name.split("/").last
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Hap
|
2
|
+
module Generators
|
3
|
+
class Gemfile < Thor::Group
|
4
|
+
|
5
|
+
include Hap::Helpers
|
6
|
+
|
7
|
+
argument :app, required: true
|
8
|
+
argument :env, default: Hap.env
|
9
|
+
class_option :force, default: true
|
10
|
+
|
11
|
+
def self.source_root
|
12
|
+
File.dirname(__FILE__)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_cfg_file
|
16
|
+
template "templates/config/#{Hap::BACK_END}/Gemfile.erb", target_file
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def target_file
|
22
|
+
@target_file ||= "#{Hap.app_root}/#{Hap::DEPLOYMENT_DIR}/#{app}/Gemfile"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Hap
|
2
|
+
module Generators
|
3
|
+
class Haproxy < Thor::Group
|
4
|
+
|
5
|
+
include Hap::Helpers
|
6
|
+
|
7
|
+
argument :env, default: Hap.env
|
8
|
+
class_option :force, default: true
|
9
|
+
|
10
|
+
def self.source_root
|
11
|
+
File.dirname(__FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_cfg_file
|
15
|
+
template "templates/config/#{Hap::FRONT_END}/haproxy.cfg.erb", target_file
|
16
|
+
end
|
17
|
+
|
18
|
+
def fix_port
|
19
|
+
gsub_file target_file, "$PORT", "5000" unless to.production?
|
20
|
+
end
|
21
|
+
|
22
|
+
def convert_to_profile
|
23
|
+
if to.production?
|
24
|
+
copy_file target_file, target_file.gsub( File.basename(target_file), "haproxy.cfg" )
|
25
|
+
prepend_file target_file do
|
26
|
+
"#!/bin/bash\n"
|
27
|
+
"cat > haproxy.cfg <<EOF\n"
|
28
|
+
end
|
29
|
+
append_file target_file do
|
30
|
+
"\nEOF"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def stats_admin
|
38
|
+
@stats_admin ||= ENV['STATS_ADMIN'] ||= "admin"
|
39
|
+
end
|
40
|
+
|
41
|
+
def stats_password
|
42
|
+
@stats_password ||= ENV['STATS_PASSWORD'] ||= "password"
|
43
|
+
end
|
44
|
+
|
45
|
+
def target_file
|
46
|
+
@target_file ||= "#{Hap.app_root}/#{target}/#{Hap::FRONT_END}/#{filename}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def filename
|
50
|
+
@filename ||= to.production? ? ".profile" : "haproxy.cfg"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Hap
|
2
|
+
module Generators
|
3
|
+
class Install < Thor::Group
|
4
|
+
|
5
|
+
include Hap::Helpers
|
6
|
+
|
7
|
+
argument :path, required: true
|
8
|
+
class_option :bundle, default: false
|
9
|
+
class_option :remote, default: false
|
10
|
+
|
11
|
+
def self.source_root
|
12
|
+
File.dirname(__FILE__)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_app_directory_structure
|
16
|
+
directory("templates/app", path)
|
17
|
+
empty_directory "#{app_path}/#{Hap::DEPLOYMENT_DIR}"
|
18
|
+
empty_directory "#{app_path}/#{Hap::RUNTIME_DIR}"
|
19
|
+
empty_directory "#{app_path}/app/endpoints"
|
20
|
+
empty_directory "#{app_path}/config"
|
21
|
+
Hap.app_root = app_path
|
22
|
+
end
|
23
|
+
|
24
|
+
def install_bundle
|
25
|
+
return unless options[:bundle]
|
26
|
+
inside(app_path) do
|
27
|
+
Bundler.with_clean_env do
|
28
|
+
run "bundle install", capture: true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def init_git
|
34
|
+
inside(app_path) do
|
35
|
+
git_init
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_remote_app
|
40
|
+
create_app Hap::FRONT_END if options[:remote]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def app_path
|
46
|
+
"#{Dir.pwd}/#{path}"
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Hap
|
2
|
+
module Generators
|
3
|
+
class Procfile < Thor::Group
|
4
|
+
|
5
|
+
include Hap::Helpers
|
6
|
+
|
7
|
+
argument :app, default: Hap::FRONT_END
|
8
|
+
argument :env, default: Hap.env
|
9
|
+
class_option :force, default: true
|
10
|
+
|
11
|
+
def self.source_root
|
12
|
+
File.dirname(__FILE__)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_procfile
|
16
|
+
template "templates/config/#{type}/Procfile.erb",
|
17
|
+
"#{Hap.app_root}/#{target}/#{app}/Procfile"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def type
|
23
|
+
app == Hap::FRONT_END ? Hap::FRONT_END : Hap::BACK_END
|
24
|
+
end
|
25
|
+
|
26
|
+
def haproxy
|
27
|
+
to.production? ? "./haproxy -f haproxy.cfg" :
|
28
|
+
"haproxy -f #{Hap::RUNTIME_DIR}/#{Hap::FRONT_END}/haproxy.cfg -d -V"
|
29
|
+
end
|
30
|
+
|
31
|
+
def backend
|
32
|
+
path = app.is_a?(String) ? app : app.name
|
33
|
+
endpoints(true).select{|e| e[:path] == path }[0]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
hap
|
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0-p0
|
File without changes
|
@@ -0,0 +1,8 @@
|
|
1
|
+
web: <%= haproxy %>
|
2
|
+
<% unless to.production? %>
|
3
|
+
<% endpoints(true).each do |endpoint| %>
|
4
|
+
<% endpoint[:servers].each_with_index do |server, index| %>
|
5
|
+
srv_<%= endpoint[:name] %>_<%= index %>: bundle exec ruby <%= endpoint[:file] %> -p <%= server[:port] %>
|
6
|
+
<% end %>
|
7
|
+
<% end %>
|
8
|
+
<% end %>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
global
|
2
|
+
pidfile /var/run/haproxy.pid
|
3
|
+
log 127.0.0.1 local0 info
|
4
|
+
user root
|
5
|
+
|
6
|
+
defaults
|
7
|
+
mode http
|
8
|
+
|
9
|
+
clitimeout 600000 # maximum inactivity time on the client side
|
10
|
+
srvtimeout 600000 # maximum inactivity time on the server side
|
11
|
+
timeout connect 8000 # maximum time to wait for a connection attempt to a server to succeed
|
12
|
+
|
13
|
+
stats enable
|
14
|
+
stats auth <%= stats_admin %>:<%= stats_password %>
|
15
|
+
stats uri /monitor
|
16
|
+
stats refresh 5s
|
17
|
+
retries 5
|
18
|
+
option httpchk GET /status
|
19
|
+
option redispatch
|
20
|
+
option httpclose
|
21
|
+
option abortonclose
|
22
|
+
option forwardfor
|
23
|
+
|
24
|
+
balance roundrobin # each server is used in turns, according to assigned weight
|
25
|
+
|
26
|
+
frontend http
|
27
|
+
bind :$PORT
|
28
|
+
monitor-uri /haproxy # end point to monitor HAProxy status (returns 200)
|
29
|
+
|
30
|
+
<% endpoints(true).each do |endpoint| %>
|
31
|
+
acl acl_<%= endpoint[:name] %> path_reg ^/<%= endpoint[:path] %>/?
|
32
|
+
use_backend backend_<%= endpoint[:name] %> if acl_<%= endpoint[:name] %>
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
<% endpoints(true).each do |endpoint| %>
|
36
|
+
backend backend_<%= endpoint[:name] %>
|
37
|
+
reqirep ^Host: Host:\ <%= endpoint[:host] %>
|
38
|
+
<% endpoint[:servers].each_with_index do |server, index| %>
|
39
|
+
server srv_<%= endpoint[:name] %>_<%= index %> <%= server[:address] %>:<%= server[:port] %> <%= server[:options] %>
|
40
|
+
<% end %>
|
41
|
+
<% end %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'goliath'
|
2
|
+
<% if has_module? %><% modules.each do |module_name| %>
|
3
|
+
module <%= module_name.classify %>
|
4
|
+
<% end %><% end %>
|
5
|
+
class <%= class_name.classify %> < Goliath::API
|
6
|
+
def response(env)
|
7
|
+
[200,{},"OK"]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
<% if has_module? %><% modules.each do |mname| %>
|
11
|
+
end
|
12
|
+
<% end %><% end %>
|
data/lib/hap/helpers.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Hap
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
|
7
|
+
eager_autoload do
|
8
|
+
autoload :Endpoint
|
9
|
+
autoload :Git
|
10
|
+
autoload :Heroku
|
11
|
+
autoload :UserInput
|
12
|
+
end
|
13
|
+
|
14
|
+
included do
|
15
|
+
include Heroku
|
16
|
+
include UserInput
|
17
|
+
include Git
|
18
|
+
include Endpoint
|
19
|
+
include Thor::Actions
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Hap
|
2
|
+
module Helpers
|
3
|
+
module Endpoint
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def endpoints(with_servers = false)
|
8
|
+
@endpoints ||= begin
|
9
|
+
Array.new.tap{ |endpoints|
|
10
|
+
Dir.glob("#{Hap::ENDPOINTS_DIR}/**/*.rb").each_with_index do |file,index|
|
11
|
+
return unless File.file? file
|
12
|
+
|
13
|
+
path = file.gsub(/#{Hap::ENDPOINTS_DIR}|\.rb/,"")[1..-1]
|
14
|
+
name = path.parameterize.underscore
|
15
|
+
servers = []
|
16
|
+
host = nil
|
17
|
+
|
18
|
+
if with_servers
|
19
|
+
servers = [{
|
20
|
+
address: address(path), port: port(name, index),
|
21
|
+
options: Hap::HAPROXY_BACKEND_SERVER_OPTIONS,
|
22
|
+
type: Hap::DEFAULT_PROCESS_TYPE
|
23
|
+
}]
|
24
|
+
host = get_host(path)
|
25
|
+
end
|
26
|
+
|
27
|
+
endpoints << {file: file, path: path, name: name, host: host, servers: servers}
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def deployed? endpoint
|
34
|
+
App.new(endpoint[:path]).exists?
|
35
|
+
end
|
36
|
+
|
37
|
+
def deploy_dir endpoint
|
38
|
+
"#{Hap::DEPLOYMENT_DIR}/#{endpoint[:path]}/#{Hap::ENDPOINTS_DIR}/#{File.basename(endpoint[:file])}"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def to
|
44
|
+
@to ||= ActiveSupport::StringInquirer.new(env)
|
45
|
+
end
|
46
|
+
|
47
|
+
def target
|
48
|
+
@target ||= to.production? ? Hap::DEPLOYMENT_DIR : Hap::RUNTIME_DIR
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_host path
|
52
|
+
to.production? ? address(path) : Hap::DEVELOPMENT_HOST
|
53
|
+
end
|
54
|
+
|
55
|
+
def address path
|
56
|
+
|
57
|
+
if to.production?
|
58
|
+
|
59
|
+
app = App.new(path)
|
60
|
+
|
61
|
+
app = create_app path unless app.exists?
|
62
|
+
|
63
|
+
raise Thor::Error, "App could not found" unless app.exists?
|
64
|
+
|
65
|
+
app.data["domain_name"]["domain"]
|
66
|
+
|
67
|
+
else
|
68
|
+
"0.0.0.0"
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
def port name, index
|
75
|
+
to.production? ? Hap::DEFAULT_TCP_PORT : next_port(name,index).to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def next_port name, index
|
79
|
+
if ports[name]
|
80
|
+
ports[name] += 1
|
81
|
+
else
|
82
|
+
ports[name] = ((index +1) * 1000) + 5000
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def ports
|
87
|
+
@ports ||= {}
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|