checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
---
2
2
SHA256:
3
- metadata.gz: 67a8f014f5c284c8fe9a8201fa1296130abb88a08b52e8f512e935eb178bb337
4
- data.tar.gz: ed28d0b9011b978e0939ae7ba73b8934d147f77df0ebee8f6a38464f6ed12440
3
+ metadata.gz: 782471de2e1ce1e974ac22f0420d33ce0903f909b4286094537c7169caa872f5
4
+ data.tar.gz: 7edb2bcdb5094f27932c2a3e13f6865e969ba6f43e78f34d6a26c7416cb01bc9
5
5
SHA512:
6
- metadata.gz: e63e8b8e203f408df32846e4aec20cbbcea5ac7688ef2ec06db386c9e1f7f88a13094706dcb2b12dba98639c25d9488209ff6b6ce6b9ce60af3c2237bd39f1b0
7
- data.tar.gz: e150792100e0b64286c7dee6338bf723aa0f2e88487bc9f5846485e410e57207504c974b7e861dc366082e849bea1b845a20d00cf9250951db46da62cc05ce5d
6
+ metadata.gz: 6a83f0d13f17f858332882836b22be450c9974283c9e1a8fe0db20b007f13b080a73d4c9fe43bc3516a2a2a5768821f1a4b7494bf9262874774a63c9feb78068
7
+ data.tar.gz: 7dc758ebad1c39cbaa8b64789ac0ff7db061349cd0a086a2d50cfedf2cf822e6d8745d92a1a976d63f7398b810f7ca634bf46b7f2408709c33b8cbf2a18131d0
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
# rspec failure tracking
11
11
.rspec_status
12
12
*.gem
13
+ .DS_Store
data/.rubocop.yml CHANGED
@@ -8,7 +8,7 @@ Metrics/LineLength:
8
8
Max: 100
9
9
10
10
Metrics/ClassLength:
11
- Max: 175
11
+ Max: 190
12
12
13
13
Metrics/ModuleLength:
14
14
Max: 175
@@ -23,7 +23,7 @@ Metrics/PerceivedComplexity:
23
23
- 'lib/skull_island/cli.rb'
24
24
25
25
Metrics/AbcSize:
26
- Max: 27
26
+ Max: 28
27
27
28
28
Metrics/BlockLength:
29
29
Max: 35
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
PATH
2
2
remote: .
3
3
specs:
4
- skull_island (1.2.0)
4
+ skull_island (1.2.2)
5
5
deepsort (~> 0.4)
6
6
erubi (~> 1.8)
7
7
json (~> 2.1)
@@ -15,11 +15,11 @@ GEM
15
15
specs:
16
16
addressable (2.4.0)
17
17
ast (2.4.0)
18
- backports (3.12.0)
19
- deepsort (0.4.1)
18
+ backports (3.15.0)
19
+ deepsort (0.4.2)
20
20
diff-lcs (1.3)
21
- docile (1.3.1)
22
- domain_name (0.5.20180417)
21
+ docile (1.3.2)
22
+ domain_name (0.5.20190701)
23
23
unf (>= 0.0.5, < 1.0.0)
24
24
erubi (1.8.0)
25
25
ethon (0.12.0)
@@ -28,7 +28,7 @@ GEM
28
28
multipart-post (>= 1.2, < 3)
29
29
faraday_middleware (0.13.1)
30
30
faraday (>= 0.7.4, < 1.0)
31
- ffi (1.10.0)
31
+ ffi (1.11.1)
32
32
gh (0.15.1)
33
33
addressable (~> 2.4.0)
34
34
backports
@@ -39,7 +39,7 @@ GEM
39
39
highline (1.7.10)
40
40
http-cookie (1.0.3)
41
41
domain_name (~> 0.5)
42
- jaro_winkler (1.5.2)
42
+ jaro_winkler (1.5.3)
43
43
json (2.2.0)
44
44
launchy (2.4.3)
45
45
addressable (~> 2.3)
@@ -50,15 +50,13 @@ GEM
50
50
mime-types-data (~> 3.2015)
51
51
mime-types-data (3.2019.0331)
52
52
multi_json (1.13.1)
53
- multipart-post (2.0.0)
53
+ multipart-post (2.1.1)
54
54
net-http-persistent (2.9.4)
55
55
net-http-pipeline (1.0.1)
56
56
netrc (0.11.0)
57
- parallel (1.14.0)
58
- parser (2.6.0.0)
57
+ parallel (1.17.0)
58
+ parser (2.6.3.0)
59
59
ast (~> 2.4.0)
60
- powerpack (0.1.2)
61
- psych (3.1.0)
62
60
pusher-client (0.6.2)
63
61
json
64
62
websocket (~> 1.0)
@@ -72,32 +70,30 @@ GEM
72
70
rspec-core (~> 3.8.0)
73
71
rspec-expectations (~> 3.8.0)
74
72
rspec-mocks (~> 3.8.0)
75
- rspec-core (3.8.0)
73
+ rspec-core (3.8.2)
76
74
rspec-support (~> 3.8.0)
77
- rspec-expectations (3.8.2)
75
+ rspec-expectations (3.8.4)
78
76
diff-lcs (>= 1.2.0, < 2.0)
79
77
rspec-support (~> 3.8.0)
80
- rspec-mocks (3.8.0)
78
+ rspec-mocks (3.8.1)
81
79
diff-lcs (>= 1.2.0, < 2.0)
82
80
rspec-support (~> 3.8.0)
83
- rspec-support (3.8.0)
84
- rubocop (0.65.0)
81
+ rspec-support (3.8.2)
82
+ rubocop (0.74.0)
85
83
jaro_winkler (~> 1.5.1)
86
84
parallel (~> 1.10)
87
- parser (>= 2.5, != 2.5.1.1)
85
+ parser (>= 2.6)
88
- powerpack (~> 0.1)
89
- psych (>= 3.1.0)
90
86
rainbow (>= 2.2.2, < 4.0)
91
87
ruby-progressbar (~> 1.7)
92
- unicode-display_width (~> 1.4.0)
93
- ruby-progressbar (1.10.0)
94
- simplecov (0.16.1)
88
+ unicode-display_width (>= 1.4.0, < 1.7)
89
+ ruby-progressbar (1.10.1)
90
+ simplecov (0.17.0)
95
91
docile (~> 1.1)
96
92
json (>= 1.8, < 3)
97
93
simplecov-html (~> 0.10.0)
98
94
simplecov-html (0.10.2)
99
95
thor (0.20.3)
100
- travis (1.8.9)
96
+ travis (1.8.10)
101
97
backports
102
98
faraday (~> 0.9)
103
99
faraday_middleware (~> 0.9, >= 0.9.1)
@@ -110,11 +106,11 @@ GEM
110
106
ethon (>= 0.8.0)
111
107
unf (0.1.4)
112
108
unf_ext
113
- unf_ext (0.0.7.5)
114
- unicode-display_width (1.4.1)
109
+ unf_ext (0.0.7.6)
110
+ unicode-display_width (1.6.0)
115
111
websocket (1.2.8)
116
112
will_paginate (3.1.7)
117
- yard (0.9.18)
113
+ yard (0.9.20)
118
114
119
115
PLATFORMS
120
116
ruby
@@ -127,7 +123,7 @@ DEPENDENCIES
127
123
simplecov (~> 0.15)
128
124
skull_island!
129
125
travis (~> 1.8)
130
- yard (~> 0.9)
126
+ yard (~> 0.9.20)
131
127
132
128
BUNDLED WITH
133
129
2.0.1
data/README.md CHANGED
@@ -81,6 +81,8 @@ KONG_ADMIN_URL='https://api-admin.mydomain.com' \
81
81
skull_island export --verbose /path/to/export.yml
82
82
```
83
83
84
+ Exporting, by default, exports the entire configuration of a Kong gateway, but will strip out special meta-data tags added by Skull Island to track projects. If, instead, you'd like to export **only** the configuration for a specific project, you can add `--project foo` (where `foo` is the name of your project) to export only those resources associated with it and maintain the special key in the exported YAML.
85
+
84
86
### Importing
85
87
86
88
Skull Island also supports importing configurations (both partial and full) from a YAML + ERB document:
@@ -112,6 +114,16 @@ KONG_ADMIN_URL='https://api-admin.mydomain.com' \
112
114
skull_island import --verbose --test /path/to/export.yml
113
115
```
114
116
117
+ Note that `--test` has a high likelihood of generating errors with a complicated import if required/dependent resources do not exist.
118
+
119
+ #### Importing with Projects
120
+
121
+ Skull Island 1.2.1 introduces the ability to use a special top-level key in the configuration called `project` that uses meta-data to track which resources belong to a project. This meta-data can safely be added at another time as this tool will "adopt" otherwise matching resources into a project.
122
+
123
+ To use this functionality, either add the `project` key to your configuration file (usually directly below the `version` key) with some value that will be unique on your gateway, or use `--project foo` (where `foo` is the name of your project) as a CLI flag.
124
+
125
+ When using the `project` feature of Skull Island, the CLI tool will automatically clean up old resources no longer found in your config file. This is, in fact, the _only_ circumstance under which this tool actually removes resources. Use this feature with care, as it can delete large swaths of your configuration if used incorrectly. It is **critical** that this value is unique since this project functionality is used to delete resources.
126
+
115
127
### Migrating
116
128
117
129
With Skull Island, it is possible to migrate a configuration from a 0.14.x gateway to one compatible with a 1.2.x gateway. If you have a previous export, you can just run `skull_island migrate /path/to/export.yml` and you'll receive a 1.2 compatible config on standard out. If you'd prefer, you can have that config written to a file as well (just like the export command) like so:
@@ -133,6 +145,7 @@ The import/export/migrate CLI functions produce YAML with support for embedded R
133
145
```yaml
134
146
---
135
147
version: '1.2'
148
+ project: FooV2
136
149
certificates: []
137
150
consumers:
138
151
- username: foo
@@ -190,7 +203,7 @@ plugins:
190
203
service: "<%= lookup :service, 'search_api' %>"
191
204
```
192
205
193
- All top-level keys (other than `version`) require an Array as a parameter, either by providing a list of entries or an empty Array (`[]`). The above shows how to use the `lookup()` function to refer to another resource. This "looks up" the resource type (`service` in this case) by `name` (`search_api` in this case) and resolves its `id`. This function can also be used to lookup a `route` or `upstream` by its `name`, or a `consumer` by its `username`. Note that Kong itself doesn't _require_ `route` resources to have unique names, so you'll need to enforce that practice yourself for `lookup` to be useful for Routes.
206
+ All top-level keys (other than `version` and `project`) require an Array as a parameter, either by providing a list of entries or an empty Array (`[]`). The above shows how to use the `lookup()` function to refer to another resource. This "looks up" the resource type (`service` in this case) by `name` (`search_api` in this case) and resolves its `id`. This function can also be used to lookup a `route` or `upstream` by its `name`, or a `consumer` by its `username`. Note that Kong itself doesn't _require_ `route` resources to have unique names, so you'll need to enforce that practice yourself for `lookup` to be useful for Routes.
194
207
195
208
While technically _any_ Ruby is valid, the following are pretty helpful for templating your YAML files:
196
209
data/lib/skull_island.rb CHANGED
@@ -39,6 +39,7 @@ require 'skull_island/api_client_base'
39
39
require 'skull_island/api_client'
40
40
require 'skull_island/simple_api_client'
41
41
require 'skull_island/resource_collection'
42
+ require 'skull_island/helpers/meta'
42
43
require 'skull_island/helpers/resource'
43
44
require 'skull_island/helpers/resource_class'
44
45
require 'skull_island/helpers/migration'
data/lib/skull_island/api_client_base.rb CHANGED
@@ -67,6 +67,12 @@ module SkullIsland
67
67
end
68
68
end
69
69
70
+ def delete(uri)
71
+ client_action do |client|
72
+ client[uri].delete(json_headers)
73
+ end
74
+ end
75
+
70
76
private
71
77
72
78
def client_action
data/lib/skull_island/cli.rb CHANGED
@@ -15,9 +15,10 @@ module SkullIsland
15
15
class_option :verbose, type: :boolean
16
16
17
17
desc 'export [OPTIONS] [OUTPUT|-]', 'Export the current configuration to OUTPUT'
18
+ option :project, desc: 'Project identifier for metadata'
18
19
def export(output_file = '-')
19
20
if output_file == '-'
20
- STDERR.puts '[INFO] Outputting to STDOUT' if options['verbose']
21
+ warn '[INFO] Outputting to STDOUT' if options['verbose']
21
22
else
22
23
full_filename = File.expand_path(output_file)
23
24
dirname = File.dirname(full_filename)
@@ -29,6 +30,7 @@ module SkullIsland
29
30
validate_server_version
30
31
31
32
output = { 'version' => '1.2' }
33
+ output['project'] = options['project'] if options['project']
32
34
33
35
[
34
36
Resources::Certificate,
@@ -46,6 +48,7 @@ module SkullIsland
46
48
end
47
49
48
50
desc 'import [OPTIONS] [INPUT|-]', 'Import a configuration from INPUT'
51
+ option :project, desc: 'Project identifier for metadata'
49
52
option :test, type: :boolean, desc: "Don't do anything, just show what would happen"
50
53
def import(input_file = '-')
51
54
raw ||= acquire_input(input_file, options['verbose'])
@@ -56,19 +59,23 @@ module SkullIsland
56
59
57
60
validate_config_version input['version']
58
61
62
+ import_time = Time.now.utc.to_i
63
+ input['project'] = options['project'] if options['project']
64
+
59
65
[
60
66
Resources::Certificate,
61
67
Resources::Consumer,
62
68
Resources::Upstream,
63
69
Resources::Service,
64
70
Resources::Plugin
65
- ].each { |clname| import_class(clname, input) }
71
+ ].each { |clname| import_class(clname, input, import_time) }
66
72
end
67
73
68
74
desc(
69
75
'migrate [OPTIONS] [INPUT|-] [OUTPUT|-]',
70
76
'Migrate an older config from INPUT to OUTPUT'
71
77
)
78
+ option :project, desc: 'Project identifier for metadata'
72
79
def migrate(input_file = '-', output_file = '-')
73
80
raw ||= acquire_input(input_file, options['verbose'])
74
81
@@ -79,9 +86,10 @@ module SkullIsland
79
86
validate_migrate_version input['version']
80
87
81
88
output = migrate_config(input)
89
+ output['project'] = options['project'] if options['project']
82
90
83
91
if output_file == '-'
84
- STDERR.puts '[INFO] Outputting to STDOUT' if options['verbose']
92
+ warn '[INFO] Outputting to STDOUT' if options['verbose']
85
93
STDOUT.puts output.to_yaml
86
94
else
87
95
full_filename = File.expand_path(output_file)
@@ -97,23 +105,30 @@ module SkullIsland
97
105
private
98
106
99
107
def export_class(class_name, output_data)
100
- STDERR.puts "[INFO] Processing #{class_name.route_key}" if options['verbose']
101
- output_data[class_name.route_key] = class_name.all.collect(&:export)
108
+ warn "[INFO] Processing #{class_name.route_key}" if options['verbose']
109
+ output_data[class_name.route_key] = if options['project']
110
+ class_name.where(:project, options['project'])
111
+ .collect(&:export)
112
+ else
113
+ class_name.all.collect(&:export)
114
+ end
102
115
end
103
116
104
- def import_class(class_name, import_data)
105
- STDERR.puts "[INFO] Processing #{class_name.route_key}" if options['verbose']
117
+ def import_class(class_name, import_data, import_time)
118
+ warn "[INFO] Processing #{class_name.route_key}" if options['verbose']
106
119
class_name.batch_import(
107
120
import_data[class_name.route_key],
108
121
verbose: options['verbose'],
109
- test: options['test']
122
+ test: options['test'],
123
+ time: import_time,
124
+ project: import_data['project']
110
125
)
111
126
end
112
127
113
128
# Used to pull input from either STDIN or the specified file
114
129
def acquire_input(input_file, verbose = false)
115
130
if input_file == '-'
116
- STDERR.puts '[INFO] Reading from STDIN' if verbose
131
+ warn '[INFO] Reading from STDIN' if verbose
117
132
STDIN.read
118
133
else
119
134
full_filename = File.expand_path(input_file)
@@ -133,10 +148,10 @@ module SkullIsland
133
148
if version && ['1.1', '1.2'].include?(version)
134
149
validate_server_version
135
150
elsif version && ['0.14', '1.0'].include?(version)
136
- STDERR.puts '[CRITICAL] Config version is too old. Try `migrate` instead of `import`.'
151
+ warn '[CRITICAL] Config version is too old. Try `migrate` instead of `import`.'
137
152
exit 2
138
153
else
139
- STDERR.puts '[CRITICAL] Config version is unknown or not supported.'
154
+ warn '[CRITICAL] Config version is unknown or not supported.'
140
155
exit 3
141
156
end
142
157
end
@@ -145,7 +160,7 @@ module SkullIsland
145
160
if version && version == '0.14'
146
161
true
147
162
else
148
- STDERR.puts '[CRITICAL] Config version must be 0.14 for migration.'
163
+ warn '[CRITICAL] Config version must be 0.14 for migration.'
149
164
exit 4
150
165
end
151
166
end
@@ -155,7 +170,7 @@ module SkullIsland
155
170
if server_version.match?(/^1.[12]/)
156
171
true
157
172
else
158
- STDERR.puts '[CRITICAL] Server version mismatch!'
173
+ warn '[CRITICAL] Server version mismatch!'
159
174
exit 1
160
175
end
161
176
end
data/lib/skull_island/helpers/meta.rb ADDED
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SkullIsland
4
+ module Helpers
5
+ # Useful for embedding meta-data into special tags
6
+ module Meta
7
+ def add_meta(key, value)
8
+ metatag = "_meta~#{key}~#{value}"
9
+
10
+ # filter out any existing duplicate metatags
11
+ existing_tags = raw_tags.reject { |tag| tag.start_with?("_meta~#{key}~") }
12
+
13
+ # Add the new tag directly, bypassing preprocessing
14
+ raw_set('tags', existing_tags + [metatag])
15
+ end
16
+
17
+ def import_time
18
+ metatags['import_time']
19
+ end
20
+
21
+ def import_time=(time)
22
+ add_meta('import_time', time)
23
+ end
24
+
25
+ def remove_meta(key)
26
+ # filter out an existing metatags
27
+ filtered_tags = raw_tags.reject { |tag| tag.start_with?("_meta~#{key}~") }
28
+
29
+ # Bypassing preprocessing
30
+ raw_set('tags', filtered_tags)
31
+ end
32
+
33
+ def metatags
34
+ metadata = {}
35
+ raw_tags.select { |tag| tag.start_with?('_meta~') }.each do |tag|
36
+ key, value = tag.gsub('_meta~', '').split('~', 2)
37
+ metadata[key] = value
38
+ end
39
+ metadata
40
+ end
41
+
42
+ def project
43
+ metatags['project']
44
+ end
45
+
46
+ def project=(project_id)
47
+ unless project_id.is_a?(String) && project_id.match?(/^[\w_\-\.~]+#x2F;)
48
+ raise Exceptions::InvalidArguments, 'project'
49
+ end
50
+
51
+ add_meta('project', project_id)
52
+ end
53
+
54
+ def raw_tags
55
+ reload if @lazy && !@entity.key?('tags')
56
+ @entity['tags'] || []
57
+ end
58
+
59
+ def preprocess_tags(input)
60
+ input.uniq + metatags.map { |k, v| "_meta~#{k}~#{v}" }
61
+ end
62
+
63
+ def postprocess_tags(value)
64
+ (value || []).reject { |tag| tag.start_with?('_meta~') }
65
+ end
66
+
67
+ def supports_meta?
68
+ true
69
+ end
70
+ end
71
+ end
72
+ end
data/lib/skull_island/helpers/resource.rb CHANGED
@@ -30,12 +30,13 @@ module SkullIsland
30
30
31
31
def digest
32
32
Digest::MD5.hexdigest(
33
- digest_properties.sort.map { |prop| "#{prop}=#{send(prop.to_sym)}" }.compact.join(':')
33
+ digest_properties.sort.map { |prp| "#{prp}=#{send(prp.to_sym) || ''}" }.compact.join(':')
34
34
)
35
35
end
36
36
37
37
def digest_properties
38
- properties.keys.reject { |k| %i[created_at updated_at].include? k }
38
+ props = properties.keys.reject { |k| %i[created_at updated_at].include? k }
39
+ supports_meta? ? props + [:project] : props
39
40
end
40
41
41
42
# Tests for an existing version of this resource based on its properties rather than its `id`
@@ -45,8 +46,8 @@ module SkullIsland
45
46
entity_data = @api_client.cache(result.first.relative_uri.to_s) do |client|
46
47
client.get(result.first.relative_uri.to_s)
47
48
end
48
- @entity = entity_data
49
- @lazy = false
49
+ @entity = entity_data
50
+ @lazy = false
50
51
@tainted = false
51
52
true
52
53
else
@@ -172,8 +173,8 @@ module SkullIsland
172
173
entity_data = @api_client.cache(relative_uri.to_s) do |client|
173
174
client.get(relative_uri.to_s)
174
175
end
175
- @entity = entity_data
176
- @lazy = false
176
+ @entity = entity_data
177
+ @lazy = false
177
178
@tainted = false
178
179
true
179
180
end
@@ -199,6 +200,10 @@ module SkullIsland
199
200
self.class.relative_uri
200
201
end
201
202
203
+ def supports_meta?
204
+ false
205
+ end
206
+
202
207
# ActiveRecord ActiveModel compatibility method
203
208
def update(params)
204
209
new_params = {}
data/lib/skull_island/resource.rb CHANGED
@@ -179,6 +179,13 @@ module SkullIsland
179
179
)
180
180
end
181
181
182
+ def self.cleanup_except(project, keep_these)
183
+ where(:project, project).reject { |res| keep_these.include?(res.id) }.map do |res|
184
+ puts "[WARN] ! Removing #{name} (#{res.id})"
185
+ res.destroy
186
+ end
187
+ end
188
+
182
189
def initialize(options = {})
183
190
# TODO: better options validations
184
191
raise Exceptions::InvalidOptions unless options.is_a?(Hash)
@@ -207,5 +214,17 @@ module SkullIsland
207
214
def relative_uri
208
215
"#{self.class.relative_uri}/#{id}"
209
216
end
217
+
218
+ private
219
+
220
+ # A way to add things _without_ preprocessing them
221
+ def raw_set(key, value)
222
+ raise Exceptions::ImmutableModification if immutable?
223
+
224
+ @entity[key.to_s] = value
225
+
226
+ @modified_properties << key.to_sym
227
+ @tainted = true
228
+ end
210
229
end
211
230
end
data/lib/skull_island/resources/basicauth_credential.rb CHANGED
@@ -18,13 +18,18 @@ module SkullIsland
18
18
def self.batch_import(data, verbose: false, test: false)
19
19
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
20
20
21
+ known_ids = []
22
+
21
23
data.each_with_index do |resource_data, index|
22
24
resource = new
23
- resource.username = resource_data['username']
24
- resource.password = resource_data['password'] if resource_data['password']
25
+ resource.delayed_set(:username, resource_data, 'username')
26
+ resource.delayed_set(:password, resource_data, 'password') if resource_data['password']
25
27
resource.delayed_set(:consumer, resource_data, 'consumer')
26
28
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
29
+ known_ids << resource.id
27
30
end
31
+
32
+ known_ids
28
33
end
29
34
30
35
def self.relative_uri
@@ -56,6 +61,10 @@ module SkullIsland
56
61
false
57
62
end
58
63
64
+ def project
65
+ consumer ? consumer.project : nil
66
+ end
67
+
59
68
private
60
69
61
70
def postprocess_consumer(value)
data/lib/skull_island/resources/certificate.rb CHANGED
@@ -7,29 +7,42 @@ module SkullIsland
7
7
#
8
8
# @see https://docs.konghq.com/1.1.x/admin-api/#certificate-object Certificate API definition
9
9
class Certificate < Resource
10
+ include Helpers::Meta
11
+
10
12
property :cert, required: true, validate: true
11
13
property :key, required: true, validate: true
12
14
property :snis, validate: true
13
15
property :created_at, read_only: true, postprocess: true
14
- property :tags, validate: true
16
+ property :tags, validate: true, preprocess: true, postprocess: true
15
17
16
- def self.batch_import(data, verbose: false, test: false)
18
+ # rubocop:disable Metrics/CyclomaticComplexity
19
+ # rubocop:disable Metrics/PerceivedComplexity
20
+ def self.batch_import(data, verbose: false, test: false, project: nil, time: nil)
17
21
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
18
22
23
+ known_ids = []
24
+
19
25
data.each_with_index do |resource_data, index|
20
26
resource = new
21
- resource.cert = resource_data['cert']
22
- resource.key = resource_data['key']
27
+ resource.delayed_set(:cert, resource_data, 'cert')
28
+ resource.delayed_set(:key, resource_data, 'key')
23
29
resource.snis = resource_data['snis'] if resource_data['snis']
24
30
resource.tags = resource_data['tags'] if resource_data['tags']
31
+ resource.project = project if project
32
+ resource.import_time = (time || Time.now.utc.to_i) if project
25
33
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
34
+ known_ids << resource.id
26
35
end
36
+
37
+ cleanup_except(project, known_ids) if project
27
38
end
39
+ # rubocop:enable Metrics/CyclomaticComplexity
40
+ # rubocop:enable Metrics/PerceivedComplexity
28
41
29
42
def export(options = {})
30
43
hash = { 'cert' => cert, 'key' => key }
31
44
hash['snis'] = snis if snis && !snis.empty?
32
- hash['tags'] = tags if tags
45
+ hash['tags'] = tags unless tags.empty?
33
46
[*options[:exclude]].each do |exclude|
34
47
hash.delete(exclude.to_s)
35
48
end
data/lib/skull_island/resources/consumer.rb CHANGED
@@ -7,22 +7,33 @@ module SkullIsland
7
7
#
8
8
# @see https://docs.konghq.com/1.1.x/admin-api/#consumer-object Consumer API definition
9
9
class Consumer < Resource
10
+ include Helpers::Meta
11
+
10
12
property :username
11
13
property :custom_id
12
14
property :created_at, read_only: true, postprocess: true
13
- property :tags, validate: true
15
+ property :tags, validate: true, preprocess: true, postprocess: true
14
16
15
- def self.batch_import(data, verbose: false, test: false)
17
+ # rubocop:disable Metrics/CyclomaticComplexity
18
+ # rubocop:disable Metrics/PerceivedComplexity
19
+ # rubocop:disable Metrics/AbcSize
20
+ def self.batch_import(data, verbose: false, test: false, project: nil, time: nil)
16
21
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
17
22
23
+ known_ids = []
24
+
25
+ # rubocop:disable Metrics/BlockLength
18
26
data.each_with_index do |resource_data, index|
19
27
resource = new
20
28
resource.username = resource_data['username']
21
29
resource.custom_id = resource_data['custom_id']
22
30
resource.tags = resource_data['tags'] if resource_data['tags']
31
+ resource.project = project if project
32
+ resource.import_time = (time || Time.now.utc.to_i) if project
23
33
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
34
+ known_ids << resource.id
24
35
25
- BasicauthCredential.batch_import(
36
+ known_basic_auths = BasicauthCredential.batch_import(
26
37
(
27
38
resource_data.dig('credentials', 'basic-auth') || []
28
39
).map { |t| t.merge('consumer' => { 'id' => resource.id }) },
@@ -30,7 +41,7 @@ module SkullIsland
30
41
test: test
31
42
)
32
43
33
- JWTCredential.batch_import(
44
+ known_jwt_auths = JWTCredential.batch_import(
34
45
(
35
46
resource_data.dig('credentials', 'jwt') || []
36
47
).map { |t| t.merge('consumer' => { 'id' => resource.id }) },
@@ -38,20 +49,36 @@ module SkullIsland
38
49
test: test
39
50
)
40
51
41
- KeyauthCredential.batch_import(
52
+ known_key_auths = KeyauthCredential.batch_import(
42
53
(
43
54
resource_data.dig('credentials', 'key-auth') || []
44
55
).map { |t| t.merge('consumer' => { 'id' => resource.id }) },
45
56
verbose: verbose,
46
57
test: test
47
58
)
59
+
60
+ next unless project
61
+
62
+ BasicauthCredential.all.reject { |res| known_basic_auths.include?(res.id) }.map do |res|
63
+ puts "[WARN] ! Removing #{res.class.name} (#{res.id})"
64
+ res.destroy
65
+ end
66
+
67
+ JWTCredential.all.reject { |res| known_jwt_auths.include?(res.id) }.map do |res|
68
+ puts "[WARN] ! Removing #{res.class.name} (#{res.id})"
69
+ res.destroy
70
+ end
71
+
72
+ KeyauthCredential.all.reject { |res| known_key_auths.include?(res.id) }.map do |res|
73
+ puts "[WARN] ! Removing #{res.class.name} (#{res.id})"
74
+ res.destroy
75
+ end
48
76
end
77
+ # rubocop:enable Metrics/BlockLength
78
+
79
+ cleanup_except(project, known_ids) if project
49
80
end
50
81
51
- # Convenience method to add upstream targets
52
- # rubocop:disable Metrics/AbcSize
53
- # rubocop:disable Metrics/CyclomaticComplexity
54
- # rubocop:disable Metrics/PerceivedComplexity
55
82
def add_credential!(details)
56
83
r = if [BasicauthCredential, JWTCredential, KeyauthCredential].include? details.class
57
84
details
@@ -100,7 +127,7 @@ module SkullIsland
100
127
hash = { 'username' => username, 'custom_id' => custom_id }
101
128
creds = credentials_for_export
102
129
hash['credentials'] = creds unless creds.empty?
103
- hash['tags'] = tags if tags
130
+ hash['tags'] = tags unless tags.empty?
104
131
[*options[:exclude]].each do |exclude|
105
132
hash.delete(exclude.to_s)
106
133
end
data/lib/skull_island/resources/jwt_credential.rb CHANGED
@@ -20,17 +20,22 @@ module SkullIsland
20
20
def self.batch_import(data, verbose: false, test: false)
21
21
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
22
22
23
+ known_ids = []
24
+
23
25
data.each_with_index do |resource_data, index|
24
26
resource = new
25
27
resource.algorithm = resource_data['algorithm']
26
- resource.key = resource_data['key'] if resource_data['key']
27
- resource.secret = resource_data['secret'] if resource_data['secret']
28
+ resource.delayed_set(:key, resource_data, 'key') if resource_data['key']
29
+ resource.delayed_set(:secret, resource_data, 'secret') if resource_data['secret']
28
30
if resource_data['rsa_public_key']
29
- resource.rsa_public_key = resource_data['rsa_public_key']
31
+ resource.delayed_set(:rsa_public_key, resource_data, 'rsa_public_key')
30
32
end
31
33
resource.delayed_set(:consumer, resource_data, 'consumer')
32
34
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
35
+ known_ids << resource.id
33
36
end
37
+
38
+ known_ids
34
39
end
35
40
36
41
def self.relative_uri
@@ -65,6 +70,10 @@ module SkullIsland
65
70
false
66
71
end
67
72
73
+ def project
74
+ consumer ? consumer.project : nil
75
+ end
76
+
68
77
private
69
78
70
79
def postprocess_consumer(value)
data/lib/skull_island/resources/keyauth_credential.rb CHANGED
@@ -19,7 +19,7 @@ module SkullIsland
19
19
20
20
data.each_with_index do |resource_data, index|
21
21
resource = new
22
- resource.key = resource_data['key']
22
+ resource.delayed_set(:key, resource_data, 'key')
23
23
resource.delayed_set(:consumer, resource_data, 'consumer')
24
24
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
25
25
end
data/lib/skull_island/resources/plugin.rb CHANGED
@@ -7,6 +7,8 @@ module SkullIsland
7
7
#
8
8
# @see https://docs.konghq.com/1.1.x/admin-api/#plugin-object Plugin API definition
9
9
class Plugin < Resource
10
+ include Helpers::Meta
11
+
10
12
property :name
11
13
property :enabled, type: :boolean
12
14
property :run_on, validate: true
@@ -15,11 +17,15 @@ module SkullIsland
15
17
property :route, validate: true, preprocess: true, postprocess: true
16
18
property :service, validate: true, preprocess: true, postprocess: true
17
19
property :created_at, read_only: true, postprocess: true
18
- property :tags, validate: true
20
+ property :tags, validate: true, preprocess: true, postprocess: true
19
21
20
- def self.batch_import(data, verbose: false, test: false)
22
+ # rubocop:disable Metrics/CyclomaticComplexity
23
+ # rubocop:disable Metrics/PerceivedComplexity
24
+ def self.batch_import(data, verbose: false, test: false, project: nil, time: nil)
21
25
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
22
26
27
+ known_ids = []
28
+
23
29
data.each_with_index do |resource_data, index|
24
30
resource = new
25
31
resource.name = resource_data['name']
@@ -27,12 +33,19 @@ module SkullIsland
27
33
resource.run_on = resource_data['run_on'] if resource_data['run_on']
28
34
resource.config = resource_data['config'].deep_sort if resource_data['config']
29
35
resource.tags = resource_data['tags'] if resource_data['tags']
36
+ resource.project = project if project
37
+ resource.import_time = (time || Time.now.utc.to_i) if project
30
38
resource.delayed_set(:consumer, resource_data, 'consumer')
31
39
resource.delayed_set(:route, resource_data, 'route')
32
40
resource.delayed_set(:service, resource_data, 'service')
33
41
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
42
+ known_ids << resource.id
34
43
end
44
+
45
+ cleanup_except(project, known_ids) if project
35
46
end
47
+ # rubocop:enable Metrics/CyclomaticComplexity
48
+ # rubocop:enable Metrics/PerceivedComplexity
36
49
37
50
def self.enabled_names(api_client: APIClient.instance)
38
51
api_client.get("#{relative_uri}/enabled")['enabled_plugins']
@@ -55,7 +68,7 @@ module SkullIsland
55
68
hash['consumer'] = "<%= lookup :consumer, '#{consumer.username}' %>" if consumer
56
69
hash['route'] = "<%= lookup :route, '#{route.name}' %>" if route
57
70
hash['service'] = "<%= lookup :service, '#{service.name}' %>" if service
58
- hash['tags'] = tags if tags
71
+ hash['tags'] = tags unless tags.empty?
59
72
[*options[:exclude]].each do |exclude|
60
73
hash.delete(exclude.to_s)
61
74
end
data/lib/skull_island/resources/route.rb CHANGED
@@ -7,6 +7,8 @@ module SkullIsland
7
7
#
8
8
# @see https://docs.konghq.com/1.1.x/admin-api/#route-object Route API definition
9
9
class Route < Resource
10
+ include Helpers::Meta
11
+
10
12
property :name
11
13
property :methods
12
14
property :paths
@@ -21,14 +23,16 @@ module SkullIsland
21
23
property :service, validate: true, preprocess: true, postprocess: true
22
24
property :created_at, read_only: true, postprocess: true
23
25
property :updated_at, read_only: true, postprocess: true
24
- property :tags, validate: true
26
+ property :tags, validate: true, preprocess: true, postprocess: true
25
27
26
28
# rubocop:disable Metrics/CyclomaticComplexity
27
29
# rubocop:disable Metrics/PerceivedComplexity
28
30
# rubocop:disable Metrics/AbcSize
29
- def self.batch_import(data, verbose: false, test: false)
31
+ def self.batch_import(data, verbose: false, test: false, project: nil, time: nil)
30
32
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
31
33
34
+ known_ids = []
35
+
32
36
data.each_with_index do |rdata, index|
33
37
resource = new
34
38
resource.name = rdata['name']
@@ -41,9 +45,14 @@ module SkullIsland
41
45
resource.preserve_host = rdata['preserve_host'] unless rdata['preserve_host'].nil?
42
46
resource.snis = rdata['snis'] if rdata['snis']
43
47
resource.tags = rdata['tags'] if rdata['tags']
48
+ resource.project = project if project
49
+ resource.import_time = (time || Time.now.utc.to_i) if project
44
50
resource.delayed_set(:service, rdata, 'service')
45
51
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
52
+ known_ids << resource.id
46
53
end
54
+
55
+ cleanup_except(project, known_ids) if project
47
56
end
48
57
# rubocop:enable Metrics/CyclomaticComplexity
49
58
# rubocop:enable Metrics/PerceivedComplexity
@@ -68,7 +77,7 @@ module SkullIsland
68
77
}
69
78
hash['service'] = "<%= lookup :service, '#{service.name}' %>" if service
70
79
hash['snis'] = snis if snis && !snis.empty?
71
- hash['tags'] = tags if tags
80
+ hash['tags'] = tags unless tags.empty?
72
81
[*options[:exclude]].each do |exclude|
73
82
hash.delete(exclude.to_s)
74
83
end
data/lib/skull_island/resources/service.rb CHANGED
@@ -7,6 +7,8 @@ module SkullIsland
7
7
#
8
8
# @see https://docs.konghq.com/1.1.x/admin-api/#service-object Service API definition
9
9
class Service < Resource
10
+ include Helpers::Meta
11
+
10
12
property :name
11
13
property :retries
12
14
property :protocol, validate: true, required: true
@@ -18,27 +20,32 @@ module SkullIsland
18
20
property :read_timeout, validate: true
19
21
property :created_at, read_only: true, postprocess: true
20
22
property :updated_at, read_only: true, postprocess: true
21
- property :tags, validate: true
23
+ property :tags, validate: true, preprocess: true, postprocess: true
22
24
23
25
# rubocop:disable Metrics/CyclomaticComplexity
24
26
# rubocop:disable Metrics/PerceivedComplexity
25
27
# rubocop:disable Metrics/AbcSize
26
- def self.batch_import(data, verbose: false, test: false)
28
+ def self.batch_import(data, verbose: false, test: false, project: nil, time: nil)
27
29
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
28
30
31
+ known_ids = []
32
+
29
33
data.each_with_index do |rdata, index|
30
34
resource = new
31
35
resource.name = rdata['name']
32
36
resource.retries = rdata['retries'] if rdata['retries']
33
37
resource.protocol = rdata['protocol']
34
- resource.host = rdata['host']
35
- resource.port = rdata['port']
38
+ resource.delayed_set(:host, rdata, 'host')
39
+ resource.delayed_set(:port, rdata, 'port')
36
40
resource.path = rdata['path'] if rdata['path']
37
41
resource.connect_timeout = rdata['connect_timeout'] if rdata['connect_timeout']
38
42
resource.write_timeout = rdata['write_timeout'] if rdata['write_timeout']
39
43
resource.read_timeout = rdata['read_timeout'] if rdata['read_timeout']
40
44
resource.tags = rdata['tags'] if rdata['tags']
45
+ resource.project = project if project
46
+ resource.import_time = (time || Time.now.utc.to_i) if project
41
47
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
48
+ known_ids << resource.id
42
49
43
50
Route.batch_import(
44
51
(rdata['routes'] || []).map { |r| r.merge('service' => { 'id' => resource.id }) },
@@ -46,6 +53,8 @@ module SkullIsland
46
53
test: test
47
54
)
48
55
end
56
+
57
+ cleanup_except(project, known_ids) if project
49
58
end
50
59
# rubocop:enable Metrics/CyclomaticComplexity
51
60
# rubocop:enable Metrics/PerceivedComplexity
@@ -59,6 +68,11 @@ module SkullIsland
59
68
r.save
60
69
end
61
70
71
+ def destroy
72
+ routes.each(&:destroy)
73
+ super
74
+ end
75
+
62
76
# Provides a collection of related {Route} instances
63
77
def routes
64
78
Route.where(:service, self, api_client: api_client)
@@ -82,7 +96,7 @@ module SkullIsland
82
96
'read_timeout' => read_timeout
83
97
}
84
98
hash['routes'] = routes.collect { |route| route.export(exclude: 'service') }
85
- hash['tags'] = tags if tags
99
+ hash['tags'] = tags unless tags.empty?
86
100
[*options[:exclude]].each do |exclude|
87
101
hash.delete(exclude.to_s)
88
102
end
data/lib/skull_island/resources/upstream.rb CHANGED
@@ -7,6 +7,8 @@ module SkullIsland
7
7
#
8
8
# @see https://docs.konghq.com/1.1.x/admin-api/#upstream-objects Upstream API definition
9
9
class Upstream < Resource
10
+ include Helpers::Meta
11
+
10
12
property :name, required: true, validate: true
11
13
property :slots, validate: true
12
14
property :hash_on, validate: true
@@ -17,14 +19,16 @@ module SkullIsland
17
19
property :hash_on_cookie_path, validate: true
18
20
property :healthchecks, validate: true
19
21
property :created_at, read_only: true, postprocess: true
20
- property :tags, validate: true
22
+ property :tags, validate: true, preprocess: true, postprocess: true
21
23
22
24
# rubocop:disable Metrics/CyclomaticComplexity
23
25
# rubocop:disable Metrics/PerceivedComplexity
24
26
# rubocop:disable Metrics/AbcSize
25
- def self.batch_import(data, verbose: false, test: false)
27
+ def self.batch_import(data, verbose: false, test: false, project: nil, time: nil)
26
28
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
27
29
30
+ known_ids = []
31
+
28
32
data.each_with_index do |rdata, index|
29
33
resource = new
30
34
resource.name = rdata['name']
@@ -41,15 +45,22 @@ module SkullIsland
41
45
end
42
46
resource.healthchecks = rdata['healthchecks'] if rdata['healthchecks']
43
47
resource.tags = rdata['tags'] if rdata['tags']
48
+ resource.project = project if project
49
+ resource.import_time = (time || Time.now.utc.to_i) if project
44
50
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
51
+ known_ids << resource.id
45
52
puts '[INFO] Processing UpstreamTarget entries...' if verbose
46
53
47
54
UpstreamTarget.batch_import(
48
55
(rdata['targets'] || []).map { |t| t.merge('upstream' => { 'id' => resource.id }) },
49
56
verbose: verbose,
50
- test: test
57
+ test: test,
58
+ project: project,
59
+ time: time
51
60
)
52
61
end
62
+
63
+ cleanup_except(project, known_ids) if project
53
64
end
54
65
# rubocop:enable Metrics/CyclomaticComplexity
55
66
# rubocop:enable Metrics/PerceivedComplexity
@@ -113,7 +124,7 @@ module SkullIsland
113
124
'healthchecks' => healthchecks
114
125
}
115
126
hash['targets'] = targets.collect { |target| target.export(exclude: 'upstream') }
116
- hash['tags'] = tags if tags
127
+ hash['tags'] = tags unless tags.empty?
117
128
[*options[:exclude]].each do |exclude|
118
129
hash.delete(exclude.to_s)
119
130
end
data/lib/skull_island/resources/upstream_target.rb CHANGED
@@ -7,6 +7,8 @@ module SkullIsland
7
7
#
8
8
# @see https://docs.konghq.com/1.1.x/admin-api/#target-object Target API definition
9
9
class UpstreamTarget < Resource
10
+ include Helpers::Meta
11
+
10
12
property :target, required: true, validate: true, preprocess: true
11
13
property(
12
14
:upstream,
@@ -14,20 +16,31 @@ module SkullIsland
14
16
)
15
17
property :weight, validate: true
16
18
property :created_at, read_only: true, postprocess: true
17
- property :tags, validate: true
19
+ property :tags, validate: true, preprocess: true, postprocess: true
18
20
19
- def self.batch_import(data, verbose: false, test: false)
21
+ # rubocop:disable Metrics/CyclomaticComplexity
22
+ # rubocop:disable Metrics/PerceivedComplexity
23
+ def self.batch_import(data, verbose: false, test: false, project: nil, time: nil)
20
24
raise(Exceptions::InvalidArguments) unless data.is_a?(Array)
21
25
26
+ known_ids = []
27
+
22
28
data.each_with_index do |resource_data, index|
23
29
resource = new
24
- resource.target = resource_data['target']
30
+ resource.delayed_set(:target, resource_data, 'target')
25
31
resource.delayed_set(:upstream, resource_data, 'upstream')
26
32
resource.weight = resource_data['weight'] if resource_data['weight']
27
33
resource.tags = resource_data['tags'] if resource_data['tags']
34
+ resource.project = project if project
35
+ resource.import_time = (time || Time.now.utc.to_i) if project
28
36
resource.import_update_or_skip(index: index, verbose: verbose, test: test)
37
+ known_ids << resource.id
29
38
end
39
+
40
+ cleanup_except(project, known_ids) if project
30
41
end
42
+ # rubocop:enable Metrics/CyclomaticComplexity
43
+ # rubocop:enable Metrics/PerceivedComplexity
31
44
32
45
def self.all(options = {})
33
46
api_client = options[:api_client] || APIClient.instance
@@ -68,7 +81,7 @@ module SkullIsland
68
81
def export(options = {})
69
82
hash = { 'target' => target, 'weight' => weight }
70
83
hash['upstream'] = "<%= lookup :upstream, '#{upstream.name}' %>" if upstream
71
- hash['tags'] = tags if tags
84
+ hash['tags'] = tags unless tags.empty?
72
85
[*options[:exclude]].each do |exclude|
73
86
hash.delete(exclude.to_s)
74
87
end
@@ -79,19 +92,7 @@ module SkullIsland
79
92
end
80
93
81
94
def modified_existing?
82
- return false unless new?
95
+ false # Upstream Targets can not be PATCHed, so can not be modified
83
-
84
- # Find routes of the same name
85
- same_target_and_upstream = self.class.where(:target, target).and(:upstream, upstream)
86
-
87
- existing = same_target_and_upstream.size == 1 ? same_target_and_upstream.first : nil
88
-
89
- if existing
90
- @entity['id'] = existing.id
91
- save
92
- else
93
- false
94
- end
95
96
end
96
97
97
98
private
data/lib/skull_island/validations/resource.rb CHANGED
@@ -24,7 +24,7 @@ module SkullIsland
24
24
def validate_tags(value)
25
25
# allow only valid hostnames
26
26
value.each do |tag|
27
- return false unless tag.is_a?(String) && tag.match?(/\w_-\.~/)
27
+ return false unless tag.is_a?(String) && tag.match?(/^[\w_\-\.~]+#x2F;)
28
28
end
29
29
true
30
30
end
data/lib/skull_island/version.rb CHANGED
@@ -4,6 +4,6 @@ module SkullIsland
4
4
VERSION = [
5
5
1, # Major
6
6
2, # Minor
7
- 0 # Patch
7
+ 2 # Patch
8
8
].join('.')
9
9
end
data/skull_island.gemspec CHANGED
@@ -40,5 +40,5 @@ Gem::Specification.new do |spec|
40
40
spec.add_development_dependency 'rubocop', '~> 0.50'
41
41
spec.add_development_dependency 'simplecov', '~> 0.15'
42
42
spec.add_development_dependency 'travis', '~> 1.8'
43
- spec.add_development_dependency 'yard', '~> 0.9'
43
+ spec.add_development_dependency 'yard', '~> 0.9.20'
44
44
end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
--- !ruby/object:Gem::Specification
2
2
name: skull_island
3
3
version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.2
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-06-26 00:00:00.000000000 Z
11
+ date: 2019-08-09 00:00:00.000000000 Z
12
12
dependencies:
13
13
- !ruby/object:Gem::Dependency
14
14
name: deepsort
@@ -198,14 +198,14 @@ dependencies:
198
198
requirements:
199
199
- - "~>"
200
200
- !ruby/object:Gem::Version
201
- version: '0.9'
201
+ version: 0.9.20
202
202
type: :development
203
203
prerelease: false
204
204
version_requirements: !ruby/object:Gem::Requirement
205
205
requirements:
206
206
- - "~>"
207
207
- !ruby/object:Gem::Version
208
- version: '0.9'
208
+ version: 0.9.20
209
209
description: A Ruby SDK for Kong 0.14.x
210
210
email:
211
211
- jonathan.gnagy@gmail.com
@@ -242,6 +242,7 @@ files:
242
242
- lib/skull_island/exceptions/invalid_where_query.rb
243
243
- lib/skull_island/exceptions/new_instance_with_id.rb
244
244
- lib/skull_island/helpers/api_client.rb
245
+ - lib/skull_island/helpers/meta.rb
245
246
- lib/skull_island/helpers/migration.rb
246
247
- lib/skull_island/helpers/resource.rb
247
248
- lib/skull_island/helpers/resource_class.rb
@@ -284,7 +285,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
284
285
- !ruby/object:Gem::Version
285
286
version: '0'
286
287
requirements: []
287
- rubygems_version: 3.0.4
288
+ rubyforge_project:
289
+ rubygems_version: 2.7.7
288
290
signing_key:
289
291
specification_version: 4
290
292
summary: Ruby SDK for Kong