Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: tfe/duostack-client
base: master
...
head fork: duostack/duostack-client
compare: master
Checking mergeability… Don’t worry, you can still create the pull request.
  • 5 commits
  • 14 files changed
  • 0 commit comments
  • 1 contributor
View
1  Rakefile
@@ -65,6 +65,7 @@ namespace :package do
files = [
'package/package.json',
'package/scripts/ruby-check.sh',
+ 'package/bin/.duostack-expect',
'package/bin/.duostack-console-expect',
'package/bin/.duostack-startcom.pem',
'package/bin/duostack'
View
21 src/.duostack-console-expect
@@ -1,7 +1,18 @@
#!/usr/bin/env expect
-set appname [lindex $argv 0]
-spawn -noecho ssh cli-console@duostack.net
-expect "Connecting to Ruby console for "
-send "$appname...\n"
-interact
+set timeout 3600
+
+set client [lindex $argv 0]
+set type [lindex $argv 1]
+set prompt [lindex $argv 2]
+set command [lindex $argv 3]
+set appname [lindex $argv 4]
+
+spawn $client console $type --app $appname
+expect $prompt
+send "\r"
+send -- "[read [open $command r]]"
+send "\r"
+expect $prompt
+send "exit\r"
+expect eof
View
9 src/.duostack-expect
@@ -0,0 +1,9 @@
+#!/usr/bin/env expect
+set target [lindex $argv 0]
+set message [lindex $argv 1]
+set response [lindex $argv 2]
+
+spawn -noecho ssh $target
+expect $message
+send "$response...\n"
+interact
View
154 src/duostack
@@ -28,13 +28,14 @@ $client = File.basename(__FILE__)
module Duostack
class Client
- VERSION = '0.3.1'
+ VERSION = '0.5.0'
DEPENDENCIES_LAST_MODIFIED = 1298683372
USER_AGENT = "duostack-#{VERSION}"
DEFAULTS = {
:credentials_location => '~/.duostack',
- :remote_name => 'duostack'
+ :remote_name => 'duostack',
+ :console_type => 'app'
}
FLAGS = [ # app and remote get special handling
@@ -69,6 +70,7 @@ module Duostack
:compound => [ # mult-part commands that expect subsequent arguments, must validate extra args on their own
'help',
'create',
+ 'console',
'rake',
'config',
'env',
@@ -78,6 +80,34 @@ module Duostack
]
}
+ CONSOLES = {
+ :app => {
+ :target => 'cli-console@duostack.net',
+ :display_type => 'Ruby',
+ :display_name_suffix => '',
+ :prompt => '>> '
+ },
+ :mysql => {
+ :target => 'cli-dbconsole@duostack.net',
+ :display_type => 'database',
+ :display_name_suffix => ' (mysql)',
+ :prompt => 'mysql> '
+ },
+ :mongodb => {
+ :target => 'cli-dbconsole@duostack.net',
+ :display_type => 'database',
+ :display_name_suffix => ' (mongodb)',
+ :prompt => '> '
+ },
+ :redis => {
+ :target => 'cli-dbconsole@duostack.net',
+ :display_type => 'database',
+ :display_name_suffix => ' (redis)',
+ :prompt => 'redis> '
+ }
+ }
+
+
def initialize(args=[], client='duostack')
@args = args
@client = client
@@ -622,12 +652,65 @@ module Duostack
def console
# TODO: just use ruby expect lib
- # first check for expect dependency
+ # get console and ensure it's valid
+ arg = (@args.shift || DEFAULTS[:console_type]).downcase
+ console = CONSOLES.fetch(arg.to_sym) do |invalid_type|
+ exit_with "invalid console type given ('#{invalid_type}'), try #{sentencize(CONSOLES.keys.collect { |k| k.to_s})}"
+ end
+
+ # take piped input first as command, fall back to using remaining command line args
+ if $stdin.tty?
+ console_command = @args.join(' ') unless @args.empty?
+ @args.clear
+ else
+ console_command = ''
+ $stdin.each_line { |line| console_command << line }
+ end
+
+ # we're finished processing args any remaning ones are invalid
+ unless @args.empty?
+ exit_with("unrecognized argument: '#{@args.first}', run '#{@client} help #{@command}' for usage")
+ end
+
+ # check for expect dependency
if `which expect`.empty?
exit_with "missing dependency, please install Expect (http://expect.sourceforge.net/)"
end
- exec("#{$dir}/.duostack-console-expect #{@app_name}")
+ if console_command
+
+ # generate console command filename
+ timestamp = begin
+ t = Time.now
+ "#{t.to_i}.#{t.usec}"
+ end
+ console_command_file = File.join('/tmp', "ds-#{timestamp}.txt")
+
+ # write console command to file
+ file = File.new(console_command_file, 'w+')
+ file.chmod(0600)
+ file.write(console_command)
+ file.close
+
+ result = `#{$dir}/.duostack-console-expect '#{File.join($dir, $client)}' '#{arg}' '#{console[:prompt]}' '#{console_command_file}' '#{@app_name}'`
+
+ # clean up command file
+ File.delete(console_command_file)
+
+ # munge results
+ result = result.split("\r\n")
+ start = (result.index("#{console[:prompt]}") || 0) + 2
+ stop = (result.index("#{console[:prompt]}exit") || 0) - 1
+ result = result.slice(start..stop)
+
+ # clean up ruby return value marker
+ result[-1] = result[-1][3..-1] if arg == 'app'
+
+ puts result.join("\r\n")
+
+ else
+ exec("#{$dir}/.duostack-expect '#{console[:target]}' 'Connecting to #{console[:display_type]} console for ' '#{@app_name}#{console[:display_name_suffix]}'")
+ end
end
@@ -807,6 +890,7 @@ module Duostack
restart Restart instances
ps List instances with current status
destroy Destroy Duostack App and associated data
+ console [<type> [command]] Connect to app or database console, run command
config [<name> [<setting>]] Show or set configuration options
env [<operation>] Manage environment variables
access [<operation>] Manage app collaborator access
@@ -814,7 +898,6 @@ module Duostack
instances [<operation>] Manage app instance count
App Commands - Ruby:
- console Connect to IRB/Rails console
rake [<command>] Run a Rake command
EOF
@@ -875,7 +958,7 @@ module Duostack
Usage: #{$client} info
- Retreives a summary of app data, e.g. Git remote URL, instance count, etc.
+ Retrieves a summary of app data, e.g. Git remote URL, instance count, etc.
EOF
end
@@ -885,7 +968,7 @@ module Duostack
Usage: #{$client} logs
- Retreives aggregate logs from all of the app's instances.
+ Retrieves aggregate logs from all of the app's instances.
EOF
end
@@ -912,7 +995,7 @@ module Duostack
Usage: #{$client} ps
- Retreives a listing of all of the app's instances with status information
+ Retrieves a listing of all of the app's instances with status information
(uptime) for each.
EOF
@@ -1071,12 +1154,57 @@ module Duostack
def console
<<-EOF
- Usage: #{$client} console
+ Usage:
+ #{$client} console [<type> [<command>]]
+ launches console session of <type> (defaults to
+ 'app' console), optionally runs specified
+ <command> in console and prints the result
+
+ #{$client} console <type> <command> > <output-file>
+ pipes the output from running <command> to the
+ specified <output-file>
+
+ #{$client} console <type> < <input-file>
+ pipes the contents of <input-file> into the
+ specified console <type>, prints the result
+
+ Examples:
+ #{$client} console same as running "#{$client} console app"
+ #{$client} console app launches interactive app console (Ruby apps only)
+ #{$client} console mysql launches interactive MySQL database console
+ #{$client} console mongodb launches interactive MongoDB database console
+ #{$client} console redis launches interactive Redis database console
+
+ #{$client} console app "puts('hello world')"
+ #{$client} console mysql "SHOW TABLES;"
+ runs the specified command and prints the result
+
+ #{$client} console mysql "SHOW TABLES;" > tables.txt
+ runs "SHOW TABLES;" in the app's MySQL console,
+ piping output into the file "tables.txt"
+
+ #{$client} console mysql < import.sql
+ (EXPERIMENTAL) pipes the contents of "import.sql"
+ into the app's MySQL console, printing the result
+
+ The "console" command can operate in three ways:
+
+ 1. Launching an interactive console session with your app or one of its
+ databases. If no argument is given, it connects to the app console itself
+ (applicable only to Ruby apps). If a database name is given, connects to a
+ database console session for that database. Valid database names are: mysql,
+ mongodb, redis.
+
+ You can use then use these interactive console sessions to make any action you
+ would normally make in your app or database console.
- Launches an interactive IRB/console session with your app. You can use this to
- make any action you would normally make in your app's console.
+ 2. Running an ad-hoc command specified on the command line in the remaining
+ arguments, and printing the result. You could also then pipe the output into an
+ arbitrary file.
- Applicable only to Ruby applications.
+ 3. (EXPERIMENTAL) Sending the contents of a piped-in file to the specified (app
+ or database) console. The resulting output will be collected and printed to your
+ terminal.
EOF
end
@@ -1093,7 +1221,7 @@ module Duostack
Passing of environment variables if your rake task requires them is supported
(e.g. rake db:seed MODEL=Posts).
- Applicable only to Ruby applications.
+ Applicable only to Ruby apps.
EOF
end
View
2  support/gem/Rakefile
@@ -13,5 +13,5 @@ Jeweler::Tasks.new do |gem|
gem.authors = "Todd Eichel"
gem.require_paths = ['.'] # default is ["lib"] but we don't have that
- gem.executables = ["duostack", ".duostack-console-expect"]
+ gem.executables = ["duostack", ".duostack-expect", ".duostack-console-expect"]
end
View
3  support/gem/bin/.duostack-console-expect
@@ -1,3 +0,0 @@
-#!/usr/bin/env ruby
-
-exec(File.join(File.dirname(__FILE__), 'bash', File.basename(__FILE__)) + ' ' + ARGV[0])
View
1  support/gem/bin/.duostack-console-expect
View
4 support/gem/bin/.duostack-expect
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+
+args_string = ARGV.collect { |a| "'#{a}'" }.join(' ')
+exec(File.join(File.dirname(__FILE__), 'bash', File.basename(__FILE__)) + ' ' + args_string)
View
1  support/gem/bin/bash/.duostack-expect
View
1  support/npm/package/bin/.duostack-expect
View
1  support/npm/package/package.json.template
@@ -14,6 +14,7 @@
},
"bin": {
"duostack": "./bin/duostack",
+ ".duostack-expect": "./bin/.duostack-expect",
".duostack-console-expect": "./bin/.duostack-console-expect",
".duostack-startcom.pem": "./bin/.duostack-startcom.pem"
}
View
30 test/client_spec.rb
@@ -107,10 +107,6 @@
result = run_command("create #{@app_name.next} --remote staging", @app_path)
result.should match("App created")
result.should match("Git remote added")
-
- # add an env var so we can ID this app later
- # TODO: use info command instead
- run_command("env add name=#{@app_name.next} --app #{@app_name.next}")
end
@@ -128,6 +124,14 @@
result.should_not match("Unable to restart app")
end
+ it "should retrieve app info" do
+ result = run_command("info --app #{@app_name}")
+ result.should match('App Name')
+ result.should match(@app_name)
+ result.should match('Git')
+ result.should match('Stack')
+ end
+
it "should retrieve logs" do
run_command("logs --app #{@app_name}").should match('==>')
end
@@ -159,8 +163,7 @@
end
it "should allow using --remote to set alternate app" do
- # TODO: replace with info command so this test isn't dependent on env working
- run_command("env --remote staging", @app_path).should match(@app_name.next)
+ run_command("info --remote staging", @app_path).should match(@app_name.next)
end
end
@@ -184,9 +187,9 @@
it "should reflect stack change upon re-push" do
`cd #{@app_path} && touch poke && git add poke && git commit -m "poke" && git push duostack master 2>&1`
- result = expect_console("IO.popen('ruby -v') { |f| puts f.gets }", @app_name)
+ result = expect_console('app', '>> ', "IO.popen('ruby -v') { |f| puts f.gets }", @app_name)
expected = <<-END.gsub(/^ {12}/, '').gsub("\r", '')
- spawn #{$client_executable} console --app #{@app_name}#{' ' if windows?}
+ spawn #{$client_executable} console app --app #{@app_name}#{' ' if windows?}
Connecting to Ruby console for #{@app_name}...
>> IO.popen('ruby -v') { |f| puts f.gets }
ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]
@@ -247,9 +250,9 @@
it "should see env vars in console" do
run_command("restart --app #{@app_name}") # need to restart first
- result = expect_console("ENV['env1']", @app_name)
+ result = expect_console('app', '>> ', "ENV['env1']", @app_name)
expected = <<-END.gsub(/^ {12}/, '').gsub("\r", '')
- spawn #{$client_executable} console --app #{@app_name}#{' ' if windows?}
+ spawn #{$client_executable} console app --app #{@app_name}#{' ' if windows?}
Connecting to Ruby console for #{@app_name}...
>> ENV['env1']
=> "var1"
@@ -421,12 +424,13 @@
# TODO: check account verification process (currently tests need to run with verified account)
end
+ # TODO: extended console tests
describe "for Ruby apps" do
- it "should start a console session" do
- result = expect_console("puts 'console test'", @app_name)
+ it "should start an app console session" do
+ result = expect_console('app', '>> ', "puts 'console test'", @app_name)
expected = <<-END.gsub(/^ {12}/, '').gsub("\r", '')
- spawn #{$client_executable} console --app #{@app_name}#{' ' if windows?}
+ spawn #{$client_executable} console app --app #{@app_name}#{' ' if windows?}
Connecting to Ruby console for #{@app_name}...
>> puts 'console test'
console test
View
12 test/console_test.expect
@@ -1,13 +1,15 @@
#!/usr/bin/env expect
set client [lindex $argv 0]
-set command [lindex $argv 1]
-set appname [lindex $argv 2]
+set type [lindex $argv 1]
+set prompt [lindex $argv 2]
+set command [lindex $argv 3]
+set appname [lindex $argv 4]
-spawn $client console --app $appname
-expect ">> "
+spawn $client console $type --app $appname
+expect $prompt
send $command
send "\n"
-expect ">> "
+expect $prompt
send "exit\n"
expect eof
View
4 test/support.rb
@@ -10,8 +10,8 @@ def run_command(command, app_path='.')
`cd #{app_path} && #{build_command(command)} 2>&1`.chomp
end
-def expect_console(command, app_name)
- `expect #{File.dirname(__FILE__)}/console_test.expect #{$client_executable} "#{command}" #{app_name}`.gsub("\r", '')
+def expect_console(type, prompt, command, app_name)
+ `expect #{File.dirname(__FILE__)}/console_test.expect #{$client_executable} "#{type}" "#{prompt}" "#{command}" "#{app_name}"`.gsub("\r", '')
end
def expect_billing_confirmation(response, app_name)

No commit comments for this range

Something went wrong with that request. Please try again.