viaduct-toolkit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/viaduct/toolkit.rb +76 -0
- data/lib/viaduct/toolkit/commands/app_console.rb +136 -0
- data/lib/viaduct/toolkit/commands/app_deployments.rb +25 -0
- data/lib/viaduct/toolkit/commands/app_env.rb +35 -0
- data/lib/viaduct/toolkit/commands/app_info.rb +42 -0
- data/lib/viaduct/toolkit/commands/app_list.rb +36 -0
- data/lib/viaduct/toolkit/commands/app_public_key.rb +12 -0
- data/lib/viaduct/toolkit/commands/app_status.rb +118 -0
- data/lib/viaduct/toolkit/commands/domain_list.rb +19 -0
- data/lib/viaduct/toolkit/commands/login.rb +70 -0
- data/lib/viaduct/toolkit/commands/ssh_key_add.rb +40 -0
- data/lib/viaduct/toolkit/commands/ssh_key_list.rb +26 -0
- data/lib/viaduct/toolkit/helpers.rb +111 -0
- data/lib/viaduct/toolkit/version.rb +5 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a7eceb02225b5d2bdba774374a4b0cc66ad28d43
|
4
|
+
data.tar.gz: 3af40448a600abcf1708c9e15d05bbdeed9d898f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bfd1069aada93499f1ede946a415bce09be8e1733e2b9acdabd29b632dc4c8748fb445daa78243e59b5c9c5aaf696e46fe49d8a1193b07924158bc8084eb247b
|
7
|
+
data.tar.gz: da954154572bede916028769b4dac835d2976c54fb13504281e2d0f35544948ff025c877567328bace915fdf8c6014d0f4fe520e91e3edac1e0bbafa177177cb
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'colorize'
|
3
|
+
require 'commander'
|
4
|
+
require 'viaduct/toolkit/version'
|
5
|
+
require 'viaduct/api'
|
6
|
+
|
7
|
+
# Hardcoded for this application.
|
8
|
+
Viaduct::API.application_token = '3148984b-8a50-424c-98f5-117e8dea2971'
|
9
|
+
|
10
|
+
# Add our helpers to Commander
|
11
|
+
require 'viaduct/toolkit/helpers'
|
12
|
+
Commander::Methods.send :include, Viaduct::Toolkit::Helpers
|
13
|
+
|
14
|
+
module Viaduct
|
15
|
+
module Toolkit
|
16
|
+
|
17
|
+
class Error < StandardError
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
def binary
|
23
|
+
File.expand_path(File.join('..', '..', '..', 'bin', 'viaduct'), __FILE__)
|
24
|
+
end
|
25
|
+
|
26
|
+
def cli
|
27
|
+
@cli ||= begin
|
28
|
+
c = Commander::Runner.instance
|
29
|
+
c.program :name, "Viaduct Toolkit"
|
30
|
+
c.program :version, Viaduct::Toolkit::VERSION
|
31
|
+
c.program :description, "A CLI toolkit for Viaduct developers"
|
32
|
+
c.global_option('-c', '--config FILE', 'The config file to store local credentials within') { |file| $config_file_path = file }
|
33
|
+
c.default_command :help
|
34
|
+
c
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_commands
|
39
|
+
Dir[File.expand_path(File.join('..', 'toolkit', 'commands', '*.rb'), __FILE__)].each do |file|
|
40
|
+
require file
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def api
|
45
|
+
@api ||= begin
|
46
|
+
Viaduct::API::Client.new(config['token'], config['secret'])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset_api
|
51
|
+
@api = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def config_file_path
|
55
|
+
$config_file_path || File.join(ENV['HOME'], '.viaduct')
|
56
|
+
end
|
57
|
+
|
58
|
+
def config
|
59
|
+
@config ||= begin
|
60
|
+
if File.exist?(config_file_path)
|
61
|
+
YAML.load_file(config_file_path)
|
62
|
+
else
|
63
|
+
{}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def save_config
|
69
|
+
File.open(config_file_path, 'w') do |f|
|
70
|
+
f.write self.config.to_yaml
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "app:console" do |c|
|
2
|
+
|
3
|
+
c.syntax = "app:console SUBDOMAIN_OF_APP"
|
4
|
+
c.description = "Start a new SSH-based console session"
|
5
|
+
c.option "--disable", "Disable access to the SSH console"
|
6
|
+
c.option "--enable", "Enable access to the SSH console"
|
7
|
+
c.option "--status", "Display the status of the port forward"
|
8
|
+
|
9
|
+
c.action do |args, opts|
|
10
|
+
include Commander::Methods
|
11
|
+
ensure_logged_in!
|
12
|
+
if app = find_application(args[0])
|
13
|
+
|
14
|
+
# Check the user's SSH keys
|
15
|
+
response = Viaduct::Toolkit.api.ssh_keys.all
|
16
|
+
if response.success?
|
17
|
+
|
18
|
+
if response.data.empty?
|
19
|
+
puts "You haven't uploaded any SSH keys to your Viaduct user account.".red
|
20
|
+
puts "You cannot use SSH console access without them.".red
|
21
|
+
puts
|
22
|
+
puts "Upload your key using the command below".blue
|
23
|
+
puts
|
24
|
+
puts " $ vdt ssh_keys:add".blue
|
25
|
+
puts
|
26
|
+
exit(1)
|
27
|
+
else
|
28
|
+
stdout, stderr, status = run("ssh-add -l")
|
29
|
+
if status == 0
|
30
|
+
remote_fingerprints = response.data.map { |d| d['fingerprint'] }
|
31
|
+
local_fingerprints = stdout.split(/\n/).map { |l| l.split(/\s+/)[1] }
|
32
|
+
unless remote_fingerprints.any? { |f| local_fingerprints.include?(f) }
|
33
|
+
puts "Warning: it doesn't seem as though your SSH key has been uploaded".yellow
|
34
|
+
puts "to your Viaduct account. This session may not succeed. If it doesn't".yellow
|
35
|
+
puts "ensure that you have uploaded to your SSH key to your Viaduct account.".yellow
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
else
|
41
|
+
error "Couldn't verify remote SSH keys. Please try again."
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the console port forward for the application
|
45
|
+
response = Viaduct::Toolkit.api.port_forwards.all(:application => app['subdomain'])
|
46
|
+
if response.success?
|
47
|
+
unless console = response.data.select { |c| c['mode'] == 'console'}.first
|
48
|
+
error "Console access is not supported by this application. Please contact support."
|
49
|
+
end
|
50
|
+
else
|
51
|
+
error "Couldn't get port forward information from API for application."
|
52
|
+
end
|
53
|
+
|
54
|
+
if opts.status
|
55
|
+
details do
|
56
|
+
heading "SSH console access"
|
57
|
+
field "Status", console['enabled'] ? "Enabled" : "Disabled"
|
58
|
+
field "Auto disable?", console['auto_disable_at'] ? time(console['auto_disable_at']) : ''
|
59
|
+
field "Connection", "#{console['ip_address']}:#{console['port']}"
|
60
|
+
console['allowed_ips'].each_with_index do |ip, i|
|
61
|
+
field i == 0 ? 'Allowed IPs' : '', ip
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
elsif opts.disable
|
66
|
+
|
67
|
+
#
|
68
|
+
# Just disable console access
|
69
|
+
#
|
70
|
+
|
71
|
+
if console['enabled']
|
72
|
+
response = Viaduct::Toolkit.api.port_forwards.save(:id => console['id'], :enabled => 0)
|
73
|
+
if response.success?
|
74
|
+
puts "Console access has been disabled.".green
|
75
|
+
else
|
76
|
+
error "We couldn't disable console access at this time. Please try later."
|
77
|
+
end
|
78
|
+
else
|
79
|
+
puts "Console access isn't currently enabled.".yellow
|
80
|
+
end
|
81
|
+
|
82
|
+
elsif opts.enable
|
83
|
+
|
84
|
+
#
|
85
|
+
# Just enable console access
|
86
|
+
#
|
87
|
+
|
88
|
+
if console['enabled']
|
89
|
+
puts "Console is already enabled.".yellow
|
90
|
+
else
|
91
|
+
auto_disable = agree("Would you like to disable this again after an hour?".blue)
|
92
|
+
response = Viaduct::Toolkit.api.port_forwards.save(:id => console['id'], :enabled => 1, :auto_disable_at => (auto_disable ? '1 hour from now' : nil))
|
93
|
+
if response.success?
|
94
|
+
if auto_disable
|
95
|
+
puts "Console access has been enaled and will be automatically disabled again in 1 hour.".green
|
96
|
+
else
|
97
|
+
puts "Console access has been enabled.".green
|
98
|
+
end
|
99
|
+
else
|
100
|
+
error "We couldn't enable console access at this time. Please try later."
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
else
|
105
|
+
|
106
|
+
#
|
107
|
+
# Enable if needed and connect.
|
108
|
+
#
|
109
|
+
unless console['enabled']
|
110
|
+
puts "SSH Console access is not currently enabled for this application.".magenta
|
111
|
+
if enable = agree("Would you like to enable it now?".blue)
|
112
|
+
disable_after_hour = agree("Would you like to disable this again after an hour?".blue)
|
113
|
+
response = Viaduct::Toolkit.api.port_forwards.save(:id => console['id'], :enabled => 1, :auto_disable_at => (disable_after_hour ? "1 hour from now" : nil))
|
114
|
+
unless response.success?
|
115
|
+
puts response.inspect
|
116
|
+
error "We couldn't enable console access at this time. Please try later."
|
117
|
+
end
|
118
|
+
else
|
119
|
+
puts "Unfortunately, unless enabled you cannot use this command.".red
|
120
|
+
exit(1)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
command = "ssh vdt@#{console['ip_address']} -p #{console['port']}"
|
125
|
+
|
126
|
+
exec(command)
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
Viaduct::Toolkit.cli.alias_command "app:ssh", "app:console"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "app:deployments" do |c|
|
2
|
+
|
3
|
+
c.syntax = "app:deployments SUBDOMAIN_OF_APP"
|
4
|
+
c.description = "List all deployments for an application"
|
5
|
+
c.option "--page PAGE", Integer, "The page of deployments to return"
|
6
|
+
|
7
|
+
c.action do |args, opts|
|
8
|
+
include Commander::Methods
|
9
|
+
ensure_logged_in!
|
10
|
+
if application = find_application(args[0])
|
11
|
+
response = Viaduct::Toolkit.api.applications.deployments(:application => application['subdomain'], :page => opts.page ? opts.page.to_i : 1)
|
12
|
+
if response.success?
|
13
|
+
|
14
|
+
require 'terminal-table'
|
15
|
+
rows = response.data.map do |d|
|
16
|
+
[d['number'], d['status'], d['version']['description'].to_s[0,30], d['user'] ? d['user']['username'] : '', d['timing']['created_at']]
|
17
|
+
end
|
18
|
+
puts Terminal::Table.new :rows => rows, :headings => ['#', 'Status', 'Description', 'User', 'Time']
|
19
|
+
|
20
|
+
else
|
21
|
+
error "Couldn't get application deployment list"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "app:env" do |c|
|
2
|
+
|
3
|
+
c.syntax = "app:env SUBDOMAIN_OF_APP"
|
4
|
+
c.description = "Show all environment variables for an application"
|
5
|
+
c.option "--simple", "Return environment variables "
|
6
|
+
c.option "--yaml", "Return environment variables as YAML file"
|
7
|
+
c.option "--export", "Return environment variables as export lines"
|
8
|
+
c.action do |args, opts|
|
9
|
+
include Commander::Methods
|
10
|
+
ensure_logged_in!
|
11
|
+
if application = find_application(args[0])
|
12
|
+
response = Viaduct::Toolkit.api.applications.environment_variables(:application => application['subdomain'])
|
13
|
+
if response.success?
|
14
|
+
if opts.simple
|
15
|
+
response.data.each do |key, value|
|
16
|
+
puts "#{key}: #{value}"
|
17
|
+
end
|
18
|
+
elsif opts.yaml
|
19
|
+
require 'yaml'
|
20
|
+
puts response.data.to_yaml
|
21
|
+
elsif opts.export
|
22
|
+
response.data.each do |key, value|
|
23
|
+
puts "export #{key}=\"#{value}\""
|
24
|
+
end
|
25
|
+
else
|
26
|
+
require 'terminal-table'
|
27
|
+
puts Terminal::Table.new :rows => response.data.to_a
|
28
|
+
end
|
29
|
+
else
|
30
|
+
error "Couldn't get environment variables"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "app:info" do |c|
|
2
|
+
|
3
|
+
c.syntax = "app:info SUBDOMAIN_OF_APP"
|
4
|
+
c.description = "Show details of an existing application"
|
5
|
+
c.action do |args, opts|
|
6
|
+
include Commander::Methods
|
7
|
+
ensure_logged_in!
|
8
|
+
if application = find_application(args[0])
|
9
|
+
details do
|
10
|
+
heading "Application Details"
|
11
|
+
field "Name", application['name']
|
12
|
+
field "Viaduct Domain", application['viaduct_domain']
|
13
|
+
field "Status", application['status']
|
14
|
+
field "Maintenance", application['maintenance?'] ? 'Yes' : 'No'
|
15
|
+
field 'Owner', "#{application['user']['name']} (#{application['user']['username']})"
|
16
|
+
field "Zone", application['zone']['name']
|
17
|
+
field "Platform", "#{application['platform']['name']} (on #{application['platform']['stack']['name']} stack)"
|
18
|
+
field "Subnet(s)", application['subnets'].map { |s| s['description'] }.join(', ')
|
19
|
+
if application['repository'] && application['source_backend_module'] == 'Viaduct::SourceBackends::Repository'
|
20
|
+
heading "Repository"
|
21
|
+
field 'URL', application['repository']['repository']['url']
|
22
|
+
field 'Status', application['repository']['repository']['status']
|
23
|
+
field 'Last update', application['repository']['repository']['last_updated_at']
|
24
|
+
field 'Username', application['repository']['repository']['username']
|
25
|
+
field 'Branch', application['repository']['branch']
|
26
|
+
end
|
27
|
+
|
28
|
+
if application['deployment']
|
29
|
+
heading "Active Deployment"
|
30
|
+
field 'Number', application['deployment']['number']
|
31
|
+
field 'Started at', application['deployment']['timing']['created_at']
|
32
|
+
field 'Time', application['deployment']['timing']['time'].round(1).to_s + "s"
|
33
|
+
field 'Description', application['deployment']['version']['description']
|
34
|
+
if application['deployment']['user']
|
35
|
+
field 'Deployer', "#{application['deployment']['user']['name']} (#{application['deployment']['user']['username']})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "app:list" do |c|
|
2
|
+
c.syntax = "app:list"
|
3
|
+
c.description = "Show a list of all applications you have access to"
|
4
|
+
c.option "--simple", "Return just a list of subdomains"
|
5
|
+
c.action do |args, opts|
|
6
|
+
include Commander::Methods
|
7
|
+
ensure_logged_in!
|
8
|
+
|
9
|
+
pages_seen = 0
|
10
|
+
applications = []
|
11
|
+
loop do
|
12
|
+
response = Viaduct::Toolkit.api.applications.all(:page => pages_seen + 1)
|
13
|
+
if response.success?
|
14
|
+
applications = applications | response.data
|
15
|
+
else
|
16
|
+
error "Couldn't get list of applications."
|
17
|
+
end
|
18
|
+
pages_seen += 1
|
19
|
+
if pages_seen == response.flags['paginated']['page']
|
20
|
+
break
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if opts.simple
|
25
|
+
applications.each do |application|
|
26
|
+
puts application['subdomain']
|
27
|
+
end
|
28
|
+
else
|
29
|
+
rows = applications.map do |app|
|
30
|
+
[app['name'], app['subdomain'], app['status'], app['user']['name']]
|
31
|
+
end
|
32
|
+
table ['Name', 'Subdomain', 'Status', 'Owner'], rows
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "app:public_key" do |c|
|
2
|
+
c.syntax = "app:public_key NAME_OF_APP"
|
3
|
+
c.description = "Return the public key for a given application"
|
4
|
+
c.action do |args, opts|
|
5
|
+
include Commander::Methods
|
6
|
+
ensure_logged_in!
|
7
|
+
if app = find_application(args[0])
|
8
|
+
puts app['public_key'] + " Viaduct key for #{app['subdomain']}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
Viaduct::Toolkit.cli.alias_command "app:pk", "app:public_key"
|
@@ -0,0 +1,118 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "app:status" do |c|
|
2
|
+
c.syntax = "app:status NAME_OF_APP"
|
3
|
+
c.description = "Return current status of an application"
|
4
|
+
c.option "--process PROCESS_TYPE", String, "A process to show expanded information for"
|
5
|
+
c.option "--database DATABASE_ID", String, "The ID of the database to display further information for"
|
6
|
+
|
7
|
+
c.action do |args, opts|
|
8
|
+
include Commander::Methods
|
9
|
+
ensure_logged_in!
|
10
|
+
if app = find_application(args[0])
|
11
|
+
response = Viaduct::Toolkit.api.applications.status(:application => app['subdomain'])
|
12
|
+
if response.success?
|
13
|
+
|
14
|
+
if opts.process
|
15
|
+
|
16
|
+
if process = response.data['processes'].select { |p| p['label'] == opts.process.to_s }.first
|
17
|
+
details do
|
18
|
+
heading "Details"
|
19
|
+
field "Label", process['label']
|
20
|
+
field "Command", process['container']['command']
|
21
|
+
field "Public?", process['container']['public'] ? 'Yes' : 'No'
|
22
|
+
field "Status", process['process']['status'].capitalize
|
23
|
+
process_details(process['process'])
|
24
|
+
end
|
25
|
+
else
|
26
|
+
puts "No process found matching '#{opts.process}'".red
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
elsif opts.database
|
32
|
+
|
33
|
+
if db = response.data['databases'][(opts.database.to_i - 1).abs]
|
34
|
+
details do
|
35
|
+
heading "Database Details"
|
36
|
+
field "Label", db['label']
|
37
|
+
field "Database", db['stack']['name']
|
38
|
+
process_details(db['process'])
|
39
|
+
end
|
40
|
+
else
|
41
|
+
puts "No database found with ID '#{opts.database}'".red
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
|
45
|
+
else
|
46
|
+
|
47
|
+
puts "Deployment Details".yellow
|
48
|
+
details do
|
49
|
+
puts "+" + '-' * 78 + '+'
|
50
|
+
field "Number", response.data['deployment']['number']
|
51
|
+
field 'Version', response.data['deployment']['version']['id']
|
52
|
+
field "Description", response.data['deployment']['version']['description']
|
53
|
+
field "Deployer", response.data['deployment']['user'] ? response.data['deployment']['user']['full_name'] : ''
|
54
|
+
field "Started", time(response.data['deployment']['timing']['started_at'])
|
55
|
+
end
|
56
|
+
|
57
|
+
processes = response.data['processes'].map do |p|
|
58
|
+
memory_used = (p['process']['resources']['memory_usage'] / 1024 / 1024).to_s + "MB"
|
59
|
+
memory_available = (p['process']['resources']['max_memory'] / 1024 / 1024).to_s + "MB"
|
60
|
+
memory = "#{memory_used}/#{memory_available}"
|
61
|
+
|
62
|
+
respawns = "#{p['process']['respawning']['current']}/#{p['process']['respawning']['maximum']}"
|
63
|
+
[p['label'], p['process']['status'].capitalize, memory, p['container']['command'], respawns]
|
64
|
+
end
|
65
|
+
puts
|
66
|
+
puts "Processes".yellow
|
67
|
+
table ['Label', 'Status', 'Memory', 'Command', 'Respawns'], processes
|
68
|
+
|
69
|
+
count = 0
|
70
|
+
databases = response.data['databases'].map do |d|
|
71
|
+
count += 1
|
72
|
+
memory_used = (d['process']['resources']['memory_usage'] / 1024 / 1024).to_s + "MB"
|
73
|
+
memory_available = (d['process']['resources']['max_memory'] / 1024 / 1024).to_s + "MB"
|
74
|
+
memory = "#{memory_used}/#{memory_available}"
|
75
|
+
respawns = "#{d['process']['respawning']['current']}/#{d['process']['respawning']['maximum']}"
|
76
|
+
|
77
|
+
[count, d['label'], d['process']['status'].capitalize, d['stack']['name'], memory, respawns, d['process']['networking']['ip_address']]
|
78
|
+
end
|
79
|
+
unless databases.empty?
|
80
|
+
puts
|
81
|
+
puts "Dedicated Databases".yellow
|
82
|
+
table ['#', 'Label', 'Status', 'Type', 'Memory', 'Respawns', 'IP Address'], databases
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
else
|
87
|
+
error "Couldn't get application status"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def process_details(p)
|
94
|
+
if p['host'] && p['lxc_name']
|
95
|
+
heading "Host (admin only)"
|
96
|
+
field "Host", p['host']
|
97
|
+
field "LXC Name", p['lxc_name']
|
98
|
+
end
|
99
|
+
|
100
|
+
heading "Networking"
|
101
|
+
field "IP Address", p['networking']['ip_address']
|
102
|
+
field "MAC Address", p['networking']['mac_address']
|
103
|
+
field "Data", "RX: #{p['networking']['rx']} TX: #{p['networking']['tx']}"
|
104
|
+
|
105
|
+
heading "Timing"
|
106
|
+
field "Started", time(p['timing']['started'])
|
107
|
+
field "Run time", length_of_time(p['timing']['run_time'])
|
108
|
+
field "Last seen", time(p['timing']['last_seen_at'])
|
109
|
+
|
110
|
+
heading "Resources"
|
111
|
+
field "Memory", "#{p['resources']['memory_usage'] /1024/1024}MB of #{p['resources']['max_memory']/1024/1024}MB"
|
112
|
+
field "CPU Usage", p['resources']['cpu_usage']
|
113
|
+
|
114
|
+
heading "Respawning"
|
115
|
+
field "Respawns", "#{p['respawning']['current']} / #{p['respawning']['maximum']}"
|
116
|
+
field "Last respawn", time(p['respawning']['last'])
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "domain:list" do |c|
|
2
|
+
c.syntax = "domain:list NAME_OF_APP"
|
3
|
+
c.description = "Return a list of domains for an application"
|
4
|
+
c.action do |args, opts|
|
5
|
+
include Commander::Methods
|
6
|
+
ensure_logged_in!
|
7
|
+
if app = find_application(args[0])
|
8
|
+
response = Viaduct::Toolkit.api.domains.all(:application => app['subdomain'])
|
9
|
+
if response.success?
|
10
|
+
rows = response.data.map do |d|
|
11
|
+
[d['name'], boolean(d['verification']['verified?']), d['ssl'], d['routing']['cname'], d['routing']['ip']]
|
12
|
+
end
|
13
|
+
table ['Name', 'Verified', 'SSL', 'CNAME', 'IP'], rows
|
14
|
+
else
|
15
|
+
error "Couldn't get domain list"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "login" do |c|
|
2
|
+
c.syntax = "login"
|
3
|
+
c.description = "Authenticate this computer with your Viaduct account"
|
4
|
+
c.action do |args, options|
|
5
|
+
|
6
|
+
Viaduct::Toolkit.config['token'] = nil
|
7
|
+
Viaduct::Toolkit.config['secret'] = nil
|
8
|
+
Viaduct::Toolkit.save_config
|
9
|
+
|
10
|
+
response = Viaduct::Toolkit.api.authentication.create_login_token
|
11
|
+
if response.success?
|
12
|
+
|
13
|
+
puts "To log you in we need to open a browser window to allow".magenta
|
14
|
+
puts "you to enter your login details. ".magenta
|
15
|
+
puts
|
16
|
+
if agree("Shall we open this for you?".blue)
|
17
|
+
system("open", response.data['url'])
|
18
|
+
else
|
19
|
+
puts
|
20
|
+
puts "That's fine. Just go to the URL below in your browser.".magenta
|
21
|
+
puts "This command will continue to run until you complete this".magenta
|
22
|
+
puts "action.".magenta
|
23
|
+
puts
|
24
|
+
puts response.data['url']
|
25
|
+
end
|
26
|
+
|
27
|
+
puts
|
28
|
+
puts "Please wait while we verify your login...".magenta
|
29
|
+
puts
|
30
|
+
check_response = nil
|
31
|
+
100.times do
|
32
|
+
sleep 3
|
33
|
+
check_response = Viaduct::Toolkit.api.authentication.exchange(:token => response.data['token'])
|
34
|
+
if check_response.success?
|
35
|
+
if check_response.data['status'] == 'approved'
|
36
|
+
Viaduct::Toolkit.reset_api
|
37
|
+
@authorised = true
|
38
|
+
break
|
39
|
+
elsif check_response.data['status'] == 'denied'
|
40
|
+
error "The login request was rejected. Ensure that you approve the login request."
|
41
|
+
end
|
42
|
+
else
|
43
|
+
error "Couldn't successfully exchange login token for an API token. Please try again later."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if @authorised
|
48
|
+
Viaduct::Toolkit.config['token'] = check_response.data['token']['token']
|
49
|
+
Viaduct::Toolkit.config['secret'] = check_response.data['token']['secret']
|
50
|
+
|
51
|
+
user_check = Viaduct::Toolkit.api.user.details
|
52
|
+
if user_check.success?
|
53
|
+
Viaduct::Toolkit.save_config
|
54
|
+
puts "Hello #{user_check.data['name']}!".green
|
55
|
+
puts "Your user account is now authorised. Your login details are".magenta
|
56
|
+
puts "stored in a .viaduct file in your home directory.".magenta
|
57
|
+
puts
|
58
|
+
else
|
59
|
+
error "We couldn't verify your user details. Please try again."
|
60
|
+
end
|
61
|
+
else
|
62
|
+
error "We didn't receive a login response in a timely manner. Please try again."
|
63
|
+
end
|
64
|
+
|
65
|
+
else
|
66
|
+
error "Couldn't generate a remote login token. Please try again."
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "ssh_key:add" do |c|
|
2
|
+
|
3
|
+
c.syntax = "ssh_keys:add PATH_TO_PUBLIC_KEY"
|
4
|
+
c.description = "Add a new SSH public key to your user account"
|
5
|
+
|
6
|
+
c.action do |args, opts|
|
7
|
+
include Commander::Methods
|
8
|
+
ensure_logged_in!
|
9
|
+
|
10
|
+
key_path = args[0]
|
11
|
+
|
12
|
+
default_key_path = File.join(ENV['HOME'], '.ssh', 'id_rsa.pub')
|
13
|
+
if key_path.nil? && File.exist?(default_key_path)
|
14
|
+
puts "You haven't provided an SSH key to this command.".yellow
|
15
|
+
if agree("Shall we use your default key at #{default_key_path}?".blue)
|
16
|
+
key_path = default_key_path
|
17
|
+
else
|
18
|
+
exit(1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if File.exist?(key_path)
|
23
|
+
response = Viaduct::Toolkit.api.ssh_keys.add(:label => "Added from #{`hostname`}", :key => File.read(key_path))
|
24
|
+
if response.success?
|
25
|
+
puts "Key successfully added to your account".green
|
26
|
+
else
|
27
|
+
if response.is_a?(MoonropeClient::Responses::ValidationError)
|
28
|
+
puts "Errors occurred while adding your key:".red
|
29
|
+
validation_errors response.data['errors']
|
30
|
+
else
|
31
|
+
error "Couldn't add your key"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
else
|
35
|
+
puts "No public key found at '#{key_path}'".red
|
36
|
+
exit(1)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Viaduct::Toolkit.cli.command "ssh_key:list" do |c|
|
2
|
+
|
3
|
+
c.syntax = "ssh_key:list"
|
4
|
+
c.description = "List the keys associated with your account"
|
5
|
+
|
6
|
+
c.action do |args, opts|
|
7
|
+
include Commander::Methods
|
8
|
+
ensure_logged_in!
|
9
|
+
|
10
|
+
response = Viaduct::Toolkit.api.ssh_keys.all
|
11
|
+
if response.success?
|
12
|
+
require 'terminal-table'
|
13
|
+
rows = response.data.map do |app|
|
14
|
+
[app['label'], app['fingerprint']]
|
15
|
+
end
|
16
|
+
table = Terminal::Table.new :rows => rows, :headings => ['Label', 'Fingerprint']
|
17
|
+
puts table
|
18
|
+
|
19
|
+
else
|
20
|
+
error "Couldn't get the SSH key list."
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Viaduct
|
4
|
+
module Toolkit
|
5
|
+
module Helpers
|
6
|
+
def ensure_logged_in!
|
7
|
+
if Viaduct::Toolkit.config['token'].nil? && Viaduct::Toolkit.config['secret'].nil?
|
8
|
+
puts "You need to login before using this toolkit. Use the command below".yellow
|
9
|
+
puts "to login to your Viaduct account.".yellow
|
10
|
+
puts
|
11
|
+
puts " $ vdt login"
|
12
|
+
puts
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def length_of_time(seconds)
|
18
|
+
"#{seconds} seconds"
|
19
|
+
end
|
20
|
+
|
21
|
+
def time(time)
|
22
|
+
require 'time'
|
23
|
+
time = Time.parse(time) rescue nil
|
24
|
+
time ? time.strftime("%d %B %Y at %H:%M:%S UTC") : ''
|
25
|
+
end
|
26
|
+
|
27
|
+
def boolean(bool)
|
28
|
+
bool ? "\u2713".green : "-".red
|
29
|
+
end
|
30
|
+
|
31
|
+
def table(headings, rows)
|
32
|
+
require 'terminal-table'
|
33
|
+
puts Terminal::Table.new :rows => rows, :headings => headings.map(&:blue)
|
34
|
+
end
|
35
|
+
|
36
|
+
def validation_errors(errors)
|
37
|
+
errors.each do |field, messages|
|
38
|
+
messages.each do |message|
|
39
|
+
puts " * #{field} #{message}".red
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def error(message)
|
45
|
+
raise Viaduct::Toolkit::Error, message
|
46
|
+
end
|
47
|
+
|
48
|
+
def run(*commands)
|
49
|
+
stdin, stdout, stderr, w = Open3.popen3(*commands)
|
50
|
+
[stdout.gets(nil), stderr.gets(nil), w.value]
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_application(subdomain)
|
54
|
+
if subdomain.is_a?(String) && subdomain.length > 0
|
55
|
+
app = Viaduct::Toolkit.api.applications.info(:application => subdomain)
|
56
|
+
if app.success?
|
57
|
+
return app.data
|
58
|
+
else
|
59
|
+
puts "Couldn't find application with subdomain matching '#{subdomain}'".red
|
60
|
+
exit(1)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
# Look up from repo
|
64
|
+
out, err, status = run("git", "remote", "-v")
|
65
|
+
if status == 0
|
66
|
+
potential_repositories = out.split("\n").map { |l| l.split(/\s+/)[1] }.uniq
|
67
|
+
app = Viaduct::Toolkit.api.applications.all(:filter => {:repo => potential_repositories})
|
68
|
+
if app.success?
|
69
|
+
if app.data.empty?
|
70
|
+
puts "No Viaduct applications found for any of the following repositories:".red
|
71
|
+
potential_repositories.each do |repo|
|
72
|
+
puts " * #{repo}".red
|
73
|
+
end
|
74
|
+
exit(1)
|
75
|
+
elsif app.data.size == 1
|
76
|
+
return find_application(app.data.first['subdomain'])
|
77
|
+
else
|
78
|
+
puts "Multiple applications found matching your repository. Choose an application...".yellow
|
79
|
+
choice = choose('', *app.data.map { |a| "#{a['subdomain']}: #{a['name']}"})
|
80
|
+
choice = choice.split(":", 2).first
|
81
|
+
return find_application(choice)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
puts "Couldn't determine a Viaduct application from command.".red
|
88
|
+
exit(1)
|
89
|
+
end
|
90
|
+
|
91
|
+
def heading(title)
|
92
|
+
puts "+" + ("-" * 78) + "+"
|
93
|
+
puts "| #{title.ljust(76).yellow} |"
|
94
|
+
puts "+" + ("-" * 78) + "+"
|
95
|
+
end
|
96
|
+
|
97
|
+
def field(key, value)
|
98
|
+
key = key[0,16].ljust(16, ' ')
|
99
|
+
value = value.to_s[0,58].ljust(58)
|
100
|
+
|
101
|
+
puts "| #{key.blue}| #{value} |"
|
102
|
+
end
|
103
|
+
|
104
|
+
def details(&block)
|
105
|
+
block.call
|
106
|
+
puts "+" + ("-" * 78) + "+"
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: viaduct-toolkit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Cooke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: commander
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: viaduct-api
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: colorize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.7'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: terminal-table
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.4'
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.4.5
|
65
|
+
type: :runtime
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - "~>"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '1.4'
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.4.5
|
75
|
+
description: A set of useful tools to help developers use & manage their Viaduct applications.
|
76
|
+
email:
|
77
|
+
- adam@viaduct.io
|
78
|
+
executables: []
|
79
|
+
extensions: []
|
80
|
+
extra_rdoc_files: []
|
81
|
+
files:
|
82
|
+
- lib/viaduct/toolkit.rb
|
83
|
+
- lib/viaduct/toolkit/commands/app_console.rb
|
84
|
+
- lib/viaduct/toolkit/commands/app_deployments.rb
|
85
|
+
- lib/viaduct/toolkit/commands/app_env.rb
|
86
|
+
- lib/viaduct/toolkit/commands/app_info.rb
|
87
|
+
- lib/viaduct/toolkit/commands/app_list.rb
|
88
|
+
- lib/viaduct/toolkit/commands/app_public_key.rb
|
89
|
+
- lib/viaduct/toolkit/commands/app_status.rb
|
90
|
+
- lib/viaduct/toolkit/commands/domain_list.rb
|
91
|
+
- lib/viaduct/toolkit/commands/login.rb
|
92
|
+
- lib/viaduct/toolkit/commands/ssh_key_add.rb
|
93
|
+
- lib/viaduct/toolkit/commands/ssh_key_list.rb
|
94
|
+
- lib/viaduct/toolkit/helpers.rb
|
95
|
+
- lib/viaduct/toolkit/version.rb
|
96
|
+
homepage: http://viaduct.io
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 2.2.2
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: A developer toolkit for working with Viaduct from an CLI
|
120
|
+
test_files: []
|
121
|
+
has_rdoc:
|