kong-client 0.4.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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.hound.yml +2 -0
  4. data/.rubocop.yml +360 -0
  5. data/.travis.yml +11 -0
  6. data/CHANGELOG.md +37 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE +191 -0
  9. data/README.md +295 -0
  10. data/Rakefile +5 -0
  11. data/kong.gemspec +24 -0
  12. data/lib/kong/acl.rb +8 -0
  13. data/lib/kong/api.rb +19 -0
  14. data/lib/kong/base.rb +180 -0
  15. data/lib/kong/basic_auth.rb +8 -0
  16. data/lib/kong/belongs_to_api.rb +30 -0
  17. data/lib/kong/belongs_to_consumer.rb +30 -0
  18. data/lib/kong/client.rb +232 -0
  19. data/lib/kong/consumer.rb +103 -0
  20. data/lib/kong/error.rb +10 -0
  21. data/lib/kong/hmac_auth.rb +9 -0
  22. data/lib/kong/jwt.rb +8 -0
  23. data/lib/kong/key_auth.rb +8 -0
  24. data/lib/kong/oauth2_token.rb +14 -0
  25. data/lib/kong/oauth_app.rb +8 -0
  26. data/lib/kong/plugin.rb +31 -0
  27. data/lib/kong/server.rb +23 -0
  28. data/lib/kong/target.rb +61 -0
  29. data/lib/kong/upstream.rb +24 -0
  30. data/lib/kong/util.rb +16 -0
  31. data/lib/kong/version.rb +3 -0
  32. data/lib/kong.rb +20 -0
  33. data/spec/kong/acl_spec.rb +19 -0
  34. data/spec/kong/api_spec.rb +32 -0
  35. data/spec/kong/base_spec.rb +169 -0
  36. data/spec/kong/basic_auth_spec.rb +26 -0
  37. data/spec/kong/client_spec.rb +297 -0
  38. data/spec/kong/consumer_spec.rb +72 -0
  39. data/spec/kong/error_spec.rb +23 -0
  40. data/spec/kong/hmac_auth_spec.rb +26 -0
  41. data/spec/kong/key_auth_spec.rb +26 -0
  42. data/spec/kong/oauth2_token_spec.rb +19 -0
  43. data/spec/kong/oauth_app_spec.rb +19 -0
  44. data/spec/kong/plugin_spec.rb +62 -0
  45. data/spec/kong/server_spec.rb +39 -0
  46. data/spec/kong/target_spec.rb +53 -0
  47. data/spec/kong/upstream_spec.rb +37 -0
  48. data/spec/kong/util_spec.rb +29 -0
  49. data/spec/spec_helper.rb +19 -0
  50. data/tasks/rspec.rake +5 -0
  51. metadata +153 -0
data/README.md ADDED
@@ -0,0 +1,295 @@
1
+ # Kong Client for Ruby
2
+
3
+ [Kong](http://getkong.org) API client for Ruby
4
+
5
+ [![Build Status](https://travis-ci.org/kontena/kong-client-ruby.svg?branch=master)](https://travis-ci.org/kontena/kong-client-ruby)
6
+ [![Gem Version](https://badge.fury.io/rb/kong.svg)](https://badge.fury.io/rb/kong)
7
+
8
+ ## Installation
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'kong'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install kong
20
+
21
+ ## Usage
22
+
23
+ By default Kong client tries to connect `http://localhost:8001` or address provided by environment variable: `KONG_URI='http://your-kong-url:8001'`.
24
+
25
+ You can set it also in your code:
26
+ ```ruby
27
+ require 'kong'
28
+ Kong::Client.api_url = 'http://your-kong-url:8001'
29
+ ```
30
+
31
+ ### Design
32
+
33
+ Kong client follows a design of resoures as Plain Old Ruby objects(tm). For examples to create a new Consumer resource you can do it like this:
34
+
35
+ ```ruby
36
+ consumer = Kong::Consumer.create({ username: 'testuser', custom_id: 'custom_id' })
37
+ ```
38
+
39
+ OR
40
+
41
+ ```ruby
42
+ consumer = Kong::Consumer.new({ username: 'testuser'})
43
+ consumer.custom_id = '12345'
44
+ consumer.save
45
+ ```
46
+
47
+ To find existing consumer:
48
+
49
+ ```ruby
50
+ consumer = Kong::Consumer.find_by_username('testuser')
51
+ consumer = Kong::Consumer.find_by_custom_id('custom_id')
52
+ ```
53
+
54
+ ### All Resources and Actions
55
+
56
+ To see the complete Kong Admin API documentation, please visit: https://getkong.org/docs/0.11.x/admin-api/
57
+
58
+ #### Consumer
59
+
60
+ ```ruby
61
+ Kong::Consumer.list(filters)
62
+ Kong::Consumer.all()
63
+ Kong::Consumer.find(id)
64
+ Kong::Consumer.find_by_*(value)
65
+ Kong::Consumer.find_all_by_*(value)
66
+ Kong::Consumer.create(attributes)
67
+
68
+ consumer = Kong::Consumer.new({ username: 'test-user' })
69
+ consumer.get # reloads resource
70
+ consumer.create
71
+ consumer.update
72
+ consumer.save # requests create_or_update action
73
+ consumer.delete
74
+
75
+ consumer.plugins
76
+ consumer.oauth_apps
77
+ consumer.key_auths
78
+ consumer.basic_auths
79
+ consumer.oauth2_tokens
80
+ ```
81
+
82
+ #### API
83
+
84
+ ```ruby
85
+ Kong::Api.list(filters)
86
+ Kong::Api.all()
87
+ Kong::Api.find(id)
88
+ Kong::Api.find_by_*(value)
89
+ Kong::Api.create(attributes)
90
+
91
+ api = Kong::Api.new({
92
+ name: 'Mockbin',
93
+ hosts: ['example.com'],
94
+ uris: ['/someservice'],
95
+ methods: ['GET'],
96
+ strip_uri: false,
97
+ preserve_host: false,
98
+ upstream_url: 'https://mockbin.com'
99
+ })
100
+ api.get # reloads resource
101
+ api.create
102
+ api.update
103
+ api.save # requests create_or_update action
104
+ api.delete
105
+
106
+ api.plugins
107
+ ```
108
+
109
+ #### Plugin
110
+
111
+ ```ruby
112
+ Kong::Plugin.list(filters)
113
+ Kong::Plugin.all()
114
+ Kong::Plugin.find(id)
115
+ Kong::Plugin.find_by_*(value)
116
+ Kong::Plugin.create(attributes)
117
+
118
+ plugin = Kong::Plugin.new({
119
+ api_id: '5fd1z584-1adb-40a5-c042-63b19db49x21',
120
+ consumer_id: 'a3dX2dh2-1adb-40a5-c042-63b19dbx83hF4',
121
+ name: 'rate-limiting',
122
+ config: {
123
+ minute: 20,
124
+ hour: 500
125
+ }
126
+ })
127
+
128
+ plugin.get # reloads resource
129
+ plugin.create
130
+ plugin.update
131
+ plugin.save # requests create_or_update action
132
+ plugin.delete
133
+ ```
134
+
135
+ #### Upstream and Targets (for load-balanced APIs)
136
+
137
+ ```ruby
138
+ Kong::Upstream.list(filters)
139
+ Kong::Upstream.all()
140
+ Kong::Upstream.find(id)
141
+ Kong::Upstream.find_by_*(value)
142
+ Kong::Upstream.create(attributes)
143
+
144
+ upstream = Kong::Upstream.new({ name: 'myservice' })
145
+
146
+ upstream.get # reloads resource
147
+ upstream.create
148
+ upstream.update
149
+ upstream.save # requests create_or_update action
150
+ upstream.delete
151
+
152
+ upstream.targets # lists active targets
153
+
154
+ # Add targets
155
+ Kong::Target.new({ upstream_id: upstream.id, target: 'appserver1:80' }).save
156
+ Kong::Target.new({ upstream_id: upstream.id, target: 'appserver2:80' }).save
157
+
158
+ # Add the API
159
+ Kong::Api.new({
160
+ ...
161
+ upstream_url: 'http://myservice'
162
+ }).save
163
+ ```
164
+
165
+ #### OAuthApp
166
+
167
+ ```ruby
168
+ Kong::OAuthApp.list(filters)
169
+ Kong::OAuthApp.all()
170
+ Kong::OAuthApp.find(consumer_id)
171
+ Kong::OAuthApp.find_by_*(value)
172
+ Kong::OAuthApp.create(attributes)
173
+
174
+ app = Kong::OAuthApp.new({
175
+ consumer_id: 'a3dX2dh2-1adb-40a5-c042-63b19dbx83hF4',
176
+ redirect_uri: 'http://some-domain/endpoint/'
177
+ })
178
+
179
+ app.create
180
+ app.get # reloads resource
181
+ app.update
182
+ app.save # requests create_or_update action
183
+ app.delete
184
+ ```
185
+
186
+ #### KeyAuth
187
+
188
+ ```ruby
189
+ Kong::KeyAuth.create(attributes)
190
+
191
+ auth = Kong::KeyAuth.new({
192
+ consumer_id: 'a3dX2dh2-1adb-40a5-c042-63b19dbx83hF4',
193
+ })
194
+
195
+ auth.create
196
+ auth.get # reloads resource
197
+ auth.update
198
+ auth.save # requests create_or_update action
199
+ auth.delete
200
+ ```
201
+
202
+ #### BasicAuth
203
+
204
+ ```ruby
205
+ Kong::BasicAuth.create(attributes)
206
+
207
+ auth = Kong::BasicAuth.new({
208
+ consumer_id: 'a3dX2dh2-1adb-40a5-c042-63b19dbx83hF4',
209
+ username: 'user123',
210
+ password: 'secret'
211
+ })
212
+
213
+ auth.create
214
+ auth.get # reloads resource
215
+ auth.update
216
+ auth.save # requests create_or_update action
217
+ auth.delete
218
+ ```
219
+
220
+ #### OAuth2Token
221
+
222
+ ```ruby
223
+ token = Kong::OAuth2Token.find_by_access_token('SOME-TOKEN')
224
+
225
+ token = Kong::OAuth2Token.new({
226
+ credential_id: 'KONG-APPLICATION-ID',
227
+ token_type: 'bearer',
228
+ access_token: 'SOME-TOKEN',
229
+ refresh_token: 'SOME-TOKEN',
230
+ expires_in: 3600
231
+ })
232
+
233
+ token.create
234
+ token.update
235
+ token.save # requests create_or_update action
236
+ token.delete
237
+
238
+ token.oauth_app
239
+ ```
240
+
241
+ #### JWT
242
+
243
+ ```ruby
244
+
245
+ jwt = Kong::JWT.new({
246
+ consumer_id: 'a3dX2dh2-1adb-40a5-c042-63b19dbx83hF4',
247
+ key: 'a36c3049b36249a3c9f8891cb127243c',
248
+ secret: 'e71829c351aa4242c2719cbfbe671c09'
249
+ })
250
+
251
+ jwt.create
252
+ jwt.update
253
+ jwt.save # requests create_or_update action
254
+ jwt.delete
255
+
256
+ consumer = Kong::Consumer.find_by_username('testuser')
257
+ consumer.jwts
258
+ ```
259
+
260
+ #### ACL
261
+
262
+ ```ruby
263
+
264
+ acl = Kong::Acl.new({
265
+ consumer_id: 'a3dX2dh2-1adb-40a5-c042-63b19dbx83hF4',
266
+ group: 'group1',
267
+ tags: ["tag1", "tag2"]
268
+ })
269
+
270
+ acl.create
271
+ acl.update
272
+ acl.save # requests create_or_update action
273
+ acl.delete
274
+
275
+ consumer = Kong::Consumer.find_by_username('testuser')
276
+ consumer.acls
277
+ ```
278
+
279
+ #### Server Information
280
+
281
+ ```ruby
282
+ Kong::Server.info
283
+ Kong::Server.version
284
+ Kong::Server.status
285
+ Kong::Server.cluster
286
+ Kong::Server.remove_node(node_name)
287
+ ```
288
+
289
+ ## Contributing
290
+
291
+ 1. Fork it ( https://github.com/ResultadosDigitais/kong-client-ruby/fork )
292
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
293
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
294
+ 4. Push to the branch (`git push origin my-new-feature`)
295
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ Dir.glob('tasks/*.rake').each { |r| import r }
4
+
5
+ task :default => :spec
data/kong.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kong/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kong-client"
8
+ spec.version = Kong::VERSION
9
+ spec.authors = ["Ecosystem API Team"]
10
+ spec.email = ["ecosystem-apis@rdstation.com", "renan.porto@rdstation.com", "rogerio.angeliski@rdstation.com"]
11
+ spec.summary = %q{A Ruby client for the Kong API }
12
+ spec.description = %q{A Ruby client for the Kong API}
13
+ spec.homepage = "https://github.com/ResultadosDigitais/kong-client-ruby"
14
+ spec.license = "Apache-2.0"
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.required_ruby_version = ">= 2.0.0"
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.17"
22
+ spec.add_development_dependency "rake", "~> 12.0"
23
+ spec.add_runtime_dependency "excon"
24
+ end
data/lib/kong/acl.rb ADDED
@@ -0,0 +1,8 @@
1
+ module Kong
2
+ class Acl
3
+ include Base
4
+ include BelongsToConsumer
5
+ ATTRIBUTE_NAMES = %w(id group tags consumer_id).freeze
6
+ API_END_POINT = "/acls/".freeze
7
+ end
8
+ end
data/lib/kong/api.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Kong
2
+ class Api
3
+ include Base
4
+
5
+ ATTRIBUTE_NAMES = %w(
6
+ id name request_host request_path strip_request_path
7
+ hosts uris strip_uri preserve_host upstream_url retries
8
+ upstream_connect_timeout upstream_send_timeout upstream_read_timeout
9
+ https_only http_if_terminated methods
10
+ ).freeze
11
+ API_END_POINT = '/apis/'.freeze
12
+
13
+ ##
14
+ # @return [Array<Kong::Plugin>]
15
+ def plugins
16
+ Plugin.list({ api_id: self.id })
17
+ end
18
+ end
19
+ end
data/lib/kong/base.rb ADDED
@@ -0,0 +1,180 @@
1
+ module Kong
2
+ module Base
3
+ module ClassMethods
4
+
5
+ # List resources
6
+ # @return [Array]
7
+ def list(params = {})
8
+ result = []
9
+ json_data = Client.instance.get(self::API_END_POINT, params)
10
+ if json_data['data']
11
+ json_data['data'].each do |instance|
12
+ result << self.new(instance)
13
+ end
14
+ end
15
+ result
16
+ end
17
+
18
+ alias_method :all, :list
19
+
20
+ # Create resource
21
+ # @param [Hash] attributes
22
+ def create(attributes = {})
23
+ self.new(attributes).create
24
+ end
25
+
26
+ # Find resource
27
+ # @param [String] id
28
+ def find(id)
29
+ self.new.get(id)
30
+ end
31
+
32
+ def method_missing(method, *arguments, &block)
33
+ if method.to_s.start_with?('find_by_')
34
+ attribute = method.to_s.sub('find_by_', '')
35
+ if self.attribute_names.include?(attribute)
36
+ self.list({ attribute => arguments[0] })[0]
37
+ else
38
+ super
39
+ end
40
+ elsif method.to_s.start_with?('find_all_by_')
41
+ attribute = method.to_s.sub('find_all_by_', '')
42
+ if self.attribute_names.include?(attribute)
43
+ self.list({ attribute => arguments[0] })
44
+ else
45
+ super
46
+ end
47
+ else
48
+ super
49
+ end
50
+ end
51
+
52
+ def respond_to?(method, include_private = false)
53
+ if method.to_s.start_with?('find_by_')
54
+ attribute = method.to_s.sub('find_by_', '')
55
+ if self.attribute_names.include?(attribute)
56
+ return true
57
+ else
58
+ super
59
+ end
60
+ elsif method.to_s.start_with?('find_all_by_')
61
+ attribute = method.to_s.sub('find_all_by_', '')
62
+ if self.attribute_names.include?(attribute)
63
+ return true
64
+ else
65
+ super
66
+ end
67
+ else
68
+ super
69
+ end
70
+ end
71
+ end
72
+
73
+ attr_accessor :attributes, :api_end_point
74
+
75
+ def self.included(base)
76
+ base.extend(ClassMethods)
77
+
78
+ base.send(:define_singleton_method, :attribute_names) do
79
+ base::ATTRIBUTE_NAMES
80
+ end
81
+
82
+ base.send(:define_method, :init_api_end_point) do
83
+ @api_end_point = base::API_END_POINT
84
+ end
85
+ end
86
+
87
+ ##
88
+ # @param [Hash] attributes
89
+ def initialize(attributes = {})
90
+ init_api_end_point
91
+ init_attributes(attributes)
92
+ end
93
+
94
+ # Get Kong API client
95
+ # @return [Kong::Client]
96
+ def client
97
+ Client.instance
98
+ end
99
+
100
+ # Get resource
101
+ # @param [String] key
102
+ def get(key = nil)
103
+ key = self.id if key.nil?
104
+ path = @api_end_point + key
105
+ response = client.get(path) rescue nil
106
+ return nil if response.nil?
107
+ init_attributes(response)
108
+ self
109
+ end
110
+
111
+ # Delete resource
112
+ def delete
113
+ client.delete("#{@api_end_point}#{self.id}")
114
+ end
115
+
116
+ def new?
117
+ self.id.nil?
118
+ end
119
+
120
+ # Save resource to Kong
121
+ def save
122
+ create_or_update
123
+ end
124
+
125
+ # Create resource
126
+ def create
127
+ headers = { 'Content-Type' => 'application/json' }
128
+ response = client.post(@api_end_point, attributes, nil, headers)
129
+ init_attributes(response)
130
+ self
131
+ end
132
+
133
+ # Create or update resource
134
+ # Data is sent to Kong in JSON format and HTTP PUT request is used
135
+ def create_or_update
136
+ headers = { 'Content-Type' => 'application/json' }
137
+ response = client.put(@api_end_point, attributes, nil, headers)
138
+ init_attributes(response)
139
+ self
140
+ end
141
+
142
+ # Update resource
143
+ def update
144
+ headers = { 'Content-Type' => 'application/json' }
145
+ response = client.patch("#{@api_end_point}#{self.id}", attributes, nil, headers)
146
+ init_attributes(response)
147
+ self
148
+ end
149
+
150
+ def method_missing(method, *arguments, &block)
151
+ if self.class.attribute_names.include?(method.to_s)
152
+ @attributes[method.to_s]
153
+ elsif method.to_s.end_with?('=') && self.class.attribute_names.include?(attribute = method.to_s.split('=').first)
154
+ @attributes[attribute] = arguments[0]
155
+ else
156
+ super
157
+ end
158
+ end
159
+
160
+ def respond_to?(method, include_private = false)
161
+ if self.class.attribute_names.include?(method.to_s.split('=')[0])
162
+ true
163
+ else
164
+ super
165
+ end
166
+ end
167
+
168
+ private
169
+
170
+ def init_attributes(attributes)
171
+ @attributes = {}
172
+ attributes.each do |key, value|
173
+ @attributes[key.to_s] = value
174
+ end
175
+ use_consumer_end_point if respond_to?(:use_consumer_end_point)
176
+ use_api_end_point if respond_to?(:use_api_end_point)
177
+ use_upstream_end_point if respond_to?(:use_upstream_end_point)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,8 @@
1
+ module Kong
2
+ class BasicAuth
3
+ include Base
4
+ include BelongsToConsumer
5
+ ATTRIBUTE_NAMES = %w(id username password consumer_id).freeze
6
+ API_END_POINT = "/basic-auth/".freeze
7
+ end
8
+ end
@@ -0,0 +1,30 @@
1
+ module Kong
2
+ module BelongsToApi
3
+ attr_accessor :api
4
+
5
+ # Convert API end point relative to Kong API resource
6
+ def use_api_end_point
7
+ self.api_end_point = "/apis/#{self.api_id}#{self.class::API_END_POINT}" if self.api_id
8
+ end
9
+
10
+ # Get Api resource
11
+ # @return [Kong::Api]
12
+ def api
13
+ @api ||= Api.find(self.api_id)
14
+ end
15
+
16
+ # Set Api resource
17
+ # @param [Kong::Api] api
18
+ def api=(api)
19
+ @api = api
20
+ self.api_id = api.id
21
+ end
22
+
23
+ # Set Api id
24
+ # @param [String] id
25
+ def api_id=(id)
26
+ super(id)
27
+ use_api_end_point
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module Kong
2
+ module BelongsToConsumer
3
+ attr_accessor :consumer
4
+
5
+ # Convert API end point relative to Kong Consumer resource
6
+ def use_consumer_end_point
7
+ self.api_end_point = "/consumers/#{self.consumer_id}#{self.class::API_END_POINT}" if self.consumer_id
8
+ end
9
+
10
+ # Get Consumer resource
11
+ # @return [Kong::Consumer]
12
+ def consumer
13
+ @consumer ||= Consumer.find(self.consumer_id)
14
+ end
15
+
16
+ # Set Consumer resource
17
+ # @param [Kong::Consumer] consumer
18
+ def consumer=(consumer)
19
+ @consumer = consumer
20
+ self.consumer_id = consumer.id
21
+ end
22
+
23
+ # Set Consumer id
24
+ # @param [String] id
25
+ def consumer_id=(id)
26
+ super(id)
27
+ use_consumer_end_point
28
+ end
29
+ end
30
+ end