appfront 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'appfront/cli'
4
+
5
+ Appfront::CLI.run ARGV
data/lib/appfront.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Appfront
2
+ require 'appfront/cli'
3
+ end
@@ -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
@@ -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: []