td 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +16 -0
- data/README.rdoc +18 -0
- data/bin/td +6 -0
- data/lib/td/api.rb +305 -0
- data/lib/td/api_iface.rb +323 -0
- data/lib/td/command/account.rb +84 -0
- data/lib/td/command/common.rb +121 -0
- data/lib/td/command/database.rb +82 -0
- data/lib/td/command/import.rb +286 -0
- data/lib/td/command/list.rb +115 -0
- data/lib/td/command/query.rb +167 -0
- data/lib/td/command/server.rb +15 -0
- data/lib/td/command/table.rb +101 -0
- data/lib/td/command/td.rb +82 -0
- data/lib/td/config.rb +79 -0
- data/lib/td/error.rb +29 -0
- data/lib/td/version.rb +5 -0
- metadata +131 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
|
2
|
+
module TD
|
3
|
+
module Command
|
4
|
+
module List
|
5
|
+
|
6
|
+
LIST = []
|
7
|
+
ALIASES = {}
|
8
|
+
GUESS = {}
|
9
|
+
|
10
|
+
def self.add_list(file, cmd, description)
|
11
|
+
LIST << [cmd, file, description]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.add_alias(new_cmd, old_cmd)
|
15
|
+
ALIASES[new_cmd] = old_cmd
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get_description(command)
|
19
|
+
LIST.each {|cmd,file,description|
|
20
|
+
if cmd == command
|
21
|
+
return description
|
22
|
+
end
|
23
|
+
}
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.add_guess(wrong, correct)
|
28
|
+
GUESS[wrong] = correct
|
29
|
+
end
|
30
|
+
|
31
|
+
add_list 'list', 'help', 'Show usage of a command'
|
32
|
+
add_list 'account', 'account', 'Setup a Treasure Data account'
|
33
|
+
add_list 'server', 'server-status', 'Show status of the Treasure Data server'
|
34
|
+
add_list 'database', 'show-databases', 'Show list of databases'
|
35
|
+
add_list 'table', 'show-tables', 'Show list of tables'
|
36
|
+
add_list 'query', 'show-jobs', 'Show list of jobs'
|
37
|
+
add_list 'database', 'create-database', 'Create a database'
|
38
|
+
add_list 'table', 'create-log-table', 'Create a log table'
|
39
|
+
#add_list 'table', 'create-item-table', 'Create a item table'
|
40
|
+
add_list 'database', 'drop-database', 'Delete a database'
|
41
|
+
add_list 'table', 'drop-table', 'Delete a table'
|
42
|
+
add_list 'query', 'query', 'Start a query'
|
43
|
+
add_list 'query', 'job', 'Show status and result of a job'
|
44
|
+
add_list 'import', 'import', 'Import files to a table'
|
45
|
+
add_list 'list', 'version', 'Show version'
|
46
|
+
|
47
|
+
add_alias 'show-dbs', 'show-databases'
|
48
|
+
add_alias 'show-database', 'show-databases'
|
49
|
+
add_alias 'create-db', 'create-databases'
|
50
|
+
add_alias 'drop-db', 'create-databases'
|
51
|
+
add_alias 'show-table', 'show-tables'
|
52
|
+
add_alias 'delete-database', 'drop-database'
|
53
|
+
add_alias 'delete-table', 'drop-table'
|
54
|
+
add_alias 'jobs', 'show-jobs'
|
55
|
+
|
56
|
+
add_guess 'create-table', 'create-log-table'
|
57
|
+
add_guess 'drop-log-table', 'drop-table'
|
58
|
+
#add_guess 'drop-item-table', 'drop-table'
|
59
|
+
add_guess 'delete-log-table', 'drop-table'
|
60
|
+
#add_guess 'delete-item-table', 'drop-table'
|
61
|
+
add_guess 'show-job', 'job'
|
62
|
+
|
63
|
+
def self.get_method(command)
|
64
|
+
command = ALIASES[command] || command
|
65
|
+
LIST.each {|cmd,file,description|
|
66
|
+
if cmd == command
|
67
|
+
require 'td/command/common'
|
68
|
+
require "td/command/#{file}"
|
69
|
+
name = command.gsub('-','_')
|
70
|
+
return Object.new.extend(Command).method(name)
|
71
|
+
end
|
72
|
+
}
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.show_guess(wrong)
|
77
|
+
if correct = GUESS[wrong]
|
78
|
+
$stderr.puts "Did you mean this?: #{correct}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.help(indent)
|
83
|
+
LIST.map {|cmd,file,description|
|
84
|
+
if cmd != 'help'
|
85
|
+
"#{indent}%-18s %s" % [cmd, description.split("\n").first]
|
86
|
+
end
|
87
|
+
}.join("\n")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def help
|
92
|
+
op = cmd_opt 'help', :command
|
93
|
+
cmd = op.cmd_parse
|
94
|
+
|
95
|
+
ARGV.clear
|
96
|
+
ARGV[0] = '--help'
|
97
|
+
|
98
|
+
method = List.get_method(cmd)
|
99
|
+
unless method
|
100
|
+
$stderr.puts "'#{cmd}' is not a td command. Run '#{$prog}' to show the list."
|
101
|
+
List.show_guess(cmd)
|
102
|
+
exit 1
|
103
|
+
end
|
104
|
+
|
105
|
+
method.call
|
106
|
+
end
|
107
|
+
|
108
|
+
def version
|
109
|
+
require 'td/version'
|
110
|
+
puts "td-#{TD::VERSION}"
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
@@ -0,0 +1,167 @@
|
|
1
|
+
|
2
|
+
module TD
|
3
|
+
module Command
|
4
|
+
|
5
|
+
def query
|
6
|
+
op = cmd_opt 'query', :sql
|
7
|
+
|
8
|
+
op.banner << "\noptions:\n"
|
9
|
+
|
10
|
+
db_name = nil
|
11
|
+
op.on('-d', '--database DB_NAME', 'use the database') {|s|
|
12
|
+
db_name = s
|
13
|
+
}
|
14
|
+
|
15
|
+
wait = false
|
16
|
+
op.on('-w', '--wait', 'wait for finishing the job', TrueClass) {|b|
|
17
|
+
wait = b
|
18
|
+
}
|
19
|
+
|
20
|
+
sql = op.cmd_parse
|
21
|
+
|
22
|
+
conf = cmd_config
|
23
|
+
api = cmd_api(conf)
|
24
|
+
|
25
|
+
if db_name
|
26
|
+
find_database(api, db_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
job = api.query(sql, db_name)
|
30
|
+
|
31
|
+
$stderr.puts "Job #{job.job_id} is started."
|
32
|
+
$stderr.puts "Use '#{$prog} job #{job.job_id}' to show the status."
|
33
|
+
$stderr.puts "See #{job.url} to see the progress."
|
34
|
+
|
35
|
+
if wait && !job.finished?
|
36
|
+
wait_job(job)
|
37
|
+
puts "Status : #{job.status}"
|
38
|
+
puts "Result :"
|
39
|
+
puts cmd_render_table(job.result, :max_width=>10000)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def show_jobs
|
44
|
+
op = cmd_opt 'show-jobs', :max?, :from?
|
45
|
+
max, from = op.cmd_parse
|
46
|
+
|
47
|
+
max = (max || 20).to_i
|
48
|
+
from = (from || 0).to_i
|
49
|
+
|
50
|
+
conf = cmd_config
|
51
|
+
api = cmd_api(conf)
|
52
|
+
|
53
|
+
jobs = api.jobs(from, from+max-1)
|
54
|
+
|
55
|
+
rows = []
|
56
|
+
jobs.each {|job|
|
57
|
+
start = job.start_at
|
58
|
+
finish = job.end_at
|
59
|
+
if start
|
60
|
+
if !finish
|
61
|
+
finish = Time.now.utc
|
62
|
+
end
|
63
|
+
e = finish.to_i - start.to_i
|
64
|
+
elapsed = ''
|
65
|
+
if e >= 3600
|
66
|
+
elapsed << "#{e/3600}h "
|
67
|
+
e %= 3600
|
68
|
+
elapsed << "% 2dm " % (e/60)
|
69
|
+
e %= 60
|
70
|
+
elapsed << "% 2dsec" % e
|
71
|
+
elsif e >= 60
|
72
|
+
elapsed << "% 2dm " % (e/60)
|
73
|
+
e %= 60
|
74
|
+
elapsed << "% 2dsec" % e
|
75
|
+
else
|
76
|
+
elapsed << "% 2dsec" % e
|
77
|
+
end
|
78
|
+
else
|
79
|
+
elapsed = ''
|
80
|
+
end
|
81
|
+
elapsed = "% 10s" % elapsed # right aligned
|
82
|
+
|
83
|
+
rows << {:JobID => job.job_id, :Status => job.status, :Query => job.query.to_s, :Start => start, :Elapsed => elapsed}
|
84
|
+
}
|
85
|
+
|
86
|
+
puts cmd_render_table(rows, :fields => [:JobID, :Status, :Start, :Elapsed, :Query])
|
87
|
+
end
|
88
|
+
|
89
|
+
def job
|
90
|
+
op = cmd_opt 'job', :job_id
|
91
|
+
|
92
|
+
op.banner << "\noptions:\n"
|
93
|
+
|
94
|
+
verbose = nil
|
95
|
+
op.on('-v', '--verbose', 'show logs', TrueClass) {|b|
|
96
|
+
verbose = b
|
97
|
+
}
|
98
|
+
|
99
|
+
wait = false
|
100
|
+
op.on('-w', '--wait', 'wait for finishing the job', TrueClass) {|b|
|
101
|
+
wait = b
|
102
|
+
}
|
103
|
+
|
104
|
+
job_id = op.cmd_parse
|
105
|
+
|
106
|
+
conf = cmd_config
|
107
|
+
api = cmd_api(conf)
|
108
|
+
|
109
|
+
job = api.job(job_id)
|
110
|
+
|
111
|
+
puts "JobID : #{job.job_id}"
|
112
|
+
puts "URL : #{job.url}"
|
113
|
+
puts "Status : #{job.status}"
|
114
|
+
puts "Query : #{job.query}"
|
115
|
+
|
116
|
+
if wait && !job.finished?
|
117
|
+
wait_job(job)
|
118
|
+
puts "Result :"
|
119
|
+
puts cmd_render_table(job.result, :max_width=>10000)
|
120
|
+
|
121
|
+
else
|
122
|
+
if job.finished?
|
123
|
+
puts "Result :"
|
124
|
+
puts cmd_render_table(job.result, :max_width=>10000)
|
125
|
+
end
|
126
|
+
|
127
|
+
if verbose
|
128
|
+
puts ""
|
129
|
+
puts "cmdout:"
|
130
|
+
job.debug['cmdout'].to_s.split("\n").each {|line|
|
131
|
+
puts " "+line
|
132
|
+
}
|
133
|
+
puts ""
|
134
|
+
puts "stderr:"
|
135
|
+
job.debug['stderr'].to_s.split("\n").each {|line|
|
136
|
+
puts " "+line
|
137
|
+
}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
$stderr.puts "Use '-v' option to show detailed messages." unless verbose
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
def wait_job(job)
|
146
|
+
$stderr.puts "running..."
|
147
|
+
|
148
|
+
cmdout_lines = 0
|
149
|
+
stderr_lines = 0
|
150
|
+
|
151
|
+
until job.finished?
|
152
|
+
sleep 2
|
153
|
+
|
154
|
+
job.update_status!
|
155
|
+
|
156
|
+
cmdout = job.debug['cmdout'].to_s.split("\n")[cmdout_lines..-1] || []
|
157
|
+
stderr = job.debug['stderr'].to_s.split("\n")[stderr_lines..-1] || []
|
158
|
+
(cmdout + stderr).each {|line|
|
159
|
+
puts " "+line
|
160
|
+
}
|
161
|
+
cmdout_lines += cmdout.size
|
162
|
+
stderr_lines += stderr.size
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
module TD
|
3
|
+
module Command
|
4
|
+
|
5
|
+
def create_table_type(type, db_name, table_name)
|
6
|
+
conf = cmd_config
|
7
|
+
api = cmd_api(conf)
|
8
|
+
|
9
|
+
begin
|
10
|
+
api.create_table(db_name, table_name, type)
|
11
|
+
rescue NotFoundError
|
12
|
+
cmd_debug_error $!
|
13
|
+
$stderr.puts "Database '#{db_name}' does not exist."
|
14
|
+
$stderr.puts "Use '#{$prog} create-database #{db_name}' to create the database."
|
15
|
+
exit 1
|
16
|
+
rescue AlreadyExistsError
|
17
|
+
cmd_debug_error $!
|
18
|
+
$stderr.puts "Table '#{db_name}.#{table_name}' already exists."
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
$stderr.puts "Table '#{db_name}.#{table_name}' is created."
|
23
|
+
end
|
24
|
+
private :create_table_type
|
25
|
+
|
26
|
+
def create_log_table
|
27
|
+
op = cmd_opt 'create-log-table', :db_name, :table_name
|
28
|
+
db_name, table_name = op.cmd_parse
|
29
|
+
|
30
|
+
create_table_type(:log, db_name, table_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_item_table
|
34
|
+
op = cmd_opt 'create-item-table', :db_name, :table_name
|
35
|
+
db_name, table_name = op.cmd_parse
|
36
|
+
|
37
|
+
create_table_type(:item, db_name, table_name)
|
38
|
+
end
|
39
|
+
|
40
|
+
def drop_table
|
41
|
+
op = cmd_opt 'drop-table', :db_name, :table_name
|
42
|
+
db_name, table_name = op.cmd_parse
|
43
|
+
|
44
|
+
conf = cmd_config
|
45
|
+
api = cmd_api(conf)
|
46
|
+
|
47
|
+
begin
|
48
|
+
api.delete_table(db_name, table_name)
|
49
|
+
rescue NotFoundError
|
50
|
+
cmd_debug_error $!
|
51
|
+
$stderr.puts "Table '#{db_name}.#{table_name}' does not exist."
|
52
|
+
$stderr.puts "Use '#{$prog} show-tables #{db_name}' to show list of the tables."
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
|
56
|
+
$stderr.puts "Table '#{db_name}.#{table_name}' is deleted."
|
57
|
+
end
|
58
|
+
|
59
|
+
def show_tables
|
60
|
+
op = cmd_opt 'show-tables', :db_name?
|
61
|
+
db_name = op.cmd_parse
|
62
|
+
|
63
|
+
conf = cmd_config
|
64
|
+
api = cmd_api(conf)
|
65
|
+
|
66
|
+
if db_name
|
67
|
+
db = find_database(api, db_name)
|
68
|
+
dbs = [db]
|
69
|
+
else
|
70
|
+
dbs = api.databases
|
71
|
+
end
|
72
|
+
|
73
|
+
rows = []
|
74
|
+
dbs.each {|db|
|
75
|
+
db.tables.each {|table|
|
76
|
+
rows << {:Database => db.name, :Table => table.name, :Type => table.type.to_s, :Count => table.count.to_s}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
rows = rows.sort_by {|map|
|
80
|
+
[map[:Database], map[:Type].size, map[:Table]]
|
81
|
+
}
|
82
|
+
|
83
|
+
puts cmd_render_table(rows, :fields => [:Database, :Table, :Type, :Count])
|
84
|
+
|
85
|
+
if rows.empty?
|
86
|
+
if db_name
|
87
|
+
$stderr.puts "Database '#{db_name}' has no tables."
|
88
|
+
$stderr.puts "Use '#{$prog} create-log-table #{db_name} <table_name>' to create a table."
|
89
|
+
elsif dbs.empty?
|
90
|
+
$stderr.puts "There are no databases."
|
91
|
+
$stderr.puts "Use '#{$prog} create-database <db_name>' to create a database."
|
92
|
+
else
|
93
|
+
$stderr.puts "There are no tables."
|
94
|
+
$stderr.puts "Use '#{$prog} create-log-table <db_name> <table_name>' to create a table."
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
$prog = File.basename($0)
|
5
|
+
|
6
|
+
op = OptionParser.new
|
7
|
+
op.banner = <<EOF
|
8
|
+
usage: #{$prog} [options] COMMAND [args]
|
9
|
+
|
10
|
+
options:
|
11
|
+
EOF
|
12
|
+
|
13
|
+
op.summary_indent = " "
|
14
|
+
|
15
|
+
(class<<self;self;end).module_eval do
|
16
|
+
define_method(:usage) do |errmsg|
|
17
|
+
require 'td/command/list'
|
18
|
+
puts op.to_s
|
19
|
+
puts ""
|
20
|
+
puts "commands:"
|
21
|
+
puts TD::Command::List.help(op.summary_indent)
|
22
|
+
puts ""
|
23
|
+
puts "Type 'td help COMMAND' for more information on a specific command."
|
24
|
+
if errmsg
|
25
|
+
puts "error: #{errmsg}"
|
26
|
+
exit 1
|
27
|
+
else
|
28
|
+
exit 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
config_path = File.join(ENV['HOME'], '.td', 'td.conf')
|
34
|
+
$verbose = false
|
35
|
+
#$debug = false
|
36
|
+
|
37
|
+
op.on('-c', '--config PATH', "path to config file (~/.td/td.conf)") {|s|
|
38
|
+
config_path = s
|
39
|
+
}
|
40
|
+
|
41
|
+
op.on('-v', '--verbose', "verbose mode", TrueClass) {|b|
|
42
|
+
$verbose = b
|
43
|
+
}
|
44
|
+
|
45
|
+
#op.on('-d', '--debug', "debug mode", TrueClass) {|b|
|
46
|
+
# $debug = b
|
47
|
+
#}
|
48
|
+
|
49
|
+
begin
|
50
|
+
op.order!(ARGV)
|
51
|
+
usage nil if ARGV.empty?
|
52
|
+
cmd = ARGV.shift
|
53
|
+
$TRD_CONFIG_PATH = config_path
|
54
|
+
rescue
|
55
|
+
usage $!.to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
require 'td/command/list'
|
59
|
+
|
60
|
+
method = TD::Command::List.get_method(cmd)
|
61
|
+
unless method
|
62
|
+
$stderr.puts "'#{cmd}' is not a td command. Run '#{$prog}' to show the list."
|
63
|
+
TD::Command::List.show_guess(cmd)
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
|
67
|
+
require 'td/error'
|
68
|
+
|
69
|
+
begin
|
70
|
+
method.call
|
71
|
+
rescue TD::ConfigError
|
72
|
+
$stderr.puts "TreasureData account is not configured yet."
|
73
|
+
$stderr.puts "Run '#{$prog} account' first."
|
74
|
+
rescue
|
75
|
+
$stderr.puts "error #{$!.class}: backtrace:"
|
76
|
+
$!.backtrace.each {|b|
|
77
|
+
$stderr.puts " #{b}"
|
78
|
+
}
|
79
|
+
puts ""
|
80
|
+
puts $!
|
81
|
+
end
|
82
|
+
|