appfront 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/appfront +5 -0
- data/lib/appfront.rb +3 -0
- data/lib/appfront/api.rb +76 -0
- data/lib/appfront/cli.rb +55 -0
- data/lib/appfront/command.rb +67 -0
- data/lib/appfront/command/alias.rb +30 -0
- data/lib/appfront/command/auth.rb +67 -0
- data/lib/appfront/command/base.rb +78 -0
- data/lib/appfront/command/clusters.rb +97 -0
- data/lib/appfront/command/config.rb +70 -0
- data/lib/appfront/command/flows.rb +68 -0
- data/lib/appfront/command/help.rb +68 -0
- data/lib/appfront/command/logs.rb +15 -0
- data/lib/appfront/command/providers.rb +34 -0
- data/lib/appfront/command/ps.rb +128 -0
- data/lib/appfront/initializers/string.rb +13 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 505d73e7f8af1914c2103171441ba6e705bcd073
|
4
|
+
data.tar.gz: cf4ae7841945f10427fcf413cb867866b9c45bb7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: accd83b850c4cf86a43b757b63dd8d65e1541d76c4d1ab50eaad252d4bfada70003163a9a6b3724f80c76b0f8091b3e1e7c3fb8ef024da18c0fa2d5e20bff740
|
7
|
+
data.tar.gz: a66551fd5579be5222de0271f4e7d129beae87699f8a00fc9a6252f01f4e776f4a441e38cc168005bbc001f5f7836fcc456fea33609d20d017a1ce67e865e40f
|
data/bin/appfront
ADDED
data/lib/appfront.rb
ADDED
data/lib/appfront/api.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
class Appfront::API
|
2
|
+
require 'restclient'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
attr_reader :email, :host
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
@host = opts[:host] || 'https://api.appfront.io/v1'
|
9
|
+
|
10
|
+
if opts[:email] and opts[:password]
|
11
|
+
@email, @pass = opts[:email], opts[:password]
|
12
|
+
else
|
13
|
+
@email, @pass = Appfront::Command::Auth.credentials
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def login
|
18
|
+
return unless (@email && @pass)
|
19
|
+
|
20
|
+
@pass = RestClient.post(
|
21
|
+
"#{@host}/auth",
|
22
|
+
email: @email, password: @pass
|
23
|
+
)
|
24
|
+
rescue => e
|
25
|
+
puts e.response
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(m, *args, &block)
|
30
|
+
unless ['get', 'post', 'put', 'delete'].include? m.to_s
|
31
|
+
raise NoMethodError, "undefined method: #{m}"
|
32
|
+
end
|
33
|
+
|
34
|
+
res = self.send('req', m, args)
|
35
|
+
#TODO Da capire JSON.parse res rescue ""
|
36
|
+
JSON.parse res rescue res
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def req(method, *args)
|
42
|
+
raise if args.empty?
|
43
|
+
args = args.shift
|
44
|
+
path = args.shift
|
45
|
+
|
46
|
+
resource = RestClient::Resource.new(
|
47
|
+
"#{@host}#{path}", user: @pass, password: 'x'
|
48
|
+
)
|
49
|
+
|
50
|
+
begin
|
51
|
+
if path != '/flow.yaml'
|
52
|
+
resource.send method, (args.first || {})
|
53
|
+
else
|
54
|
+
file = args.first[:file]
|
55
|
+
request = RestClient::Request.new(
|
56
|
+
:method => method.to_sym,
|
57
|
+
:url => "#{host}#{path}",
|
58
|
+
:user => @pass,
|
59
|
+
:password => 'x',
|
60
|
+
:payload => {
|
61
|
+
:multipart => true,
|
62
|
+
:file => File.new(file, 'rb')
|
63
|
+
})
|
64
|
+
response = request.execute
|
65
|
+
end
|
66
|
+
rescue => e
|
67
|
+
if e.respond_to? :response
|
68
|
+
puts e.response
|
69
|
+
else
|
70
|
+
puts 'Something went wrong, we have been notified.'
|
71
|
+
end
|
72
|
+
|
73
|
+
exit 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/appfront/cli.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Appfront
|
2
|
+
libs = Dir[File.join(File.dirname(__FILE__), "initializers", "*.rb")]
|
3
|
+
libs.each { |file| require file }
|
4
|
+
|
5
|
+
require 'appfront/command'
|
6
|
+
|
7
|
+
class CLI
|
8
|
+
require 'optparse'
|
9
|
+
|
10
|
+
def self.parse(args)
|
11
|
+
options = {}
|
12
|
+
|
13
|
+
opt_parser = OptionParser.new do |opts|
|
14
|
+
opts.banner = "Usage: example.rb [options]"
|
15
|
+
|
16
|
+
opts.on("-f", "--flow Flow", "Specify the flow involved") do |v|
|
17
|
+
options[:flow] = v
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on("-p", "--provider Provider", "Specify the provider involved") do |v|
|
21
|
+
options[:provider] = v
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("-a", "--access Access Key", "Specify the access key involved") do |v|
|
25
|
+
options[:access] = v
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-s", "--secret Secret Key", "Specify the secret key involved") do |v|
|
29
|
+
options[:secret] = v
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on("-y", "--yaml File", "Specify the yaml file to upload") do |v|
|
33
|
+
options[:yaml] = v
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("-t", "--tail", "Continue running and print new events (off)") do |v|
|
37
|
+
options[:follow] = v
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("-h", "--help", "Show this help") do |v|
|
41
|
+
options[:help] = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
opt_parser.parse! args
|
46
|
+
|
47
|
+
[ARGV, options]
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.run(args)
|
51
|
+
cmd, opts = parse ARGV
|
52
|
+
Appfront::Command.run cmd, opts
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Appfront::Command
|
2
|
+
require 'appfront/api'
|
3
|
+
require 'appfront/command/base'
|
4
|
+
|
5
|
+
libs = Dir[File.join(File.dirname(__FILE__), "command", "*.rb")]
|
6
|
+
libs.each { |file| require file }
|
7
|
+
|
8
|
+
def self.run(args, opts)
|
9
|
+
cmd = args.shift
|
10
|
+
|
11
|
+
unless cmd
|
12
|
+
Appfront::Command::Help.root_help
|
13
|
+
exit 0
|
14
|
+
end
|
15
|
+
|
16
|
+
klass, act = cmd.split ':', 2
|
17
|
+
klass = klass.downcase
|
18
|
+
|
19
|
+
if klass == 'help'
|
20
|
+
m = args.shift || 'root_help'
|
21
|
+
m = m.split(':')[0]
|
22
|
+
|
23
|
+
Appfront::Command::Help.send m
|
24
|
+
exit 0
|
25
|
+
end
|
26
|
+
|
27
|
+
unless (klass == 'login' or cmd == 'auth:login')
|
28
|
+
Appfront::Command::Auth.authenticate!
|
29
|
+
end
|
30
|
+
|
31
|
+
Appfront::Command::Base.api = Appfront::API.new
|
32
|
+
|
33
|
+
if Appfront::Command::Alias.respond_to? klass
|
34
|
+
Appfront::Command::Alias.send klass.downcase, args, opts
|
35
|
+
exit 0
|
36
|
+
end
|
37
|
+
|
38
|
+
act ||= 'list'
|
39
|
+
|
40
|
+
begin
|
41
|
+
klass = "Appfront::Command::#{klass.capitalize}".constantize
|
42
|
+
rescue
|
43
|
+
unless Appfront::Command::Base.respond_to? cmd
|
44
|
+
puts 'Invalid command: ' + cmd
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
|
48
|
+
return Appfront::Command::Base.send cmd
|
49
|
+
end
|
50
|
+
|
51
|
+
unless klass.respond_to? act
|
52
|
+
puts 'Invalid action ' + act
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
|
56
|
+
method_args = klass.method(act).arity
|
57
|
+
|
58
|
+
case method_args
|
59
|
+
when 0
|
60
|
+
klass.send act
|
61
|
+
when 1
|
62
|
+
klass.send act, opts
|
63
|
+
when 2
|
64
|
+
klass.send act, args, opts
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Appfront::Command::Alias < Appfront::Command::Base
|
2
|
+
def self.login(*opts)
|
3
|
+
Appfront::Command::Auth.login
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.logout(*opts)
|
7
|
+
Appfront::Command::Auth.logout
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.signup(*opts)
|
11
|
+
puts 'Please go to: https://appfront.io/signup'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.version(*opts)
|
15
|
+
puts 'Appfront CLI 1.0.0'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.ls(*opts)
|
19
|
+
Appfront::Command::Flows.list
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.create(args, opts)
|
23
|
+
Appfront::Command::Flows.create args, opts
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.info(args, opts)
|
27
|
+
Appfront::Command::Flows.info opts
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Appfront::Command::Auth < Appfront::Command::Base
|
2
|
+
require 'netrc'
|
3
|
+
|
4
|
+
def self.login
|
5
|
+
puts "Enter your Appfront credentials."
|
6
|
+
|
7
|
+
print "email: "
|
8
|
+
email = ask
|
9
|
+
|
10
|
+
print "password: "
|
11
|
+
|
12
|
+
echo_off
|
13
|
+
pass = ask_for_password
|
14
|
+
echo_on
|
15
|
+
|
16
|
+
api = Appfront::API.new(email: email, password: pass)
|
17
|
+
key = api.login
|
18
|
+
|
19
|
+
netrc.delete 'jarvis.appfront.io'
|
20
|
+
|
21
|
+
netrc['jarvis.appfront.io'] = email, key
|
22
|
+
netrc.save
|
23
|
+
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.logout
|
28
|
+
netrc.delete 'jarvis.appfront.io'
|
29
|
+
netrc.save
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.authenticate!
|
35
|
+
return true if authenticated?
|
36
|
+
login
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.credentials
|
40
|
+
netrc['jarvis.appfront.io']
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def self.netrc
|
45
|
+
@netrc ||= Netrc.read netrc_path
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.netrc_path
|
49
|
+
default = Netrc.default_path
|
50
|
+
encrypted = default + ".gpg"
|
51
|
+
|
52
|
+
File.exists?(encrypted) ? encrypted : default
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.ask_for_password
|
56
|
+
echo_off
|
57
|
+
password = ask
|
58
|
+
puts
|
59
|
+
echo_on
|
60
|
+
|
61
|
+
password
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.authenticated?
|
65
|
+
netrc['jarvis.appfront.io'] ? true : false
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class Module
|
2
|
+
def cattr_accessor(attribute_name)
|
3
|
+
class_eval <<-CODE
|
4
|
+
def self.#{attribute_name}
|
5
|
+
@@#{attribute_name} ||= nil
|
6
|
+
end
|
7
|
+
def self.#{attribute_name}=(value)
|
8
|
+
@@#{attribute_name} = value
|
9
|
+
end
|
10
|
+
CODE
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Appfront::Command::Base
|
15
|
+
cattr_accessor :api
|
16
|
+
|
17
|
+
protected
|
18
|
+
def self.echo_off
|
19
|
+
with_tty do
|
20
|
+
system "stty -echo"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.echo_on
|
25
|
+
with_tty do
|
26
|
+
system "stty echo"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.with_tty(&block)
|
31
|
+
return unless $stdin.isatty
|
32
|
+
yield
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.ask
|
36
|
+
$stdin.gets.to_s.strip
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.find_flow!(opts)
|
40
|
+
if opts[:flow]
|
41
|
+
@flow = opts[:flow]
|
42
|
+
end
|
43
|
+
|
44
|
+
return true if @flow
|
45
|
+
|
46
|
+
puts 'No flow specified.'
|
47
|
+
puts 'Specify which flow to use with --flow FLOW.'
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.which(cmd)
|
52
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
53
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
54
|
+
exts.each { |ext|
|
55
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
56
|
+
return exe if File.executable? exe
|
57
|
+
}
|
58
|
+
end
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.spinner(desc, &block)
|
63
|
+
chars = %w{ | / - \\ }
|
64
|
+
|
65
|
+
t = Thread.new { block.call }
|
66
|
+
while t.alive?
|
67
|
+
print "#{desc} [#{chars[0]}]\t"
|
68
|
+
sleep 0.1
|
69
|
+
print "\r"
|
70
|
+
|
71
|
+
chars.push chars.shift
|
72
|
+
end
|
73
|
+
|
74
|
+
t.join
|
75
|
+
|
76
|
+
print "#{desc} [ok] \t"
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
class Appfront::Command::Clusters < Appfront::Command::Base
|
2
|
+
|
3
|
+
def self.list
|
4
|
+
clusters = api.get "/clusters"
|
5
|
+
puts '=== Clusters List:'
|
6
|
+
clusters.each do |cl|
|
7
|
+
puts "Cluster #{cl['uuid']} ---> Name: #{cl['name']} \tProvider: #{cl['provider']}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.info(args, opts)
|
12
|
+
exit 1 unless args[0]
|
13
|
+
uuid = args[0]
|
14
|
+
exit 1 unless opts[:provider]
|
15
|
+
exit 1 unless opts[:provider] == 'amazon' or opts[:provider] == 'digitalocean' or opts[:provider] == 'manual'
|
16
|
+
c = api.get "/cluster/#{uuid}"
|
17
|
+
puts "=== Cluster #{uuid}: \n"
|
18
|
+
puts "\t === Name: #{c['name']}\n"
|
19
|
+
puts "\t === Token: #{c['token']}\n"
|
20
|
+
puts "\t === Region: #{c['region']}\n"
|
21
|
+
puts "\t === Active Boxes: #{c['boxes']}\n"
|
22
|
+
puts "\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.create(args, opts)
|
26
|
+
exit 1 unless opts[:provider]
|
27
|
+
exit 1 unless opts[:provider] == 'amazon' or opts[:provider] == 'digitalocean' or opts[:provider] == 'manual'
|
28
|
+
|
29
|
+
if args.count == 3
|
30
|
+
name = args[0]
|
31
|
+
type = args[1]
|
32
|
+
region = args[2]
|
33
|
+
elsif args.count == 2
|
34
|
+
type = args[0]
|
35
|
+
region = args[1]
|
36
|
+
elsif args.count == 1
|
37
|
+
name = args[0]
|
38
|
+
type = 'manual'
|
39
|
+
region = 'manual'
|
40
|
+
end
|
41
|
+
|
42
|
+
tier = opts[:provider]
|
43
|
+
if type == 'manual'
|
44
|
+
spinner "Creating new manual cluster ..." do
|
45
|
+
@info = api.post "/provider/#{tier}/cluster/#{region}/#{type}" unless name
|
46
|
+
@info = api.post "/provider/#{tier}/cluster/#{region}/#{type}", name: name if name
|
47
|
+
end
|
48
|
+
else
|
49
|
+
spinner "Creating new cluster on region #{region}..." do
|
50
|
+
@info = api.post "/provider/#{tier}/cluster/#{region}/#{type}" unless name
|
51
|
+
@info = api.post "/provider/#{tier}/cluster/#{region}/#{type}", name: name if name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
print "\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.rm(args, opts)
|
58
|
+
exit 1 unless args[0]
|
59
|
+
uuid = args[0]
|
60
|
+
exit 1 unless opts[:provider]
|
61
|
+
exit 1 unless opts[:provider] == 'amazon' or opts[:provider] == 'digitalocean' or opts[:provider] == 'manual'
|
62
|
+
tier = opts[:provider]
|
63
|
+
|
64
|
+
spinner "Removing cluster #{uuid}..." do
|
65
|
+
sleep 1
|
66
|
+
@info = api.delete "/provider/#{tier}/cluster/#{uuid}"
|
67
|
+
end
|
68
|
+
print "\n"
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.scale_up(args, opts)
|
72
|
+
exit 1 unless args[0]
|
73
|
+
uuid = args[0]
|
74
|
+
exit 1 unless opts[:provider]
|
75
|
+
exit 1 unless opts[:provider] == 'amazon' or opts[:provider] == 'digitalocean'
|
76
|
+
|
77
|
+
spinner "Scaling UP cluster #{uuid}..." do
|
78
|
+
api.put "/provider/cluster/#{uuid}/scale/up"
|
79
|
+
end
|
80
|
+
print "\n"
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.scale_down(args, opts)
|
85
|
+
uuid = args[0]
|
86
|
+
exit 1 unless args[0]
|
87
|
+
exit 1 unless opts[:provider]
|
88
|
+
exit 1 unless opts[:provider] == 'amazon' or opts[:provider] == 'digitalocean'
|
89
|
+
exit 1 unless args[0]
|
90
|
+
|
91
|
+
spinner "Scaling DOWN cluster #{uuid}..." do
|
92
|
+
api.put "/provider/cluster/#{uuid}/scale/down"
|
93
|
+
end
|
94
|
+
print "\n"
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class Appfront::Command::Config < Appfront::Command::Base
|
2
|
+
def self.set(vars, opts)
|
3
|
+
unless vars.size > 0 and vars.all? { |a| a.include?('=') }
|
4
|
+
puts "Usage: appfront config:set KEY1=VALUE1 [KEY2=VALUE2 ...]\nMust specify KEY and VALUE to set."
|
5
|
+
exit 1
|
6
|
+
end
|
7
|
+
|
8
|
+
vars = parse_vars! vars
|
9
|
+
|
10
|
+
find_app! opts
|
11
|
+
|
12
|
+
puts "Setting ENV vars..."
|
13
|
+
|
14
|
+
api.post "/apps/#{@app}/vars", vars: vars
|
15
|
+
|
16
|
+
vars.each { |k, v| puts "#{k}:\t#{v}" }
|
17
|
+
|
18
|
+
puts
|
19
|
+
puts 'Please use ps:restart command to restart your app when you\'re ready.'
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.unset(vars, opts)
|
23
|
+
if vars.empty?
|
24
|
+
puts "Usage: appfront config:unset KEY1 [KEY2 ...]\nMust specify KEY to unset."
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
|
28
|
+
find_app! opts
|
29
|
+
|
30
|
+
puts "Unsetting ENV vars..."
|
31
|
+
|
32
|
+
api.post "/apps/#{@app}/vars/unset", vars: vars
|
33
|
+
|
34
|
+
puts
|
35
|
+
puts 'Please use ps:restart command to restart your app when you\'re ready.'
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.get(args, opts)
|
39
|
+
var = args.shift
|
40
|
+
find_app! opts
|
41
|
+
|
42
|
+
vars = api.get("/domains/#{@app}")['vars']
|
43
|
+
|
44
|
+
value = vars.select {|v| v['key_name'] == var }.first['value'] rescue nil
|
45
|
+
|
46
|
+
puts value if value
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.list(opts)
|
50
|
+
find_app! opts
|
51
|
+
|
52
|
+
puts "=== #{@app} Config Vars"
|
53
|
+
|
54
|
+
vars = api.get("/domains/#{@app}")['vars']
|
55
|
+
|
56
|
+
vars.each {|v| puts "#{v['key_name']}:\t#{v['value']}" }
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def self.parse_vars!(vars)
|
61
|
+
parsed_vars = {}
|
62
|
+
|
63
|
+
vars.each do |var|
|
64
|
+
k, v = var.split '=', 2
|
65
|
+
parsed_vars[k] = v || ''
|
66
|
+
end
|
67
|
+
|
68
|
+
parsed_vars
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class Appfront::Command::Flows < Appfront::Command::Base
|
2
|
+
|
3
|
+
def self.yaml(opts)
|
4
|
+
find_flow! opts
|
5
|
+
spinner "Retrieving YAML configuration from Jarvis..." do
|
6
|
+
@file = api.get "/flow/#{@flow}/yaml"
|
7
|
+
end
|
8
|
+
puts "\nYAML file starts from here: \n\n"
|
9
|
+
puts @file
|
10
|
+
print "\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.up(args, opts)
|
14
|
+
spinner "Uploading Flow YAML configuration to Jarvis..." do
|
15
|
+
api.post "/flow.yaml", file: args[0]
|
16
|
+
end
|
17
|
+
puts "\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.update(args, opts)
|
21
|
+
spinner "Updating Flow YAML configuration to Jarvis..." do
|
22
|
+
api.put "/flow.yaml", file: args[0]
|
23
|
+
end
|
24
|
+
puts "\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.rm(args, opts)
|
28
|
+
find_flow! opts
|
29
|
+
spinner "Removing Flow..." do
|
30
|
+
api.delete "/flow/#{@flow}"
|
31
|
+
end
|
32
|
+
puts "\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.info(opts)
|
36
|
+
find_flow! opts
|
37
|
+
|
38
|
+
h = api.get "/flow/#{@flow}"
|
39
|
+
|
40
|
+
puts "=== #{@flow}"
|
41
|
+
puts
|
42
|
+
puts "Git URL:\t#{h['git_repo']}"
|
43
|
+
puts "Owner Email:\t#{h['owner_email']}"
|
44
|
+
puts "Region:\t\tEU"
|
45
|
+
# puts "Slug Size:\t#{h['slug_size']}"
|
46
|
+
puts "Web URL:\thttp://#{h['live_address']}"
|
47
|
+
|
48
|
+
return unless (db = h['db'])
|
49
|
+
|
50
|
+
puts
|
51
|
+
puts "=== Database #{db['identifier']}"
|
52
|
+
puts "Plan: \t#{db['plan']['name']}"
|
53
|
+
puts "Nodes: \t#{db['plan']['nodes']}" unless db['plan']['shared']
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.list
|
57
|
+
flows = api.get "/flows"
|
58
|
+
unless flows.count == 0
|
59
|
+
puts '=== Flows List'
|
60
|
+
flows.each do |flow|
|
61
|
+
puts "#{flow['name']}:#{flow['tier']} ---> status: #{flow['status']}"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
puts '=== You have no flows.'
|
65
|
+
end
|
66
|
+
puts "\n"
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class Appfront::Command::Help < Appfront::Command::Base
|
2
|
+
def self.root_help
|
3
|
+
puts <<-HELP
|
4
|
+
Usage: appfront COMMAND [-f FLOW] [command-specific-options]
|
5
|
+
|
6
|
+
Primary help topics, type "appfront help TOPIC" for more details:
|
7
|
+
|
8
|
+
flows # manage flows
|
9
|
+
clusters # manage clusters
|
10
|
+
providers # manager connection with Amazon or DigitalOcean
|
11
|
+
|
12
|
+
Additional topics:
|
13
|
+
|
14
|
+
help # list commands and display help
|
15
|
+
version # display version
|
16
|
+
HELP
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.flows
|
20
|
+
puts <<-HELP
|
21
|
+
Flows commands:
|
22
|
+
|
23
|
+
flows:create [NAME] # create a new flow
|
24
|
+
flows:destroy # permanently destroy an flow
|
25
|
+
flows:info # show detailed flow information
|
26
|
+
flows:list # show flow list
|
27
|
+
HELP
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.clusters
|
31
|
+
puts <<-HELP
|
32
|
+
Clusters commands:
|
33
|
+
|
34
|
+
clusters:create -p [provider] [NAME] [type] [region] # create a new cluster
|
35
|
+
clusters:rm # permanently destroy a cluster
|
36
|
+
clusters:info # show detailed cluster information
|
37
|
+
clusters:list # show clusters list
|
38
|
+
HELP
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.providers
|
42
|
+
puts <<-HELP
|
43
|
+
Providers commands:
|
44
|
+
|
45
|
+
providers:connect -p [amazon|digitalocean] -a access_key -s secret_key # connect to the choosen provider
|
46
|
+
providers:disconnect # disconnect from provider
|
47
|
+
HELP
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def self.ps
|
52
|
+
puts <<-HELP
|
53
|
+
PS commands:
|
54
|
+
|
55
|
+
ps:resize web=TIER # resize dot to the given tier
|
56
|
+
ps:scale web=N [mode=scaling|manual] # scale dots by the given amount
|
57
|
+
ps:stop # stop all dots
|
58
|
+
ps:reload # restart all dots
|
59
|
+
HELP
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.method_missing(m, *args, &block)
|
63
|
+
unless self.respond_to? m
|
64
|
+
puts "Invalid command: #{m}"
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Appfront::Command::Logs < Appfront::Command::Base
|
2
|
+
def self.list(opts)
|
3
|
+
find_flow! opts
|
4
|
+
|
5
|
+
max_id = nil
|
6
|
+
|
7
|
+
begin
|
8
|
+
res = api.get "/flow/#{@flow}/logs?max_id=#{max_id}"
|
9
|
+
max_id, events = res['max_id'], res['events']
|
10
|
+
events.each {|e| puts e }
|
11
|
+
sleep 2
|
12
|
+
end while opts[:follow]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Appfront::Command::Providers < Appfront::Command::Base
|
2
|
+
|
3
|
+
def self.list
|
4
|
+
providers = api.get "/providers"
|
5
|
+
unless providers.count == 0
|
6
|
+
puts '=== Providers Connection Status:'
|
7
|
+
providers.each do |p|
|
8
|
+
puts "\t#{p['tier'].capitalize} ---> Connected"
|
9
|
+
end
|
10
|
+
else
|
11
|
+
puts '=== No Providers connected with this account'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.disconnect(args, opts)
|
16
|
+
provider = opts[:provider]
|
17
|
+
exit 1 unless provider == 'amazon' or provider == 'digitalocean'
|
18
|
+
|
19
|
+
spinner "Disconnecting #{provider.capitalize}... " do
|
20
|
+
api.delete "/provider/#{provider}"
|
21
|
+
end
|
22
|
+
puts "\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.connect(opts)
|
26
|
+
exit 1 unless opts[:provider] and opts[:access] and opts[:secret]
|
27
|
+
provider = opts[:provider]
|
28
|
+
spinner "Connecting your account with #{provider.capitalize}... " do
|
29
|
+
api.post "/provider/#{provider}", access_key: opts[:access], secret_key: opts[:secret]
|
30
|
+
end
|
31
|
+
puts "\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
class Appfront::Command::Ps < Appfront::Command::Base
|
2
|
+
|
3
|
+
def self.reload(args, opts)
|
4
|
+
find_flow! opts
|
5
|
+
api.put "/flow/#{@flow}/reload"
|
6
|
+
|
7
|
+
spinner "Reloading Flow..." do
|
8
|
+
loop do
|
9
|
+
sleep 3
|
10
|
+
info = api.get "/flow/#{@flow}"
|
11
|
+
break if info['status'] == 'running'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
puts "\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.force_restart(args, opts)
|
18
|
+
find_flow! opts
|
19
|
+
api.put "/flow/#{@flow}/force-restart"
|
20
|
+
|
21
|
+
spinner "Force restarting Flow..." do
|
22
|
+
loop do
|
23
|
+
sleep 3
|
24
|
+
info = api.get "/flow/#{@flow}"
|
25
|
+
break if info['status'] == 'running'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
puts "\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.stop(args, opts)
|
32
|
+
find_flow! opts
|
33
|
+
api.put "/flow/#{@flow}/stop"
|
34
|
+
|
35
|
+
spinner "Stopping Flow..." do
|
36
|
+
loop do
|
37
|
+
sleep 3
|
38
|
+
info = api.get "/flow/#{@flow}"
|
39
|
+
break if info['status'] == 'deactivated'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
puts "\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.run(args, opts)
|
46
|
+
find_flow! opts
|
47
|
+
api.put "/flow/#{@flow}/run"
|
48
|
+
|
49
|
+
spinner "Running Flow..." do
|
50
|
+
loop do
|
51
|
+
sleep 3
|
52
|
+
info = api.get "/flow/#{@flow}"
|
53
|
+
break if info['status'] == 'running'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
puts "\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.scale_up(opts)
|
60
|
+
find_flow! opts
|
61
|
+
|
62
|
+
spinner "Scaling up flow instances..." do
|
63
|
+
api.put "/flow/#{@flow}/scale/up"
|
64
|
+
end
|
65
|
+
puts "\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.scale_down(opts)
|
69
|
+
find_flow! opts
|
70
|
+
|
71
|
+
spinner "Scaling down flow instances..." do
|
72
|
+
api.put "/flow/#{@flow}/scale/down"
|
73
|
+
end
|
74
|
+
|
75
|
+
puts "\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.scale(settings, opts)
|
79
|
+
find_flow! opts
|
80
|
+
|
81
|
+
count, mode = nil
|
82
|
+
settings.each do |s|
|
83
|
+
p, v = s.split('=',2)
|
84
|
+
count = v if p == 'instances'
|
85
|
+
mode = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
unless count
|
89
|
+
puts 'Usage: appfront ps:scale instances=N'
|
90
|
+
exit 1
|
91
|
+
end
|
92
|
+
|
93
|
+
params[:scaling] = mode if mode
|
94
|
+
params[:instances] = count
|
95
|
+
|
96
|
+
api.put "/flows/#{@flow}/scaling", params
|
97
|
+
|
98
|
+
spinner "Scaling flow instances..." do
|
99
|
+
loop do
|
100
|
+
sleep 3
|
101
|
+
info = api.get "/flow/#{@flow}"
|
102
|
+
break if info['status'] == 'running'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
puts "Now running N instances"
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def self.time_ago(since)
|
112
|
+
if since.is_a?(String)
|
113
|
+
since = Time.parse(since)
|
114
|
+
end
|
115
|
+
|
116
|
+
elapsed = Time.now - since
|
117
|
+
|
118
|
+
message = since.strftime("%Y/%m/%d %H:%M:%S")
|
119
|
+
if elapsed <= 60
|
120
|
+
message << " (~ #{elapsed.floor}s ago)"
|
121
|
+
elsif elapsed <= (60 * 60)
|
122
|
+
message << " (~ #{(elapsed / 60).floor}m ago)"
|
123
|
+
elsif elapsed <= (60 * 60 * 25)
|
124
|
+
message << " (~ #{(elapsed / 60 / 60).floor}h ago)"
|
125
|
+
end
|
126
|
+
message
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class String
|
2
|
+
def constantize
|
3
|
+
names = self.split('::')
|
4
|
+
names.shift if names.empty? || names.first.empty?
|
5
|
+
|
6
|
+
constant = Object
|
7
|
+
names.each do |name|
|
8
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
9
|
+
end
|
10
|
+
constant
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: appfront
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Appfront
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: netrc
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.7
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.7.7
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rest-client
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.6.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.6.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: launchy
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.4.2
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.4.2
|
55
|
+
description: Appfront Command Line Tool
|
56
|
+
email: hello@appfront.io
|
57
|
+
executables:
|
58
|
+
- appfront
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- bin/appfront
|
63
|
+
- lib/appfront.rb
|
64
|
+
- lib/appfront/api.rb
|
65
|
+
- lib/appfront/cli.rb
|
66
|
+
- lib/appfront/command.rb
|
67
|
+
- lib/appfront/command/alias.rb
|
68
|
+
- lib/appfront/command/auth.rb
|
69
|
+
- lib/appfront/command/base.rb
|
70
|
+
- lib/appfront/command/clusters.rb
|
71
|
+
- lib/appfront/command/config.rb
|
72
|
+
- lib/appfront/command/flows.rb
|
73
|
+
- lib/appfront/command/help.rb
|
74
|
+
- lib/appfront/command/logs.rb
|
75
|
+
- lib/appfront/command/providers.rb
|
76
|
+
- lib/appfront/command/ps.rb
|
77
|
+
- lib/appfront/initializers/string.rb
|
78
|
+
homepage: https://appfront.io
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.0.3
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Appfront Command Line Tool
|
102
|
+
test_files: []
|