duostack 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -13,5 +13,5 @@ Jeweler::Tasks.new do |gem|
13
13
  gem.authors = "Todd Eichel"
14
14
  gem.require_paths = ['.'] # default is ["lib"] but we don't have that
15
15
 
16
- gem.executables = ["duostack", ".duostack-console-expect"]
16
+ gem.executables = ["duostack", ".duostack-expect", ".duostack-console-expect"]
17
17
  end
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- exec(File.join(File.dirname(__FILE__), 'bash', File.basename(__FILE__)) + ' ' + ARGV[0])
3
+ args_string = ARGV.collect { |a| "'#{a}'" }.join(' ')
4
+ exec(File.join(File.dirname(__FILE__), 'bash', File.basename(__FILE__)) + ' ' + args_string)
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ args_string = ARGV.collect { |a| "'#{a}'" }.join(' ')
4
+ exec(File.join(File.dirname(__FILE__), 'bash', File.basename(__FILE__)) + ' ' + args_string)
@@ -1,7 +1,18 @@
1
1
  #!/usr/bin/env expect
2
- set appname [lindex $argv 0]
3
2
 
4
- spawn -noecho ssh cli-console@duostack.net
5
- expect "Connecting to Ruby console for "
6
- send "$appname...\n"
7
- interact
3
+ set timeout 3600
4
+
5
+ set client [lindex $argv 0]
6
+ set type [lindex $argv 1]
7
+ set prompt [lindex $argv 2]
8
+ set command [lindex $argv 3]
9
+ set appname [lindex $argv 4]
10
+
11
+ spawn $client console $type --app $appname
12
+ expect $prompt
13
+ send "\r"
14
+ send -- "[read [open $command r]]"
15
+ send "\r"
16
+ expect $prompt
17
+ send "exit\r"
18
+ expect eof
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env expect
2
+ set target [lindex $argv 0]
3
+ set message [lindex $argv 1]
4
+ set response [lindex $argv 2]
5
+
6
+ spawn -noecho ssh $target
7
+ expect $message
8
+ send "$response...\n"
9
+ interact
data/bin/duostack CHANGED
@@ -28,13 +28,14 @@ $client = File.basename(__FILE__)
28
28
  module Duostack
29
29
  class Client
30
30
 
31
- VERSION = '0.3.0'
31
+ VERSION = '0.4.0'
32
32
  DEPENDENCIES_LAST_MODIFIED = 1298683372
33
33
  USER_AGENT = "duostack-#{VERSION}"
34
34
 
35
35
  DEFAULTS = {
36
36
  :credentials_location => '~/.duostack',
37
- :remote_name => 'duostack'
37
+ :remote_name => 'duostack',
38
+ :console_type => 'app'
38
39
  }
39
40
 
40
41
  FLAGS = [ # app and remote get special handling
@@ -53,6 +54,7 @@ module Duostack
53
54
  'billing_reset'
54
55
  ],
55
56
  :app => [ # commands requiring an app to be specified (either by git inference or flag)
57
+ 'info',
56
58
  'logs',
57
59
  'restart',
58
60
  'ps',
@@ -68,6 +70,7 @@ module Duostack
68
70
  :compound => [ # mult-part commands that expect subsequent arguments, must validate extra args on their own
69
71
  'help',
70
72
  'create',
73
+ 'console',
71
74
  'rake',
72
75
  'config',
73
76
  'env',
@@ -77,6 +80,28 @@ module Duostack
77
80
  ]
78
81
  }
79
82
 
83
+ CONSOLES = {
84
+ :app => {
85
+ :target => 'cli-console@duostack.net',
86
+ :display_type => 'Ruby',
87
+ :display_name_suffix => '',
88
+ :prompt => '>> '
89
+ },
90
+ :mysql => {
91
+ :target => 'cli-dbconsole@duostack.net',
92
+ :display_type => 'database',
93
+ :display_name_suffix => ' (mysql)',
94
+ :prompt => 'mysql> '
95
+ },
96
+ :mongodb => {
97
+ :target => 'cli-dbconsole@duostack.net',
98
+ :display_type => 'database',
99
+ :display_name_suffix => ' (mongodb)',
100
+ :prompt => '> '
101
+ }
102
+ }
103
+
104
+
80
105
  def initialize(args=[], client='duostack')
81
106
  @args = args
82
107
  @client = client
@@ -225,8 +250,15 @@ module Duostack
225
250
  puts "First-time Duostack client setup"
226
251
  print "Email: "
227
252
  username = $stdin.gets.chomp
228
- password = `bash -c 'read -sp "Password: " passwd; echo $passwd'`.chomp
229
- puts '' # clears the line after
253
+ print "Password: "
254
+ begin
255
+ # http://stackoverflow.com/questions/133719/how-to-read-a-password-in-ruby
256
+ system "stty -echo"
257
+ password = $stdin.gets.chomp
258
+ puts '' # clears the line after
259
+ ensure
260
+ system "stty echo" # this is important
261
+ end
230
262
 
231
263
  username = CGI::escape(username)
232
264
  password = CGI::escape(password)
@@ -574,6 +606,11 @@ module Duostack
574
606
  end
575
607
 
576
608
 
609
+ def info
610
+ puts api_get('get_info')
611
+ end
612
+
613
+
577
614
  def logs
578
615
  puts api_get('get_logs')
579
616
  end
@@ -609,12 +646,65 @@ module Duostack
609
646
  def console
610
647
  # TODO: just use ruby expect lib
611
648
 
612
- # first check for expect dependency
649
+ # get console and ensure it's valid
650
+ arg = (@args.shift || DEFAULTS[:console_type]).downcase
651
+ console = CONSOLES.fetch(arg.to_sym) do |invalid_type|
652
+ exit_with "invalid console type given ('#{invalid_type}'), try #{sentencize(CONSOLES.keys.collect { |k| k.to_s})}"
653
+ end
654
+
655
+ # take piped input first as command, fall back to using remaining command line args
656
+ if $stdin.tty?
657
+ console_command = @args.join(' ') unless @args.empty?
658
+ @args.clear
659
+ else
660
+ console_command = ''
661
+ $stdin.each_line { |line| console_command << line }
662
+ end
663
+
664
+ # we're finished processing args any remaning ones are invalid
665
+ unless @args.empty?
666
+ exit_with("unrecognized argument: '#{@args.first}', run '#{@client} help #{@command}' for usage")
667
+ end
668
+
669
+ # check for expect dependency
613
670
  if `which expect`.empty?
614
671
  exit_with "missing dependency, please install Expect (http://expect.sourceforge.net/)"
615
672
  end
616
673
 
617
- exec("#{$dir}/.duostack-console-expect #{@app_name}")
674
+ if console_command
675
+
676
+ # generate console command filename
677
+ timestamp = begin
678
+ t = Time.now
679
+ "#{t.to_i}.#{t.usec}"
680
+ end
681
+ console_command_file = File.join('/tmp', "ds-#{timestamp}.txt")
682
+
683
+ # write console command to file
684
+ file = File.new(console_command_file, 'w+')
685
+ file.chmod(0600)
686
+ file.write(console_command)
687
+ file.close
688
+
689
+ result = `#{$dir}/.duostack-console-expect '#{File.join($dir, $client)}' '#{arg}' '#{console[:prompt]}' '#{console_command_file}' '#{@app_name}'`
690
+
691
+ # clean up command file
692
+ File.delete(console_command_file)
693
+
694
+ # munge results
695
+ result = result.split("\r\n")
696
+ start = (result.index("#{console[:prompt]}") || 0) + 2
697
+ stop = (result.index("#{console[:prompt]}exit") || 0) - 1
698
+ result = result.slice(start..stop)
699
+
700
+ # clean up ruby return value marker
701
+ result[-1] = result[-1][3..-1] if arg == 'app'
702
+
703
+ puts result.join("\r\n")
704
+
705
+ else
706
+ exec("#{$dir}/.duostack-expect '#{console[:target]}' 'Connecting to #{console[:display_type]} console for ' '#{@app_name}#{console[:display_name_suffix]}'")
707
+ end
618
708
  end
619
709
 
620
710
 
@@ -789,10 +879,12 @@ module Duostack
789
879
  version Show version of this Duostack client
790
880
 
791
881
  App Commands:
882
+ info Show app summary information
792
883
  logs Retrieve server logs
793
884
  restart Restart instances
794
885
  ps List instances with current status
795
886
  destroy Destroy Duostack App and associated data
887
+ console [<type> [command]] Connect to app or database console, run command
796
888
  config [<name> [<setting>]] Show or set configuration options
797
889
  env [<operation>] Manage environment variables
798
890
  access [<operation>] Manage app collaborator access
@@ -800,7 +892,6 @@ module Duostack
800
892
  instances [<operation>] Manage app instance count
801
893
 
802
894
  App Commands - Ruby:
803
- console Connect to IRB/Rails console
804
895
  rake [<command>] Run a Rake command
805
896
 
806
897
  EOF
@@ -856,12 +947,22 @@ module Duostack
856
947
  EOF
857
948
  end
858
949
 
950
+ def info
951
+ <<-EOF
952
+
953
+ Usage: #{$client} info
954
+
955
+ Retrieves a summary of app data, e.g. Git remote URL, instance count, etc.
956
+
957
+ EOF
958
+ end
959
+
859
960
  def logs
860
961
  <<-EOF
861
962
 
862
963
  Usage: #{$client} logs
863
964
 
864
- Retreives aggregate logs from all of the app's instances.
965
+ Retrieves aggregate logs from all of the app's instances.
865
966
 
866
967
  EOF
867
968
  end
@@ -888,7 +989,7 @@ module Duostack
888
989
 
889
990
  Usage: #{$client} ps
890
991
 
891
- Retreives a listing of all of the app's instances with status information
992
+ Retrieves a listing of all of the app's instances with status information
892
993
  (uptime) for each.
893
994
 
894
995
  EOF
@@ -1039,7 +1140,7 @@ module Duostack
1039
1140
 
1040
1141
  Any increment, decrement, or set command that would result in an invalid
1041
1142
  instance count will be rejected with an error message. Instance counts must be
1042
- greater than 0 and not greater than 15.
1143
+ greater than 0.
1043
1144
 
1044
1145
  EOF
1045
1146
  end
@@ -1047,12 +1148,56 @@ module Duostack
1047
1148
  def console
1048
1149
  <<-EOF
1049
1150
 
1050
- Usage: #{$client} console
1151
+ Usage:
1152
+ #{$client} console [<type> [<command>]]
1153
+ launches console session of <type> (defaults to
1154
+ 'app' console), optionally runs specified
1155
+ <command> in console and prints the result
1156
+
1157
+ #{$client} console <type> <command> > <output-file>
1158
+ pipes the output from running <command> to the
1159
+ specified <output-file>
1160
+
1161
+ #{$client} console <type> < <input-file>
1162
+ pipes the contents of <input-file> into the
1163
+ specified console <type>, prints the result
1164
+
1165
+ Examples:
1166
+ #{$client} console same as running "#{$client} console app"
1167
+ #{$client} console app launches interactive app console (Ruby apps only)
1168
+ #{$client} console mysql launches interactive MySQL database console
1169
+ #{$client} console mongodb launches interactive MongoDB database console
1170
+
1171
+ #{$client} console app "puts('hello world')"
1172
+ #{$client} console mysql "SHOW TABLES;"
1173
+ runs the specified command and prints the result
1174
+
1175
+ #{$client} console mysql "SHOW TABLES;" > tables.txt
1176
+ runs "SHOW TABLES;" in the app's MySQL console,
1177
+ piping output into the file "tables.txt"
1178
+
1179
+ #{$client} console mysql < import.sql
1180
+ (EXPERIMENTAL) pipes the contents of "import.sql"
1181
+ into the app's MySQL console, printing the result
1182
+
1183
+ The "console" command can operate in three ways:
1184
+
1185
+ 1. Launching an interactive console session with your app or one of its
1186
+ databases. If no argument is given, it connects to the app console itself
1187
+ (applicable only to Ruby apps). If a database name is given, connects to a
1188
+ database console session for that database. Valid database names are: mysql,
1189
+ mongodb.
1190
+
1191
+ You can use then use these interactive console sessions to make any action you
1192
+ would normally make in your app or database console.
1051
1193
 
1052
- Launches an interactive IRB/console session with your app. You can use this to
1053
- make any action you would normally make in your app's console.
1194
+ 2. Running an ad-hoc command specified on the command line in the remaining
1195
+ arguments, and printing the result. You could also then pipe the output into an
1196
+ arbitrary file.
1054
1197
 
1055
- Applicable only to Ruby applications.
1198
+ 3. (EXPERIMENTAL) Sending the contents of a piped-in file to the specified (app
1199
+ or database) console. The resulting output will be collected and printed to your
1200
+ terminal.
1056
1201
 
1057
1202
  EOF
1058
1203
  end
@@ -1069,7 +1214,7 @@ module Duostack
1069
1214
  Passing of environment variables if your rake task requires them is supported
1070
1215
  (e.g. rake db:seed MODEL=Posts).
1071
1216
 
1072
- Applicable only to Ruby applications.
1217
+ Applicable only to Ruby apps.
1073
1218
 
1074
1219
  EOF
1075
1220
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duostack
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Todd Eichel
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-03 00:00:00 -05:00
18
+ date: 2011-03-20 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -23,6 +23,7 @@ description: "Duostack command line client: create and manage Duostack apps"
23
23
  email: todd@toddeichel.com
24
24
  executables:
25
25
  - duostack
26
+ - .duostack-expect
26
27
  - .duostack-console-expect
27
28
  extensions: []
28
29
 
@@ -31,7 +32,9 @@ extra_rdoc_files: []
31
32
  files:
32
33
  - Rakefile
33
34
  - bin/.duostack-console-expect
35
+ - bin/.duostack-expect
34
36
  - bin/bash/.duostack-console-expect
37
+ - bin/bash/.duostack-expect
35
38
  - bin/duostack
36
39
  - vendor/duostack-startcom.pem
37
40
  has_rdoc: true