kumonos 0.3.1 → 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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/example/book.yml +3 -1
- data/example/example-with-tls.yml +18 -0
- data/exe/kumonos +52 -38
- data/kumonos.gemspec +1 -1
- data/lib/kumonos/clusters.rb +29 -0
- data/lib/kumonos/envoy.rb +45 -0
- data/lib/kumonos/{configuration.rb → envoy_definition.rb} +1 -1
- data/lib/kumonos/output.rb +27 -0
- data/lib/kumonos/routes.rb +38 -0
- data/lib/kumonos/schemas.rb +3 -3
- data/lib/kumonos/version.rb +1 -1
- data/lib/kumonos.rb +6 -90
- data/lib/schemas/service_definition.json +6 -0
- metadata +12 -7
- /data/example/{kumonos.json → envoy.json} +0 -0
- /data/lib/schemas/{kumonos_config.json → envoy.json} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3180e4389f67a7973e06cc18560503ebe4606e3a
|
4
|
+
data.tar.gz: 5e104c41129d76a4628cd02dcb68df71c3c55e36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 551f54b256647e1b04e9c25e26d774b9a85f2d295b28628999fdc18ace1db1bb299534649382823bda7a5d8bd341d57c62e215fa95d8c464d003112d53e7e6e4
|
7
|
+
data.tar.gz: cc933fb394e59ef965612a90f20ed3df3af1f9f12ffe590fed577f38db6c84783952003630d96f9e87491884d424ccd63f0dcd8fa0af4af77cc038d18e883916
|
data/.rspec
CHANGED
data/example/book.yml
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
# A definition for book service.
|
1
|
+
# A example definition for book service.
|
2
2
|
# The book service uses user service and ab-testing service.
|
3
3
|
version: 1
|
4
4
|
dependencies:
|
5
5
|
- name: 'user'
|
6
6
|
lb: 'user:8080'
|
7
|
+
tls: false
|
7
8
|
connect_timeout_ms: 250
|
8
9
|
circuit_breaker:
|
9
10
|
max_connections: 64
|
@@ -18,6 +19,7 @@ dependencies:
|
|
18
19
|
per_try_timeout_ms: 1000
|
19
20
|
- name: 'ab-testing'
|
20
21
|
lb: 'ab-testing:8080'
|
22
|
+
tls: false
|
21
23
|
connect_timeout_ms: 250
|
22
24
|
circuit_breaker:
|
23
25
|
max_connections: 64
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Example for using TLS
|
2
|
+
version: 1
|
3
|
+
dependencies:
|
4
|
+
- name: 'example'
|
5
|
+
lb: 'example.com:443'
|
6
|
+
tls: true
|
7
|
+
connect_timeout_ms: 250
|
8
|
+
circuit_breaker:
|
9
|
+
max_connections: 64
|
10
|
+
max_pending_requests: 128
|
11
|
+
max_retries: 3
|
12
|
+
routes:
|
13
|
+
- prefix: '/'
|
14
|
+
timeout_ms: 3000
|
15
|
+
retry_policy:
|
16
|
+
retry_on: '5xx,connect-failure,refused-stream'
|
17
|
+
num_retries: 3
|
18
|
+
per_try_timeout_ms: 1000
|
data/exe/kumonos
CHANGED
@@ -3,54 +3,52 @@
|
|
3
3
|
|
4
4
|
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
5
5
|
require 'kumonos'
|
6
|
-
|
7
|
-
require 'pathname'
|
8
6
|
require 'thor'
|
9
7
|
|
10
8
|
# KumonosCli
|
11
9
|
class KumonosCli < Thor
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
config = Kumonos::Configuration.from_hash(h)
|
25
|
-
puts JSON.dump(Kumonos.generate(config, service_name))
|
10
|
+
def self.exit_on_failure?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'envoy ENVOY_DEFINITION', 'Generate envoy configuration'
|
15
|
+
def envoy(path)
|
16
|
+
validate_path!(path)
|
17
|
+
h = JSON.parse(File.read(path))
|
18
|
+
validate_envoy_definition!(h, path)
|
19
|
+
|
20
|
+
definition = Kumonos::EnvoyDefinition.from_hash(h)
|
21
|
+
puts JSON.dump(Kumonos::Envoy.generate(definition))
|
26
22
|
end
|
27
23
|
|
28
|
-
desc 'clusters
|
29
|
-
|
30
|
-
|
24
|
+
desc 'clusters SERVIVE_DEFINITION', 'Generate clusters configuration'
|
25
|
+
method_option :output, aliases: '-o', desc: 'Output directory', required: true, type: :string
|
26
|
+
def clusters(filepath)
|
27
|
+
output_dir = options.fetch(:output)
|
28
|
+
validate_path!(filepath)
|
29
|
+
validate_path!(output_dir)
|
30
|
+
|
31
31
|
definition = YAML.load_file(filepath)
|
32
32
|
validate_service_definition!(definition, filepath)
|
33
|
-
|
33
|
+
json = JSON.dump(Kumonos::Clusters.generate(definition))
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
target.parent.mkpath unless target.parent.exist?
|
38
|
-
target.write(out)
|
39
|
-
puts target
|
35
|
+
output = Kumonos::Output.new(output_dir, :clusters, File.basename(filepath, '.*'))
|
36
|
+
puts output.write(json)
|
40
37
|
end
|
41
38
|
|
42
|
-
desc 'routes
|
43
|
-
|
44
|
-
|
39
|
+
desc 'routes SERVIVE_DEFINITION', 'Generate routes configuration'
|
40
|
+
method_option :output, aliases: '-o', desc: 'Output directory', required: true, type: :string
|
41
|
+
def routes(filepath)
|
42
|
+
output_dir = options.fetch(:output)
|
43
|
+
validate_path!(filepath)
|
44
|
+
validate_path!(output_dir)
|
45
|
+
|
45
46
|
definition = YAML.load_file(filepath)
|
46
47
|
validate_service_definition!(definition, filepath)
|
47
|
-
|
48
|
+
json = JSON.dump(Kumonos::Routes.generate(definition))
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
target.parent.mkpath unless target.parent.exist?
|
52
|
-
target.write(out)
|
53
|
-
puts target
|
50
|
+
output = Kumonos::Output.new(output_dir, :routes, File.basename(filepath, '.*'))
|
51
|
+
puts output.write(json)
|
54
52
|
end
|
55
53
|
|
56
54
|
desc 'init NAME', 'Generate a service definition'
|
@@ -67,13 +65,29 @@ class KumonosCli < Thor
|
|
67
65
|
|
68
66
|
private
|
69
67
|
|
70
|
-
def
|
71
|
-
|
68
|
+
def validate_path!(path)
|
69
|
+
return if File.exist?(path)
|
70
|
+
|
71
|
+
warn("No such file or directory: #{path}")
|
72
|
+
exit 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate_envoy_definition!(h, path)
|
76
|
+
result = Kumonos::Schemas.validate_envoy_definition(h)
|
72
77
|
return if result.empty?
|
78
|
+
warn_and_exit(result, path, Kumonos::Schemas::ENVOY_SCHEMA_PATH)
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate_service_definition!(h, path)
|
82
|
+
result = Kumonos::Schemas.validate_service_definition(h)
|
83
|
+
return if result.empty?
|
84
|
+
warn_and_exit(result, path, Kumonos::Schemas::SERVIVE_DEFINITION_PATH)
|
85
|
+
end
|
73
86
|
|
74
|
-
|
87
|
+
def warn_and_exit(result, path, schema_path)
|
88
|
+
warn("#{path} has invalid format:")
|
75
89
|
warn(result)
|
76
|
-
warn("
|
90
|
+
warn("The schema file is #{schema_path}")
|
77
91
|
exit 1
|
78
92
|
end
|
79
93
|
end
|
data/kumonos.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ['Taiki Ono']
|
9
9
|
spec.email = ['taiks.4559@gmail.com']
|
10
10
|
|
11
|
-
spec.summary = '
|
11
|
+
spec.summary = 'A management tool for building Microservices "service mesh".'
|
12
12
|
spec.description = spec.summary
|
13
13
|
spec.homepage = 'https://github.com/taiki45/kumonos'
|
14
14
|
spec.license = 'MIT'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Kumonos
|
2
|
+
# Generate clusters configuration.
|
3
|
+
module Clusters
|
4
|
+
class << self
|
5
|
+
def generate(definition)
|
6
|
+
{
|
7
|
+
clusters: definition['dependencies'].map { |s| service_to_cluster(s) }
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def service_to_cluster(service)
|
14
|
+
out = {
|
15
|
+
name: service['name'],
|
16
|
+
connect_timeout_ms: service['connect_timeout_ms'],
|
17
|
+
type: 'strict_dns',
|
18
|
+
lb_type: 'round_robin',
|
19
|
+
hosts: [{ url: "tcp://#{service['lb']}" }],
|
20
|
+
circuit_breakers: {
|
21
|
+
default: service['circuit_breaker']
|
22
|
+
}
|
23
|
+
}
|
24
|
+
out[:ssl_context] = {} if service['tls']
|
25
|
+
out
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Kumonos
|
2
|
+
# Generate envoy configuration.
|
3
|
+
module Envoy
|
4
|
+
class << self
|
5
|
+
# @param [Kumonos::EnvoyDefinition] definition
|
6
|
+
# @return [Hash] envoy configuration hash
|
7
|
+
def generate(definition)
|
8
|
+
{
|
9
|
+
listeners: [
|
10
|
+
{
|
11
|
+
address: definition.listener.fetch(:address),
|
12
|
+
filters: [
|
13
|
+
type: 'read',
|
14
|
+
name: 'http_connection_manager',
|
15
|
+
config: {
|
16
|
+
codec_type: 'auto',
|
17
|
+
stat_prefix: 'ingress_http',
|
18
|
+
access_log: [{ path: definition.listener.fetch(:access_log_path) }],
|
19
|
+
rds: {
|
20
|
+
cluster: definition.ds.fetch(:name),
|
21
|
+
route_config_name: DEFAULT_ROUTE_NAME,
|
22
|
+
refresh_delay_ms: definition.ds.fetch(:refresh_delay_ms)
|
23
|
+
},
|
24
|
+
filters: [{ type: 'decoder', name: 'router', config: {} }]
|
25
|
+
}
|
26
|
+
]
|
27
|
+
}
|
28
|
+
],
|
29
|
+
admin: {
|
30
|
+
access_log_path: definition.admin.fetch(:access_log_path),
|
31
|
+
address: definition.admin.fetch(:address)
|
32
|
+
},
|
33
|
+
statsd_tcp_cluster_name: definition.statsd.fetch(:name),
|
34
|
+
cluster_manager: {
|
35
|
+
clusters: [definition.statsd],
|
36
|
+
cds: {
|
37
|
+
cluster: definition.ds.fetch(:cluster),
|
38
|
+
refresh_delay_ms: definition.ds.fetch(:refresh_delay_ms)
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Kumonos
|
4
|
+
# Output manipulation.
|
5
|
+
class Output
|
6
|
+
def initialize(dir, type, name)
|
7
|
+
@dir = Pathname.new(dir)
|
8
|
+
@type = type
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def write(json)
|
13
|
+
target =
|
14
|
+
case @type
|
15
|
+
when :clusters
|
16
|
+
@dir.join('v1', 'clusters', @name, @name)
|
17
|
+
when :routes
|
18
|
+
@dir.join('v1', 'routes', Kumonos::DEFAULT_ROUTE_NAME, @name, @name)
|
19
|
+
else
|
20
|
+
raise %(Unknown type "#{@type}" given)
|
21
|
+
end
|
22
|
+
target.parent.mkpath unless target.parent.exist?
|
23
|
+
target.write(json)
|
24
|
+
target
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Kumonos
|
2
|
+
# Generate routes configuration
|
3
|
+
module Routes
|
4
|
+
class << self
|
5
|
+
def generate(definition)
|
6
|
+
{
|
7
|
+
validate_clusters: false,
|
8
|
+
virtual_hosts: definition['dependencies'].map { |s| service_to_vhost(s) }
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def service_to_vhost(service)
|
15
|
+
name = service['name']
|
16
|
+
{
|
17
|
+
name: name,
|
18
|
+
domains: [name],
|
19
|
+
routes: service['routes'].flat_map { |r| split_route(r, name) }
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Split route definition to apply retry definition only to GET/HEAD requests.
|
24
|
+
def split_route(route, name)
|
25
|
+
base = {
|
26
|
+
prefix: route['prefix'],
|
27
|
+
timeout_ms: route['timeout_ms'],
|
28
|
+
cluster: name
|
29
|
+
}
|
30
|
+
with_retry = base.merge(
|
31
|
+
retry_policy: route['retry_policy'],
|
32
|
+
headers: [{ name: ':method', value: '(GET|HEAD)', regex: true }]
|
33
|
+
)
|
34
|
+
[with_retry, base]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/kumonos/schemas.rb
CHANGED
@@ -5,12 +5,12 @@ module Kumonos
|
|
5
5
|
# Schemas
|
6
6
|
module Schemas
|
7
7
|
ROOT = Pathname.new(File.expand_path('../schemas', __dir__))
|
8
|
-
|
8
|
+
ENVOY_SCHEMA_PATH = ROOT.join('envoy.json')
|
9
9
|
SERVIVE_DEFINITION_PATH = ROOT.join('service_definition.json')
|
10
10
|
|
11
11
|
class << self
|
12
|
-
def
|
13
|
-
schema = load_schema(
|
12
|
+
def validate_envoy_definition(hash)
|
13
|
+
schema = load_schema(ENVOY_SCHEMA_PATH)
|
14
14
|
JSON::Validator.fully_validate(schema, hash)
|
15
15
|
end
|
16
16
|
|
data/lib/kumonos/version.rb
CHANGED
data/lib/kumonos.rb
CHANGED
@@ -3,97 +3,13 @@ require 'yaml'
|
|
3
3
|
|
4
4
|
require 'kumonos/version'
|
5
5
|
require 'kumonos/schemas'
|
6
|
-
require 'kumonos/
|
6
|
+
require 'kumonos/envoy_definition'
|
7
|
+
require 'kumonos/envoy'
|
8
|
+
require 'kumonos/routes'
|
9
|
+
require 'kumonos/clusters'
|
10
|
+
require 'kumonos/output'
|
7
11
|
|
8
12
|
# Kumonos
|
9
13
|
module Kumonos
|
10
|
-
|
11
|
-
def generate(config, name)
|
12
|
-
{
|
13
|
-
listeners: [
|
14
|
-
{
|
15
|
-
address: config.listener.fetch(:address),
|
16
|
-
filters: [
|
17
|
-
type: 'read',
|
18
|
-
name: 'http_connection_manager',
|
19
|
-
config: {
|
20
|
-
codec_type: 'auto',
|
21
|
-
stat_prefix: 'ingress_http',
|
22
|
-
access_log: [{ path: config.listener.fetch(:access_log_path) }],
|
23
|
-
rds: {
|
24
|
-
cluster: config.ds.fetch(:name),
|
25
|
-
route_config_name: name,
|
26
|
-
refresh_delay_ms: config.ds.fetch(:refresh_delay_ms)
|
27
|
-
},
|
28
|
-
filters: [{ type: 'decoder', name: 'router', config: {} }]
|
29
|
-
}
|
30
|
-
]
|
31
|
-
}
|
32
|
-
],
|
33
|
-
admin: {
|
34
|
-
access_log_path: config.admin.fetch(:access_log_path),
|
35
|
-
address: config.admin.fetch(:address)
|
36
|
-
},
|
37
|
-
statsd_tcp_cluster_name: config.statsd.fetch(:name),
|
38
|
-
cluster_manager: {
|
39
|
-
clusters: [config.statsd],
|
40
|
-
cds: {
|
41
|
-
cluster: config.ds.fetch(:cluster),
|
42
|
-
refresh_delay_ms: config.ds.fetch(:refresh_delay_ms)
|
43
|
-
}
|
44
|
-
}
|
45
|
-
}
|
46
|
-
end
|
47
|
-
|
48
|
-
def generate_routes(definition)
|
49
|
-
{
|
50
|
-
validate_clusters: false,
|
51
|
-
virtual_hosts: definition['dependencies'].map { |s| service_to_vhost(s) }
|
52
|
-
}
|
53
|
-
end
|
54
|
-
|
55
|
-
def generate_clusters(definition)
|
56
|
-
{
|
57
|
-
clusters: definition['dependencies'].map { |s| service_to_cluster(s) }
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def service_to_vhost(service)
|
64
|
-
name = service['name']
|
65
|
-
{
|
66
|
-
name: name,
|
67
|
-
domains: [name],
|
68
|
-
routes: service['routes'].flat_map { |r| split_route(r, name) }
|
69
|
-
}
|
70
|
-
end
|
71
|
-
|
72
|
-
# Split route definition to apply retry definition only to GET/HEAD requests.
|
73
|
-
def split_route(route, name)
|
74
|
-
base = {
|
75
|
-
prefix: route['prefix'],
|
76
|
-
timeout_ms: route['timeout_ms'],
|
77
|
-
cluster: name
|
78
|
-
}
|
79
|
-
with_retry = base.merge(
|
80
|
-
retry_policy: route['retry_policy'],
|
81
|
-
headers: [{ name: ':method', value: '(GET|HEAD)', regex: true }]
|
82
|
-
)
|
83
|
-
[with_retry, base]
|
84
|
-
end
|
85
|
-
|
86
|
-
def service_to_cluster(service)
|
87
|
-
{
|
88
|
-
name: service['name'],
|
89
|
-
connect_timeout_ms: service['connect_timeout_ms'],
|
90
|
-
type: 'strict_dns',
|
91
|
-
lb_type: 'round_robin',
|
92
|
-
hosts: [{ url: "tcp://#{service['lb']}" }],
|
93
|
-
circuit_breakers: {
|
94
|
-
default: service['circuit_breaker']
|
95
|
-
}
|
96
|
-
}
|
97
|
-
end
|
98
|
-
end
|
14
|
+
DEFAULT_ROUTE_NAME = 'default'.freeze
|
99
15
|
end
|
@@ -24,6 +24,7 @@
|
|
24
24
|
"required": [
|
25
25
|
"name",
|
26
26
|
"lb",
|
27
|
+
"tls",
|
27
28
|
"connect_timeout_ms",
|
28
29
|
"circuit_breaker",
|
29
30
|
"routes"
|
@@ -39,6 +40,11 @@
|
|
39
40
|
"id": "/properties/dependencies/items/properties/lb",
|
40
41
|
"default": "host:port"
|
41
42
|
},
|
43
|
+
"tls": {
|
44
|
+
"type": "boolean",
|
45
|
+
"id": "/properties/dependencies/items/properties/tls",
|
46
|
+
"default": false
|
47
|
+
},
|
42
48
|
"connect_timeout_ms": {
|
43
49
|
"type": "integer",
|
44
50
|
"id": "/properties/dependencies/items/properties/connect_timeout_ms",
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kumonos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Taiki Ono
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json-schema
|
@@ -122,7 +122,7 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 0.51.0
|
125
|
-
description:
|
125
|
+
description: A management tool for building Microservices "service mesh".
|
126
126
|
email:
|
127
127
|
- taiks.4559@gmail.com
|
128
128
|
executables:
|
@@ -142,14 +142,19 @@ files:
|
|
142
142
|
- bin/setup
|
143
143
|
- bump
|
144
144
|
- example/book.yml
|
145
|
-
- example/
|
145
|
+
- example/envoy.json
|
146
|
+
- example/example-with-tls.yml
|
146
147
|
- exe/kumonos
|
147
148
|
- kumonos.gemspec
|
148
149
|
- lib/kumonos.rb
|
149
|
-
- lib/kumonos/
|
150
|
+
- lib/kumonos/clusters.rb
|
151
|
+
- lib/kumonos/envoy.rb
|
152
|
+
- lib/kumonos/envoy_definition.rb
|
153
|
+
- lib/kumonos/output.rb
|
154
|
+
- lib/kumonos/routes.rb
|
150
155
|
- lib/kumonos/schemas.rb
|
151
156
|
- lib/kumonos/version.rb
|
152
|
-
- lib/schemas/
|
157
|
+
- lib/schemas/envoy.json
|
153
158
|
- lib/schemas/service_definition.json
|
154
159
|
homepage: https://github.com/taiki45/kumonos
|
155
160
|
licenses:
|
@@ -174,5 +179,5 @@ rubyforge_project:
|
|
174
179
|
rubygems_version: 2.5.2.1
|
175
180
|
signing_key:
|
176
181
|
specification_version: 4
|
177
|
-
summary:
|
182
|
+
summary: A management tool for building Microservices "service mesh".
|
178
183
|
test_files: []
|
File without changes
|
File without changes
|