hap 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0bd84f54373818e6ba80184570c1a09b97acd82c
|
4
|
+
data.tar.gz: 0fe55b6a035aede51d26deb6be94812b2e4cfd82
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c2485b9dddba790b5ca17bdb727de1690c527ee39911c4de5b13df7b4c7e59944f49dff0129b8bd76ffb1bd83e165c35774cf17941814df7368c3f5c3caf0122
|
7
|
+
data.tar.gz: 9834d429146c3752eeffba291ba8c2edd0045c25f7c27ec315f3a7723cbd15f4fa401250fa594ded05261b9079aa140a58980ea6a7b2cdb619abc8e334a7ab11
|
data/.DS_Store
ADDED
Binary file
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
hap
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0-p0
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 lemmycaution
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Hap
|
2
|
+
|
3
|
+
An Interface to help creating multi-server high scalable APIs on Heroku!
|
4
|
+
|
5
|
+
Hap is a CLI and a bit much more to manage `App per Resource` APIs powered by Goliath and HaProxy
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'hap'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install hap
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Beware!, before start, this gem has not been fully tested on various platforms and i'm using it with ruby 2.0 on mac os x, but designed to work on heroku, so it works on heroku :)
|
24
|
+
|
25
|
+
Go and create you firt hap app by running
|
26
|
+
|
27
|
+
$ hap new my_intergalactic_api [--bundle] [--remote]
|
28
|
+
|
29
|
+
Ok, get in the directory
|
30
|
+
|
31
|
+
$ cd my_intergalactic_api
|
32
|
+
|
33
|
+
Then create the very first endpoint of this little api
|
34
|
+
|
35
|
+
$ hap endpoint showmeok
|
36
|
+
|
37
|
+
Run it locally, then head to localhost:5000/showmeok (!wow!)
|
38
|
+
|
39
|
+
$ hap server
|
40
|
+
|
41
|
+
Well, to deploy this tiny api you need a heroku account and api key, if you have already, run;
|
42
|
+
but first, if you are using multiple heroku accounts deploy may fail, but are you usin heroku:accounts
|
43
|
+
then run
|
44
|
+
|
45
|
+
$ hap account YourAccountName
|
46
|
+
|
47
|
+
well, we'r ready now, go live, hurray!
|
48
|
+
|
49
|
+
$ hap deploy
|
50
|
+
|
51
|
+
Viola! Your highly scalable, haproxy powered multi-app (not dyno!) api deployed!
|
52
|
+
|
53
|
+
If you missed the address of you api, you can find all data about heroku apps under deploy directory.
|
54
|
+
|
55
|
+
Good luck
|
56
|
+
|
57
|
+
Drop me a line if you liked or interested in this, @onuruyar at twitter.
|
58
|
+
|
59
|
+
ps: Thanks https://github.com/kiafaldorius/ for his buildpack, it rocks!
|
60
|
+
|
61
|
+
## Todo
|
62
|
+
|
63
|
+
write more tests
|
64
|
+
write documentation
|
65
|
+
it can be sexier than this!
|
66
|
+
|
67
|
+
## Contributing
|
68
|
+
|
69
|
+
1. Fork it
|
70
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
71
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
72
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
73
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "./env"
|
2
|
+
|
3
|
+
$stderr.sync = true
|
4
|
+
|
5
|
+
require "bundler/gem_tasks"
|
6
|
+
|
7
|
+
# Bundling
|
8
|
+
require 'bundler'
|
9
|
+
Bundler.setup
|
10
|
+
Bundler.require
|
11
|
+
|
12
|
+
# Test Task
|
13
|
+
task :test, [:path] do |t, args|
|
14
|
+
|
15
|
+
# set environment
|
16
|
+
ENV["RACK_ENV"] = "test"
|
17
|
+
ENV['HEROKU_API_KEY']='TEST_API_KEY'
|
18
|
+
ENV['HEROKU_ACCOUNT']='fluxproject'
|
19
|
+
ENV['GEM_ROOT'] = File.dirname(File.expand_path(__FILE__))
|
20
|
+
|
21
|
+
require 'hap'
|
22
|
+
|
23
|
+
# include support files
|
24
|
+
Dir.glob('spec/support/*.rb') { |f| require f }
|
25
|
+
|
26
|
+
# Run them all or only one
|
27
|
+
if ARGV.length < 2
|
28
|
+
Dir.glob('spec/**/*_spec.rb') { |f| require f }
|
29
|
+
else
|
30
|
+
require "#{ARGV[1]}_spec.rb"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
task :default => ["test"]
|
data/bin/hap
ADDED
data/env.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift File.dirname( __FILE__)
|
2
|
+
|
3
|
+
# real time stdout
|
4
|
+
$stdout.sync = true
|
5
|
+
# $stderr.sync = true
|
6
|
+
|
7
|
+
# ENV vars
|
8
|
+
if File.exists?(".env")
|
9
|
+
Hash[File.read(".env").
|
10
|
+
gsub("\n\n","\n").
|
11
|
+
split("\n").
|
12
|
+
compact.map{|v| v.split("=")}].
|
13
|
+
each { |k,v| ENV[k] = v }
|
14
|
+
end
|
data/hap.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hap/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hap"
|
8
|
+
spec.version = Hap::VERSION
|
9
|
+
spec.authors = ["lemmycaution"]
|
10
|
+
spec.email = ["fluxproject@gmail.com"]
|
11
|
+
spec.description = %q{An Interface to help creating multi-server high scalable APIs on heroku}
|
12
|
+
spec.summary = %q{Hap is a CLI and a bit much more to manage `App per Resource` APIs based on Goliath and HaProxy}
|
13
|
+
spec.homepage = "http://github.com/lemmycaution/hap"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
|
24
|
+
spec.add_development_dependency "minitest"
|
25
|
+
spec.add_dependency "thor"
|
26
|
+
spec.add_dependency "activesupport"
|
27
|
+
spec.add_dependency "heroku-api"
|
28
|
+
spec.add_dependency "heroku"
|
29
|
+
spec.add_dependency "oj"
|
30
|
+
end
|
data/lib/hap.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require "kernel"
|
2
|
+
require 'pathname'
|
3
|
+
require "active_support/inflector"
|
4
|
+
require "active_support/string_inquirer"
|
5
|
+
require "active_support/concern"
|
6
|
+
require "active_support/dependencies/autoload"
|
7
|
+
|
8
|
+
module Hap
|
9
|
+
|
10
|
+
extend ActiveSupport::Autoload
|
11
|
+
|
12
|
+
eager_autoload do
|
13
|
+
autoload :App
|
14
|
+
autoload :CLI
|
15
|
+
autoload :Constants
|
16
|
+
autoload :Generators
|
17
|
+
autoload :Helpers
|
18
|
+
autoload :Version
|
19
|
+
end
|
20
|
+
|
21
|
+
include Constants
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
def env
|
26
|
+
@env ||= ActiveSupport::StringInquirer.new(ENV['RACK_ENV'] ||= "development")
|
27
|
+
end
|
28
|
+
|
29
|
+
def env=(environment)
|
30
|
+
@env = ActiveSupport::StringInquirer.new(environment)
|
31
|
+
end
|
32
|
+
|
33
|
+
def app_root
|
34
|
+
@app_root ||= find_root_with_flag('server.rb', Dir.pwd)
|
35
|
+
end
|
36
|
+
|
37
|
+
def app_root= app_root
|
38
|
+
@app_root = app_root
|
39
|
+
end
|
40
|
+
|
41
|
+
def in_app_dir?
|
42
|
+
File.exists?("#{Hap.app_root}/server.rb")
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# i steal this from rails
|
48
|
+
def find_root_with_flag(flag, default=nil)
|
49
|
+
root_path = self.class.called_from[0]
|
50
|
+
|
51
|
+
while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}")
|
52
|
+
parent = File.dirname(root_path)
|
53
|
+
root_path = parent != root_path && parent
|
54
|
+
end
|
55
|
+
|
56
|
+
root = File.exist?("#{root_path}/#{flag}") ? root_path : default
|
57
|
+
raise "Could not find root path for #{self}" unless root
|
58
|
+
|
59
|
+
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ?
|
60
|
+
Pathname.new(root).expand_path : Pathname.new(root).realpath
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/lib/hap/app.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'oj'
|
2
|
+
require 'heroku-api'
|
3
|
+
|
4
|
+
module Hap
|
5
|
+
class App
|
6
|
+
|
7
|
+
@data = {}
|
8
|
+
|
9
|
+
attr_accessor :name, :file, :data
|
10
|
+
|
11
|
+
def initialize name
|
12
|
+
@name = name
|
13
|
+
@file = "#{Hap.app_root}/#{Hap::DEPLOYMENT_DIR}/#{name}/#{Hap::APP_DATA_FILE}"
|
14
|
+
@data = Oj.load(File.read(@file)) if exists?
|
15
|
+
end
|
16
|
+
|
17
|
+
def exists?
|
18
|
+
@exists ||= File.exists?(@file)
|
19
|
+
end
|
20
|
+
|
21
|
+
def frontend?
|
22
|
+
@name == Hap::FRONT_END
|
23
|
+
end
|
24
|
+
|
25
|
+
def data
|
26
|
+
@data["domain_name"] = {"domain" => "TEST"} if Hap.env.test?
|
27
|
+
@data
|
28
|
+
end
|
29
|
+
|
30
|
+
def create!(api_key = nil)
|
31
|
+
return self if exists?
|
32
|
+
self.api_key = api_key
|
33
|
+
@data = heroku.post_app(name: nil).body
|
34
|
+
add_buildpack if frontend?
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroy!(api_key = nil)
|
39
|
+
return unless exists?
|
40
|
+
self.api_key = api_key
|
41
|
+
heroku.delete_app @data["name"] rescue nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def api_key= api_key
|
45
|
+
@api_key = api_key unless api_key.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def missing_method(method,*args,&block)
|
49
|
+
return @data.send(method, *args, &block) if @data.respond_to?(method)
|
50
|
+
super(method,*args,&block)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def heroku
|
56
|
+
@heroku ||= ::Heroku::API.new(api_key: @api_key, :mock => Hap.env.test?)
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_buildpack
|
60
|
+
heroku.put_config_vars @data["name"], {
|
61
|
+
"BUILDPACK_URL" => Hap::BUILDPACK_URL
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
data/lib/hap/cli.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
module Hap
|
2
|
+
|
3
|
+
class CLI < Thor
|
4
|
+
|
5
|
+
include Hap::Helpers
|
6
|
+
|
7
|
+
def self.source_root
|
8
|
+
Hap.app_root
|
9
|
+
end
|
10
|
+
|
11
|
+
unless Hap.in_app_dir?
|
12
|
+
|
13
|
+
desc "new [PATH]", "Creates new Hap App"
|
14
|
+
def new(*args)
|
15
|
+
Hap::Generators::Install.start args
|
16
|
+
end
|
17
|
+
|
18
|
+
else
|
19
|
+
|
20
|
+
desc "endpoint [NAME]", "Creates new endpoint"
|
21
|
+
def endpoint(*args)
|
22
|
+
Hap::Generators::Endpoint.start args
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "create [APP]", "Creates Heroku app"
|
26
|
+
def create(*args)
|
27
|
+
create_app *args
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "delete [APP]", "Deletes Heroku app"
|
31
|
+
def delete(app, *args)
|
32
|
+
delete_app app
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "delete_all", "Deletes All Heroku apps"
|
36
|
+
def delete_all(*args)
|
37
|
+
delete_app Hap::FRONT_END
|
38
|
+
endpoints.each do |endpoint|
|
39
|
+
delete_app endpoint[:path]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "account [HEROKU_ACCOUNT]", "Sets default heroku account if heroku:accounts plugin available"
|
44
|
+
def account(account)
|
45
|
+
if has_accounts_plugin?
|
46
|
+
|
47
|
+
set_env_var "HEROKU_ACCOUNT", account
|
48
|
+
|
49
|
+
if File.exists? Hap::DEPLOYED_FRONTEND
|
50
|
+
inside Hap::DEPLOYED_FRONTEND do
|
51
|
+
run "heroku accounts:set #{account}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
endpoints.each do |endpoint|
|
56
|
+
next unless deployed? endpoint
|
57
|
+
inside deploy_dir(endpoint) do
|
58
|
+
run "heroku accounts:set #{account}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "server", "Starts Hap server"
|
66
|
+
def server(*args)
|
67
|
+
Hap::Generators::Haproxy.start args
|
68
|
+
Hap::Generators::Procfile.start args
|
69
|
+
system "foreman start -f #{Hap::RUNTIME_DIR}/#{Hap::FRONT_END}/Procfile -d ."
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "deploy", "Deploys to heroku"
|
73
|
+
def deploy(*args)
|
74
|
+
old_env = Hap.env
|
75
|
+
Hap.env = "production"
|
76
|
+
|
77
|
+
# Deploy Frontend
|
78
|
+
create_app Hap::FRONT_END
|
79
|
+
|
80
|
+
Hap::Generators::Procfile.start [Hap::FRONT_END, "production"]
|
81
|
+
Hap::Generators::Haproxy.start ["production"]
|
82
|
+
bundle_and_git Hap::FRONT_END
|
83
|
+
|
84
|
+
# Deploy Backend
|
85
|
+
endpoints.each do |endpoint|
|
86
|
+
|
87
|
+
create_app endpoint[:path]
|
88
|
+
|
89
|
+
Hap::Generators::Procfile.start [endpoint[:path], "production"]
|
90
|
+
Hap::Generators::Gemfile.start [endpoint[:path], "production"]
|
91
|
+
|
92
|
+
copy_file endpoint[:file], deploy_dir(endpoint)
|
93
|
+
|
94
|
+
bundle_and_git endpoint[:path]
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
Hap.env = old_env
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def env
|
106
|
+
Hap.env
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|