3scale_toolbox 0.1.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 +7 -0
- data/README.md +35 -0
- data/exe/3scale +14 -0
- data/exe/3scale-copy +136 -0
- data/exe/3scale-help +9 -0
- data/lib/3scale_toolbox.rb +5 -0
- data/lib/3scale_toolbox/cli.rb +78 -0
- data/lib/3scale_toolbox/version.rb +3 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2041609888563be7ca7b60bbd7cdb6ddd8f60911
|
4
|
+
data.tar.gz: 417e59c876b5111b84af2472ca3df9e51dd1f4e8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7ffdb592223d2a9bf649bba41c8984d63b13c27863fb146a73623a98b561a62bc56f3ae4544dd9679fa8ffa65c782b5dbf4baebcdc826272ef65a059e36c1eea
|
7
|
+
data.tar.gz: 47750f59a33e3839b0e4cfa3b2005b247a20f4476029ea3e546d34290dca615d0406c466e2109fff9f04e591917ac0a0e38d00163372a02bd35bc1b8ee8a1d4c
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# 3scale toolbox
|
2
|
+
|
3
|
+
3scale toolbox is a set of tools to help you manage your 3scale product.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
|
8
|
+
Install the CLI:
|
9
|
+
|
10
|
+
$ gem install 3scale_toolbox
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
```shell
|
15
|
+
3scale help
|
16
|
+
```
|
17
|
+
|
18
|
+
### Copy a service
|
19
|
+
|
20
|
+
Will create a new service, copy existing methods, metrics, application plans and their usage limits.
|
21
|
+
|
22
|
+
```shell
|
23
|
+
3scale copy service NUMBER --endpoint=https://foo-admin.3scale.net --provider-key=your-key
|
24
|
+
```
|
25
|
+
|
26
|
+
## Development
|
27
|
+
|
28
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. Run `bundle exec threescale_toolbox` to use the gem in this directory, ignoring other installed copies of this gem.
|
29
|
+
|
30
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
31
|
+
|
32
|
+
## Contributing
|
33
|
+
|
34
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/3scale/3scale_toolbox.
|
35
|
+
|
data/exe/3scale
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require '3scale_toolbox/cli'
|
4
|
+
|
5
|
+
options, argv = ThreeScaleToolbox::CLI.parse
|
6
|
+
|
7
|
+
unless options.command
|
8
|
+
puts 'Available subcommands: '
|
9
|
+
puts ThreeScaleToolbox::CLI.subcommands
|
10
|
+
puts
|
11
|
+
ThreeScaleToolbox::CLI.print_help!
|
12
|
+
end
|
13
|
+
|
14
|
+
exec options.command.full_path, *ARGV
|
data/exe/3scale-copy
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require '3scale_toolbox/cli'
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
OptionParser.new do |parser|
|
9
|
+
parser.banner = '3scale copy <command> [options]'
|
10
|
+
|
11
|
+
parser.on('-e', '--endpoint ENDPOINT', "Endpoint") do |domain|
|
12
|
+
options[:endpoint] = domain
|
13
|
+
end
|
14
|
+
|
15
|
+
parser.on('-p', '--provider-key PROVIDER_KEY', "Provider Key") do |provider_key|
|
16
|
+
options[:provider_key] = provider_key
|
17
|
+
end
|
18
|
+
|
19
|
+
parser.on('-h', '--help', 'Prints this help') do
|
20
|
+
puts parser
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
end.parse!
|
24
|
+
|
25
|
+
endpoint = options.fetch(:endpoint) { raise OptionParser::MissingArgument, 'endpoint' }
|
26
|
+
provider_key = options.fetch(:provider_key) { raise OptionParser::MissingArgument, 'provider_key' }
|
27
|
+
|
28
|
+
require '3scale/api'
|
29
|
+
|
30
|
+
client = ThreeScale::API.new(endpoint: endpoint, provider_key: provider_key)
|
31
|
+
|
32
|
+
case (command = ARGV.shift)
|
33
|
+
when 'service'
|
34
|
+
service_id = ARGV.shift or raise OptionParser::MissingArgument, 'service_id'
|
35
|
+
|
36
|
+
service = client.show_service(service_id)
|
37
|
+
|
38
|
+
name = "#{service['name']} (copy)"
|
39
|
+
|
40
|
+
copy = client.list_services.find do |service|
|
41
|
+
service['name'] == name
|
42
|
+
end
|
43
|
+
|
44
|
+
copy ||= client.create_service(name: name,
|
45
|
+
end_user_registration_required: service['end_user_registration_required'])
|
46
|
+
service_copy_id = copy.fetch('id')
|
47
|
+
|
48
|
+
puts "new service id #{service_copy_id}"
|
49
|
+
|
50
|
+
metrics = client.list_metrics(service_id)
|
51
|
+
metrics_copies = client.list_metrics(service_copy_id)
|
52
|
+
|
53
|
+
hits = metrics.find{ |metric| metric['system_name'] == 'hits' } or raise 'missing hits metric'
|
54
|
+
hits_copy = metrics_copies.find{ |metric| metric['system_name'] == 'hits' } or raise 'missing hits metric'
|
55
|
+
|
56
|
+
methods = client.list_methods(service_id, hits['id'])
|
57
|
+
methods_copies = client.list_methods(service_copy_id, hits_copy['id'])
|
58
|
+
|
59
|
+
puts "original service hits metric #{hits['id']} has #{methods.size} methods"
|
60
|
+
puts "copied service hits metric #{hits_copy['id']} has #{methods_copies.size} methods"
|
61
|
+
|
62
|
+
missing_methods = methods.reject { |method| methods_copies.find{|copy| method.fetch('system_name') == copy.fetch('system_name') } }
|
63
|
+
|
64
|
+
puts "creating #{missing_methods.size} missing methods on copied service"
|
65
|
+
|
66
|
+
missing_methods.each do |method|
|
67
|
+
copy = { friendly_name: method['friendly_name'], system_name: method['system_name'] }
|
68
|
+
client.create_method(service_copy_id, hits_copy['id'], copy)
|
69
|
+
end
|
70
|
+
|
71
|
+
metrics_copies = client.list_metrics(service_copy_id)
|
72
|
+
|
73
|
+
puts "original service has #{metrics.size} metrics"
|
74
|
+
puts "copied service has #{metrics.size} metrics"
|
75
|
+
|
76
|
+
missing_metrics = metrics.reject { |metric| metrics_copies.find{|copy| metric.fetch('system_name') == copy.fetch('system_name') } }
|
77
|
+
|
78
|
+
missing_metrics.map do |metric|
|
79
|
+
metric.delete('links')
|
80
|
+
client.create_metric(service_copy_id, metric)
|
81
|
+
end
|
82
|
+
|
83
|
+
puts "created #{missing_metrics.size} metrics on the copied service"
|
84
|
+
|
85
|
+
plans = client.list_service_application_plans(service_id)
|
86
|
+
plan_copies = client.list_service_application_plans(service_copy_id)
|
87
|
+
|
88
|
+
puts "original service has #{plans.size} application plans "
|
89
|
+
puts "copied service has #{plan_copies.size} application plans"
|
90
|
+
|
91
|
+
missing_application_plans = plans.reject { |plan| plan_copies.find{|copy| plan.fetch('system_name') == copy.fetch('system_name') } }
|
92
|
+
|
93
|
+
puts "copied service missing #{missing_application_plans.size} application plans"
|
94
|
+
|
95
|
+
missing_application_plans.each do |plan|
|
96
|
+
plan.delete('links')
|
97
|
+
plan.delete('default') # TODO: handle default plan
|
98
|
+
|
99
|
+
if plan.delete('custom') # TODO: what to do with custom plans?
|
100
|
+
puts "skipping custom plan #{plan}"
|
101
|
+
else
|
102
|
+
client.create_application_plan(service_copy_id, plan)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
application_plan_mapping = client.list_service_application_plans(service_copy_id).map do |plan_copy|
|
107
|
+
plan = plans.find{|plan| plan.fetch('system_name') == plan_copy.fetch('system_name') }
|
108
|
+
|
109
|
+
[plan['id'], plan_copy['id']]
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
metrics_mapping = client.list_metrics(service_copy_id).map do |copy|
|
114
|
+
metric = metrics.find{|metric| metric.fetch('system_name') == copy.fetch('system_name') }
|
115
|
+
|
116
|
+
[metric['id'], copy['id']]
|
117
|
+
end.to_h
|
118
|
+
|
119
|
+
application_plan_mapping.each do |original_id, copy_id|
|
120
|
+
limits = client.list_application_plan_limits(original_id)
|
121
|
+
limits_copy = client.list_application_plan_limits(copy_id)
|
122
|
+
|
123
|
+
missing_limits = limits.reject { |limit| limits_copy.find{|limit_copy| limit.fetch('period') == limit_copy.fetch('period') } }
|
124
|
+
|
125
|
+
missing_limits.each do |limit|
|
126
|
+
limit.delete('links')
|
127
|
+
client.create_application_plan_limit(copy_id, metrics_mapping.fetch(limit.fetch('metric_id')), limit)
|
128
|
+
end
|
129
|
+
puts "copied application plan #{copy_id} is missing #{missing_limits.size} from the original plan #{original_id}"
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
else
|
135
|
+
|
136
|
+
end
|
data/exe/3scale-help
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module ThreeScaleToolbox
|
4
|
+
module CLI
|
5
|
+
Options = Struct.new(:command)
|
6
|
+
|
7
|
+
class Parser
|
8
|
+
def self.parse(options)
|
9
|
+
args = Options.new(nil)
|
10
|
+
|
11
|
+
opt_parser = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: 3scale <command> [options]"
|
13
|
+
|
14
|
+
|
15
|
+
opts.on("-h", "--help", "Prints this help") do
|
16
|
+
puts opts
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
begin
|
22
|
+
opt_parser.order!(options)
|
23
|
+
rescue OptionParser::InvalidOption => e
|
24
|
+
p e
|
25
|
+
end
|
26
|
+
|
27
|
+
return args
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse(argv = ARGV)
|
32
|
+
options = Parser.parse(argv)
|
33
|
+
options.command = argv.shift
|
34
|
+
options.command = subcommands.find { |subcommand| subcommand.name == options.command }
|
35
|
+
|
36
|
+
[ options, argv ]
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.print_help!
|
40
|
+
Parser.parse %w[--help]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.plugins
|
44
|
+
Gem.loaded_specs.select{ |name, _| name.start_with?('3scale') }.values
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.current_command
|
48
|
+
File.expand_path($0, Dir.pwd)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.subcommands
|
52
|
+
plugins
|
53
|
+
.flat_map { |spec| spec.executables.flat_map{ |bin| Subcommand.new(bin, spec) } }
|
54
|
+
.reject { |subcommand| subcommand.full_path == current_command || subcommand.name.nil? }
|
55
|
+
end
|
56
|
+
|
57
|
+
class Subcommand
|
58
|
+
attr_reader :executable, :spec
|
59
|
+
|
60
|
+
def initialize(executable, spec)
|
61
|
+
@executable = executable
|
62
|
+
@spec = spec
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
name
|
67
|
+
end
|
68
|
+
|
69
|
+
def name
|
70
|
+
executable.split('-', 2)[1]
|
71
|
+
end
|
72
|
+
|
73
|
+
def full_path
|
74
|
+
spec.bin_file(executable)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: 3scale_toolbox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michal Cichra
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: 3scale-api
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.1.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.1.0
|
55
|
+
description: 3scale CLI tools to manage your API from the terminal.
|
56
|
+
email:
|
57
|
+
- michal@3scale.net
|
58
|
+
executables:
|
59
|
+
- 3scale
|
60
|
+
- 3scale-copy
|
61
|
+
- 3scale-help
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- README.md
|
66
|
+
- exe/3scale
|
67
|
+
- exe/3scale-copy
|
68
|
+
- exe/3scale-help
|
69
|
+
- lib/3scale_toolbox.rb
|
70
|
+
- lib/3scale_toolbox/cli.rb
|
71
|
+
- lib/3scale_toolbox/version.rb
|
72
|
+
homepage: https://github.com/3scale/3scale_toolbox
|
73
|
+
licenses: []
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.5.1
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: 3scale CLI Toolbox.
|
95
|
+
test_files: []
|
96
|
+
has_rdoc:
|