appfront 1.0.1
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/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: []
|