kong_schema 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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