3scale_toolbox 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c06fef14e4b85f8c23bfbde952198345f502744e
4
- data.tar.gz: efd42927ca217afa27625810d1fecab9c23c5316
2
+ SHA256:
3
+ metadata.gz: 1faca82c3c88cb4312fa76704e70d656a400f53c3aecb03c507e8a1aeb374284
4
+ data.tar.gz: 32c28726d94b60b8238720b1ffe055b06b4ecb1aab3945fd069b899d19f12595
5
5
  SHA512:
6
- metadata.gz: 3926f07e4057c625aaff424c16a8721eaa639205abfe5f1d104c78cc9b6da537751289744faab53607e9c4635b2f6b8c3903d007afdc7e7c5aa550c11cf0b5ae
7
- data.tar.gz: 6010129bbd8a4a8ce8026ad98dcfaf0749133514be63baccd2c1f9da7b427ef6e74edfebb90cda81e5090f40afedcfbd46ffba6a9907865fb6020b7b0833a18f
6
+ metadata.gz: 720b320e9e20edecdb93256c2a804ffaff13593f180960e2e10027d5c915c94094a7c2bbf2aca9c9a3b78fc7e9c993bcfc3151d4ac61eab8bf0113a63ed0d5d5
7
+ data.tar.gz: c27a4f1aea1076ad163437cdf0b0082763a6f49fa10bcd2045fe63047ea08dc3ba0258833f8fcd8015a8de4604909c9ba7cf9b477b55511999a4c3544dcab5a2
data/exe/3scale-copy CHANGED
@@ -60,6 +60,7 @@ def copy_service_params(original, system_name)
60
60
  {
61
61
  name: original['name'],
62
62
  system_name: system_name,
63
+ backend_version: original['backend_version'],
63
64
  end_user_registration_required: original['end_user_registration_required']
64
65
  }.reject { |key, value| !value }
65
66
  end
data/exe/3scale-update ADDED
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require '3scale_toolbox/cli'
4
+ require 'optparse'
5
+ require '3scale/api'
6
+ require 'uri'
7
+
8
+ options = {}
9
+
10
+ parser = OptionParser.new do |parser|
11
+ parser.banner = '3scale update <command> [options]'
12
+
13
+ parser.on('-s', '--source SOURCE', "Source") do |domain|
14
+ options[:source] = domain
15
+ end
16
+
17
+ parser.on('-d', '--destination DESTINATION', "Destination") do |domain|
18
+ options[:destination] = domain
19
+ end
20
+
21
+ parser.on('-f', '--force', 'Overwrites the mapping rules by deleting all rules from target service first') do
22
+ options[:force] = true
23
+ end
24
+
25
+ # TODO: parametrize what parts of service need to be copied
26
+ parser.on('-r', '--rules-only', 'Updates only the mapping rules') do
27
+ options[:rules_only] = true
28
+ end
29
+
30
+ parser.on('-h', '--help', 'Prints this help') do
31
+ puts parser
32
+ puts
33
+ puts 'Available Commands:', ['service <source_service_id> <destination_service_id>', 'help']
34
+ exit
35
+ end
36
+ end
37
+
38
+ print_help = ->(error = nil) do
39
+ if error
40
+ puts "Error: #{error}"
41
+ puts
42
+ end
43
+ parser.parse(['--help'])
44
+ end
45
+
46
+ parser.parse!
47
+
48
+ def fetch_option(options, key)
49
+ options.fetch(key) { raise OptionParser::MissingArgument, key }
50
+ end
51
+
52
+ class ServiceUpdater
53
+
54
+ attr_reader :source_client, :target_client, :source_service_id, :target_service_id
55
+
56
+ def initialize (source, source_service_id, destination, target_service_id)
57
+ @source_client = ThreeScale::API.new(
58
+ endpoint: endpoint_from_url(source),
59
+ provider_key: provider_key_from_url(source)
60
+ )
61
+ @target_client = ThreeScale::API.new(
62
+ endpoint: endpoint_from_url(destination),
63
+ provider_key: provider_key_from_url(destination)
64
+ )
65
+ @source_service_id = source_service_id
66
+ @target_service_id = target_service_id
67
+ end
68
+
69
+ def compare_hashes(first, second, keys)
70
+ keys.map{ |key| first.fetch(key) } == keys.map{ |key| second.fetch(key) }
71
+ end
72
+
73
+ def provider_key_from_url(url)
74
+ URI(url).user
75
+ end
76
+
77
+ def endpoint_from_url(url)
78
+ uri = URI(url)
79
+ uri.user = nil
80
+
81
+ uri.to_s
82
+ end
83
+
84
+ def target_service_params(source)
85
+ # NOTE: backend_version and deployment_option are not yet returned by show_service method
86
+ params = %w(name backend_version deployment_option end_user_registration_required)
87
+ source.select { |k,v| params.include?(k) && v }
88
+ end
89
+
90
+ def source_metrics
91
+ @source_metrics ||= source_client.list_metrics(source_service_id)
92
+ end
93
+
94
+ def metrics_mapping
95
+ @metrics_mapping ||= target_client.list_metrics(target_service_id).map do |target|
96
+ metric = source_metrics.find{|metric| metric.fetch('system_name') == target.fetch('system_name') }
97
+ metric ||= {}
98
+
99
+ [metric['id'], target['id']]
100
+ end.to_h
101
+ end
102
+
103
+ def copy_service_settings
104
+ source_service = source_client.show_service(source_service_id)
105
+ puts "updating service settings for service id #{target_service_id}..."
106
+ target_update_response = target_client.update_service(target_service_id, target_service_params(source_service))
107
+ raise "Service has not been saved. Errors: #{target_update_response['errors']}" unless target_update_response['errors'].nil?
108
+ end
109
+
110
+ def copy_proxy_settings
111
+ puts "updating proxy configuration for service id #{target_service_id}..."
112
+ proxy = source_client.show_proxy(source_service_id)
113
+ target_client.update_proxy(target_service_id, proxy)
114
+ puts "updated proxy of #{target_service_id} to match the source #{source_service_id}"
115
+ end
116
+
117
+ def copy_metrics_and_methods
118
+ target_metrics = target_client.list_metrics(target_service_id)
119
+
120
+ source_hits = source_metrics.find{ |metric| metric['system_name'] == 'hits' } or raise 'missing hits metric'
121
+ target_hits = target_metrics.find{ |metric| metric['system_name'] == 'hits' } or raise 'missing hits metric'
122
+
123
+ source_methods = source_client.list_methods(source_service_id, source_hits['id'])
124
+ target_methods = target_client.list_methods(target_service_id, target_hits['id'])
125
+
126
+ puts "source service hits metric #{source_hits['id']} has #{source_methods.size} methods"
127
+ puts "target service hits metric #{target_hits['id']} has #{target_methods.size} methods"
128
+
129
+ missing_methods = source_methods.reject { |source_method| target_methods.find{|target_method| compare_hashes(source_method, target_method, ['system_name']) } }
130
+
131
+ puts "creating #{missing_methods.size} missing methods on target service..."
132
+ missing_methods.each do |method|
133
+ target = { friendly_name: method['friendly_name'], system_name: method['system_name'] }
134
+ target_client.create_method(target_service_id, target_hits['id'], target)
135
+ end
136
+
137
+ target_metrics = target_client.list_metrics(target_service_id)
138
+
139
+ puts "source service has #{source_metrics.size} metrics"
140
+ puts "target service has #{target_metrics.size} metrics"
141
+
142
+ missing_metrics = source_metrics.reject { |source_metric| target_metrics.find{|target_metric| compare_hashes(source_metric, target_metric, ['system_name']) } }
143
+
144
+ missing_metrics.map do |metric|
145
+ metric.delete('links')
146
+ target_client.create_metric(target_service_id, metric)
147
+ end
148
+
149
+ puts "created #{missing_metrics.size} metrics on the target service"
150
+ end
151
+
152
+ def copy_application_plans
153
+ source_plans = source_client.list_service_application_plans(source_service_id)
154
+ target_plans = target_client.list_service_application_plans(target_service_id)
155
+
156
+ puts "source service has #{source_plans.size} application plans"
157
+ puts "target service has #{target_plans.size} application plans"
158
+
159
+ missing_application_plans = source_plans.reject { |source_plan| target_plans.find{|target_plan| source_plan.fetch('system_name') == target_plan.fetch('system_name') } }
160
+
161
+ puts "creating #{missing_application_plans.size} missing application plans..."
162
+
163
+ missing_application_plans.each do |plan|
164
+ plan.delete('links')
165
+ plan.delete('default') # TODO: handle default plans
166
+
167
+ if plan.delete('custom') # TODO: what to do with custom plans?
168
+ puts "skipping custom plan #{plan}"
169
+ else
170
+ target_client.create_application_plan(target_service_id, plan)
171
+ end
172
+ end
173
+
174
+ puts "updating limits for application plans..."
175
+
176
+ application_plan_mapping = target_client.list_service_application_plans(target_service_id).map do |plan_target|
177
+ plan = source_plans.find{|plan| plan.fetch('system_name') == plan_target.fetch('system_name') }
178
+ plan ||= {}
179
+ [plan['id'], plan_target['id']]
180
+ end.to_h.reject { |key, value| !key }
181
+
182
+ application_plan_mapping.each do |source_id, target_id|
183
+ source_limits = source_client.list_application_plan_limits(source_id)
184
+ target_limits = target_client.list_application_plan_limits(target_id)
185
+
186
+ missing_limits = source_limits.reject { |limit| target_limits.find{|limit_target| limit.fetch('period') == limit_target.fetch('period') } }
187
+
188
+ puts "target application plan #{target_id} is missing #{missing_limits.size} from the source plan #{source_id}"
189
+
190
+ missing_limits.each do |limit|
191
+ limit.delete('links')
192
+ target_client.create_application_plan_limit(target_id, metrics_mapping.fetch(limit.fetch('metric_id')), limit)
193
+ end
194
+
195
+ end
196
+ end
197
+
198
+ def copy_mapping_rules force_mapping_rules
199
+ source_mapping_rules = source_client.list_mapping_rules(source_service_id)
200
+ target_mapping_rules = target_client.list_mapping_rules(target_service_id)
201
+
202
+ puts "the source service has #{source_mapping_rules.size} mapping rules"
203
+ puts "the target has #{target_mapping_rules.size} mapping rules"
204
+
205
+ if force_mapping_rules
206
+ puts "force mode was chosen, deleting existing mapping rules on target service..."
207
+ target_mapping_rules.each do |rule|
208
+ target_client.delete_mapping_rule(target_service_id, rule['id'])
209
+ end
210
+ missing_mapping_rules = source_mapping_rules
211
+ else
212
+ unique_target_mapping_rules = target_mapping_rules.dup
213
+
214
+ missing_mapping_rules = source_mapping_rules.reject do |mapping_rule|
215
+ matching_metric = unique_target_mapping_rules.find do |target|
216
+ compare_hashes(mapping_rule, target, %w(pattern http_method delta)) &&
217
+ metrics_mapping.fetch(mapping_rule.fetch('metric_id')) == target.fetch('metric_id')
218
+ end
219
+
220
+ unique_target_mapping_rules.delete(matching_metric)
221
+ end
222
+ end
223
+
224
+ puts "missing #{missing_mapping_rules.size} mapping rules"
225
+
226
+ missing_mapping_rules.each do |mapping_rule|
227
+ mapping_rule.delete('links')
228
+ mapping_rule['metric_id'] = metrics_mapping.fetch(mapping_rule.delete('metric_id'))
229
+ target_client.create_mapping_rule(target_service_id, mapping_rule)
230
+ end
231
+ puts "created #{missing_mapping_rules.size} mapping rules"
232
+ end
233
+
234
+ def update_service force_mapping_rules=false
235
+ copy_service_settings
236
+ copy_proxy_settings
237
+ copy_metrics_and_methods
238
+ copy_application_plans
239
+ copy_mapping_rules force_mapping_rules
240
+ end
241
+ end
242
+
243
+ case (command = ARGV.shift)
244
+ when 'service'
245
+ source = fetch_option options, :source
246
+ destination = fetch_option options, :destination
247
+
248
+ rules_only = options[:rules_only] == true
249
+ force_update = options[:force] == true
250
+
251
+ source_service_id = ARGV.shift or raise OptionParser::MissingArgument, 'source_service_id'
252
+ target_service_id = ARGV.shift or raise OptionParser::MissingArgument, 'destination_service_id'
253
+
254
+ updater = ServiceUpdater.new(source, source_service_id, destination, target_service_id)
255
+
256
+ if rules_only
257
+ updater.copy_mapping_rules force_update
258
+ else
259
+ updater.update_service force_update
260
+ end
261
+
262
+ when 'help'
263
+ print_help.call
264
+ when nil
265
+ print_help.call("missing subcommand")
266
+ exit 1
267
+ else
268
+ print_help.call("unknown command #{command}")
269
+ exit 1
270
+ end
@@ -1,3 +1,3 @@
1
1
  module ThreeScaleToolbox
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: 3scale_toolbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Cichra
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-06-14 00:00:00.000000000 Z
11
+ date: 2018-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -60,6 +60,7 @@ executables:
60
60
  - 3scale-copy
61
61
  - 3scale-help
62
62
  - 3scale-import
63
+ - 3scale-update
63
64
  extensions: []
64
65
  extra_rdoc_files: []
65
66
  files:
@@ -68,6 +69,7 @@ files:
68
69
  - exe/3scale-copy
69
70
  - exe/3scale-help
70
71
  - exe/3scale-import
72
+ - exe/3scale-update
71
73
  - lib/3scale_toolbox.rb
72
74
  - lib/3scale_toolbox/cli.rb
73
75
  - lib/3scale_toolbox/version.rb
@@ -91,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
93
  version: '0'
92
94
  requirements: []
93
95
  rubyforge_project:
94
- rubygems_version: 2.6.12
96
+ rubygems_version: 2.7.3
95
97
  signing_key:
96
98
  specification_version: 4
97
99
  summary: 3scale CLI Toolbox.