kong_schema 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ce66ed2926b783ba19d2eee0e5e85d2606ead664
4
+ data.tar.gz: f040ed071ff55f64c1ea86906403b3f093c4e4b6
5
+ SHA512:
6
+ metadata.gz: b2ff0756f8345d767ac68f28a57b07b05336252360906cf901386f247dbd12320fe8e501b2c736a45051e0a8878a969ba3ae315e9bd945f9aa9d158fb25c4975
7
+ data.tar.gz: 75e4876cd89fcb9ab5dd1538427f79d0a52f5670ae4bb4429d95b60859be221b6140b4d6be98bc469d750bfa9abc9a75904fb2973c458b9d0172567e5256eee5
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ /.bundle
2
+ /coverage
3
+ /bin
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,31 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'spec/**/*_spec.rb'
4
+ - 'lib/kong_schema/cli.rb'
5
+
6
+ Lint/EndAlignment:
7
+ AlignWith: variable
8
+
9
+ Style/EachWithObject:
10
+ Enabled: false
11
+
12
+ Style/SpaceInsideBrackets:
13
+ Enabled: false
14
+
15
+ Style/Documentation:
16
+ Enabled: false
17
+
18
+ Style/StringLiterals:
19
+ Enabled: false
20
+
21
+ Style/FrozenStringLiteralComment:
22
+ Enabled: false
23
+
24
+ Style/SignalException:
25
+ Enabled: false
26
+
27
+ Metrics/MethodLength:
28
+ Max: 25
29
+
30
+ Style/ModuleFunction:
31
+ EnforcedStyle: extend_self
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.4
data/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # 1.0.1
2
+
3
+ - Fixed a bug where an empty array of "hosts", "uris", or "methods" in "apis"
4
+ would raise an exception.
5
+ - An error will now be raised if `admin_host` is not defined in config (and a
6
+ connection to Kong admin can not be established)
7
+
8
+ # 1.0.0
9
+
10
+ Initial release with support for the following Kong objects:
11
+
12
+ - [Apis](https://getkong.org/docs/0.11.x/admin-api/#add-api)
13
+ - [Upstreams](https://getkong.org/docs/0.11.x/admin-api/#add-upstream)
14
+ - [Targets](https://getkong.org/docs/0.11.x/admin-api/#add-target)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in kong_schema.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,85 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ kong_schema (1.1.0)
5
+ diffy (~> 3.1)
6
+ gli (~> 2.16)
7
+ kong (~> 0.3)
8
+ tty-prompt (~> 0.13)
9
+ tty-table (~> 0.8)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ diff-lcs (1.3)
15
+ diffy (3.1.0)
16
+ docile (1.1.5)
17
+ equatable (0.5.0)
18
+ excon (0.49.0)
19
+ gli (2.16.1)
20
+ hitimes (1.2.6)
21
+ json (2.1.0)
22
+ kong (0.3.0)
23
+ excon (~> 0.49.0)
24
+ necromancer (0.4.0)
25
+ pastel (0.7.1)
26
+ equatable (~> 0.5.0)
27
+ tty-color (~> 0.4.0)
28
+ rspec (3.6.0)
29
+ rspec-core (~> 3.6.0)
30
+ rspec-expectations (~> 3.6.0)
31
+ rspec-mocks (~> 3.6.0)
32
+ rspec-core (3.6.0)
33
+ rspec-support (~> 3.6.0)
34
+ rspec-expectations (3.6.0)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.6.0)
37
+ rspec-mocks (3.6.0)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.6.0)
40
+ rspec-support (3.6.0)
41
+ simplecov (0.15.0)
42
+ docile (~> 1.1.0)
43
+ json (>= 1.8, < 3)
44
+ simplecov-html (~> 0.10.0)
45
+ simplecov-html (0.10.2)
46
+ simplecov_compact_json (1.0.1)
47
+ timers (4.1.2)
48
+ hitimes
49
+ tty-color (0.4.2)
50
+ tty-cursor (0.5.0)
51
+ tty-prompt (0.13.2)
52
+ necromancer (~> 0.4.0)
53
+ pastel (~> 0.7.0)
54
+ timers (~> 4.1.2)
55
+ tty-cursor (~> 0.5.0)
56
+ tty-reader (~> 0.1.0)
57
+ tty-reader (0.1.0)
58
+ wisper (~> 2.0.0)
59
+ tty-screen (0.5.0)
60
+ tty-table (0.8.0)
61
+ equatable (~> 0.5.0)
62
+ necromancer (~> 0.4.0)
63
+ pastel (~> 0.7.0)
64
+ tty-screen (~> 0.5.0)
65
+ unicode-display_width (~> 1.1.0)
66
+ verse (~> 0.5.0)
67
+ unicode-display_width (1.1.3)
68
+ unicode_utils (1.4.0)
69
+ verse (0.5.0)
70
+ unicode-display_width (~> 1.1.0)
71
+ unicode_utils (~> 1.4.0)
72
+ wisper (2.0.0)
73
+
74
+ PLATFORMS
75
+ ruby
76
+
77
+ DEPENDENCIES
78
+ bundler (~> 1.15)
79
+ kong_schema!
80
+ rspec (~> 3.6)
81
+ simplecov (~> 0.15)
82
+ simplecov_compact_json (~> 1.0)
83
+
84
+ BUNDLED WITH
85
+ 1.15.4
data/LICENSE.md ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (C) 2017 Instructure, INC.
2
+
3
+ This program is free software: you can redistribute it and/or modify
4
+ it under the terms of the GNU Affero General Public License as
5
+ published by the Free Software Foundation, either version 3 of the
6
+ License, or (at your option) any later version.
7
+
8
+ This program is distributed in the hope that it will be useful,
9
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ GNU Affero General Public License for more details.
12
+
13
+ You should have received a copy of the GNU Affero General Public License
14
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
data/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # kong_schema
2
+
3
+ Configure [Kong](https://getkong.org) using YAML or JSON source files.
4
+
5
+ This package is intended for use by programmers in development environments and
6
+ may not be optimal for admins since it does not attempt to expose the full
7
+ power of Kong's REST API.
8
+
9
+ Use of this package requires no knowledge of [Kong's REST
10
+ API](https://getkong.org/docs/0.11.x/admin-api/) - it is meant to abstract it
11
+ away from the user.
12
+
13
+ ## Installation
14
+
15
+ The package requires a recent version of Ruby.
16
+
17
+ ```shell
18
+ gem install kong_schema
19
+ ```
20
+
21
+ If you're using Bundler, add it to your Gemfile instead:
22
+
23
+ ```ruby
24
+ group :development do
25
+ gem 'kong_schema'
26
+ end
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ Write your desired configuration for Kong in a file using either YAML or JSON.
32
+ The supported "directives" are described later in this document.
33
+
34
+ For now we'll assume we have such a config file:
35
+
36
+ ```yaml
37
+ # file: config/kong.yml
38
+ kong:
39
+ admin_host: localhost:8001
40
+ apis:
41
+ - name: application-api
42
+ hosts:
43
+ - api.application.dev
44
+ upstream_url: http://application-api-lb
45
+ uris:
46
+ - /api/.+
47
+ - /auth/.+
48
+ preserve_host: true
49
+ strip_uri: false
50
+ upstreams:
51
+ - name: application-api-lb
52
+ targets:
53
+ - upstream_id: application-api-lb
54
+ target: 127.0.0.1:3000
55
+ ```
56
+
57
+ Then if we run the following command:
58
+
59
+ ```shell
60
+ bundle exec kong_schema up config/kong.yml
61
+ ```
62
+
63
+ kong_schema will read the directives found under the `kong` dictionary and
64
+ prompt you with a list of changes it will apply to Kong through the REST API.
65
+
66
+ ```shell
67
+ +-----------------+------------------------------------------------+
68
+ | Change | Parameters |
69
+ +-----------------+------------------------------------------------+
70
+ | Create Api | { |
71
+ | | "name": "application-api", |
72
+ | | "hosts": [ |
73
+ | | "api.application.dev" |
74
+ | | ], |
75
+ | | "upstream_url": "http://application-api-lb", |
76
+ | | "uris": [ |
77
+ | | "/api/.+", |
78
+ | | "/auth/.+" |
79
+ | | ], |
80
+ | | "preserve_host": true |
81
+ | | } |
82
+ | Create Upstream | { |
83
+ | | "name": "application-api-lb" |
84
+ | | } |
85
+ | Create Target | { |
86
+ | | "upstream_id": "application-api-lb", |
87
+ | | "target": "127.0.0.1:3000" |
88
+ | | } |
89
+ +-----------------+------------------------------------------------+
90
+ Commit the changes to Kong? (y/N)
91
+ ```
92
+
93
+ If you agree and everything goes well, you should see an affirmative message:
94
+
95
+ ```
96
+ ✓ Kong has been reconfigured!
97
+ ```
98
+
99
+ Running the command again should report that there are no changes to reflect:
100
+
101
+ ```
102
+ ✓ Nothing to update.
103
+ ```
104
+
105
+ But if you _do_ change something it will result in an "update" operation and present you with the changes it perceives to have been made.
106
+
107
+ Let's add another `uri` to our API:
108
+
109
+ ```yaml
110
+ # file: config/kong.yml
111
+ # ...
112
+ apis:
113
+ - name: application-api
114
+ hosts:
115
+ - api.application.dev
116
+ upstream_url: http://application-api-lb
117
+ uris:
118
+ - /api/.+
119
+ - /auth/.+
120
+ - /oauth2/.+
121
+ ```
122
+
123
+ And re-run `kong_schema up` as we did before:
124
+
125
+ ```shell
126
+ +------------+-------------------------------------------------+
127
+ | Change | Parameters |
128
+ +------------+-------------------------------------------------+
129
+ | Update Api | { |
130
+ | | "name": "application-api", |
131
+ | | "hosts": [ |
132
+ | | "api.application.dev" |
133
+ | | ], |
134
+ | | "upstream_url": "http://application-api-lb", |
135
+ | | "uris": [ |
136
+ | | "/api/.+", |
137
+ | | - "/auth/.+" |
138
+ | | + "/auth/.+", |
139
+ | | + "/oauth2/.+" |
140
+ | | ], |
141
+ | | "preserve_host": true |
142
+ | | } |
143
+ +------------+-------------------------------------------------+
144
+ ```
145
+
146
+ Nice and easy!
147
+
148
+ ## Configuration
149
+
150
+ ### `admin_host: String`
151
+
152
+ ### `apis: Array<Kong::Api>`
153
+
154
+ [Kong::Api](https://getkong.org/docs/0.11.x/admin-api/#add-api) configuration:
155
+
156
+ - name: String
157
+ - host: String
158
+ - upstream_url: String
159
+ - uris: Array<String>
160
+ - preserve_host: Boolean
161
+
162
+ ### `upstreams: Array<Kong::Upstream>`
163
+
164
+ [Kong::Upstream](https://getkong.org/docs/0.11.x/admin-api/#add-upstream)
165
+ configuration:
166
+
167
+ - name: String
168
+ - ?slots: Number
169
+ - ?orderlist: Array<Number>
170
+
171
+ ### `targets: Array<Kong::Target>`
172
+
173
+ [Kong::Target](https://getkong.org/docs/0.11.x/admin-api/#add-target)
174
+ configuration:
175
+
176
+ - upstream_id: String
177
+ - target: String
178
+ - ?weight: Number
179
+
180
+ ## TODO
181
+
182
+ Add support for the remaining Kong API objects:
183
+
184
+ - [consumers](https://getkong.org/docs/0.11.x/admin-api/#create-consumer)
185
+ - [plugins](https://getkong.org/docs/0.11.x/admin-api/#add-plugin)
186
+ - [certificates](https://getkong.org/docs/0.11.x/admin-api/#add-certificate)
187
+ - [snis](https://getkong.org/docs/0.11.x/admin-api/#add-sni)
188
+
189
+ ## License
190
+
191
+ Copyright (C) 2017 Instructure, INC.
192
+
193
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
194
+
195
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
196
+
197
+ You should have received a copy of the GNU Affero General Public License along with this program. If not, see http://www.gnu.org/licenses/.
data/bin/kong_schema ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'kong_schema'
4
+
5
+ exit KongSchema::CLI.new.run(ARGV)
6
+ # require 'tty-prompt'
7
+ # require 'pastel'
8
+
9
+ # pastel = Pastel.new
10
+ # schema = KongSchema::Schema
11
+ # config = YAML.load_file('config/application.yml')['kong']
12
+
13
+ # schema.scan(config).tap do |changes|
14
+ # if changes.empty?
15
+ # puts "#{pastel.green('✓')} Nothing to update."
16
+
17
+ # Process.exit(0)
18
+ # else
19
+ # puts KongSchema::Reporter.report(changes, object_format: :json)
20
+
21
+ # if TTY::Prompt.new.yes?('Commit the changes to Kong?', default: false)
22
+ # schema.commit(config, changes)
23
+
24
+ # puts "#{pastel.green('✓')} Kong has been reconfigured!"
25
+ # end
26
+ # end
27
+ # end
@@ -0,0 +1,36 @@
1
+ require 'kong'
2
+
3
+ module Kong
4
+ class Upstream
5
+ # monkey patch to make it return only "active" targets
6
+ #
7
+ # see https://github.com/Mashape/kong/issues/2876#issuecomment-328667296
8
+ # see https://github.com/kontena/kong-client-ruby/blob/master/lib/kong/upstream.rb#L17
9
+ def targets
10
+ targets = []
11
+ json_data = Client.instance.get("#{API_END_POINT}#{self.id}/targets")
12
+
13
+ if json_data['data']
14
+ json_data['data'].each do |target_data|
15
+ targets << Target.new(target_data)
16
+ end
17
+ end
18
+
19
+ by_target = targets.reduce({}) do |map, target|
20
+ map[target.target] ||= []
21
+ map[target.target] << target
22
+ map
23
+ end
24
+
25
+ by_target.keys.reduce([]) do |list, key|
26
+ target = by_target[key].sort_by { |x| x.attributes[:created_at].to_i }.last
27
+
28
+ if target.active?
29
+ list.push(target)
30
+ else
31
+ list
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+
3
+ require_relative "./lib/kong_schema/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "kong_schema"
7
+ spec.version = KongSchema::VERSION
8
+ spec.authors = ["Ahmad Amireh"]
9
+ spec.email = ["ahmad@instructure.com"]
10
+ spec.summary = "Configure Kong from a file using its REST API."
11
+ spec.license = 'AGPL-3.0'
12
+ spec.homepage = 'https://github.com/amireh/kong_schema'
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = %w(kong_schema)
16
+ spec.test_files = spec.files.grep(%r{^spec/})
17
+ spec.require_paths = %w(lib)
18
+
19
+ spec.add_dependency "gli", "~> 2.16"
20
+ spec.add_dependency "diffy", "~> 3.1"
21
+ spec.add_dependency "kong", "~> 0.3"
22
+ spec.add_dependency "tty-prompt", "~> 0.13"
23
+ spec.add_dependency "tty-table", "~> 0.8"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.15"
26
+ spec.add_development_dependency "rspec", "~> 3.6"
27
+ spec.add_development_dependency "simplecov", "~> 0.15"
28
+ spec.add_development_dependency "simplecov_compact_json", "~> 1.0"
29
+ end
@@ -0,0 +1,44 @@
1
+ module KongSchema
2
+ # TODO: make a validation pass before #apply
3
+ module Actions
4
+ class Create
5
+ attr_reader :model, :params
6
+
7
+ def initialize(model:, params:)
8
+ @model = model
9
+ @params = params
10
+ end
11
+
12
+ def apply(*)
13
+ @model.create(@params)
14
+ end
15
+ end
16
+
17
+ class Update
18
+ attr_reader :model, :params, :record
19
+
20
+ def initialize(model:, record:, params:)
21
+ @model = model
22
+ @params = params
23
+ @record = record
24
+ end
25
+
26
+ def apply(*)
27
+ @model.update(@record, @params)
28
+ end
29
+ end
30
+
31
+ class Delete
32
+ attr_reader :model, :record
33
+
34
+ def initialize(model:, record:)
35
+ @model = model
36
+ @record = record
37
+ end
38
+
39
+ def apply(*)
40
+ @model.delete(@record)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KongSchema
4
+ class Adapter
5
+ def self.for(model)
6
+ new(model: model)
7
+ end
8
+
9
+ def initialize(model:)
10
+ @model = model
11
+ end
12
+
13
+ def create(params)
14
+ @model.create(params)
15
+ end
16
+
17
+ def changed?(record, directive)
18
+ directive.keys.any? do |key|
19
+ !record.send(key).eql?(directive[key])
20
+ end
21
+ end
22
+
23
+ def update(record, params)
24
+ params.keys.each do |key|
25
+ record.send "#{key}=", params[key]
26
+ end
27
+
28
+ record.save
29
+ end
30
+
31
+ def delete(record)
32
+ record.delete
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,86 @@
1
+ require 'gli'
2
+ require 'tty-prompt'
3
+ require 'pastel'
4
+
5
+ require_relative './schema'
6
+ require_relative './reporter'
7
+
8
+ module KongSchema
9
+ class CLI
10
+ include GLI::App
11
+
12
+ def run(argv)
13
+ program_desc 'Configure Kong from file.'
14
+
15
+ version KongSchema::VERSION
16
+
17
+ desc 'Apply configuration from a .yml or .json file.'
18
+ arg(:config_file)
19
+
20
+ command :up do |c|
21
+ c.flag([ 'k', 'key' ], {
22
+ default_value: 'kong',
23
+ desc: 'The root configuration property key.',
24
+ arg_name: 'NAME'
25
+ })
26
+
27
+ c.flag([ 'f', 'format' ], {
28
+ default_value: 'json',
29
+ desc: 'Format to use for reporting objects. Either "json" or "yaml".',
30
+ long_desc: 'Available formats: "json" or "yaml".',
31
+ arg_name: 'FORMAT',
32
+ must_match: %w(json yaml)
33
+ })
34
+
35
+ c.switch([ 'confirm' ], {
36
+ default_value: true,
37
+ desc: 'Prompt for confirmation before applying changes.'
38
+ })
39
+
40
+ c.action do |global_options, options, args|
41
+ up(filepath: args.first, options: options)
42
+ end
43
+ end
44
+
45
+ super(argv)
46
+ end
47
+
48
+ private
49
+
50
+ def up(filepath:, options:)
51
+ pastel = Pastel.new
52
+ schema = KongSchema::Schema
53
+ config = read_property(load_file(filepath), options[:key])
54
+
55
+ schema.scan(config).tap do |changes|
56
+ if changes.empty?
57
+ puts "#{pastel.green('✓')} Nothing to update."
58
+ else
59
+ puts KongSchema::Reporter.report(changes, object_format: options[:format].to_sym)
60
+
61
+ if TTY::Prompt.new.yes?('Commit the changes to Kong?', default: false)
62
+ schema.commit(config, changes)
63
+
64
+ puts "#{pastel.green('✓')} Kong has been reconfigured!"
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def load_file(filepath)
71
+ if filepath.end_with?('.json')
72
+ JSON.parse(File.read(filepath))
73
+ else
74
+ YAML.load_file(filepath)
75
+ end
76
+ end
77
+
78
+ def read_property(config, key)
79
+ if key.to_s.empty?
80
+ config
81
+ else
82
+ config.fetch(key.to_s)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kong'
4
+
5
+ module KongSchema
6
+ module Client
7
+ # Configure Kong::Client to use the host specified in config['admin_host']
8
+ # for the duration of a proc.
9
+ #
10
+ # Example:
11
+ #
12
+ # KongSchema::Schema.connect({ 'admin_host' => '127.0.0.1:8001' }) do
13
+ # Kong::Api.all()
14
+ # end
15
+ def self.connect(config, &_)
16
+ api_url = Kong::Client.api_url
17
+ admin_host = config['admin_host']
18
+
19
+ if admin_host.nil?
20
+ fail "Missing 'admin_host' property; can not connect to Kong admin!"
21
+ end
22
+
23
+ Kong::Client.api_url = "http://#{admin_host}"
24
+
25
+ yield Kong::Client
26
+ ensure
27
+ Kong::Client.api_url = api_url
28
+ end
29
+ end
30
+ end