hap 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.gitignore +17 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +73 -0
  9. data/Rakefile +34 -0
  10. data/bin/hap +7 -0
  11. data/env.rb +14 -0
  12. data/hap.gemspec +30 -0
  13. data/lib/hap.rb +65 -0
  14. data/lib/hap/app.rb +66 -0
  15. data/lib/hap/cli.rb +111 -0
  16. data/lib/hap/constants.rb +23 -0
  17. data/lib/hap/generators.rb +15 -0
  18. data/lib/hap/generators/endpoint.rb +43 -0
  19. data/lib/hap/generators/gemfile.rb +28 -0
  20. data/lib/hap/generators/haproxy.rb +56 -0
  21. data/lib/hap/generators/install.rb +52 -0
  22. data/lib/hap/generators/procfile.rb +39 -0
  23. data/lib/hap/generators/templates/app/.ruby-gemset +1 -0
  24. data/lib/hap/generators/templates/app/.ruby-version +1 -0
  25. data/lib/hap/generators/templates/app/Gemfile +8 -0
  26. data/lib/hap/generators/templates/app/Rakefile +0 -0
  27. data/lib/hap/generators/templates/app/env.rb +10 -0
  28. data/lib/hap/generators/templates/app/server.rb +2 -0
  29. data/lib/hap/generators/templates/config/backend/Gemfile.erb +5 -0
  30. data/lib/hap/generators/templates/config/backend/Procfile.erb +3 -0
  31. data/lib/hap/generators/templates/config/frontend/Procfile.erb +8 -0
  32. data/lib/hap/generators/templates/config/frontend/haproxy.cfg.erb +41 -0
  33. data/lib/hap/generators/templates/endpoint.erb +12 -0
  34. data/lib/hap/helpers.rb +23 -0
  35. data/lib/hap/helpers/endpoint.rb +92 -0
  36. data/lib/hap/helpers/git.rb +41 -0
  37. data/lib/hap/helpers/heroku.rb +63 -0
  38. data/lib/hap/helpers/user_input.rb +29 -0
  39. data/lib/hap/version.rb +3 -0
  40. data/lib/kernel.rb +6 -0
  41. data/spec/cli_spec.rb +36 -0
  42. data/spec/fixtures/backend/Procfile +2 -0
  43. data/spec/fixtures/backend/Procfile.production +2 -0
  44. data/spec/fixtures/frontend/Procfile +3 -0
  45. data/spec/fixtures/frontend/Procfile.production +1 -0
  46. data/spec/fixtures/frontend/haproxy.cfg +31 -0
  47. data/spec/fixtures/frontend/haproxy.cfg.production +31 -0
  48. data/spec/fixtures/my_end_point.rb +7 -0
  49. data/spec/fixtures/namespace_my_end_point.rb +11 -0
  50. data/spec/generators/endpoint_spec.rb +29 -0
  51. data/spec/generators/haproxy_spec.rb +29 -0
  52. data/spec/generators/install_spec.rb +30 -0
  53. data/spec/generators/procfile_spec.rb +41 -0
  54. data/spec/support/minitest_helper.rb +18 -0
  55. 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,15 @@
1
+ module Hap
2
+ module Generators
3
+
4
+ extend ActiveSupport::Autoload
5
+
6
+ eager_autoload do
7
+ autoload :Endpoint
8
+ autoload :Gemfile
9
+ autoload :Haproxy
10
+ autoload :Install
11
+ autoload :Procfile
12
+ end
13
+
14
+ end
15
+ 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
+ ruby-2.0.0-p0
@@ -0,0 +1,8 @@
1
+ ruby '2.0.0'
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'hap', path: "/Users/onuruyar/Sites/hap/gem"
6
+
7
+ gem 'goliath'
8
+ gem 'foreman'
@@ -0,0 +1,10 @@
1
+ $:.unshift File.dirname( __FILE__)
2
+
3
+ # real time stdout
4
+ $stdout.sync = true
5
+ $stderr.sync = true
6
+
7
+ # Bundling
8
+ require 'bundler'
9
+ Bundler.setup
10
+ Bundler.require
@@ -0,0 +1,2 @@
1
+ require './env'
2
+ require ARGV[0]
@@ -0,0 +1,5 @@
1
+ ruby '2.0.0'
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'goliath'
@@ -0,0 +1,3 @@
1
+ <% backend[:servers].each_with_index do |server, index| %>
2
+ <%= server[:type] %>: bundle exec ruby <%= backend[:file] %> -p $PORT
3
+ <% end %>
@@ -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 %>
@@ -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