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.
- 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
|