turbot 0.1.36 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +22 -0
  8. data/README.md +44 -25
  9. data/Rakefile +16 -0
  10. data/appveyor.yml +35 -0
  11. data/bin/turbot +2 -16
  12. data/data/schema.json +134 -0
  13. data/{templates → data/templates}/LICENSE.txt +0 -0
  14. data/{templates → data/templates}/manifest.json +0 -0
  15. data/{templates → data/templates}/python/scraper.py +0 -0
  16. data/{templates → data/templates}/ruby/scraper.rb +0 -0
  17. data/dist/deb.rake +32 -0
  18. data/dist/gem.rake +16 -0
  19. data/dist/manifest.rake +9 -0
  20. data/dist/pkg.rake +60 -0
  21. data/dist/resources/deb/control +10 -0
  22. data/dist/resources/deb/postinst +45 -0
  23. data/dist/resources/deb/turbot +25 -0
  24. data/dist/resources/deb/turbot-release-key.txt +30 -0
  25. data/dist/resources/pkg/Distribution.erb +15 -0
  26. data/dist/resources/pkg/PackageInfo.erb +6 -0
  27. data/dist/resources/pkg/postinstall +45 -0
  28. data/dist/resources/pkg/turbot +24 -0
  29. data/dist/resources/tgz/turbot +24 -0
  30. data/dist/rpm.rake +35 -0
  31. data/dist/tgz.rake +26 -0
  32. data/dist/zip.rake +40 -0
  33. data/lib/turbot.rb +18 -15
  34. data/lib/turbot/cli.rb +10 -27
  35. data/lib/turbot/command.rb +59 -212
  36. data/lib/turbot/command/auth.rb +72 -34
  37. data/lib/turbot/command/base.rb +22 -61
  38. data/lib/turbot/command/bots.rb +251 -300
  39. data/lib/turbot/command/help.rb +57 -110
  40. data/lib/turbot/command/version.rb +6 -10
  41. data/lib/turbot/handlers/base_handler.rb +21 -0
  42. data/lib/turbot/handlers/dump_handler.rb +10 -0
  43. data/lib/turbot/handlers/preview_handler.rb +30 -0
  44. data/lib/turbot/handlers/validation_handler.rb +17 -0
  45. data/lib/turbot/helpers.rb +14 -482
  46. data/lib/turbot/helpers/api_helper.rb +41 -0
  47. data/lib/turbot/helpers/netrc_helper.rb +66 -0
  48. data/lib/turbot/helpers/shell_helper.rb +36 -0
  49. data/lib/turbot/version.rb +1 -1
  50. data/spec/fixtures/bad_permissions +0 -0
  51. data/spec/fixtures/empty +0 -0
  52. data/spec/fixtures/netrc +6 -0
  53. data/spec/spec_helper.rb +17 -219
  54. data/spec/support/bot_helper.rb +102 -0
  55. data/spec/support/command_helper.rb +20 -0
  56. data/spec/support/custom_matchers.rb +5 -0
  57. data/spec/support/fixture_helper.rb +9 -0
  58. data/spec/support/netrc_helper.rb +21 -0
  59. data/spec/turbot/command/auth_spec.rb +202 -20
  60. data/spec/turbot/command/base_spec.rb +22 -58
  61. data/spec/turbot/command/bots_spec.rb +580 -89
  62. data/spec/turbot/command/help_spec.rb +32 -75
  63. data/spec/turbot/command/version_spec.rb +11 -10
  64. data/spec/turbot/command_spec.rb +55 -87
  65. data/spec/turbot/helpers_spec.rb +28 -44
  66. data/turbot.gemspec +31 -0
  67. metadata +88 -178
  68. data/data/cacert.pem +0 -3988
  69. data/lib/turbot/auth.rb +0 -315
  70. data/lib/turbot/client.rb +0 -757
  71. data/lib/turbot/client/cisaurus.rb +0 -25
  72. data/lib/turbot/client/pgbackups.rb +0 -113
  73. data/lib/turbot/client/rendezvous.rb +0 -111
  74. data/lib/turbot/client/ssl_endpoint.rb +0 -25
  75. data/lib/turbot/client/turbot_postgresql.rb +0 -148
  76. data/lib/turbot/command/ssl.rb +0 -43
  77. data/lib/turbot/deprecated.rb +0 -5
  78. data/lib/turbot/deprecated/help.rb +0 -38
  79. data/lib/turbot/distribution.rb +0 -9
  80. data/lib/turbot/errors.rb +0 -28
  81. data/lib/turbot/excon.rb +0 -11
  82. data/lib/turbot/helpers/log_displayer.rb +0 -70
  83. data/lib/turbot/helpers/pg_dump_restore.rb +0 -115
  84. data/lib/turbot/helpers/turbot_postgresql.rb +0 -213
  85. data/lib/turbot/plugin.rb +0 -165
  86. data/lib/turbot/updater.rb +0 -171
  87. data/lib/vendor/turbot/okjson.rb +0 -598
  88. data/spec/helper/legacy_help.rb +0 -16
  89. data/spec/helper/pg_dump_restore_spec.rb +0 -67
  90. data/spec/spec.opts +0 -1
  91. data/spec/support/display_message_matcher.rb +0 -49
  92. data/spec/support/dummy_api.rb +0 -120
  93. data/spec/support/openssl_mock_helper.rb +0 -8
  94. data/spec/support/organizations_mock_helper.rb +0 -11
  95. data/spec/turbot/auth_spec.rb +0 -214
  96. data/spec/turbot/client/pgbackups_spec.rb +0 -43
  97. data/spec/turbot/client/rendezvous_spec.rb +0 -62
  98. data/spec/turbot/client/ssl_endpoint_spec.rb +0 -48
  99. data/spec/turbot/client/turbot_postgresql_spec.rb +0 -71
  100. data/spec/turbot/client_spec.rb +0 -548
  101. data/spec/turbot/helpers/turbot_postgresql_spec.rb +0 -181
  102. data/spec/turbot/plugin_spec.rb +0 -172
  103. data/spec/turbot/updater_spec.rb +0 -44
@@ -1,5 +0,0 @@
1
- require "turbot"
2
-
3
- module Turbot::Deprecated
4
- end
5
-
@@ -1,38 +0,0 @@
1
- require "turbot/deprecated"
2
-
3
- module Turbot::Deprecated::Help
4
- def self.included(base)
5
- base.extend ClassMethods
6
- end
7
-
8
- class HelpGroup < Array
9
- attr_reader :title
10
-
11
- def initialize(title)
12
- @title = title
13
- end
14
-
15
- def command(name, description)
16
- self << [name, description]
17
- end
18
-
19
- def space
20
- self << ['', '']
21
- end
22
- end
23
-
24
- module ClassMethods
25
- def groups
26
- @groups ||= []
27
- end
28
-
29
- def group(title, &block)
30
- groups << begin
31
- group = HelpGroup.new(title)
32
- yield group
33
- group
34
- end
35
- end
36
- end
37
- end
38
-
@@ -1,9 +0,0 @@
1
- module Turbot
2
- module Distribution
3
- def self.files
4
- (Dir[File.expand_path("../../../{Gemfile,turbot.gemspec,.gitmodules,schema}", __FILE__)] + Dir[File.expand_path("../../../{.git,bin,data,lib,templates}/**/*", __FILE__)]).select do |file|
5
- File.file?(file)
6
- end
7
- end
8
- end
9
- end
data/lib/turbot/errors.rb DELETED
@@ -1,28 +0,0 @@
1
- module Turbot
2
- class API
3
- module Errors
4
- class Error < StandardError; end
5
-
6
- class ErrorWithResponse < Error
7
- attr_reader :response
8
-
9
- def initialize(message, response=nil)
10
- message = message << "\nbody: #{response.body.inspect}" if response
11
- super message
12
- @response = response
13
- end
14
- end
15
-
16
- class Unauthorized < ErrorWithResponse; end
17
- class VerificationRequired < ErrorWithResponse; end
18
- class Forbidden < ErrorWithResponse; end
19
- class NotFound < ErrorWithResponse; end
20
- class Timeout < ErrorWithResponse; end
21
- class Locked < ErrorWithResponse; end
22
- class RateLimitExceeded < ErrorWithResponse; end
23
- class RequestFailed < ErrorWithResponse; end
24
- class NilApp < ErrorWithResponse; end
25
- class MissingManifest < ErrorWithResponse; end
26
- end
27
- end
28
- end
data/lib/turbot/excon.rb DELETED
@@ -1,11 +0,0 @@
1
- module Excon
2
-
3
- def self.get_with_redirect(url, options={})
4
- res = Excon.get(url, options)
5
- if [301, 302].include?(res.status)
6
- return self.get_with_redirect(res.headers["Location"], options)
7
- end
8
- res
9
- end
10
-
11
- end
@@ -1,70 +0,0 @@
1
- require "turbot/helpers"
2
-
3
- module Turbot::Helpers
4
- class LogDisplayer
5
-
6
- include Turbot::Helpers
7
-
8
- attr_reader :api, :bot, :opts
9
-
10
- def initialize(api, bot, opts)
11
- @api, @bot, @opts = api, bot, opts
12
- end
13
-
14
- def display_logs
15
- @assigned_colors = {}
16
- @line_start = true
17
- @token = nil
18
-
19
- api.read_logs(bot, opts).each do |chunk|
20
- unless chunk.empty?
21
- if STDOUT.isatty && ENV.has_key?("TERM")
22
- display(colorize(chunk))
23
- else
24
- display(chunk)
25
- end
26
- end
27
- end
28
- rescue Errno::EPIPE
29
- rescue Interrupt => interrupt
30
- if STDOUT.isatty && ENV.has_key?("TERM")
31
- display("\e[0m")
32
- end
33
- raise(interrupt)
34
- end
35
-
36
- COLORS = %w( cyan yellow green magenta red )
37
- COLOR_CODES = {
38
- "red" => 31,
39
- "green" => 32,
40
- "yellow" => 33,
41
- "magenta" => 35,
42
- "cyan" => 36,
43
- }
44
-
45
- def colorize(chunk)
46
- lines = []
47
- chunk.split("\n").map do |line|
48
- if parsed_line = parse_log(line)
49
- header, identifier, body = parsed_line
50
- @assigned_colors[identifier] ||= COLORS[@assigned_colors.size % COLORS.size]
51
- lines << [
52
- "\e[#{COLOR_CODES[@assigned_colors[identifier]]}m",
53
- header,
54
- "\e[0m",
55
- body,
56
- ].join("")
57
- elsif not line.empty?
58
- lines << line
59
- end
60
- end
61
- lines.join("\n")
62
- end
63
-
64
- def parse_log(log)
65
- return unless parsed = log.match(/^(.*?\[([\w-]+)([\d\.]+)?\]:)(.*)?$/)
66
- [1, 2, 4].map { |i| parsed[i] }
67
- end
68
-
69
- end
70
- end
@@ -1,115 +0,0 @@
1
- require 'uri'
2
- class PgDumpRestore
3
- attr_reader :command
4
-
5
- def initialize(source, target, command)
6
- @source = URI.parse(source)
7
- @target = URI.parse(target)
8
- @command = command
9
-
10
- fill_in_shorthand_uris!
11
- end
12
-
13
- def execute
14
- prepare
15
- run
16
- verify
17
- end
18
-
19
- def prepare
20
- if @target.host == 'localhost'
21
- create_local_db
22
- else
23
- ensure_remote_db_empty
24
- end
25
- end
26
-
27
- def verify
28
- verify_extensions_match
29
- end
30
-
31
- def dump_restore_cmd
32
- pg_restore = gen_pg_restore_command(@target)
33
- pg_dump = gen_pg_dump_command(@source)
34
- "#{pg_dump} | #{pg_restore}"
35
- end
36
-
37
- private
38
-
39
- def create_local_db
40
- dbname = @target.path[1..-1]
41
- cdb_output = `createdb #{dbname} 2>&1`
42
- if $?.exitstatus != 0
43
- if cdb_output =~ /already exists/
44
- command.error(cdb_output + "\nPlease drop the local database (`dropdb #{dbname}`) and try again.")
45
- else
46
- command.error(cdb_output + "\nUnable to create new local database. Ensure your local Postgres is working and try again.")
47
- end
48
- end
49
- end
50
-
51
- def ensure_remote_db_empty
52
- sql = 'select count(*) = 0 from pg_stat_user_tables;'
53
- result = exec_sql_on_uri(sql, @target)
54
- unless result == " ?column? \n----------\n t\n(1 row)\n\n"
55
- command.error("Remote database is not empty.\nPlease create a new database, or use `turbot pg:reset`")
56
- end
57
- end
58
-
59
- def gen_pg_dump_command(uri)
60
- # It is occasionally necessary to override PGSSLMODE, as when the server
61
- # wasn't built to support SSL.
62
- %{ env PGPASSWORD=#{uri.password} PGSSLMODE=prefer pg_dump --verbose -F c -Z 0 #{connstring(uri, :skip_d_flag)} }
63
- end
64
-
65
- def gen_pg_restore_command(uri)
66
- %{ env PGPASSWORD=#{uri.password} pg_restore --verbose --no-acl --no-owner #{connstring(uri)} }
67
- end
68
-
69
- def connstring(uri, skip_d_flag=false)
70
- database = uri.path[1..-1]
71
- user = uri.user ? "-U #{uri.user}" : ""
72
- %Q{#{user} -h #{uri.host} -p #{uri.port} #{skip_d_flag ? '' : '-d'} #{database} }
73
- end
74
-
75
- def fill_in_shorthand_uris!
76
- [@target, @source].each do |uri|
77
- uri.host ||= 'localhost'
78
- uri.port ||= Integer(ENV['PGPORT'] || 5432)
79
- end
80
- end
81
-
82
- def verify_extensions_match
83
- # It's pretty common for local DBs to not have extensions available that
84
- # are used by the remote bot, so take the final precaution of warning if
85
- # the extensions available in the local database don't match. We don't
86
- # report it if the difference is solely in the version of an extension
87
- # used, though.
88
- ext_sql = "SELECT extname FROM pg_extension ORDER BY extname;"
89
- target_exts = exec_sql_on_uri(ext_sql, @target)
90
- source_exts = exec_sql_on_uri(ext_sql, @source)
91
- if target_exts != source_exts
92
- command.error <<-EOM
93
- WARNING: Extensions in newly created target database differ from existing source database.
94
-
95
- Target extensions:
96
- #{target_exts}
97
- Source extensions:
98
- #{source_exts}
99
- HINT: You should review output to ensure that any errors
100
- ignored are acceptable - entire tables may have been missed, where a dependency
101
- could not be resolved. You may need to to install a postgresql-contrib package
102
- and retry.
103
- EOM
104
- end
105
- end
106
-
107
- def exec_sql_on_uri(sql, uri)
108
- command.send(:exec_sql_on_uri, sql, uri)
109
- end
110
-
111
- def run
112
- system dump_restore_cmd
113
- end
114
- end
115
-
@@ -1,213 +0,0 @@
1
- require "turbot/helpers"
2
-
3
- module Turbot::Helpers::TurbotPostgresql
4
-
5
- extend self
6
- extend Turbot::Helpers
7
-
8
- class Attachment
9
- attr_reader :bot, :name, :config_var, :resource_name, :url, :addon, :plan
10
- def initialize(raw)
11
- @raw = raw
12
- @bot = raw['bot']['name']
13
- @name = raw['name']
14
- @config_var = raw['config_var']
15
- @resource_name = raw['resource']['name']
16
- @url = raw['resource']['value']
17
- @addon, @plan = raw['resource']['type'].split(':')
18
- end
19
-
20
- def starter_plan?
21
- plan =~ /dev|basic/
22
- end
23
-
24
- def display_name
25
- config_var + (primary_attachment? ? " (DATABASE_URL)" : '')
26
- end
27
-
28
- def primary_attachment!
29
- @primary_attachment = true
30
- end
31
-
32
- def primary_attachment?
33
- @primary_attachment
34
- end
35
- end
36
-
37
- def hpg_resolve(identifier, default=nil)
38
- $stderr.puts " ! #hpg_resolve is deprecated. Please run `turbot plugins:update` to update your plugins."
39
- $stderr.puts " ! from: #{caller.first}"
40
- Resolver.new(bot, api).resolve(identifier , default)
41
- end
42
-
43
- class Resolver
44
- include Turbot::Helpers
45
- attr_reader :api, :bot_name
46
- def initialize(bot_name, api)
47
- @bot_name = bot_name
48
- @api = api
49
- end
50
-
51
- def resolve(identifier, default=nil)
52
- if identifier =~ /::/
53
- @bot_name, db_name = identifier.split('::')
54
- else
55
- db_name = identifier
56
- end
57
-
58
- hpg_resolve(db_name, default)
59
- end
60
-
61
- def all_databases
62
- hpg_databases
63
- end
64
-
65
- def database_name_from_url(url)
66
- vars = bot_config_vars.reject {|key,value| key == 'DATABASE_URL'}
67
- if var = vars.invert[url]
68
- var.gsub(/_URL$/, '')
69
- else
70
- uri = URI.parse(url)
71
- "Database on #{uri.host}:#{uri.port || 5432}#{uri.path}"
72
- end
73
- end
74
-
75
- def hpg_addon_name
76
- if ENV['SHOGUN']
77
- "shogun-#{ENV['SHOGUN']}"
78
- else
79
- ENV['TURBOT_POSTGRESQL_ADDON_NAME'] || 'turbot-postgresql'
80
- end
81
- end
82
-
83
- private
84
-
85
- def protect_missing_bot
86
- # in the case where --bot was left out, AND bot::db shorthand was not used, AND no bot autodetect
87
- unless bot_name
88
- error("No bot specified.\nRun this command from an bot folder or specify which bot to use with --bot APP.")
89
- end
90
- end
91
-
92
- def bot_config_vars
93
- protect_missing_bot
94
- @bot_config_vars ||= api.get_config_vars(bot_name).body
95
- end
96
-
97
- def bot_attachments
98
- protect_missing_bot
99
- @bot_attachments ||= api.get_attachments(bot_name).body.map { |raw| Attachment.new(raw) }
100
- end
101
-
102
- def hpg_databases
103
- return @hpg_databases if @hpg_databases
104
- pairs = bot_attachments.select {|att|
105
- att.addon == hpg_addon_name
106
- }.map { |att|
107
- [att.config_var, att]
108
- }
109
- @hpg_databases = Hash[ pairs ]
110
-
111
- if find_database_url_real_attachment
112
- @hpg_databases['DATABASE_URL'] = find_database_url_real_attachment
113
- end
114
-
115
- return @hpg_databases
116
- end
117
-
118
- def resource_url(resource)
119
- api.get_resource(resource).body['value']
120
- end
121
-
122
- def forget_config!
123
- @hpg_databases = nil
124
- @bot_config_vars = nil
125
- @bot_attachments = nil
126
- end
127
-
128
- def find_database_url_real_attachment
129
- raw_primary_db_url = bot_config_vars['DATABASE_URL']
130
- return unless raw_primary_db_url
131
-
132
- primary_db_url = raw_primary_db_url.split("?").first
133
- return unless primary_db_url && !primary_db_url.empty?
134
-
135
- real_config = bot_config_vars.detect {|k,v| k != 'DATABASE_URL' && v == primary_db_url }
136
- if real_config
137
- real = hpg_databases[real_config.first]
138
- real.primary_attachment! if real
139
- return real
140
- else
141
- return nil
142
- end
143
- end
144
-
145
- def match_attachments_by_name(name)
146
- return [] if name.empty?
147
- return [name] if hpg_databases[name]
148
- hpg_databases.keys.grep(%r{#{ name }}i)
149
- end
150
-
151
- def hpg_resolve(name, default=nil)
152
- name = '' if name.nil?
153
- name = 'DATABASE_URL' if name == 'DATABASE'
154
-
155
- if hpg_databases.empty?
156
- error("Your bot has no databases.")
157
- end
158
-
159
- found_attachment = nil
160
- candidates = match_attachments_by_name(name)
161
- if default && name.empty? && bot_config_vars[default]
162
- found_attachment = hpg_databases[default]
163
- elsif candidates.size == 1
164
- found_attachment = hpg_databases[candidates.first]
165
- end
166
-
167
- if found_attachment.nil?
168
- error("Unknown database#{': ' + name unless name.empty?}. Valid options are: #{hpg_databases.keys.sort.join(", ")}")
169
- end
170
-
171
- return found_attachment
172
- end
173
- end
174
-
175
- def hpg_translate_fork_and_follow(addon, config)
176
- $stderr.puts " ! #hpg_translate_fork_and_follow is deprecated. Update your plugins."
177
- hpg_translate_db_opts_to_urls(addon, config)
178
- end
179
-
180
- def hpg_translate_db_opts_to_urls(addon, config)
181
- bot_name = bot rescue nil
182
- resolver = Resolver.new(bot_name, api)
183
- if addon =~ /^#{resolver.hpg_addon_name}/
184
- %w[fork follow rollback].each do |opt|
185
- if val = config[opt]
186
- unless val.is_a?(String)
187
- error("--#{opt} requires a database argument.")
188
- end
189
-
190
- uri = URI.parse(val) rescue nil
191
- if uri && uri.scheme && uri.scheme == 'postgres'
192
- argument_url = uri.to_s
193
- else
194
- attachment = resolver.resolve(val)
195
- if attachment.starter_plan?
196
- error("#{opt.tr 'f', 'F'} is only available on production databases.")
197
- end
198
- argument_url = attachment.url
199
- end
200
-
201
- config[opt] = argument_url
202
- end
203
- end
204
- end
205
- end
206
-
207
- private
208
-
209
- def hpg_promote(url)
210
- api.put_config_vars(bot, "DATABASE_URL" => url)
211
- end
212
-
213
- end