3scale_toolbox 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +143 -23
  3. data/exe/3scale +10 -3
  4. data/lib/3scale_toolbox.rb +18 -0
  5. data/lib/3scale_toolbox/3scale_client_factory.rb +33 -0
  6. data/lib/3scale_toolbox/base_command.rb +52 -14
  7. data/lib/3scale_toolbox/cli.rb +26 -5
  8. data/lib/3scale_toolbox/cli/error_handler.rb +120 -0
  9. data/lib/3scale_toolbox/commands.rb +3 -9
  10. data/lib/3scale_toolbox/commands/3scale_command.rb +8 -6
  11. data/lib/3scale_toolbox/commands/copy_command.rb +4 -4
  12. data/lib/3scale_toolbox/commands/copy_command/copy_service.rb +40 -193
  13. data/lib/3scale_toolbox/commands/help_command.rb +1 -1
  14. data/lib/3scale_toolbox/commands/import_command.rb +6 -4
  15. data/lib/3scale_toolbox/commands/import_command/import_csv.rb +15 -41
  16. data/lib/3scale_toolbox/commands/import_command/openapi.rb +70 -0
  17. data/lib/3scale_toolbox/commands/import_command/openapi/create_mapping_rule_step.rb +18 -0
  18. data/lib/3scale_toolbox/commands/import_command/openapi/create_method_step.rb +39 -0
  19. data/lib/3scale_toolbox/commands/import_command/openapi/create_service_step.rb +69 -0
  20. data/lib/3scale_toolbox/commands/import_command/openapi/mapping_rule.rb +35 -0
  21. data/lib/3scale_toolbox/commands/import_command/openapi/method.rb +25 -0
  22. data/lib/3scale_toolbox/commands/import_command/openapi/operation.rb +22 -0
  23. data/lib/3scale_toolbox/commands/import_command/openapi/resource_reader.rb +49 -0
  24. data/lib/3scale_toolbox/commands/import_command/openapi/step.rb +45 -0
  25. data/lib/3scale_toolbox/commands/import_command/openapi/threescale_api_spec.rb +33 -0
  26. data/lib/3scale_toolbox/commands/remote_command.rb +36 -0
  27. data/lib/3scale_toolbox/commands/remote_command/remote_add.rb +47 -0
  28. data/lib/3scale_toolbox/commands/remote_command/remote_list.rb +29 -0
  29. data/lib/3scale_toolbox/commands/remote_command/remote_remove.rb +26 -0
  30. data/lib/3scale_toolbox/commands/remote_command/remote_rename.rb +42 -0
  31. data/lib/3scale_toolbox/commands/update_command.rb +4 -4
  32. data/lib/3scale_toolbox/commands/update_command/update_service.rb +45 -235
  33. data/lib/3scale_toolbox/configuration.rb +35 -0
  34. data/lib/3scale_toolbox/entities.rb +1 -0
  35. data/lib/3scale_toolbox/entities/service.rb +113 -0
  36. data/lib/3scale_toolbox/error.rb +8 -0
  37. data/lib/3scale_toolbox/helper.rb +37 -0
  38. data/lib/3scale_toolbox/remotes.rb +93 -0
  39. data/lib/3scale_toolbox/tasks.rb +10 -0
  40. data/lib/3scale_toolbox/tasks/copy_app_plans_task.rb +31 -0
  41. data/lib/3scale_toolbox/tasks/copy_limits_task.rb +36 -0
  42. data/lib/3scale_toolbox/tasks/copy_mapping_rules_task.rb +29 -0
  43. data/lib/3scale_toolbox/tasks/copy_methods_task.rb +29 -0
  44. data/lib/3scale_toolbox/tasks/copy_metrics_task.rb +33 -0
  45. data/lib/3scale_toolbox/tasks/copy_service_proxy_task.rb +12 -0
  46. data/lib/3scale_toolbox/tasks/copy_task.rb +26 -0
  47. data/lib/3scale_toolbox/tasks/destroy_mapping_rules_task.rb +22 -0
  48. data/lib/3scale_toolbox/tasks/helper_task.rb +25 -0
  49. data/lib/3scale_toolbox/tasks/update_service_settings_task.rb +32 -0
  50. data/lib/3scale_toolbox/version.rb +1 -1
  51. metadata +87 -11
@@ -0,0 +1,35 @@
1
+ require 'yaml/store'
2
+
3
+ module ThreeScaleToolbox
4
+ class Configuration
5
+ attr_reader :config_file
6
+
7
+ def initialize(config_file)
8
+ @config_file = config_file
9
+ @store = YAML::Store.new(config_file)
10
+ end
11
+
12
+ def data(key)
13
+ read[key]
14
+ end
15
+
16
+ def update(key)
17
+ return if key.nil?
18
+
19
+ @store.transaction do
20
+ @store[key] = yield @store[key]
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ # returns copy of data stored
27
+ def read
28
+ @store.transaction(true) do
29
+ @store.roots.each_with_object({}) do |key, obj|
30
+ obj[key] = @store[key]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1 @@
1
+ require '3scale_toolbox/entities/service'
@@ -0,0 +1,113 @@
1
+ module ThreeScaleToolbox
2
+ module Entities
3
+ class Service
4
+ VALID_PARAMS = %w[
5
+ name backend_version deployment_option description
6
+ system_name end_user_registration_required
7
+ support_email tech_support_email admin_support_email
8
+ ].freeze
9
+ public_constant :VALID_PARAMS
10
+
11
+ class << self
12
+ def create(remote:, service:, system_name:)
13
+ svc_obj = remote.create_service copy_service_params(service, system_name)
14
+ unless svc_obj['errors'].nil?
15
+ raise ThreeScaleToolbox::Error, 'Service has not been saved. ' \
16
+ "Errors: #{svc_obj['errors']}"
17
+ end
18
+
19
+ new(id: svc_obj.fetch('id'), remote: remote)
20
+ end
21
+
22
+ private
23
+
24
+ def copy_service_params(original, system_name)
25
+ service_params = Helper.filter_params(VALID_PARAMS, original)
26
+ service_params.tap do |hash|
27
+ hash['system_name'] = system_name if system_name
28
+ end
29
+ end
30
+ end
31
+
32
+ attr_reader :id, :remote
33
+
34
+ def initialize(id:, remote:)
35
+ @id = id
36
+ @remote = remote
37
+ end
38
+
39
+ def show_service
40
+ remote.show_service id
41
+ end
42
+
43
+ def update_proxy(proxy)
44
+ remote.update_proxy id, proxy
45
+ end
46
+
47
+ def show_proxy
48
+ remote.show_proxy id
49
+ end
50
+
51
+ def metrics
52
+ remote.list_metrics id
53
+ end
54
+
55
+ def hits
56
+ hits_metric = metrics.find do |metric|
57
+ metric['system_name'] == 'hits'
58
+ end
59
+ raise ThreeScaleToolbox::Error, 'missing hits metric' if hits_metric.nil?
60
+
61
+ hits_metric
62
+ end
63
+
64
+ def methods
65
+ remote.list_methods id, hits['id']
66
+ end
67
+
68
+ def create_metric(metric)
69
+ remote.create_metric id, metric
70
+ end
71
+
72
+ def create_method(parent_metric_id, method)
73
+ remote.create_method id, parent_metric_id, method
74
+ end
75
+
76
+ def plans
77
+ remote.list_service_application_plans id
78
+ end
79
+
80
+ def create_application_plan(plan)
81
+ remote.create_application_plan id, plan
82
+ end
83
+
84
+ def plan_limits(plan_id)
85
+ remote.list_application_plan_limits(plan_id)
86
+ end
87
+
88
+ def create_application_plan_limit(plan_id, metric_id, limit)
89
+ remote.create_application_plan_limit plan_id, metric_id, limit
90
+ end
91
+
92
+ def mapping_rules
93
+ remote.list_mapping_rules id
94
+ end
95
+
96
+ def delete_mapping_rule(rule_id)
97
+ remote.delete_mapping_rule(id, rule_id)
98
+ end
99
+
100
+ def create_mapping_rule(mapping_rule)
101
+ remote.create_mapping_rule id, mapping_rule
102
+ end
103
+
104
+ def update_service(params)
105
+ remote.update_service(id, params)
106
+ end
107
+
108
+ def delete_service
109
+ remote.delete_service id
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,8 @@
1
+ module ThreeScaleToolbox
2
+ # Generic error. Superclass for all specific errors.
3
+ class Error < ::StandardError
4
+ end
5
+
6
+ class InvalidUrlError < Error
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ module ThreeScaleToolbox
2
+ module Helper
3
+ class << self
4
+ def compare_hashes(first, second, keys)
5
+ keys.map { |key| first.fetch(key, nil) } == keys.map { |key| second.fetch(key, nil) }
6
+ end
7
+
8
+ ##
9
+ # Compute array difference with custom comparator
10
+ def array_difference(ary, other_ary)
11
+ ary.reject do |ary_elem|
12
+ other_ary.find do |other_ary_elem|
13
+ yield(ary_elem, other_ary_elem)
14
+ end
15
+ end
16
+ end
17
+
18
+ ##
19
+ # Returns new hash object with not nil valid params
20
+ def filter_params(valid_params, source)
21
+ valid_params.each_with_object({}) do |key, target|
22
+ target[key] = source[key] unless source[key].nil?
23
+ end
24
+ end
25
+
26
+ def parse_uri(uri)
27
+ # raises error when remote_str is not string, but object or something else.
28
+ uri_obj = URI(uri)
29
+ # URI::HTTP is parent of URI::HTTPS
30
+ # with single check both types are checked
31
+ raise ThreeScaleToolbox::InvalidUrlError, "invalid url: #{uri}" unless uri_obj.kind_of?(URI::HTTP)
32
+
33
+ uri_obj
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,93 @@
1
+ module ThreeScaleToolbox
2
+ class Remotes
3
+ class << self
4
+ def from_uri(uri_str)
5
+ uri = Helper.parse_uri(uri_str)
6
+
7
+ authentication = uri.user
8
+ uri.user = ''
9
+ { authentication: authentication, endpoint: uri.to_s }
10
+ end
11
+ end
12
+
13
+ def initialize(config)
14
+ @config = config
15
+ end
16
+
17
+ ##
18
+ # Fetch remotes
19
+ # Perform validation
20
+ #
21
+ def all
22
+ rmts = (config.data :remotes) || {}
23
+ raise_invalid unless validate(rmts)
24
+ rmts
25
+ end
26
+
27
+ def add_uri(name, uri)
28
+ remote = self.class.from_uri(uri)
29
+ add(name, remote)
30
+ end
31
+
32
+ def add(key, remote)
33
+ update do |rmts|
34
+ rmts.tap { |r| r[key] = remote }
35
+ end
36
+ end
37
+
38
+ def delete(key)
39
+ value = nil
40
+ update do |rmts|
41
+ # block should return rmts
42
+ # but main method should return deleted value
43
+ rmts.tap do |r|
44
+ value = if block_given?
45
+ r.delete(key, &Proc.new)
46
+ else
47
+ r.delete(key)
48
+ end
49
+ end
50
+ end
51
+ value
52
+ end
53
+
54
+ def fetch(name)
55
+ all.fetch name
56
+ end
57
+
58
+ private
59
+
60
+ attr_reader :config
61
+
62
+ ##
63
+ # Update remotes
64
+ # Perform validation
65
+ #
66
+ def update
67
+ config.update(:remotes) do |rmts|
68
+ yield(rmts || {}).tap do |new_rmts|
69
+ raise_invalid unless validate(new_rmts)
70
+ end
71
+ end
72
+ end
73
+
74
+ def raise_not_found(remote_str)
75
+ raise ThreeScaleToolbox::Error, "remote '#{remote_str}' not found from config file #{config.config_file}"
76
+ end
77
+
78
+ def raise_invalid
79
+ raise ThreeScaleToolbox::Error, "invalid remote configuration from config file #{config.config_file}"
80
+ end
81
+
82
+ def valid?(remote)
83
+ remote.is_a?(Hash) && remote.key?(:endpoint) && remote.key?(:authentication)
84
+ end
85
+
86
+ def validate(remotes)
87
+ case remotes
88
+ when Hash then remotes.values.all?(&method(:valid?))
89
+ else false
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,10 @@
1
+ require '3scale_toolbox/tasks/copy_task'
2
+ require '3scale_toolbox/tasks/helper_task'
3
+ require '3scale_toolbox/tasks/copy_service_proxy_task'
4
+ require '3scale_toolbox/tasks/copy_metrics_task'
5
+ require '3scale_toolbox/tasks/copy_methods_task'
6
+ require '3scale_toolbox/tasks/copy_app_plans_task'
7
+ require '3scale_toolbox/tasks/copy_limits_task'
8
+ require '3scale_toolbox/tasks/destroy_mapping_rules_task'
9
+ require '3scale_toolbox/tasks/copy_mapping_rules_task'
10
+ require '3scale_toolbox/tasks/update_service_settings_task'
@@ -0,0 +1,31 @@
1
+ module ThreeScaleToolbox
2
+ module Tasks
3
+ class CopyApplicationPlansTask
4
+ include CopyTask
5
+
6
+ def call
7
+ source_plans = source.plans
8
+ target_plans = target.plans
9
+ missing_plans = missing_app_plans(source_plans, target_plans)
10
+ missing_plans.each do |plan|
11
+ plan.delete('links')
12
+ plan.delete('default') # TODO: handle default plan
13
+ if plan.delete('custom') # TODO: what to do with custom plans?
14
+ puts "skipping custom plan #{plan}"
15
+ else
16
+ target.create_application_plan(plan)
17
+ end
18
+ end
19
+ puts "target service missing #{missing_plans.size} application plans"
20
+ end
21
+
22
+ private
23
+
24
+ def missing_app_plans(source_plans, target_plans)
25
+ ThreeScaleToolbox::Helper.array_difference(source_plans, target_plans) do |src, target|
26
+ ThreeScaleToolbox::Helper.compare_hashes(src, target, ['system_name'])
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ module ThreeScaleToolbox
2
+ module Tasks
3
+ class CopyLimitsTask
4
+ include CopyTask
5
+ include Helper
6
+
7
+ def call
8
+ metrics_map = metrics_mapping(source.metrics, target.metrics)
9
+ plan_mapping = application_plan_mapping(source.plans, target.plans)
10
+ plan_mapping.each do |plan_id, target_plan|
11
+ limits = source.plan_limits(plan_id)
12
+ limits_target = target.plan_limits(target_plan['id'])
13
+ missing_limits = missing_limits(limits, limits_target)
14
+ missing_limits.each do |limit|
15
+ limit.delete('links')
16
+ target.create_application_plan_limit(
17
+ target_plan['id'],
18
+ metrics_map.fetch(limit.fetch('metric_id')),
19
+ limit
20
+ )
21
+ end
22
+ puts "target application plan #{target_plan['id']} is missing" \
23
+ " #{missing_limits.size} from the original plan #{plan_id}"
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def missing_limits(source_limits, target_limits)
30
+ ThreeScaleToolbox::Helper.array_difference(source_limits, target_limits) do |limit, target|
31
+ ThreeScaleToolbox::Helper.compare_hashes(limit, target, ['period'])
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ module ThreeScaleToolbox
2
+ module Tasks
3
+ class CopyMappingRulesTask
4
+ include CopyTask
5
+ include Helper
6
+
7
+ def call
8
+ metrics_map = metrics_mapping(source.metrics, target.metrics)
9
+ missing_rules = missing_mapping_rules(source.mapping_rules,
10
+ target.mapping_rules, metrics_map)
11
+ missing_rules.each do |mapping_rule|
12
+ mapping_rule.delete('links')
13
+ mapping_rule['metric_id'] = metrics_map.fetch(mapping_rule.delete('metric_id'))
14
+ target.create_mapping_rule mapping_rule
15
+ end
16
+ puts "created #{missing_rules.size} mapping rules"
17
+ end
18
+
19
+ private
20
+
21
+ def missing_mapping_rules(source_rules, target_rules, metrics_map)
22
+ ThreeScaleToolbox::Helper.array_difference(source_rules, target_rules) do |source_rule, target_rule|
23
+ ThreeScaleToolbox::Helper.compare_hashes(source_rule, target_rule, %w[pattern http_method delta]) &&
24
+ metrics_map.fetch(source_rule.fetch('metric_id')) == target_rule.fetch('metric_id')
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module ThreeScaleToolbox
2
+ module Tasks
3
+ class CopyMethodsTask
4
+ include CopyTask
5
+
6
+ def call
7
+ source_methods = source.methods
8
+ target_methods = target.methods
9
+ target_hits_metric_id = target.hits['id']
10
+ puts "original service hits metric #{source.hits['id']} has #{source_methods.size} methods"
11
+ puts "target service hits metric #{target_hits_metric_id} has #{target_methods.size} methods"
12
+ missing = missing_methods(source_methods, target_methods).each do |method|
13
+ filtered_method = ThreeScaleToolbox::Helper.filter_params(%w[friendly_name system_name],
14
+ method)
15
+ target.create_method(target_hits_metric_id, filtered_method)
16
+ end
17
+ puts "created #{missing.size} missing methods on target service"
18
+ end
19
+
20
+ private
21
+
22
+ def missing_methods(source_methods, target_methods)
23
+ ThreeScaleToolbox::Helper.array_difference(source_methods, target_methods) do |method, target|
24
+ ThreeScaleToolbox::Helper.compare_hashes(method, target, ['system_name'])
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ module ThreeScaleToolbox
2
+ module Tasks
3
+ class CopyMetricsTask
4
+ include CopyTask
5
+
6
+ def call
7
+ source_metrics = source.metrics
8
+ target_metrics = target.metrics
9
+
10
+ puts "original service has #{source_metrics.size} metrics"
11
+ puts "target service has #{target_metrics.size} metrics"
12
+
13
+ missing = missing_metrics(source_metrics, target_metrics)
14
+
15
+ missing.each do |metric|
16
+ metric.delete('links')
17
+ target.create_metric(metric)
18
+ end
19
+
20
+ puts "created #{missing.size} metrics on the target service"
21
+ end
22
+
23
+ private
24
+
25
+ def missing_metrics(source_metrics, target_metrics)
26
+ ThreeScaleToolbox::Helper.array_difference(source_metrics,
27
+ target_metrics) do |source, target|
28
+ ThreeScaleToolbox::Helper.compare_hashes(source, target, ['system_name'])
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end