viaduct-toolkit 0.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/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:
|