td 0.7.0
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.
- 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
|
+
|