3scale_toolbox 0.7.0 → 0.8.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 +7 -1
- data/lib/3scale_toolbox.rb +2 -0
- data/lib/3scale_toolbox/3scale_client_factory.rb +9 -5
- data/lib/3scale_toolbox/base_command.rb +5 -1
- data/lib/3scale_toolbox/cli.rb +8 -4
- data/lib/3scale_toolbox/commands/3scale_command.rb +6 -0
- data/lib/3scale_toolbox/commands/copy_command.rb +4 -0
- data/lib/3scale_toolbox/commands/import_command.rb +4 -0
- data/lib/3scale_toolbox/commands/import_command/openapi.rb +23 -11
- data/lib/3scale_toolbox/commands/import_command/openapi/create_activedocs_step.rb +4 -2
- data/lib/3scale_toolbox/commands/import_command/openapi/create_service_step.rb +6 -1
- data/lib/3scale_toolbox/commands/import_command/openapi/method.rb +1 -1
- data/lib/3scale_toolbox/commands/import_command/openapi/resource_reader.rb +2 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/step.rb +12 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/threescale_api_spec.rb +36 -1
- data/lib/3scale_toolbox/commands/import_command/openapi/update_policies_step.rb +89 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/update_service_oidc_conf_step.rb +59 -0
- data/lib/3scale_toolbox/commands/import_command/openapi/update_service_proxy_step.rb +68 -0
- data/lib/3scale_toolbox/commands/update_command.rb +4 -0
- data/lib/3scale_toolbox/entities/service.rb +29 -4
- data/lib/3scale_toolbox/helper.rb +6 -0
- data/lib/3scale_toolbox/proxy_logger.rb +20 -0
- data/lib/3scale_toolbox/swagger.rb +1 -0
- data/lib/3scale_toolbox/swagger/swagger.rb +121 -0
- data/lib/3scale_toolbox/tasks/update_service_settings_task.rb +19 -2
- data/lib/3scale_toolbox/version.rb +1 -1
- data/resources/swagger_meta_schema.json +1607 -0
- metadata +15 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15a0f5fc1496d1414d2e5a940b1d08d8834dfc23a226f7c3a6d17d3014f1042f
|
4
|
+
data.tar.gz: e09c49ae0c9023757c2cf22dd2c10a61d009337a67aca4d8080ae2fb7a65eb2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd16657b1d83f114fdaf3d4b0ad1fd39fc08ab3a267e441b332333d22933e620cb7bd1cad48de66711594a9c10fcee8bed894e0b03170baaf6cfee53d7decfd5
|
7
|
+
data.tar.gz: 16838e3a0c469ed68f4064babce60fe207d77a06c8f869035115422a1d461360da3f2237fd45c6f31d0cd62602646254f4ed2b6a5b25546e318a2afa85e2732a
|
data/README.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# 3scale toolbox
|
2
|
+
[](https://travis-ci.org/3scale/3scale_toolbox)
|
2
3
|
|
4
|
+
This software is licensed under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0).
|
5
|
+
|
6
|
+
See the LICENSE and NOTICE files that should have been provided along with this software for details.
|
7
|
+
|
8
|
+
## Description
|
3
9
|
3scale toolbox is a set of tools to help you manage your 3scale product. Using the [3scale API Ruby Client](https://github.com/3scale/3scale-api-ruby).
|
4
10
|
|
5
11
|
## Table of contents
|
@@ -76,7 +82,7 @@ OPTIONS
|
|
76
82
|
remote name
|
77
83
|
-s --source=<value> 3scale source instance. Url or
|
78
84
|
remote name
|
79
|
-
-t --target_system_name=<value> Target system name. Default to
|
85
|
+
-t --target_system_name=<value> Target system name. Default to
|
80
86
|
source system name
|
81
87
|
|
82
88
|
OPTIONS FOR COPY
|
data/lib/3scale_toolbox.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require '3scale_toolbox/version'
|
2
2
|
require '3scale_toolbox/helper'
|
3
3
|
require '3scale_toolbox/error'
|
4
|
+
require '3scale_toolbox/proxy_logger'
|
5
|
+
require '3scale_toolbox/swagger'
|
4
6
|
require '3scale_toolbox/configuration'
|
5
7
|
require '3scale_toolbox/remotes'
|
6
8
|
require '3scale_toolbox/3scale_client_factory'
|
@@ -1,17 +1,18 @@
|
|
1
1
|
module ThreeScaleToolbox
|
2
2
|
class ThreeScaleClientFactory
|
3
3
|
class << self
|
4
|
-
def get(remotes, remote_str, verify_ssl)
|
5
|
-
new(remotes, remote_str, verify_ssl).call
|
4
|
+
def get(remotes, remote_str, verify_ssl, verbose = false)
|
5
|
+
new(remotes, remote_str, verify_ssl, verbose).call
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
attr_reader :remotes, :remote_str, :verify_ssl
|
9
|
+
attr_reader :remotes, :remote_str, :verify_ssl, :verbose
|
10
10
|
|
11
|
-
def initialize(remotes, remote_str, verify_ssl)
|
11
|
+
def initialize(remotes, remote_str, verify_ssl, verbose)
|
12
12
|
@remotes = remotes
|
13
13
|
@remote_str = remote_str
|
14
14
|
@verify_ssl = verify_ssl
|
15
|
+
@verbose = verbose
|
15
16
|
end
|
16
17
|
|
17
18
|
def call
|
@@ -21,7 +22,10 @@ module ThreeScaleToolbox
|
|
21
22
|
remote = remotes.fetch(remote_str)
|
22
23
|
end
|
23
24
|
|
24
|
-
remote_client(remote.merge(verify_ssl: verify_ssl))
|
25
|
+
client = remote_client(remote.merge(verify_ssl: verify_ssl))
|
26
|
+
return ProxyLogger.new(client) if verbose
|
27
|
+
|
28
|
+
client
|
25
29
|
end
|
26
30
|
|
27
31
|
private
|
@@ -47,7 +47,7 @@ module ThreeScaleToolbox
|
|
47
47
|
# Input param can be endpoint url or remote name
|
48
48
|
#
|
49
49
|
def threescale_client(str)
|
50
|
-
ThreeScaleClientFactory.get(remotes, str, verify_ssl)
|
50
|
+
ThreeScaleClientFactory.get(remotes, str, verify_ssl, verbose)
|
51
51
|
end
|
52
52
|
|
53
53
|
def verify_ssl
|
@@ -55,6 +55,10 @@ module ThreeScaleToolbox
|
|
55
55
|
!options[:insecure]
|
56
56
|
end
|
57
57
|
|
58
|
+
def verbose
|
59
|
+
options[:verbose]
|
60
|
+
end
|
61
|
+
|
58
62
|
def exit_with_message(message)
|
59
63
|
raise ThreeScaleToolbox::Error, message
|
60
64
|
end
|
data/lib/3scale_toolbox/cli.rb
CHANGED
@@ -15,7 +15,9 @@ module ThreeScaleToolbox::CLI
|
|
15
15
|
|
16
16
|
def self.install_signal_handlers
|
17
17
|
# Set exit handler
|
18
|
-
|
18
|
+
# Only OS supported signals
|
19
|
+
available_signals = %w[INT TERM].select { |signal_name| Signal.list.key? signal_name }
|
20
|
+
available_signals.each do |signal|
|
19
21
|
Signal.trap(signal) do
|
20
22
|
puts
|
21
23
|
exit!(0)
|
@@ -24,9 +26,11 @@ module ThreeScaleToolbox::CLI
|
|
24
26
|
|
25
27
|
# Set stack trace dump handler
|
26
28
|
if !defined?(RUBY_ENGINE) || RUBY_ENGINE != 'jruby'
|
27
|
-
Signal.
|
28
|
-
|
29
|
-
|
29
|
+
if Signal.list.key? 'USR1'
|
30
|
+
Signal.trap('USR1') do
|
31
|
+
puts 'Caught USR1; dumping a stack trace'
|
32
|
+
puts caller.map { |i| " #{i}" }.join("\n")
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -6,6 +6,7 @@ module ThreeScaleToolbox
|
|
6
6
|
module Commands
|
7
7
|
module ThreeScaleCommand
|
8
8
|
include ThreeScaleToolbox::Command
|
9
|
+
|
9
10
|
def self.command
|
10
11
|
Cri::Command.define do
|
11
12
|
name '3scale'
|
@@ -19,10 +20,15 @@ module ThreeScaleToolbox
|
|
19
20
|
exit 0
|
20
21
|
end
|
21
22
|
flag :k, :insecure, 'Proceed and operate even for server connections otherwise considered insecure'
|
23
|
+
flag nil, :verbose, 'Verbose mode'
|
22
24
|
flag :h, :help, 'show help for this command' do |_, cmd|
|
23
25
|
puts cmd.help
|
24
26
|
exit 0
|
25
27
|
end
|
28
|
+
|
29
|
+
run do |_opts, _args, cmd|
|
30
|
+
puts cmd.help
|
31
|
+
end
|
26
32
|
end
|
27
33
|
end
|
28
34
|
end
|
@@ -12,6 +12,10 @@ module ThreeScaleToolbox
|
|
12
12
|
usage 'copy <sub-command> [options]'
|
13
13
|
summary 'copy super command'
|
14
14
|
description 'Copy 3scale entities between tenants'
|
15
|
+
|
16
|
+
run do |_opts, _args, cmd|
|
17
|
+
puts cmd.help
|
18
|
+
end
|
15
19
|
end
|
16
20
|
end
|
17
21
|
add_subcommand(CopyServiceSubcommand)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'swagger'
|
2
1
|
require '3scale_toolbox/commands/import_command/openapi/method'
|
3
2
|
require '3scale_toolbox/commands/import_command/openapi/mapping_rule'
|
4
3
|
require '3scale_toolbox/commands/import_command/openapi/operation'
|
@@ -9,6 +8,9 @@ require '3scale_toolbox/commands/import_command/openapi/create_method_step'
|
|
9
8
|
require '3scale_toolbox/commands/import_command/openapi/create_mapping_rule_step'
|
10
9
|
require '3scale_toolbox/commands/import_command/openapi/create_service_step'
|
11
10
|
require '3scale_toolbox/commands/import_command/openapi/create_activedocs_step'
|
11
|
+
require '3scale_toolbox/commands/import_command/openapi/update_service_proxy_step'
|
12
|
+
require '3scale_toolbox/commands/import_command/openapi/update_service_oidc_conf_step'
|
13
|
+
require '3scale_toolbox/commands/import_command/openapi/update_policies_step'
|
12
14
|
|
13
15
|
module ThreeScaleToolbox
|
14
16
|
module Commands
|
@@ -27,6 +29,10 @@ module ThreeScaleToolbox
|
|
27
29
|
|
28
30
|
option :d, :destination, '3scale target instance. Format: "http[s]://<authentication>@3scale_domain"', argument: :required
|
29
31
|
option :t, 'target_system_name', 'Target system name', argument: :required
|
32
|
+
flag nil, 'activedocs-hidden', 'Create ActiveDocs in hidden state'
|
33
|
+
flag nil, 'skip-openapi-validation', 'Skip OpenAPI schema validation'
|
34
|
+
option nil, 'oidc-issuer-endpoint', 'OIDC Issuer Endpoint', argument: :required
|
35
|
+
option nil, 'default-credentials-userkey', 'Default credentials policy userkey', argument: :required
|
30
36
|
param :openapi_resource
|
31
37
|
|
32
38
|
runner OpenAPISubcommand
|
@@ -34,15 +40,15 @@ module ThreeScaleToolbox
|
|
34
40
|
end
|
35
41
|
|
36
42
|
def run
|
37
|
-
openapi_resource = load_resource(arguments[:openapi_resource])
|
38
|
-
context = create_context(openapi_resource)
|
39
|
-
|
40
43
|
tasks = []
|
41
44
|
tasks << CreateServiceStep.new(context)
|
42
45
|
tasks << CreateMethodsStep.new(context)
|
43
46
|
tasks << ThreeScaleToolbox::Tasks::DestroyMappingRulesTask.new(context)
|
44
47
|
tasks << CreateMappingRulesStep.new(context)
|
45
48
|
tasks << CreateActiveDocsStep.new(context)
|
49
|
+
tasks << UpdateServiceProxyStep.new(context)
|
50
|
+
tasks << UpdateServiceOidcConfStep.new(context)
|
51
|
+
tasks << UpdatePoliciesStep.new(context)
|
46
52
|
|
47
53
|
# run tasks
|
48
54
|
tasks.each(&:call)
|
@@ -50,21 +56,27 @@ module ThreeScaleToolbox
|
|
50
56
|
|
51
57
|
private
|
52
58
|
|
53
|
-
def
|
59
|
+
def context
|
60
|
+
@context ||= create_context
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_context
|
64
|
+
openapi_resource = load_resource(arguments[:openapi_resource])
|
54
65
|
{
|
55
66
|
api_spec_resource: openapi_resource,
|
56
67
|
api_spec: ThreeScaleApiSpec.new(load_openapi(openapi_resource)),
|
57
68
|
threescale_client: threescale_client(fetch_required_option(:destination)),
|
58
|
-
target_system_name: options[:target_system_name]
|
69
|
+
target_system_name: options[:target_system_name],
|
70
|
+
activedocs_published: !options[:'activedocs-hidden'],
|
71
|
+
oidc_issuer_endpoint: options[:'oidc-issuer-endpoint'],
|
72
|
+
default_credentials_userkey: options[:'default-credentials-userkey'],
|
73
|
+
skip_openapi_validation: options[:'skip-openapi-validation']
|
59
74
|
}
|
60
75
|
end
|
61
76
|
|
62
77
|
def load_openapi(openapi_resource)
|
63
|
-
Swagger.build(openapi_resource)
|
64
|
-
|
65
|
-
# does not pass validation. Maybe library's schema is outdated?
|
66
|
-
# openapi.tap(&:validate)
|
67
|
-
rescue Swagger::InvalidDefinition, Hashie::CoercionError, Psych::SyntaxError => e
|
78
|
+
Swagger.build(openapi_resource, validate: !options[:'skip-openapi-validation'])
|
79
|
+
rescue JSON::Schema::ValidationError => e
|
68
80
|
raise ThreeScaleToolbox::Error, "OpenAPI schema validation failed: #{e.message}"
|
69
81
|
end
|
70
82
|
end
|
@@ -10,8 +10,10 @@ module ThreeScaleToolbox
|
|
10
10
|
name: api_spec.title,
|
11
11
|
system_name: activedocs_system_name,
|
12
12
|
service_id: service.id,
|
13
|
-
body: resource
|
14
|
-
description: api_spec.description
|
13
|
+
body: JSON.pretty_generate(resource),
|
14
|
+
description: api_spec.description,
|
15
|
+
published: context[:activedocs_published],
|
16
|
+
skip_swagger_validations: context[:skip_openapi_validation]
|
15
17
|
}
|
16
18
|
|
17
19
|
res = threescale_client.create_activedocs(active_doc)
|
@@ -26,7 +26,7 @@ module ThreeScaleToolbox
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def service_system_name
|
29
|
-
target_system_name || service_name.downcase.
|
29
|
+
target_system_name || service_name.downcase.gsub(/[^\w]/, '_')
|
30
30
|
end
|
31
31
|
|
32
32
|
def service_id
|
@@ -48,6 +48,7 @@ module ThreeScaleToolbox
|
|
48
48
|
default_service_settings.tap do |svc|
|
49
49
|
svc['name'] = service_name
|
50
50
|
svc['description'] = service_description
|
51
|
+
svc['backend_version'] = backend_version
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
@@ -62,6 +63,10 @@ module ThreeScaleToolbox
|
|
62
63
|
def service_description
|
63
64
|
api_spec.description
|
64
65
|
end
|
66
|
+
|
67
|
+
def backend_version
|
68
|
+
api_spec.backend_version
|
69
|
+
end
|
65
70
|
end
|
66
71
|
end
|
67
72
|
end
|
@@ -13,6 +13,8 @@ module ThreeScaleToolbox
|
|
13
13
|
def load_resource(resource)
|
14
14
|
# Json format is parsed as well
|
15
15
|
YAML.safe_load(read_content(resource))
|
16
|
+
rescue Psych::SyntaxError => e
|
17
|
+
raise ThreeScaleToolbox::Error, "JSON/YAML validation failed: #{e.message}"
|
16
18
|
end
|
17
19
|
|
18
20
|
##
|
@@ -46,6 +46,18 @@ module ThreeScaleToolbox
|
|
46
46
|
def system_name_already_taken_error?(error)
|
47
47
|
Array(Hash(error)['system_name']).any? { |msg| msg.match(/has already been taken/) }
|
48
48
|
end
|
49
|
+
|
50
|
+
def security
|
51
|
+
api_spec.security
|
52
|
+
end
|
53
|
+
|
54
|
+
def oidc_issuer_endpoint
|
55
|
+
context[:oidc_issuer_endpoint]
|
56
|
+
end
|
57
|
+
|
58
|
+
def default_credentials_userkey
|
59
|
+
context[:default_credentials_userkey]
|
60
|
+
end
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
@@ -17,15 +17,50 @@ module ThreeScaleToolbox
|
|
17
17
|
openapi.info.description
|
18
18
|
end
|
19
19
|
|
20
|
+
def host
|
21
|
+
openapi.host
|
22
|
+
end
|
23
|
+
|
24
|
+
def schemes
|
25
|
+
Array(openapi.schemes)
|
26
|
+
end
|
27
|
+
|
28
|
+
def backend_version
|
29
|
+
# default authentication mode if no security requirement
|
30
|
+
return '1' if security.nil?
|
31
|
+
|
32
|
+
case security.type
|
33
|
+
when 'oauth2'
|
34
|
+
'oidc'
|
35
|
+
when 'apiKey'
|
36
|
+
'1'
|
37
|
+
else
|
38
|
+
raise ThreeScaleToolbox::Error, "Unexpected security scheme type #{security.type}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def security
|
43
|
+
@security ||= parse_security
|
44
|
+
end
|
45
|
+
|
20
46
|
def operations
|
21
47
|
openapi.operations.map do |op|
|
22
48
|
Operation.new(
|
23
49
|
path: "#{openapi.base_path}#{op.path}",
|
24
50
|
verb: op.verb,
|
25
|
-
operationId: op.
|
51
|
+
operationId: op.operation_id
|
26
52
|
)
|
27
53
|
end
|
28
54
|
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def parse_security
|
59
|
+
raise ThreeScaleToolbox::Error, 'Invalid OAS: multiple security requirements' \
|
60
|
+
if openapi.global_security_requirements.size > 1
|
61
|
+
|
62
|
+
openapi.global_security_requirements.first
|
63
|
+
end
|
29
64
|
end
|
30
65
|
end
|
31
66
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module ThreeScaleToolbox
|
2
|
+
module Commands
|
3
|
+
module ImportCommand
|
4
|
+
module OpenAPI
|
5
|
+
class UpdatePoliciesStep
|
6
|
+
include Step
|
7
|
+
|
8
|
+
##
|
9
|
+
# Updates Proxy Policies config
|
10
|
+
def call
|
11
|
+
# to be idempotent, we must read first
|
12
|
+
source_policies_settings = service.policies
|
13
|
+
# shallow copy
|
14
|
+
# to detect policies_settings changes, should only be updated setting objects
|
15
|
+
# do not update in-place, otherwise changes will not be detected
|
16
|
+
policies_settings = source_policies_settings.dup
|
17
|
+
|
18
|
+
add_anonymous_access_policy(policies_settings)
|
19
|
+
add_rh_sso_keycloak_role_check_policy(policies_settings)
|
20
|
+
|
21
|
+
return if source_policies_settings == policies_settings
|
22
|
+
|
23
|
+
res = service.update_policies('policies_config' => policies_settings)
|
24
|
+
if res.is_a?(Hash) && (errors = res['errors'])
|
25
|
+
raise ThreeScaleToolbox::Error, "Service policies have not been updated. #{errors}"
|
26
|
+
end
|
27
|
+
|
28
|
+
puts 'Service policies updated'
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def add_anonymous_access_policy(policies)
|
34
|
+
# only on 'open api' security req
|
35
|
+
return unless security.nil?
|
36
|
+
|
37
|
+
return if policies.any? { |policy| policy['name'] == 'default_credentials' }
|
38
|
+
|
39
|
+
# Anonymous policy should be before apicast policy
|
40
|
+
# hence, adding as a first element
|
41
|
+
policies.insert(0, anonymous_policy)
|
42
|
+
end
|
43
|
+
|
44
|
+
def anonymous_policy
|
45
|
+
raise ThreeScaleToolbox::Error, 'User key must be provided by ' \
|
46
|
+
'--default-credentials-userkey optional param' \
|
47
|
+
if default_credentials_userkey.nil?
|
48
|
+
|
49
|
+
{
|
50
|
+
'name': 'default_credentials',
|
51
|
+
'version': 'builtin',
|
52
|
+
'configuration': {
|
53
|
+
'auth_type': 'user_key',
|
54
|
+
'user_key': default_credentials_userkey
|
55
|
+
},
|
56
|
+
'enabled': true
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_rh_sso_keycloak_role_check_policy(policies)
|
61
|
+
# only applies to oauth2 sec type
|
62
|
+
return if security.nil? || security.type != 'oauth2'
|
63
|
+
|
64
|
+
return if policies.any? { |policy| policy['name'] == 'keycloak_role_check' }
|
65
|
+
|
66
|
+
policies << keycloak_policy
|
67
|
+
end
|
68
|
+
|
69
|
+
def keycloak_policy
|
70
|
+
{
|
71
|
+
'name': 'keycloak_role_check',
|
72
|
+
'version': 'builtin',
|
73
|
+
'configuration': {
|
74
|
+
'type': 'whitelist',
|
75
|
+
'scopes': [
|
76
|
+
{
|
77
|
+
'realm_roles': [],
|
78
|
+
'client_roles': security.scopes.map { |scope| { 'name': scope } }
|
79
|
+
}
|
80
|
+
]
|
81
|
+
},
|
82
|
+
'enabled': true
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|