3scale_toolbox 0.5.1 → 0.6.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.
- checksums.yaml +4 -4
- data/README.md +143 -23
- data/exe/3scale +10 -3
- data/lib/3scale_toolbox.rb +18 -0
- data/lib/3scale_toolbox/3scale_client_factory.rb +33 -0
- data/lib/3scale_toolbox/base_command.rb +52 -14
- data/lib/3scale_toolbox/cli.rb +26 -5
- data/lib/3scale_toolbox/cli/error_handler.rb +120 -0
- data/lib/3scale_toolbox/commands.rb +3 -9
- data/lib/3scale_toolbox/commands/3scale_command.rb +8 -6
- data/lib/3scale_toolbox/commands/copy_command.rb +4 -4
- data/lib/3scale_toolbox/commands/copy_command/copy_service.rb +40 -193
- data/lib/3scale_toolbox/commands/help_command.rb +1 -1
- data/lib/3scale_toolbox/commands/import_command.rb +6 -4
- data/lib/3scale_toolbox/commands/import_command/import_csv.rb +15 -41
- data/lib/3scale_toolbox/commands/import_command/openapi.rb +70 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/create_mapping_rule_step.rb +18 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/create_method_step.rb +39 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/create_service_step.rb +69 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/mapping_rule.rb +35 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/method.rb +25 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/operation.rb +22 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/resource_reader.rb +49 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/step.rb +45 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/threescale_api_spec.rb +33 -0
- data/lib/3scale_toolbox/commands/remote_command.rb +36 -0
- data/lib/3scale_toolbox/commands/remote_command/remote_add.rb +47 -0
- data/lib/3scale_toolbox/commands/remote_command/remote_list.rb +29 -0
- data/lib/3scale_toolbox/commands/remote_command/remote_remove.rb +26 -0
- data/lib/3scale_toolbox/commands/remote_command/remote_rename.rb +42 -0
- data/lib/3scale_toolbox/commands/update_command.rb +4 -4
- data/lib/3scale_toolbox/commands/update_command/update_service.rb +45 -235
- data/lib/3scale_toolbox/configuration.rb +35 -0
- data/lib/3scale_toolbox/entities.rb +1 -0
- data/lib/3scale_toolbox/entities/service.rb +113 -0
- data/lib/3scale_toolbox/error.rb +8 -0
- data/lib/3scale_toolbox/helper.rb +37 -0
- data/lib/3scale_toolbox/remotes.rb +93 -0
- data/lib/3scale_toolbox/tasks.rb +10 -0
- data/lib/3scale_toolbox/tasks/copy_app_plans_task.rb +31 -0
- data/lib/3scale_toolbox/tasks/copy_limits_task.rb +36 -0
- data/lib/3scale_toolbox/tasks/copy_mapping_rules_task.rb +29 -0
- data/lib/3scale_toolbox/tasks/copy_methods_task.rb +29 -0
- data/lib/3scale_toolbox/tasks/copy_metrics_task.rb +33 -0
- data/lib/3scale_toolbox/tasks/copy_service_proxy_task.rb +12 -0
- data/lib/3scale_toolbox/tasks/copy_task.rb +26 -0
- data/lib/3scale_toolbox/tasks/destroy_mapping_rules_task.rb +22 -0
- data/lib/3scale_toolbox/tasks/helper_task.rb +25 -0
- data/lib/3scale_toolbox/tasks/update_service_settings_task.rb +32 -0
- data/lib/3scale_toolbox/version.rb +1 -1
- 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,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
|