algo 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/awesome-cluster.rb +22 -1
- data/lib/algo.rb +3 -0
- data/lib/algo/cli.rb +9 -125
- data/lib/algo/docker/network.rb +4 -0
- data/lib/algo/docker/service.rb +4 -0
- data/lib/algo/dsl.rb +7 -0
- data/lib/algo/dsl/network.rb +27 -0
- data/lib/algo/dsl/service.rb +184 -11
- data/lib/algo/error.rb +9 -0
- data/lib/algo/runner.rb +6 -0
- data/lib/algo/runner/apply.rb +130 -0
- data/lib/algo/runner/rm.rb +46 -0
- data/lib/algo/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86e649323aa163f71b928277a792a0feb51a10d1
|
4
|
+
data.tar.gz: 80a24c0659991faf6098bd2b4044535e5fcf636f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 191b611c8254f37d1883e14053babe39251ed473165adc5b1c77ddb344a71a397b97fcd9def50110de6accf2d67974745fe3330e2f211929ad93850296d49d3c
|
7
|
+
data.tar.gz: f78566709a4b7e3fb9cf1fd4ebc81e51efd2f9ed35c6ead54108c4ec7171618174497a900f43407ca0a82ad755471c36d45b06775f623a712d4a31f57aae41a2
|
data/examples/awesome-cluster.rb
CHANGED
@@ -10,7 +10,13 @@ cluster 'awesomecluster' do
|
|
10
10
|
label 'com.example.sample', 'clusterwidelabel'
|
11
11
|
|
12
12
|
# Define network
|
13
|
-
network 'net1'
|
13
|
+
network 'net1' do
|
14
|
+
ipam do
|
15
|
+
subnet '172.20.0.0/16'
|
16
|
+
ip_range '172.20.10.0/24'
|
17
|
+
gateway '172.20.10.11'
|
18
|
+
end
|
19
|
+
end
|
14
20
|
|
15
21
|
# Define service
|
16
22
|
service 'name' do
|
@@ -23,6 +29,21 @@ cluster 'awesomecluster' do
|
|
23
29
|
|
24
30
|
env 'APP_DOMAIN', 'example.com'
|
25
31
|
|
32
|
+
# Mount host's directory/file
|
33
|
+
volume do
|
34
|
+
type 'bind'
|
35
|
+
source '/tmp/mount'
|
36
|
+
target '/mnt/sample'
|
37
|
+
end
|
38
|
+
|
39
|
+
# Mount host docker volume
|
40
|
+
volume do
|
41
|
+
type 'volume'
|
42
|
+
source 'volume-name'
|
43
|
+
target '/mnt/volume-sample'
|
44
|
+
end
|
45
|
+
|
46
|
+
# join network
|
26
47
|
network 'net1'
|
27
48
|
end
|
28
49
|
|
data/lib/algo.rb
CHANGED
@@ -5,9 +5,12 @@ require 'active_support'
|
|
5
5
|
require 'active_support/core_ext'
|
6
6
|
|
7
7
|
module Algo
|
8
|
+
require 'algo/error'
|
8
9
|
require "algo/version"
|
9
10
|
require "algo/docker"
|
10
11
|
require "algo/dsl"
|
12
|
+
require "algo/runner"
|
11
13
|
require "algo/cli"
|
12
14
|
# Your code goes here...
|
15
|
+
|
13
16
|
end
|
data/lib/algo/cli.rb
CHANGED
@@ -1,70 +1,5 @@
|
|
1
1
|
module Algo
|
2
2
|
class Cli < Thor
|
3
|
-
class ValidationError < StandardError; end
|
4
|
-
|
5
|
-
class ServiceValidator
|
6
|
-
def initialize srv_spec
|
7
|
-
@srv_spec = srv_spec
|
8
|
-
end
|
9
|
-
|
10
|
-
def validate
|
11
|
-
begin
|
12
|
-
@srv = Algo::Docker::Service.find(@srv_spec['Name'])
|
13
|
-
rescue Algo::Docker::Error::NotFoundError
|
14
|
-
@srv = nil
|
15
|
-
end
|
16
|
-
check_networks
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.validate srv_spec
|
20
|
-
new(srv_spec).validate
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def check_networks
|
26
|
-
return true if @srv.blank? or @srv.spec.networks.blank?
|
27
|
-
srv_networks = @srv.spec.networks.map { |n| { 'Target' => n.info['Name'] } }
|
28
|
-
unless srv_networks != @srv_spec['Networks']
|
29
|
-
@srv_spec['Networks'] = @srv.spec.networks.map { |n| { 'Target' => n.info['Id'] } }
|
30
|
-
return true
|
31
|
-
end
|
32
|
-
raise ValidationError, 'changing network in service is not supported'
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class ServiceUpdator
|
37
|
-
|
38
|
-
def initialize srv_spec, options
|
39
|
-
@srv_spec = srv_spec
|
40
|
-
@options = options
|
41
|
-
end
|
42
|
-
|
43
|
-
def update
|
44
|
-
begin
|
45
|
-
srv = Algo::Docker::Service.find(@srv_spec['Name'])
|
46
|
-
if srv.raw_spec == @srv_spec
|
47
|
-
puts "service: #{@srv_spec['Name']}, status: ok"
|
48
|
-
return
|
49
|
-
end
|
50
|
-
srv.update @srv_spec unless dryrun?
|
51
|
-
puts "service: #{@srv_spec['Name']}, status: changed"
|
52
|
-
rescue Algo::Docker::Error::NotFoundError
|
53
|
-
Algo::Docker::Service.create(@srv_spec) unless dryrun?
|
54
|
-
puts "service: #{@srv_spec['Name']}, status: created"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.update srv_spec, dryrun=false
|
59
|
-
new(srv_spec, {dryrun: dryrun}).update
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
def dryrun?
|
65
|
-
@options[:dryrun]
|
66
|
-
end
|
67
|
-
end
|
68
3
|
|
69
4
|
desc 'apply [INVENTRY_FILE]', 'Apply configuration to clusters'
|
70
5
|
option :'dry-run', type: :boolean, default: false
|
@@ -76,73 +11,22 @@ module Algo
|
|
76
11
|
def apply inventry
|
77
12
|
Algo::Docker.url = options[:host] if options[:host]
|
78
13
|
Algo::Docker.options = docker_opts if docker_opts.present?
|
79
|
-
puts 'Running with dry-run mode...' if options[:'dry-run']
|
80
14
|
configuration = Algo::Dsl.load({}, inventry)
|
81
|
-
|
82
|
-
puts "Applying to cluster #{cluster['name']}..."
|
83
|
-
|
84
|
-
cluster['networks'].each do |net_spec|
|
85
|
-
begin
|
86
|
-
net = Algo::Docker::Network.find net_spec['Name']
|
87
|
-
puts "network: #{net_spec['Name']}, status: ok"
|
88
|
-
rescue Algo::Docker::Error::NotFoundError
|
89
|
-
Algo::Docker::Network.create net_spec unless options[:'dry-run']
|
90
|
-
puts "network: #{net_spec['Name']}, status: created"
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
cluster['services'].each do |srv_spec|
|
95
|
-
ServiceValidator.validate srv_spec
|
96
|
-
end
|
97
|
-
cluster['services'].each do |srv_spec|
|
98
|
-
ServiceUpdator.update srv_spec, options[:'dry-run']
|
99
|
-
end
|
100
|
-
Algo::Docker::Service.all
|
101
|
-
.select { |srv| srv.spec.name.start_with?("#{cluster['prefix']}-") }
|
102
|
-
.select { |srv| ! srv.spec.name.in? cluster['services'].map { |spec| spec['Name'] } }
|
103
|
-
.map { |srv|
|
104
|
-
srv_name = srv.spec.name
|
105
|
-
srv.remove unless options[:'dry-run']
|
106
|
-
puts "service: #{srv_name}, status: removed"
|
107
|
-
}
|
108
|
-
Algo::Docker::Network.all(skip_default=true)
|
109
|
-
.select { |net| net.info['Name'].start_with?("#{cluster['prefix']}-") }
|
110
|
-
.select { |net| ! net.info['Name'].in? cluster['networks'].map { |net_spec| net_spec['Name'] } }
|
111
|
-
.map { |net|
|
112
|
-
net_name = net.info['Name']
|
113
|
-
net.remove unless options[:'dry-run']
|
114
|
-
puts "network: #{net_name}, status: removed"
|
115
|
-
}
|
116
|
-
puts "Complete applying for cluster #{cluster['name']}!"
|
117
|
-
end
|
118
|
-
rescue ValidationError => e
|
119
|
-
puts 'configuration validation failed because ' + e.message
|
15
|
+
Algo::Runner::Apply.call configuration, options
|
120
16
|
end
|
121
17
|
|
122
18
|
desc 'rm [INVENTRY_FILE]', 'Terminate clusters'
|
123
19
|
option :'dry-run', type: :boolean, default: false
|
20
|
+
option :'url', type: :string, desc: 'docker swarm url like tcp://localhost:2375'
|
21
|
+
option :'client_key', type: :string, desc: 'docker swarm client key path'
|
22
|
+
option :'client_sert', type: :string, desc: 'docker swarm client sert path'
|
23
|
+
option :'ssl_ca_file', type: :string, desc: 'docker swarm ssl ca file path'
|
24
|
+
option :'scheme', type: :string, desc: 'docker swarm connection scheme'
|
124
25
|
def rm inventry
|
125
|
-
|
26
|
+
Algo::Docker.url = options[:host] if options[:host]
|
27
|
+
Algo::Docker.options = docker_opts if docker_opts.present?
|
126
28
|
configuration = Algo::Dsl.load({}, inventry)
|
127
|
-
|
128
|
-
puts "Terminating cluster #{cluster['name']}..."
|
129
|
-
Algo::Docker::Service.all
|
130
|
-
.select { |srv| srv.spec.name.start_with?("#{cluster['prefix']}-") }
|
131
|
-
.map { |srv|
|
132
|
-
srv_name = srv.spec.name
|
133
|
-
srv.remove unless options[:'dry-run']
|
134
|
-
puts "service: #{srv_name}, status: removed"
|
135
|
-
}
|
136
|
-
Algo::Docker::Network.all(skip_default=true)
|
137
|
-
.select { |net| net.info['Name'].start_with?("#{cluster['prefix']}-") }
|
138
|
-
.select { |net| ! net.info['Name'].in? cluster['networks'].map { |net_spec| "#{cluster['prefix']}-#{net_spec['Name']}" } }
|
139
|
-
.map { |net|
|
140
|
-
net_name = net.info['Name']
|
141
|
-
net.remove unless options[:'dry-run']
|
142
|
-
puts "network: #{net_name}, status: removed"
|
143
|
-
}
|
144
|
-
puts "Complete Termination for cluster #{cluster['name']}..."
|
145
|
-
end
|
29
|
+
Algo::Runner::Rm.call configuration, options
|
146
30
|
end
|
147
31
|
|
148
32
|
private
|
data/lib/algo/docker/network.rb
CHANGED
data/lib/algo/docker/service.rb
CHANGED
data/lib/algo/dsl.rb
CHANGED
@@ -21,6 +21,13 @@ module Algo
|
|
21
21
|
dsl.result
|
22
22
|
end
|
23
23
|
|
24
|
+
def self.load_text(options, text)
|
25
|
+
dsl = new(options).tap do |dsl|
|
26
|
+
dsl.instance_eval(text)
|
27
|
+
end
|
28
|
+
dsl.result
|
29
|
+
end
|
30
|
+
|
24
31
|
def initialize(options)
|
25
32
|
@options = CLUSTER_DEFAULT.dup
|
26
33
|
@options.merge!(options)
|
data/lib/algo/dsl/network.rb
CHANGED
@@ -2,6 +2,26 @@ module Algo
|
|
2
2
|
class Dsl
|
3
3
|
module Network
|
4
4
|
|
5
|
+
class IPAMContext
|
6
|
+
attr_reader :context
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@context = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def subnet item
|
13
|
+
@context['Subnet'] = item
|
14
|
+
end
|
15
|
+
|
16
|
+
def ip_range item
|
17
|
+
@context['IPRange'] = item
|
18
|
+
end
|
19
|
+
|
20
|
+
def gateway item
|
21
|
+
@context['Gateway'] = item
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
5
25
|
class Context
|
6
26
|
|
7
27
|
attr_reader :context
|
@@ -36,6 +56,13 @@ module Algo
|
|
36
56
|
@context['EnableIPv6'] = true
|
37
57
|
end
|
38
58
|
|
59
|
+
def ipam &block
|
60
|
+
ctx = Network::IPAMContext.new.tap do |ctx|
|
61
|
+
ctx.instance_eval(&block) if block_given?
|
62
|
+
end
|
63
|
+
@context['IPAM']['Config'] << ctx.context
|
64
|
+
end
|
65
|
+
|
39
66
|
private
|
40
67
|
|
41
68
|
def cluster_prefix
|
data/lib/algo/dsl/service.rb
CHANGED
@@ -2,6 +2,58 @@ module Algo
|
|
2
2
|
class Dsl
|
3
3
|
module Service
|
4
4
|
|
5
|
+
class VolumeContext
|
6
|
+
attr_reader :context
|
7
|
+
|
8
|
+
def initialize clstr_context, srv_context
|
9
|
+
@clstr_context = clstr_context
|
10
|
+
@srv_context = srv_context
|
11
|
+
@context = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def readonly
|
15
|
+
@context['ReadOnly'] = true
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [String] item volume name or host file/directory path
|
19
|
+
def source item
|
20
|
+
raise 'Need to call type at first' unless @context['Type']
|
21
|
+
if @context['Type'] == 'volume'
|
22
|
+
@context['Source'] = "#{cluster_prefix}#{item}"
|
23
|
+
else
|
24
|
+
@context['Source'] = item
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [String] item container mount path
|
29
|
+
def target item
|
30
|
+
@context['Target'] = item
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [String] volume type ex) bind,volume
|
34
|
+
def type item
|
35
|
+
@context['Type'] = item
|
36
|
+
end
|
37
|
+
|
38
|
+
def label key, val
|
39
|
+
@context['VolumeOptions'] ||= {}
|
40
|
+
@context['VolumeOptions']['Labels'] ||= {}
|
41
|
+
@context['VolumeOptions']['Labels'][key] = val
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [String] volume driver type ex) local
|
45
|
+
def driver item
|
46
|
+
@context['VolumeOptions'] ||= {}
|
47
|
+
@context['VolumeOptions'] = { 'DriverConfig' => { 'Name' => item } }
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def cluster_prefix
|
53
|
+
"#{@clstr_context['prefix']}-" if @clstr_context['prefix']
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
5
57
|
class Context
|
6
58
|
attr_reader :context
|
7
59
|
|
@@ -43,20 +95,89 @@ module Algo
|
|
43
95
|
@context['TaskTemplate']['ContainerSpec']['Env'] << "#{key}=#{val}"
|
44
96
|
end
|
45
97
|
|
98
|
+
def workdir name
|
99
|
+
@context['TaskTemplate']['ContainerSpec']['Dir'] = name
|
100
|
+
end
|
101
|
+
|
102
|
+
def user name
|
103
|
+
@context['TaskTemplate']['ContainerSpec']['User'] = name
|
104
|
+
end
|
105
|
+
|
46
106
|
# @param [String] period period string like 30s, 1m, 4h
|
47
107
|
def stop_grace_period period
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
raise
|
108
|
+
@context['TaskTemplate']['ContainerSpec']['StopGracePeriod'] = second_from_string(period) * 1e9
|
109
|
+
end
|
110
|
+
|
111
|
+
def volume &block
|
112
|
+
raise 'should be called in cluster' unless @context
|
113
|
+
ctx = Service::VolumeContext.new(@cluster, @context).tap do |ctx|
|
114
|
+
ctx.instance_eval(&block)
|
56
115
|
end
|
57
|
-
@context['TaskTemplate']['ContainerSpec']['
|
116
|
+
@context['TaskTemplate']['ContainerSpec']['Mounts'] ||= []
|
117
|
+
@context['TaskTemplate']['ContainerSpec']['Mounts'] << ctx.context
|
58
118
|
end
|
59
119
|
|
120
|
+
# Resources
|
121
|
+
|
122
|
+
def limit_cpu decimal
|
123
|
+
@context['TaskTemplate']['Resources'] ||= {}
|
124
|
+
@context['TaskTemplate']['Resources']['Limits'] ||= {}
|
125
|
+
@context['TaskTemplate']['Resources']['Limits']['NanoCPUs'] = decimal * 1e9
|
126
|
+
end
|
127
|
+
|
128
|
+
# @param [String] memory num with unit like 1B 20KB 30MB 1GB
|
129
|
+
def limit_memory memory
|
130
|
+
@context['TaskTemplate']['Resources'] ||= {}
|
131
|
+
@context['TaskTemplate']['Resources']['Limits'] ||= {}
|
132
|
+
@context['TaskTemplate']['Resources']['Limits']['MemoryBytes'] = memory_from_string memory
|
133
|
+
end
|
134
|
+
|
135
|
+
def reserve_cpu decimal
|
136
|
+
@context['TaskTemplate']['Resources'] ||= {}
|
137
|
+
@context['TaskTemplate']['Resources']['Reservation'] ||= {}
|
138
|
+
@context['TaskTemplate']['Resources']['Reservation']['NanoCPUs'] = decimal * 1e9
|
139
|
+
end
|
140
|
+
|
141
|
+
# @param [String] memory num with unit like 1B 20KB 30MB 1GB
|
142
|
+
def reserve_memory memory
|
143
|
+
@context['TaskTemplate']['Resources'] ||= {}
|
144
|
+
@context['TaskTemplate']['Resources']['Reservation'] ||= {}
|
145
|
+
@context['TaskTemplate']['Resources']['Reservation']['MemoryBytes'] = memory_from_string memory
|
146
|
+
end
|
147
|
+
|
148
|
+
# RestartPolicy
|
149
|
+
|
150
|
+
# @param [String] name none, on-failure or any
|
151
|
+
def restart_condition name
|
152
|
+
@context['TaskTemplate']['RestartPolicy'] ||= {}
|
153
|
+
@context['TaskTemplate']['RestartPolicy']['Condition'] = name
|
154
|
+
end
|
155
|
+
|
156
|
+
def restart_delay period
|
157
|
+
@context['TaskTemplate']['RestartPolicy'] ||= {}
|
158
|
+
@context['TaskTemplate']['RestartPolicy']['Delay'] = second_from_string(period) * 10e9
|
159
|
+
end
|
160
|
+
|
161
|
+
# @param [Integer] value
|
162
|
+
def restart_max_attempts value
|
163
|
+
@context['TaskTemplate']['RestartPolicy'] ||= {}
|
164
|
+
@context['TaskTemplate']['RestartPolicy']['Attempts'] = value
|
165
|
+
end
|
166
|
+
|
167
|
+
def restart_window value
|
168
|
+
@context['TaskTemplate']['RestartPolicy'] ||= {}
|
169
|
+
@context['TaskTemplate']['RestartPolicy']['Window'] = second_from_string(period) * 10e9
|
170
|
+
end
|
171
|
+
|
172
|
+
# Placement
|
173
|
+
|
174
|
+
def constraint condition
|
175
|
+
@context['TaskTemplate']['Placement'] ||= {}
|
176
|
+
@context['TaskTemplate']['Placement']['Constraints'] ||= []
|
177
|
+
@context['TaskTemplate']['Placement']['Constraints'] << condition
|
178
|
+
end
|
179
|
+
|
180
|
+
|
60
181
|
# Label
|
61
182
|
|
62
183
|
def label key, val
|
@@ -67,7 +188,31 @@ module Algo
|
|
67
188
|
# Mode
|
68
189
|
|
69
190
|
def replicas replica_size
|
70
|
-
@context['Mode']
|
191
|
+
@context['Mode'] = { 'Replicated' => { 'Replicas' => replica_size } }
|
192
|
+
end
|
193
|
+
|
194
|
+
def global
|
195
|
+
@context['Mode'] = { 'Global' => {} }
|
196
|
+
end
|
197
|
+
|
198
|
+
# EndpointSpec
|
199
|
+
|
200
|
+
# @param [String] mode vip or dnsrr
|
201
|
+
def endpoint_mode mode
|
202
|
+
@context['EndpointSpec'] = { 'Mode' => mode }
|
203
|
+
end
|
204
|
+
|
205
|
+
# @param [String] port like 80 or 80:80 or 80/udp
|
206
|
+
def publish port
|
207
|
+
port, protocol = *port.split('/') if '/'.in? port
|
208
|
+
target, publish = *port.split(':') if ':'.in? port
|
209
|
+
@context['EndpointSpec'] ||= {}
|
210
|
+
@context['EndpointSpec']['Ports'] ||= []
|
211
|
+
@context['EndpointSpec']['Ports'] << {
|
212
|
+
'Protocol' => protocol,
|
213
|
+
'TargetPort' => target,
|
214
|
+
'PublishedPort' => publish
|
215
|
+
}.compact
|
71
216
|
end
|
72
217
|
|
73
218
|
# UpdateConfig
|
@@ -87,7 +232,7 @@ module Algo
|
|
87
232
|
def network name
|
88
233
|
@context['Networks'] ||= []
|
89
234
|
@context['Networks'] << { 'Target' => "#{cluster_prefix}#{name}" }
|
90
|
-
|
235
|
+
end
|
91
236
|
|
92
237
|
private
|
93
238
|
|
@@ -95,6 +240,34 @@ module Algo
|
|
95
240
|
"#{@cluster['prefix']}-" if @cluster['prefix']
|
96
241
|
end
|
97
242
|
|
243
|
+
def second_from_string(period)
|
244
|
+
if period.end_with?('s')
|
245
|
+
period.chomp('s').to_i
|
246
|
+
elsif period.end_with?('m')
|
247
|
+
period.chomp('m').to_i * 60
|
248
|
+
elsif period.end_with?('h')
|
249
|
+
period.chomp('m').to_i * 60 * 60
|
250
|
+
else
|
251
|
+
raise
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def memory_from_string(memory)
|
256
|
+
if memory.end_with?('B')
|
257
|
+
memory.chomp('B').to_i
|
258
|
+
elsif memory.end_with?('KB')
|
259
|
+
memory.chomp('KB').to_i * 1e3
|
260
|
+
elsif memory.end_with?('MB')
|
261
|
+
memory.chomp('MB').to_i * 1e6
|
262
|
+
elsif memory.end_with?('GB')
|
263
|
+
memory.chomp('GB').to_i * 1e9
|
264
|
+
elsif memory.end_with?('TB')
|
265
|
+
memory.chomp('TB').to_i * 1e12
|
266
|
+
else
|
267
|
+
raise
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
98
271
|
end
|
99
272
|
|
100
273
|
def service name, &block
|
data/lib/algo/error.rb
ADDED
data/lib/algo/runner.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
module Algo
|
2
|
+
module Runner
|
3
|
+
class Apply
|
4
|
+
|
5
|
+
class ServiceValidator
|
6
|
+
def initialize srv_spec
|
7
|
+
@srv_spec = srv_spec
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate
|
11
|
+
begin
|
12
|
+
@srv = Algo::Docker::Service.find(@srv_spec['Name'])
|
13
|
+
rescue Algo::Docker::Error::NotFoundError
|
14
|
+
@srv = nil
|
15
|
+
end
|
16
|
+
check_networks
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.validate srv_spec
|
20
|
+
new(srv_spec).validate
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def check_networks
|
26
|
+
return true if @srv.blank? or @srv.spec.networks.blank?
|
27
|
+
srv_networks = @srv.spec.networks.map { |n| { 'Target' => n.name } }
|
28
|
+
unless srv_networks != @srv_spec['Networks']
|
29
|
+
@srv_spec['Networks'] = @srv.spec.networks.map { |n| { 'Target' => n.id } }
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
raise ValidationError, 'changing network in service is not supported'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ServiceUpdator
|
37
|
+
|
38
|
+
def initialize srv_spec, options
|
39
|
+
@srv_spec = srv_spec
|
40
|
+
@options = options
|
41
|
+
end
|
42
|
+
|
43
|
+
def update
|
44
|
+
begin
|
45
|
+
srv = Algo::Docker::Service.find(@srv_spec['Name'])
|
46
|
+
if srv.raw_spec == @srv_spec
|
47
|
+
puts "service: #{@srv_spec['Name']}, status: ok"
|
48
|
+
return
|
49
|
+
end
|
50
|
+
srv.update @srv_spec unless dryrun?
|
51
|
+
puts "service: #{@srv_spec['Name']}, status: changed"
|
52
|
+
rescue Algo::Docker::Error::NotFoundError
|
53
|
+
Algo::Docker::Service.create(@srv_spec) unless dryrun?
|
54
|
+
puts "service: #{@srv_spec['Name']}, status: created"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.update srv_spec, dryrun=false
|
59
|
+
new(srv_spec, {dryrun: dryrun}).update
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def dryrun?
|
65
|
+
@options[:dryrun]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :configuration, :options
|
70
|
+
|
71
|
+
def initialize configuration, options
|
72
|
+
@configuration = configuration
|
73
|
+
@options = options
|
74
|
+
end
|
75
|
+
|
76
|
+
def call
|
77
|
+
puts 'Running with dry-run mode...' if dryrun?
|
78
|
+
configuration.each do |cluster|
|
79
|
+
puts "Applying to cluster #{cluster['name']}..."
|
80
|
+
|
81
|
+
cluster['networks'].each do |net_spec|
|
82
|
+
begin
|
83
|
+
net = Algo::Docker::Network.find net_spec['Name']
|
84
|
+
puts "network: #{net_spec['Name']}, status: ok"
|
85
|
+
rescue Algo::Docker::Error::NotFoundError
|
86
|
+
Algo::Docker::Network.create net_spec unless dryrun?
|
87
|
+
puts "network: #{net_spec['Name']}, status: created"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
cluster['services'].each do |srv_spec|
|
91
|
+
ServiceValidator.validate srv_spec
|
92
|
+
end
|
93
|
+
cluster['services'].each do |srv_spec|
|
94
|
+
ServiceUpdator.update srv_spec, dryrun?
|
95
|
+
end
|
96
|
+
Algo::Docker::Service.all
|
97
|
+
.select { |srv| srv.spec.name.start_with?("#{cluster['prefix']}-") }
|
98
|
+
.select { |srv| ! srv.spec.name.in? cluster['services'].map { |spec| spec['Name'] } }
|
99
|
+
.map { |srv|
|
100
|
+
srv_name = srv.spec.name
|
101
|
+
srv.remove unless dryrun?
|
102
|
+
puts "service: #{srv_name}, status: removed"
|
103
|
+
}
|
104
|
+
Algo::Docker::Network.all(skip_default=true)
|
105
|
+
.select { |net| net.name.start_with?("#{cluster['prefix']}-") }
|
106
|
+
.select { |net| ! net.name.in? cluster['networks'].map { |net_spec| net_spec['Name'] } }
|
107
|
+
.map { |net|
|
108
|
+
net_name = net.name
|
109
|
+
net.remove unless dryrun?
|
110
|
+
puts "network: #{net_name}, status: removed"
|
111
|
+
}
|
112
|
+
puts "Complete applying for cluster #{cluster['name']}!"
|
113
|
+
end
|
114
|
+
rescue Algo::ValidationError => e
|
115
|
+
puts 'configuration validation failed because ' + e.message
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.call configuration, options
|
119
|
+
new(configuration, options).call
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def dryrun?
|
125
|
+
options[:'dry-run']
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Algo
|
2
|
+
module Runner
|
3
|
+
class Rm
|
4
|
+
attr_reader :configuration, :options
|
5
|
+
|
6
|
+
def initialize configuration, options
|
7
|
+
@configuration = configuration
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
puts 'Running with dry-run mode...' if dryrun?
|
13
|
+
configuration.each do |cluster|
|
14
|
+
puts "Terminating cluster #{cluster['name']}..."
|
15
|
+
Algo::Docker::Service.all
|
16
|
+
.select { |srv| srv.spec.name.start_with?("#{cluster['prefix']}-") }
|
17
|
+
.map { |srv|
|
18
|
+
srv_name = srv.spec.name
|
19
|
+
srv.remove unless dryrun?
|
20
|
+
puts "service: #{srv_name}, status: removed"
|
21
|
+
}
|
22
|
+
Algo::Docker::Network.all(skip_default=true)
|
23
|
+
.select { |net| net.info['Name'].start_with?("#{cluster['prefix']}-") }
|
24
|
+
.select { |net| ! net.info['Name'].in? cluster['networks'].map { |net_spec| "#{cluster['prefix']}-#{net_spec['Name']}" } }
|
25
|
+
.map { |net|
|
26
|
+
net_name = net.info['Name']
|
27
|
+
net.remove unless dryrun?
|
28
|
+
puts "network: #{net_name}, status: removed"
|
29
|
+
}
|
30
|
+
puts "Complete Termination for cluster #{cluster['name']}..."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.call configuration, options
|
35
|
+
new(configuration, options).call
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def dryrun?
|
41
|
+
options[:'dry-run']
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/algo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: algo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yoshiso
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -140,6 +140,10 @@ files:
|
|
140
140
|
- lib/algo/dsl/cluster.rb
|
141
141
|
- lib/algo/dsl/network.rb
|
142
142
|
- lib/algo/dsl/service.rb
|
143
|
+
- lib/algo/error.rb
|
144
|
+
- lib/algo/runner.rb
|
145
|
+
- lib/algo/runner/apply.rb
|
146
|
+
- lib/algo/runner/rm.rb
|
143
147
|
- lib/algo/version.rb
|
144
148
|
homepage: https://github.com/yoshiso/algo
|
145
149
|
licenses:
|