skull_island 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -1
- data/Gemfile.lock +6 -2
- data/README.md +88 -3
- data/exe/skull_island +15 -0
- data/lib/skull_island/cli.rb +90 -0
- data/lib/skull_island/helpers/resource.rb +77 -10
- data/lib/skull_island/helpers/resource_class.rb +2 -1
- data/lib/skull_island/resource.rb +11 -1
- data/lib/skull_island/resources/certificate.rb +39 -0
- data/lib/skull_island/resources/consumer.rb +38 -0
- data/lib/skull_island/resources/plugin.rb +63 -3
- data/lib/skull_island/resources/route.rb +61 -0
- data/lib/skull_island/resources/service.rb +64 -0
- data/lib/skull_island/resources/upstream.rb +73 -0
- data/lib/skull_island/resources/upstream_target.rb +42 -0
- data/lib/skull_island/rspec/fake_api_client.rb +0 -2
- data/lib/skull_island/version.rb +2 -2
- data/lib/skull_island.rb +3 -0
- data/skull_island.gemspec +4 -2
- metadata +38 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d6ae44ca3ddea5f866e493af05df16b8355716442acd5a475e1177049260177
|
4
|
+
data.tar.gz: 41ee93c3a6491b28b7e516e3b13166506080f52fa6b3f439bc4510e77b8cae39
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5996e8c7bf5773c8d43f98d2d85f8590d47caa61d19d9a198a4252f6e34f834dd41ff9989ea3726eedbb94ae323b3b79d0c6ad472ab8d6525eb902911191c0f5
|
7
|
+
data.tar.gz: 48f5e8d92c92c6ab399426c3ba39687a20c091dfe01f5c0126cb13d935f6efdf8fd906f0ae38fbb140c740d17781e80e866572c8d4cdab2a0ecc6cc417fb7af6
|
data/.rubocop.yml
CHANGED
@@ -12,10 +12,16 @@ Metrics/ClassLength:
|
|
12
12
|
|
13
13
|
Metrics/ModuleLength:
|
14
14
|
Max: 165
|
15
|
+
Exclude:
|
16
|
+
- 'lib/skull_island/helpers/resource.rb'
|
15
17
|
|
16
18
|
Metrics/CyclomaticComplexity:
|
17
19
|
Max: 7
|
18
20
|
|
21
|
+
Metrics/PerceivedComplexity:
|
22
|
+
Exclude:
|
23
|
+
- 'lib/skull_island/cli.rb'
|
24
|
+
|
19
25
|
Metrics/AbcSize:
|
20
26
|
Max: 25
|
21
27
|
|
@@ -32,7 +38,7 @@ Layout/IndentHeredoc:
|
|
32
38
|
|
33
39
|
Security/Eval:
|
34
40
|
Exclude:
|
35
|
-
-
|
41
|
+
- 'lib/skull_island/cli.rb'
|
36
42
|
|
37
43
|
Style/NumericLiterals:
|
38
44
|
Exclude:
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
skull_island (0.
|
5
|
-
|
4
|
+
skull_island (0.2.0)
|
5
|
+
erubi (~> 1.8)
|
6
|
+
json (~> 2.1)
|
6
7
|
linguistics (~> 2.1)
|
7
8
|
rest-client (~> 2.0)
|
9
|
+
thor (~> 0.20)
|
8
10
|
will_paginate (~> 3.1)
|
9
11
|
|
10
12
|
GEM
|
@@ -17,6 +19,7 @@ GEM
|
|
17
19
|
docile (1.3.1)
|
18
20
|
domain_name (0.5.20180417)
|
19
21
|
unf (>= 0.0.5, < 1.0.0)
|
22
|
+
erubi (1.8.0)
|
20
23
|
ethon (0.12.0)
|
21
24
|
ffi (>= 1.3.0)
|
22
25
|
faraday (0.15.4)
|
@@ -91,6 +94,7 @@ GEM
|
|
91
94
|
json (>= 1.8, < 3)
|
92
95
|
simplecov-html (~> 0.10.0)
|
93
96
|
simplecov-html (0.10.2)
|
97
|
+
thor (0.20.3)
|
94
98
|
travis (1.8.9)
|
95
99
|
backports
|
96
100
|
faraday (~> 0.9)
|
data/README.md
CHANGED
@@ -26,7 +26,92 @@ Gem::Specification.new do |spec|
|
|
26
26
|
end
|
27
27
|
```
|
28
28
|
|
29
|
-
## Usage
|
29
|
+
## CLI Usage
|
30
|
+
|
31
|
+
Skull Island comes with an executable called `skull_island` that leverages the SDK under the hood. Learn about what is can do via `help`:
|
32
|
+
|
33
|
+
```
|
34
|
+
$ skull_island help
|
35
|
+
Commands:
|
36
|
+
skull_island export [OPTIONS] OUTPUT_FILE # Export the current configuration to OUTPUT_FILE
|
37
|
+
skull_island help [COMMAND] # Describe available commands or one specific command
|
38
|
+
skull_island import [OPTIONS] INPUT_FILE # Import a configuration from INPUT_FILE
|
39
|
+
|
40
|
+
Options:
|
41
|
+
[--verbose], [--no-verbose]
|
42
|
+
```
|
43
|
+
|
44
|
+
To use the commands that interact with the Kong API, set environment variables for the required parameters:
|
45
|
+
|
46
|
+
```
|
47
|
+
KONG_ADMIN_URL='https://api-admin.mydomain.com' \
|
48
|
+
KONG_ADMIN_USERNAME='my-basicauth-user' \
|
49
|
+
KONG_ADMIN_PASSWORD='my-basicauth-password' \
|
50
|
+
skull_island ...
|
51
|
+
```
|
52
|
+
|
53
|
+
Note that you can skip `KONG_ADMIN_USERNAME` and `KONG_ADMIN_PASSWORD` if you aren't using a basic-auth reverse-proxy in front of the Admin API.
|
54
|
+
|
55
|
+
Also note that if you're having SSL issues (such as with a private CA), you can have Ruby make use of a custom CA public key using `SSL_CERT_FILE`:
|
56
|
+
|
57
|
+
```
|
58
|
+
SSL_CERT_FILE=/path/to/cabundle.pem \
|
59
|
+
KONG_ADMIN_URL='https://api-admin.mydomain.com' \
|
60
|
+
KONG_ADMIN_USERNAME='my-basicauth-user' \
|
61
|
+
KONG_ADMIN_PASSWORD='my-basicauth-password' \
|
62
|
+
skull_island ...
|
63
|
+
```
|
64
|
+
|
65
|
+
### Exporting
|
66
|
+
|
67
|
+
The CLI allows you to export an existing configuration to a YAML + ERB document (a YAML document with embedded Ruby). This format is helpful because it doesn't require you to know the IDs of resources, making your configuration portable.
|
68
|
+
|
69
|
+
The `export` command will default to outputting to STDOUT if you don't provide an output file location. Otherwise, simply specify the filename you'd like to export to:
|
70
|
+
|
71
|
+
```
|
72
|
+
KONG_ADMIN_URL='https://api-admin.mydomain.com' \
|
73
|
+
skull_island export /path/to/export.yml
|
74
|
+
```
|
75
|
+
|
76
|
+
You can also get a little more information by turning on `--verbose`:
|
77
|
+
|
78
|
+
```
|
79
|
+
KONG_ADMIN_URL='https://api-admin.mydomain.com' \
|
80
|
+
skull_island export --verbose /path/to/export.yml
|
81
|
+
```
|
82
|
+
|
83
|
+
### Importing
|
84
|
+
|
85
|
+
Skull Island also supports importing configurations (both partial and full) from a YAML + ERB document:
|
86
|
+
|
87
|
+
```
|
88
|
+
KONG_ADMIN_URL='https://api-admin.mydomain.com' \
|
89
|
+
skull_island import /path/to/export.yml
|
90
|
+
```
|
91
|
+
|
92
|
+
It'll also read from STDIN if you don't specify a file path (or if you specify `-` as the path):
|
93
|
+
|
94
|
+
```
|
95
|
+
cat /path/to/export.yml | KONG_ADMIN_URL='https://api-admin.mydomain.com' skull_island import
|
96
|
+
# OR
|
97
|
+
KONG_ADMIN_URL='https://api-admin.mydomain.com' skull_island import < /path/to/export.yml
|
98
|
+
```
|
99
|
+
|
100
|
+
You can also get a little more information by turning on `--verbose`:
|
101
|
+
|
102
|
+
```
|
103
|
+
KONG_ADMIN_URL='https://api-admin.mydomain.com' \
|
104
|
+
skull_island import --verbose /path/to/export.yml
|
105
|
+
```
|
106
|
+
|
107
|
+
Importing also supports a "dry run" functionality that shows you what it would do (but makes no changes) using `--test`:
|
108
|
+
|
109
|
+
```
|
110
|
+
KONG_ADMIN_URL='https://api-admin.mydomain.com' \
|
111
|
+
skull_island import --verbose --test /path/to/export.yml
|
112
|
+
```
|
113
|
+
|
114
|
+
## SDK Usage
|
30
115
|
|
31
116
|
The API Client requires configuration before it can be used. For now, this is a matter of calling `APIClient.configure()`, passing a Hash, with Symbols for keys:
|
32
117
|
|
@@ -59,7 +144,7 @@ APIClient.server_status
|
|
59
144
|
# => {"database"=>{"reachable"=>true...
|
60
145
|
```
|
61
146
|
|
62
|
-
This SDK also makes automatic (and mostly unobtrusive) caching behind the scenes. As long as this tool is the only tool making changes to the Admin API (at least while it is being used), this should be fine. Eventually, there will be an option to disable this cache (at the cost of poor performance). For now, it is possible to query this cache and even flushed it manually when required:
|
147
|
+
This SDK also makes use of automatic (and mostly unobtrusive) caching behind the scenes. As long as this tool is the only tool making changes to the Admin API (at least while it is being used), this should be fine. Eventually, there will be an option to disable this cache (at the cost of poor performance). For now, it is possible to query this cache and even flushed it manually when required:
|
63
148
|
|
64
149
|
```ruby
|
65
150
|
APIClient.lru_cache
|
@@ -194,7 +279,7 @@ resource.enabled = true # A Boolean
|
|
194
279
|
resource.config = { 'minute' => 50, 'hour' => 1000 } # A Hash of config keys and values
|
195
280
|
|
196
281
|
# Either reference related resources by ID
|
197
|
-
resource.service =
|
282
|
+
resource.service = '5fd1z584-1adb-40a5-c042-63b19db49x21'
|
198
283
|
resource.service
|
199
284
|
# => #<SkullIsland::Resources::Services:0x00007f9f201f6f44...
|
200
285
|
|
data/exe/skull_island
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'skull_island/cli'
|
6
|
+
|
7
|
+
configuration = {
|
8
|
+
server: ENV['KONG_ADMIN_URL']
|
9
|
+
}
|
10
|
+
configuration[:username] = ENV['KONG_ADMIN_USERNAME'] if ENV['KONG_ADMIN_USERNAME']
|
11
|
+
configuration[:password] = ENV['KONG_ADMIN_PASSWORD'] if ENV['KONG_ADMIN_PASSWORD']
|
12
|
+
|
13
|
+
SkullIsland::APIClient.configure(configuration)
|
14
|
+
|
15
|
+
SkullIsland::CLI.start(ARGV)
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Internal requirements
|
4
|
+
require 'skull_island'
|
5
|
+
|
6
|
+
# External requirements
|
7
|
+
require 'yaml'
|
8
|
+
require 'thor'
|
9
|
+
|
10
|
+
module SkullIsland
|
11
|
+
# Base CLI for SkullIsland
|
12
|
+
class CLI < Thor
|
13
|
+
class_option :verbose, type: :boolean
|
14
|
+
|
15
|
+
desc 'export [OPTIONS] OUTPUT_FILE', 'Export the current configuration to OUTPUT_FILE'
|
16
|
+
def export(output_file = '-')
|
17
|
+
if output_file == '-'
|
18
|
+
STDERR.puts '[INFO] Outputting to STDOUT' if options['verbose']
|
19
|
+
else
|
20
|
+
full_filename = File.expand_path(output_file)
|
21
|
+
dirname = File.dirname(full_filename)
|
22
|
+
unless File.exist?(dirname) && File.ftype(dirname) == 'directory'
|
23
|
+
raise Exceptions::InvalidArguments, "#{full_filename} is invalid"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
output = { 'version' => '0.14' }
|
28
|
+
|
29
|
+
[
|
30
|
+
Resources::Consumer,
|
31
|
+
Resources::Service,
|
32
|
+
Resources::Upstream,
|
33
|
+
Resources::Plugin
|
34
|
+
].each { |clname| export_class(clname, output) }
|
35
|
+
|
36
|
+
if output_file == '-'
|
37
|
+
STDOUT.puts output.to_yaml
|
38
|
+
else
|
39
|
+
File.write(full_filename, output.to_yaml)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'import [OPTIONS] INPUT_FILE', 'Import a configuration from INPUT_FILE'
|
44
|
+
option :test, type: :boolean, desc: "Don't do anything, just show what would happen"
|
45
|
+
def import(input_file = '-')
|
46
|
+
raw ||= if input_file == '-'
|
47
|
+
STDERR.puts '[INFO] Reading from STDOUT' if options['verbose']
|
48
|
+
STDIN.read
|
49
|
+
else
|
50
|
+
full_filename = File.expand_path(input_file)
|
51
|
+
unless File.exist?(full_filename) && File.ftype(full_filename) == 'file'
|
52
|
+
raise Exceptions::InvalidArguments, "#{full_filename} is invalid"
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
File.read(full_filename)
|
57
|
+
rescue StandardError => e
|
58
|
+
raise "Unable to process #{relative_path}: #{e.message}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# rubocop:disable Security/YAMLLoad
|
63
|
+
input = YAML.load(raw)
|
64
|
+
# rubocop:enable Security/YAMLLoad
|
65
|
+
|
66
|
+
[
|
67
|
+
Resources::Consumer,
|
68
|
+
Resources::Service,
|
69
|
+
Resources::Upstream,
|
70
|
+
Resources::Plugin
|
71
|
+
].each { |clname| import_class(clname, input) }
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def export_class(class_name, output_data)
|
77
|
+
STDERR.puts "[INFO] Processing #{class_name.route_key}" if options['verbose']
|
78
|
+
output_data[class_name.route_key] = class_name.all.collect(&:export)
|
79
|
+
end
|
80
|
+
|
81
|
+
def import_class(class_name, import_data)
|
82
|
+
STDERR.puts "[INFO] Processing #{class_name.route_key}" if options['verbose']
|
83
|
+
class_name.batch_import(
|
84
|
+
import_data[class_name.route_key],
|
85
|
+
verbose: options['verbose'],
|
86
|
+
test: options['test']
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -14,6 +14,45 @@ module SkullIsland
|
|
14
14
|
)
|
15
15
|
end
|
16
16
|
|
17
|
+
# rubocop:disable Style/GuardClause
|
18
|
+
def delayed_set(property, data, key)
|
19
|
+
# rubocop:disable Security/Eval
|
20
|
+
if data[key]
|
21
|
+
send(
|
22
|
+
"#{property}=".to_sym,
|
23
|
+
data[key].is_a?(String) ? eval(Erubi::Engine.new(data[key]).src) : data[key]
|
24
|
+
)
|
25
|
+
end
|
26
|
+
# rubocop:enable Security/Eval
|
27
|
+
end
|
28
|
+
# rubocop:enable Style/GuardClause
|
29
|
+
|
30
|
+
def digest
|
31
|
+
Digest::MD5.hexdigest(
|
32
|
+
digest_properties.sort.map { |prop| "#{prop}=#{send(prop.to_sym)}" }.compact.join(':')
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def digest_properties
|
37
|
+
properties.keys.reject { |k| %i[created_at updated_at].include? k }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Tests for an existing version of this resource based on its properties rather than its `id`
|
41
|
+
def find_by_digest
|
42
|
+
result = self.class.where(:digest, digest) # matching digest means the equivalent resource
|
43
|
+
if result.size == 1
|
44
|
+
entity_data = @api_client.cache(result.first.relative_uri.to_s) do |client|
|
45
|
+
client.get(result.first.relative_uri.to_s)
|
46
|
+
end
|
47
|
+
@entity = entity_data
|
48
|
+
@lazy = false
|
49
|
+
@tainted = false
|
50
|
+
true
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
17
56
|
def fresh?
|
18
57
|
!tainted?
|
19
58
|
end
|
@@ -34,6 +73,39 @@ module SkullIsland
|
|
34
73
|
self.class.immutable?
|
35
74
|
end
|
36
75
|
|
76
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
77
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
78
|
+
def import_update_or_skip(verbose: false, test: false, index:)
|
79
|
+
if find_by_digest
|
80
|
+
puts "[INFO] Skipping #{self.class} index #{index} (#{id})" if verbose
|
81
|
+
elsif test
|
82
|
+
puts "[INFO] Would have saved #{sef.class} index #{index}"
|
83
|
+
elsif modified_existing?
|
84
|
+
puts "[INFO] Modified #{self.class} index #{index} (#{id})" if verbose
|
85
|
+
elsif save
|
86
|
+
puts "[INFO] Saved #{self.class} index #{index} (#{id})" if verbose
|
87
|
+
else
|
88
|
+
puts "[ERR] Failed to save #{self.class} index #{index}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
92
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
93
|
+
|
94
|
+
def lookup(type, value)
|
95
|
+
case type
|
96
|
+
when :consumer
|
97
|
+
Resources::Consumer.find(:username, value).id
|
98
|
+
when :route
|
99
|
+
Resources::Route.find(:name, value).id
|
100
|
+
when :service
|
101
|
+
Resources::Service.find(:name, value).id
|
102
|
+
when :upstream
|
103
|
+
Resources::Upstream.find(:name, value).id
|
104
|
+
else
|
105
|
+
raise Exceptions::InvalidArguments, "#{type} is not a valid lookup type"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
37
109
|
# ActiveRecord ActiveModel::Name compatibility method
|
38
110
|
def model_name
|
39
111
|
self.class
|
@@ -68,21 +140,15 @@ module SkullIsland
|
|
68
140
|
@tainted ? true : false
|
69
141
|
end
|
70
142
|
|
71
|
-
# ActiveRecord ActiveModel::Conversion compatibility method
|
72
|
-
def to_key
|
73
|
-
new? ? [] : [id]
|
74
|
-
end
|
75
|
-
|
76
|
-
# ActiveRecord ActiveModel::Conversion compatibility method
|
77
|
-
def to_model
|
78
|
-
self
|
79
|
-
end
|
80
|
-
|
81
143
|
# ActiveRecord ActiveModel::Conversion compatibility method
|
82
144
|
def to_param
|
83
145
|
new? ? nil : id.to_s
|
84
146
|
end
|
85
147
|
|
148
|
+
def to_s
|
149
|
+
to_param.to_s
|
150
|
+
end
|
151
|
+
|
86
152
|
def destroy
|
87
153
|
raise Exceptions::ImmutableModification if immutable?
|
88
154
|
|
@@ -123,6 +189,7 @@ module SkullIsland
|
|
123
189
|
@api_client.invalidate_cache_for(relative_uri.to_s)
|
124
190
|
@entity = @api_client.put(relative_uri, saveable_data)
|
125
191
|
end
|
192
|
+
@api_client.invalidate_cache_for(self.class.relative_uri.to_s) # clear any collection class
|
126
193
|
@tainted = false
|
127
194
|
true
|
128
195
|
end
|
@@ -10,7 +10,8 @@ module SkullIsland
|
|
10
10
|
# @return [Array<Symbol>] the list of names
|
11
11
|
def determine_getter_names(original_name, opts)
|
12
12
|
names = []
|
13
|
-
names <<
|
13
|
+
names << original_name
|
14
|
+
names << "#{original_name}?" if opts[:type] == :boolean
|
14
15
|
if opts[:as]
|
15
16
|
Array(opts[:as]).each do |new_name|
|
16
17
|
names << (opts[:type] == :boolean ? "#{new_name}?" : new_name)
|
@@ -97,8 +97,12 @@ module SkullIsland
|
|
97
97
|
root = 'data' # root for API JSON response data
|
98
98
|
# TODO: do something with lazy requests...
|
99
99
|
|
100
|
+
collection_entity = api_client.cache(relative_uri) do |client|
|
101
|
+
client.get(relative_uri)[root]
|
102
|
+
end
|
103
|
+
|
100
104
|
ResourceCollection.new(
|
101
|
-
|
105
|
+
collection_entity.collect do |record|
|
102
106
|
unless options[:lazy]
|
103
107
|
api_client.invalidate_cache_for "#{relative_uri}/#{record['id']}"
|
104
108
|
api_client.cache("#{relative_uri}/#{record['id']}") do
|
@@ -131,6 +135,12 @@ module SkullIsland
|
|
131
135
|
)
|
132
136
|
end
|
133
137
|
|
138
|
+
# Returns the first (and hopefully only) resource given some criteria
|
139
|
+
# This is a very crude helper and could be made much better
|
140
|
+
def self.find(attribute, value, options = {})
|
141
|
+
where(attribute, value, options).first
|
142
|
+
end
|
143
|
+
|
134
144
|
def self.get(id, options = {})
|
135
145
|
# TODO: Add validations for options
|
136
146
|
|
@@ -12,6 +12,45 @@ module SkullIsland
|
|
12
12
|
property :snis, validate: true
|
13
13
|
property :created_at, read_only: true, postprocess: true
|
14
14
|
|
15
|
+
def self.batch_import(data, verbose: false, test: false)
|
16
|
+
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
|
17
|
+
|
18
|
+
data.each_with_index do |resource_data, index|
|
19
|
+
resource = new
|
20
|
+
resource.cert = resource_data['cert']
|
21
|
+
resource.key = resource_data['key']
|
22
|
+
resource.snis = resource_data['snis'] if resource_data['snis']
|
23
|
+
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def export(options = {})
|
28
|
+
hash = { 'cert' => cert, 'key' => key, 'snis' => snis }
|
29
|
+
[*options[:exclude]].each do |exclude|
|
30
|
+
hash.delete(exclude.to_s)
|
31
|
+
end
|
32
|
+
[*options[:include]].each do |inc|
|
33
|
+
hash[inc.to_s] = send(:inc)
|
34
|
+
end
|
35
|
+
hash.reject { |_, value| value.nil? }
|
36
|
+
end
|
37
|
+
|
38
|
+
def modified_existing?
|
39
|
+
return false unless new?
|
40
|
+
|
41
|
+
# Find certs of the same cert and key
|
42
|
+
same_key = self.class.where(:key, key)
|
43
|
+
|
44
|
+
existing = same_key.size == 1 ? same_key.first : nil
|
45
|
+
|
46
|
+
if existing
|
47
|
+
@entity['id'] = existing.id
|
48
|
+
save
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
15
54
|
private
|
16
55
|
|
17
56
|
# Used to validate {#cert} on set
|
@@ -11,10 +11,48 @@ module SkullIsland
|
|
11
11
|
property :custom_id
|
12
12
|
property :created_at, read_only: true, postprocess: true
|
13
13
|
|
14
|
+
def self.batch_import(data, verbose: false, test: false)
|
15
|
+
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
|
16
|
+
|
17
|
+
data.each_with_index do |resource_data, index|
|
18
|
+
resource = new
|
19
|
+
resource.username = resource_data['username']
|
20
|
+
resource.custom_id = resource_data['custom_id']
|
21
|
+
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
14
25
|
# Provides a collection of related {Plugin} instances
|
15
26
|
def plugins
|
16
27
|
Plugin.where(:consumer, self, api_client: api_client)
|
17
28
|
end
|
29
|
+
|
30
|
+
def export(options = {})
|
31
|
+
hash = { 'username' => username, 'custom_id' => custom_id }
|
32
|
+
[*options[:exclude]].each do |exclude|
|
33
|
+
hash.delete(exclude.to_s)
|
34
|
+
end
|
35
|
+
[*options[:include]].each do |inc|
|
36
|
+
hash[inc.to_s] = send(:inc)
|
37
|
+
end
|
38
|
+
hash.reject { |_, value| value.nil? }
|
39
|
+
end
|
40
|
+
|
41
|
+
def modified_existing?
|
42
|
+
return false unless new?
|
43
|
+
|
44
|
+
# Find consumers of the same username
|
45
|
+
same_username = self.class.where(:username, username)
|
46
|
+
|
47
|
+
existing = same_username.size == 1 ? same_username.first : nil
|
48
|
+
|
49
|
+
if existing
|
50
|
+
@entity['id'] = existing.id
|
51
|
+
save
|
52
|
+
else
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|
18
56
|
end
|
19
57
|
end
|
20
58
|
end
|
@@ -16,6 +16,21 @@ module SkullIsland
|
|
16
16
|
property :service_id, validate: true, preprocess: true, postprocess: true, as: :service
|
17
17
|
property :created_at, read_only: true, postprocess: true
|
18
18
|
|
19
|
+
def self.batch_import(data, verbose: false, test: false)
|
20
|
+
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
|
21
|
+
|
22
|
+
data.each_with_index do |resource_data, index|
|
23
|
+
resource = new
|
24
|
+
resource.name = resource_data['name']
|
25
|
+
resource.enabled = resource_data['enabled']
|
26
|
+
resource.config = resource_data['config'] if resource_data['config']
|
27
|
+
resource.delayed_set(:consumer, resource_data, 'consumer_id')
|
28
|
+
resource.delayed_set(:route, resource_data, 'route_id')
|
29
|
+
resource.delayed_set(:service, resource_data, 'service_id')
|
30
|
+
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
19
34
|
def self.enabled_names(api_client: APIClient.instance)
|
20
35
|
api_client.get("#{relative_uri}/enabled")['enabled_plugins']
|
21
36
|
end
|
@@ -24,6 +39,51 @@ module SkullIsland
|
|
24
39
|
api_client.get("#{relative_uri}/schema/#{name}")
|
25
40
|
end
|
26
41
|
|
42
|
+
def export(options = {})
|
43
|
+
hash = {
|
44
|
+
'name' => name,
|
45
|
+
'enabled' => enabled?,
|
46
|
+
'config' => config
|
47
|
+
}
|
48
|
+
hash['consumer_id'] = "<%= lookup :consumer, '#{consumer.username}' %>" if consumer
|
49
|
+
hash['route_id'] = "<%= lookup :route, '#{route.name}' %>" if route
|
50
|
+
hash['service_id'] = "<%= lookup :service, '#{service.name}' %>" if service
|
51
|
+
[*options[:exclude]].each do |exclude|
|
52
|
+
hash.delete(exclude.to_s)
|
53
|
+
end
|
54
|
+
[*options[:include]].each do |inc|
|
55
|
+
hash[inc.to_s] = send(:inc)
|
56
|
+
end
|
57
|
+
hash.reject { |_, value| value.nil? }
|
58
|
+
end
|
59
|
+
|
60
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
61
|
+
def modified_existing?
|
62
|
+
return false unless new?
|
63
|
+
|
64
|
+
# Find plugins of the same name
|
65
|
+
same_name = self.class.where(:name, name)
|
66
|
+
return false if same_name.size.zero?
|
67
|
+
|
68
|
+
same_name_and_consumer = same_name.where(:consumer, consumer)
|
69
|
+
same_name_and_route = same_name.where(:route, route)
|
70
|
+
same_name_and_service = same_name.where(:service, service)
|
71
|
+
existing = if same_name_and_consumer.size == 1
|
72
|
+
same_name_and_consumer.first
|
73
|
+
elsif same_name_and_route.size == 1
|
74
|
+
same_name_and_route.first
|
75
|
+
elsif same_name_and_service.size == 1
|
76
|
+
same_name_and_service.first
|
77
|
+
end
|
78
|
+
if existing
|
79
|
+
@entity['id'] = existing.id
|
80
|
+
save
|
81
|
+
else
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
86
|
+
|
27
87
|
private
|
28
88
|
|
29
89
|
# TODO: 1.0.x requires refactoring as `consumer_id` becomes `consumer`
|
@@ -125,19 +185,19 @@ module SkullIsland
|
|
125
185
|
# Used to validate {#consumer} on set
|
126
186
|
def validate_consumer_id(value)
|
127
187
|
# allow either a Consumer object or a Hash of a specific structure
|
128
|
-
value.is_a?(Consumer) ||
|
188
|
+
value.is_a?(Consumer) || value.is_a?(String)
|
129
189
|
end
|
130
190
|
|
131
191
|
# Used to validate {#route} on set
|
132
192
|
def validate_route_id(value)
|
133
193
|
# allow either a Route object or a Hash of a specific structure
|
134
|
-
value.is_a?(Route) ||
|
194
|
+
value.is_a?(Route) || value.is_a?(String)
|
135
195
|
end
|
136
196
|
|
137
197
|
# Used to validate {#service} on set
|
138
198
|
def validate_service_id(value)
|
139
199
|
# allow either a Service object or a Hash of a specific structure
|
140
|
-
value.is_a?(Service) ||
|
200
|
+
value.is_a?(Service) || value.is_a?(String)
|
141
201
|
end
|
142
202
|
end
|
143
203
|
end
|
@@ -23,11 +23,72 @@ module SkullIsland
|
|
23
23
|
property :created_at, read_only: true, postprocess: true
|
24
24
|
property :updated_at, read_only: true, postprocess: true
|
25
25
|
|
26
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
27
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
28
|
+
# rubocop:disable Metrics/AbcSize
|
29
|
+
def self.batch_import(data, verbose: false, test: false)
|
30
|
+
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
|
31
|
+
|
32
|
+
data.each_with_index do |rdata, index|
|
33
|
+
resource = new
|
34
|
+
resource.name = rdata['name']
|
35
|
+
resource.methods = rdata['methods'] if rdata['methods']
|
36
|
+
resource.paths = rdata['paths'] if rdata['paths']
|
37
|
+
resource.protocols = rdata['protocols'] if rdata['protocols']
|
38
|
+
resource.hosts = rdata['hosts'] if rdata['hosts']
|
39
|
+
resource.regex_priority = rdata['regex_priority'] if rdata['regex_priority']
|
40
|
+
resource.strip_path = rdata['strip_path'] unless rdata['strip_path'].nil?
|
41
|
+
resource.preserve_host = rdata['preserve_host'] unless rdata['preserve_host'].nil?
|
42
|
+
resource.delayed_set(:service, rdata, 'service')
|
43
|
+
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
47
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
48
|
+
# rubocop:enable Metrics/AbcSize
|
49
|
+
|
26
50
|
# Provides a collection of related {Plugin} instances
|
27
51
|
def plugins
|
28
52
|
Plugin.where(:route, self, api_client: api_client)
|
29
53
|
end
|
30
54
|
|
55
|
+
def export(options = {})
|
56
|
+
hash = {
|
57
|
+
'name' => name,
|
58
|
+
'methods' => methods,
|
59
|
+
'paths' => paths,
|
60
|
+
'protocols' => protocols,
|
61
|
+
'hosts' => hosts,
|
62
|
+
'regex_priority' => regex_priority,
|
63
|
+
'strip_path' => strip_path?,
|
64
|
+
'preserve_host' => preserve_host?
|
65
|
+
}
|
66
|
+
hash['service'] = { 'id' => "<%= lookup :service, '#{service.name}' %>" } if service
|
67
|
+
[*options[:exclude]].each do |exclude|
|
68
|
+
hash.delete(exclude.to_s)
|
69
|
+
end
|
70
|
+
[*options[:include]].each do |inc|
|
71
|
+
hash[inc.to_s] = send(:inc)
|
72
|
+
end
|
73
|
+
hash.reject { |_, value| value.nil? }
|
74
|
+
end
|
75
|
+
|
76
|
+
def modified_existing?
|
77
|
+
return false unless new?
|
78
|
+
|
79
|
+
# Find routes of the same name and service
|
80
|
+
same_name_and_service = self.class.where(:name, name).and(:service, service)
|
81
|
+
|
82
|
+
existing = same_name_and_service.size == 1 ? same_name_and_service.first : nil
|
83
|
+
|
84
|
+
if existing
|
85
|
+
@entity['id'] = existing.id
|
86
|
+
save
|
87
|
+
else
|
88
|
+
false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
31
92
|
private
|
32
93
|
|
33
94
|
def postprocess_service(value)
|
@@ -19,6 +19,32 @@ module SkullIsland
|
|
19
19
|
property :created_at, read_only: true, postprocess: true
|
20
20
|
property :updated_at, read_only: true, postprocess: true
|
21
21
|
|
22
|
+
# rubocop:disable Metrics/AbcSize
|
23
|
+
def self.batch_import(data, verbose: false, test: false)
|
24
|
+
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
|
25
|
+
|
26
|
+
data.each_with_index do |rdata, index|
|
27
|
+
resource = new
|
28
|
+
resource.name = rdata['name']
|
29
|
+
resource.retries = rdata['retries'] if rdata['retries']
|
30
|
+
resource.protocol = rdata['protocol']
|
31
|
+
resource.host = rdata['host']
|
32
|
+
resource.port = rdata['port']
|
33
|
+
resource.path = rdata['path'] if rdata['path']
|
34
|
+
resource.connect_timeout = rdata['connect_timeout'] if rdata['connect_timeout']
|
35
|
+
resource.write_timeout = rdata['write_timeout'] if rdata['write_timeout']
|
36
|
+
resource.read_timeout = rdata['read_timeout'] if rdata['read_timeout']
|
37
|
+
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
|
38
|
+
|
39
|
+
Route.batch_import(
|
40
|
+
rdata['routes'].map { |r| r.merge('service' => { 'id' => resource.id }) },
|
41
|
+
verbose: verbose,
|
42
|
+
test: test
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
# rubocop:enable Metrics/AbcSize
|
47
|
+
|
22
48
|
# Convenience method to add routes
|
23
49
|
def add_route!(details)
|
24
50
|
r = details.is_a?(Route) ? details : Route.from_hash(details, api_client: api_client)
|
@@ -37,6 +63,44 @@ module SkullIsland
|
|
37
63
|
Plugin.where(:service, self, api_client: api_client)
|
38
64
|
end
|
39
65
|
|
66
|
+
def export(options = {})
|
67
|
+
hash = {
|
68
|
+
'name' => name,
|
69
|
+
'retries' => retries,
|
70
|
+
'protocol' => protocol,
|
71
|
+
'host' => host,
|
72
|
+
'port' => port,
|
73
|
+
'path' => path,
|
74
|
+
'connect_timeout' => connect_timeout,
|
75
|
+
'write_timeout' => write_timeout,
|
76
|
+
'read_timeout' => read_timeout
|
77
|
+
}
|
78
|
+
hash['routes'] = routes.collect { |route| route.export(exclude: 'service') }
|
79
|
+
[*options[:exclude]].each do |exclude|
|
80
|
+
hash.delete(exclude.to_s)
|
81
|
+
end
|
82
|
+
[*options[:include]].each do |inc|
|
83
|
+
hash[inc.to_s] = send(:inc)
|
84
|
+
end
|
85
|
+
hash.reject { |_, value| value.nil? }
|
86
|
+
end
|
87
|
+
|
88
|
+
def modified_existing?
|
89
|
+
return false unless new?
|
90
|
+
|
91
|
+
# Find routes of the same name
|
92
|
+
same_name = self.class.where(:name, name)
|
93
|
+
|
94
|
+
existing = same_name.size == 1 ? same_name.first : nil
|
95
|
+
|
96
|
+
if existing
|
97
|
+
@entity['id'] = existing.id
|
98
|
+
save
|
99
|
+
else
|
100
|
+
false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
40
104
|
def url=(uri_or_string)
|
41
105
|
uri_data = URI(uri_or_string)
|
42
106
|
self.protocol = uri_data.scheme
|
@@ -18,6 +18,41 @@ module SkullIsland
|
|
18
18
|
property :healthchecks, validate: true
|
19
19
|
property :created_at, read_only: true, postprocess: true
|
20
20
|
|
21
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
22
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
23
|
+
# rubocop:disable Metrics/AbcSize
|
24
|
+
def self.batch_import(data, verbose: false, test: false)
|
25
|
+
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
|
26
|
+
|
27
|
+
data.each_with_index do |rdata, index|
|
28
|
+
resource = new
|
29
|
+
resource.name = rdata['name']
|
30
|
+
resource.slots = rdata['slots'] if rdata['slots']
|
31
|
+
resource.hash_on = rdata['hash_on']
|
32
|
+
resource.hash_fallback = rdata['hash_fallback']
|
33
|
+
resource.hash_on_header = rdata['hash_on_header']
|
34
|
+
if rdata['hash_fallback_header']
|
35
|
+
resource.hash_fallback_header = rdata['hash_fallback_header']
|
36
|
+
end
|
37
|
+
resource.hash_on_cookie = rdata['hash_on_cookie'] if rdata['hash_on_cookie']
|
38
|
+
if rdata['hash_on_cookie_path']
|
39
|
+
resource.hash_on_cookie_path = rdata['hash_on_cookie_path']
|
40
|
+
end
|
41
|
+
resource.healthchecks = rdata['healthchecks'] if rdata['healthchecks']
|
42
|
+
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
|
43
|
+
puts '[INFO] Processing UpstreamTarget entries...' if verbose
|
44
|
+
|
45
|
+
UpstreamTarget.batch_import(
|
46
|
+
rdata['targets'].map { |t| t.merge('upstream_id' => resource.id) },
|
47
|
+
verbose: verbose,
|
48
|
+
test: test
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
53
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
54
|
+
# rubocop:enable Metrics/AbcSize
|
55
|
+
|
21
56
|
def health
|
22
57
|
if new?
|
23
58
|
# No health status for new Upstreams
|
@@ -68,6 +103,44 @@ module SkullIsland
|
|
68
103
|
)
|
69
104
|
end
|
70
105
|
|
106
|
+
def export(options = {})
|
107
|
+
hash = {
|
108
|
+
'name' => name,
|
109
|
+
'slots' => slots,
|
110
|
+
'hash_on' => hash_on,
|
111
|
+
'hash_fallback' => hash_fallback,
|
112
|
+
'hash_on_header' => hash_on_header,
|
113
|
+
'hash_fallback_header' => hash_fallback_header,
|
114
|
+
'hash_on_cookie' => hash_on_cookie,
|
115
|
+
'hash_on_cookie_path' => hash_on_cookie_path,
|
116
|
+
'healthchecks' => healthchecks
|
117
|
+
}
|
118
|
+
hash['targets'] = targets.collect { |route| route.export(exclude: 'upstream_id') }
|
119
|
+
[*options[:exclude]].each do |exclude|
|
120
|
+
hash.delete(exclude.to_s)
|
121
|
+
end
|
122
|
+
[*options[:include]].each do |inc|
|
123
|
+
hash[inc.to_s] = send(:inc)
|
124
|
+
end
|
125
|
+
hash.reject { |_, value| value.nil? }
|
126
|
+
end
|
127
|
+
|
128
|
+
def modified_existing?
|
129
|
+
return false unless new?
|
130
|
+
|
131
|
+
# Find routes of the same name
|
132
|
+
same_name = self.class.where(:name, name)
|
133
|
+
|
134
|
+
existing = same_name.size == 1 ? same_name.first : nil
|
135
|
+
|
136
|
+
if existing
|
137
|
+
@entity['id'] = existing.id
|
138
|
+
save
|
139
|
+
else
|
140
|
+
false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
71
144
|
private
|
72
145
|
|
73
146
|
# Used to validate {#hash_on} on set
|
@@ -15,6 +15,18 @@ module SkullIsland
|
|
15
15
|
property :weight, validate: true
|
16
16
|
property :created_at, read_only: true, postprocess: true
|
17
17
|
|
18
|
+
def self.batch_import(data, verbose: false, test: false)
|
19
|
+
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
|
20
|
+
|
21
|
+
data.each_with_index do |resource_data, index|
|
22
|
+
resource = new
|
23
|
+
resource.target = resource_data['target']
|
24
|
+
resource.delayed_set(:upstream, resource_data, 'upstream_id')
|
25
|
+
resource.weight = resource_data['weight'] if resource_data['weight']
|
26
|
+
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
18
30
|
def self.get(id, options = {})
|
19
31
|
if options[:upstream]&.is_a?(Upstream)
|
20
32
|
options[:upstream].target(id)
|
@@ -32,6 +44,36 @@ module SkullIsland
|
|
32
44
|
upstream ? "#{upstream.relative_uri}/targets" : nil
|
33
45
|
end
|
34
46
|
|
47
|
+
def export(options = {})
|
48
|
+
hash = { 'target' => target, 'weight' => weight }
|
49
|
+
hash['upstream_id'] = "<%= lookup :upstream, '#{upstream.name}' %>" if upstream
|
50
|
+
[*options[:exclude]].each do |exclude|
|
51
|
+
hash.delete(exclude.to_s)
|
52
|
+
end
|
53
|
+
[*options[:include]].each do |inc|
|
54
|
+
hash[inc.to_s] = send(:inc)
|
55
|
+
end
|
56
|
+
hash.reject { |_, value| value.nil? }
|
57
|
+
end
|
58
|
+
|
59
|
+
def modified_existing?
|
60
|
+
return false unless new?
|
61
|
+
|
62
|
+
# Find routes of the same name
|
63
|
+
same_target_and_upstream = self.class.where(:target, target).and(:upstream, upstream)
|
64
|
+
|
65
|
+
existing = same_target_and_upstream.size == 1 ? same_target_and_upstream.first : nil
|
66
|
+
|
67
|
+
if existing
|
68
|
+
@entity['id'] = existing.id
|
69
|
+
save
|
70
|
+
else
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
35
77
|
def preprocess_target(input)
|
36
78
|
if input.is_a?(URI)
|
37
79
|
"#{input.host}:#{input.port || 8000}"
|
data/lib/skull_island/version.rb
CHANGED
data/lib/skull_island.rb
CHANGED
@@ -2,11 +2,14 @@
|
|
2
2
|
|
3
3
|
# Standard Library Requirements
|
4
4
|
require 'date'
|
5
|
+
require 'digest'
|
5
6
|
require 'json'
|
6
7
|
require 'singleton'
|
7
8
|
require 'uri'
|
9
|
+
require 'yaml'
|
8
10
|
|
9
11
|
# External Library Requirements
|
12
|
+
require 'erubi'
|
10
13
|
require 'linguistics'
|
11
14
|
Linguistics.use(:en)
|
12
15
|
require 'rest-client'
|
data/skull_island.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = ['jonathan.gnagy@gmail.com']
|
12
12
|
|
13
13
|
spec.summary = 'Ruby SDK for Kong'
|
14
|
-
spec.description = 'A Ruby SDK for Kong
|
14
|
+
spec.description = 'A Ruby SDK for Kong 0.14.x'
|
15
15
|
spec.homepage = 'https://github.com/jgnagy/skull_island'
|
16
16
|
spec.license = 'MIT'
|
17
17
|
|
@@ -26,9 +26,11 @@ Gem::Specification.new do |spec|
|
|
26
26
|
|
27
27
|
spec.required_ruby_version = '~> 2.5'
|
28
28
|
|
29
|
-
spec.add_runtime_dependency '
|
29
|
+
spec.add_runtime_dependency 'erubi', '~> 1.8'
|
30
|
+
spec.add_runtime_dependency 'json', '~> 2.1'
|
30
31
|
spec.add_runtime_dependency 'linguistics', '~> 2.1'
|
31
32
|
spec.add_runtime_dependency 'rest-client', '~> 2.0'
|
33
|
+
spec.add_runtime_dependency 'thor', '~> 0.20'
|
32
34
|
spec.add_runtime_dependency 'will_paginate', '~> 3.1'
|
33
35
|
|
34
36
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skull_island
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Gnagy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: erubi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: json
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
31
|
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
33
|
+
version: '2.1'
|
20
34
|
type: :runtime
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '2.
|
40
|
+
version: '2.1'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: linguistics
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,20 @@ dependencies:
|
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '2.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thor
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.20'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.20'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: will_paginate
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,10 +192,11 @@ dependencies:
|
|
164
192
|
- - "~>"
|
165
193
|
- !ruby/object:Gem::Version
|
166
194
|
version: '0.9'
|
167
|
-
description: A Ruby SDK for Kong
|
195
|
+
description: A Ruby SDK for Kong 0.14.x
|
168
196
|
email:
|
169
197
|
- jonathan.gnagy@gmail.com
|
170
|
-
executables:
|
198
|
+
executables:
|
199
|
+
- skull_island
|
171
200
|
extensions: []
|
172
201
|
extra_rdoc_files: []
|
173
202
|
files:
|
@@ -182,11 +211,13 @@ files:
|
|
182
211
|
- Rakefile
|
183
212
|
- bin/console
|
184
213
|
- bin/setup
|
214
|
+
- exe/skull_island
|
185
215
|
- lib/core_extensions/string/transformations.rb
|
186
216
|
- lib/skull_island.rb
|
187
217
|
- lib/skull_island/api_client.rb
|
188
218
|
- lib/skull_island/api_client_base.rb
|
189
219
|
- lib/skull_island/api_exception.rb
|
220
|
+
- lib/skull_island/cli.rb
|
190
221
|
- lib/skull_island/exceptions/api_client_not_configured.rb
|
191
222
|
- lib/skull_island/exceptions/immutable_modification.rb
|
192
223
|
- lib/skull_island/exceptions/invalid_arguments.rb
|
@@ -234,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
234
265
|
- !ruby/object:Gem::Version
|
235
266
|
version: '0'
|
236
267
|
requirements: []
|
237
|
-
rubygems_version: 3.0.
|
268
|
+
rubygems_version: 3.0.3
|
238
269
|
signing_key:
|
239
270
|
specification_version: 4
|
240
271
|
summary: Ruby SDK for Kong
|