kumonos 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c1fae897610887b93621540db85b472393a017d3
4
- data.tar.gz: df13da5be9a09a42c0cf5a5cb9443aa28e9744b4
3
+ metadata.gz: 3180e4389f67a7973e06cc18560503ebe4606e3a
4
+ data.tar.gz: 5e104c41129d76a4628cd02dcb68df71c3c55e36
5
5
  SHA512:
6
- metadata.gz: c1b80378899957b98c5cdef91af4f420c194155893300b6fd21d14d6cb7b8ed7ae610bc9b049109c29ab5c723111058922935167fb3fb36cca9a2ef3d59e6554
7
- data.tar.gz: cfca16fb8e5693bc26b82fae2f3aee19cf96505410b5802b7768d44ec6b2f31fd9d48aca672f6db881bbac0b902dbd2072a30239b5ac007a60295b3d20814e26
6
+ metadata.gz: 551f54b256647e1b04e9c25e26d774b9a85f2d295b28628999fdc18ace1db1bb299534649382823bda7a5d8bd341d57c62e215fa95d8c464d003112d53e7e6e4
7
+ data.tar.gz: cc933fb394e59ef965612a90f20ed3df3af1f9f12ffe590fed577f38db6c84783952003630d96f9e87491884d424ccd63f0dcd8fa0af4af77cc038d18e883916
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
- --format documentation
2
1
  --color
3
2
  --require spec_helper
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
- desc 'envoy SERVIVE_NAME', 'Generate envoy configuration'
13
- method_option :config, aliases: '-c', desc: 'Configuration file for kumonos'
14
- def envoy(service_name)
15
- h = JSON.parse(File.read(options[:config]))
16
- result = Kumonos::Schemas.validate_kumonos_config(h)
17
- unless result.empty?
18
- warn("#{options[:config]} has invalid format:")
19
- warn(result)
20
- warn("A schema file for kumonos-configuration is #{Kumonos::Schemas::CONFIG_SCHEMA_PATH}")
21
- exit 1
22
- end
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 FILEPATH OUT', 'Generate clusters configuration'
29
- def clusters(filepath, output_dir)
30
- name = File.basename(filepath, '.*')
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
- out = JSON.dump(Kumonos.generate_clusters(definition))
33
+ json = JSON.dump(Kumonos::Clusters.generate(definition))
34
34
 
35
- output_dir = Pathname.new(output_dir)
36
- target = output_dir.join('v1', 'clusters', name, name)
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 FILEPATH OUT', 'Generate routes configuration'
43
- def routes(filepath, output_dir)
44
- name = File.basename(filepath, '.*')
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
- out = JSON.dump(Kumonos.generate_routes(definition))
48
+ json = JSON.dump(Kumonos::Routes.generate(definition))
48
49
 
49
- output_dir = Pathname.new(output_dir)
50
- target = output_dir.join('v1', 'routes', name, name, name)
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 validate_service_definition!(definition, filepath)
71
- result = Kumonos::Schemas.validate_service_definition(definition)
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
- warn("#{filepath} has invalid format:")
87
+ def warn_and_exit(result, path, schema_path)
88
+ warn("#{path} has invalid format:")
75
89
  warn(result)
76
- warn("A schema file is #{Kumonos::Schemas::SERVIVE_DEFINITION_PATH}")
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 = 'Manage configuration for Service Mesh.'
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
@@ -1,5 +1,5 @@
1
1
  module Kumonos
2
- Configuration = Struct.new(:version, :ds, :statsd, :listener, :admin) do
2
+ EnvoyDefinition = Struct.new(:version, :ds, :statsd, :listener, :admin) do
3
3
  class << self
4
4
  def from_hash(h)
5
5
  new(
@@ -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
@@ -5,12 +5,12 @@ module Kumonos
5
5
  # Schemas
6
6
  module Schemas
7
7
  ROOT = Pathname.new(File.expand_path('../schemas', __dir__))
8
- CONFIG_SCHEMA_PATH = ROOT.join('kumonos_config.json')
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 validate_kumonos_config(hash)
13
- schema = load_schema(CONFIG_SCHEMA_PATH)
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
 
@@ -1,3 +1,3 @@
1
1
  module Kumonos
2
- VERSION = '0.3.1'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
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/configuration'
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
- class << self
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.3.1
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-06 00:00:00.000000000 Z
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: Manage configuration for Service Mesh.
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/kumonos.json
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/configuration.rb
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/kumonos_config.json
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: Manage configuration for Service Mesh.
182
+ summary: A management tool for building Microservices "service mesh".
178
183
  test_files: []
File without changes
File without changes