aggkit 0.2.6.8250 → 0.2.6.8329

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: ed42b42b8001ba82d6139bf6d94598e86bc87f34
4
- data.tar.gz: 4ad91cf44ecd2e40a5fc4f7aeae3757c89a20171
3
+ metadata.gz: dda760666b51369b116244aa6cc3ead5f69afb41
4
+ data.tar.gz: 3df47be27d64b70300707667651893475a30ff92
5
5
  SHA512:
6
- metadata.gz: c2cef1a46a08ea9a5f432d31324f2f422adaaa955a7abfe5e7265f19783d8741a63f7697adb499e241f00c991c0e5edee50747ec774d173ecbb27c9535023950
7
- data.tar.gz: ab2a09f904b76b1229e2a7e2ecfa16e6dddef79b4d758ea51efd625dd8222f40e565e336c55e1fb8769d366b791dd5d8869f926a0905a64cb7ea1dd168c2dd53
6
+ metadata.gz: 63d4ab22451f3cc95e16653f49776cba230e194e8f580bc54828a7e10037ab761352e1dbfa7adccdf35cec1acd0a34586c04c0049961de28045e40584b17928b
7
+ data.tar.gz: a820f88d3be11610a7f8bff76c1c387c334d5892eede30c96f6a146071d5ac8e859eec9a114af9982a1ab1a7ac7930a17dc3edab17c3daaa7df731b6fa558641
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- aggkit (0.2.6.8250)
4
+ aggkit (0.2.6.8329)
5
5
  diplomat
6
6
  dotenv
7
7
  json
data/aggkit.gemspec CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_dependency 'dotenv'
32
32
  spec.add_dependency 'json'
33
33
  spec.add_dependency 'tty-tree'
34
+
34
35
 
35
36
 
36
37
  spec.add_development_dependency 'bundler', '~> 1.14'
data/bin/agg CHANGED
@@ -5,6 +5,8 @@ require 'tty-tree'
5
5
  require 'aggkit/env'
6
6
  require 'json'
7
7
 
8
+ UTIL = File.basename(__FILE__)
9
+
8
10
  @commands = {}
9
11
 
10
12
  @exec = (begin
@@ -20,13 +22,13 @@ require 'json'
20
22
 
21
23
 
22
24
  exmaple_usage = "
23
- #{'agg env --list'.ljust(30)} - list avaibale environments
24
- #{'agg <envname> --show'.ljust(30)} - show environment details
25
- #{'agg <envname> exec -- cmd'.ljust(30)} - exec command in environment
25
+ #{"#{UTIL} env --list".ljust(30)} - list avaibale environments
26
+ #{"#{UTIL} <envname> --show".ljust(30)} - show environment details
27
+ #{"#{UTIL} <envname> exec -- cmd".ljust(30)} - exec command in environment
26
28
  "
27
29
 
28
30
  env_cmd_parser = OptionParser.new do |o|
29
- o.banner = "Usage: agg env [options]"
31
+ o.banner = "Usage: #{UTIL} env [options]"
30
32
  o.on("-l", "--list", "list environments") do
31
33
  @opts[:list] = true
32
34
  end
@@ -36,7 +38,7 @@ env_cmd_parser = OptionParser.new do |o|
36
38
  end
37
39
 
38
40
  exec_cmd_parser = OptionParser.new do |o|
39
- o.banner = "Usage: agg <envname> [options] exec [options] -- cmd"
41
+ o.banner = "Usage: #{UTIL} <envname> [options] exec [options] -- cmd"
40
42
  o.on('-p', '--pristine', "not include the parent processes' environment when exec child process") do
41
43
  @opts[:pristine] = true
42
44
  end
@@ -54,7 +56,7 @@ end
54
56
  }
55
57
 
56
58
  single_parser = OptionParser.new do |o|
57
- o.banner = "Aggredator environment manager\nagg <envname> [options] [subcommand [options]]"
59
+ o.banner = "Aggredator environment manager\nUsage: #{UTIL} <envname> [options] [subcommand [options]]"
58
60
 
59
61
  o.on("-s", "--show", "show envname details") do
60
62
  @opts[:show] = true
@@ -64,6 +66,10 @@ single_parser = OptionParser.new do |o|
64
66
  @opts[:export] = true
65
67
  end
66
68
 
69
+ o.on("-r", "--release", "make release compose-result.yml(without build)") do
70
+ @opts[:release] = true
71
+ end
72
+
67
73
  o.on("-h", "--help", "show help") do
68
74
  puts o.help
69
75
  exit 0
@@ -74,7 +80,7 @@ single_parser = OptionParser.new do |o|
74
80
  end
75
81
 
76
82
  global_parser = OptionParser.new do |o|
77
- o.banner = "Aggredator manager\nUsage: agg [envname] [options] [subcommand [options]]"
83
+ o.banner = "Aggredator manager\nUsage: #{UTIL} [envname] [options] [subcommand [options]]"
78
84
 
79
85
  o.on("-h", "--help", "show help") do
80
86
  puts o.help
@@ -144,6 +150,7 @@ def exec_command opts
144
150
  raise "envname not specified!" unless $env
145
151
  raise "cmd not specified!" unless @exec
146
152
 
153
+ $env.prepare release: @opts[:release]
147
154
  e = $env.environment
148
155
  e = ENV.to_h.merge(e) unless opts[:pristine]
149
156
 
@@ -161,6 +168,7 @@ if @commands[:single]
161
168
  if @opts[:show]
162
169
  raise "envname not specified!" unless $env
163
170
 
171
+ $env.prepare release: @opts[:release]
164
172
  puts JSON.pretty_generate($env.show)
165
173
  exit 0
166
174
  end
@@ -168,6 +176,7 @@ if @commands[:single]
168
176
  if @opts[:export]
169
177
  raise "envname not specified!" unless $env
170
178
 
179
+ $env.prepare release: @opts[:release]
171
180
  $env.environment.each_pair do |k, v|
172
181
  puts "export #{k.to_s}=\"#{v.to_s}\""
173
182
  end
data/bin/aggci ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aggkit/ci'
5
+
6
+ UTIL = File.basename(__FILE__)
7
+
8
+ include Aggkit::Ci
9
+
10
+ @opts = {
11
+ tag: ENV['CORE_TAG'] || ENV['CI_COMMIT_TAG'] || 'latest'
12
+ }
13
+
14
+ def parse_services services
15
+ [services].flatten.join(' ').split(/[,;| \n]/).map(&:strip).reject(&:empty?).uniq.sort
16
+ end
17
+
18
+ parser = OptionParser.new do |o|
19
+ o.banner = "Usage: #{UTIL} [options]"
20
+
21
+ o.on("--pull-cache services", 'Pull cache from docker regestry') do |services|
22
+ @opts[:pull] = parse_services(services)
23
+ end
24
+
25
+ o.on("--promote services", 'Promote service images to --stage') do |services|
26
+ @opts[:promote] = parse_services(services)
27
+ end
28
+
29
+ o.on("--promote-tag tag", 'Promote service images') do |tag|
30
+ @opts[:promotetag] = tag.to_s.strip
31
+ end
32
+
33
+ o.on("--stage stage", 'stage for image promotion Stages: dev, stge, tag') do |stage|
34
+ @opts[:stage] = stage.to_s.strip
35
+ end
36
+
37
+ o.on("--clean services", 'remove tags and images excluding last stable') do |services|
38
+ @opts[:clean] = parse_services(services)
39
+ end
40
+
41
+ o.on("--ssh-deploy env", 'ssh') do |env_and_server|
42
+ env, server = env_and_server.strip.split('@')
43
+ @opts[:deploy] = {env: env, server: server}
44
+ end
45
+
46
+ o.on("--tag tag=#{@opts[:tag].inspect}", 'Use tag as default tag for images') do |tag|
47
+ @opts[:tag] = tag.to_s.strip
48
+ end
49
+
50
+ end
51
+ parser.parse!
52
+
53
+ if services = @opts[:pull]
54
+ raise "tag not defined" if @opts[:tag].to_s.empty?
55
+ pull_images_cache services, @opts[:tag]
56
+ exit 0
57
+ elsif services = @opts[:promote]
58
+ raise "tag not defined" if @opts[:tag].to_s.empty?
59
+ raise "stage not defined" if @opts[:stage].to_s.empty?
60
+
61
+ case @opts[:stage]
62
+ when 'dev'
63
+ promote_services_to_dev!(services, @opts[:tag])
64
+ when 'stage'
65
+ promote_services_to_stage!(services, @opts[:tag])
66
+ when 'tag'
67
+ raise "promote-tag not defined" if @opts[:promotetag].to_s.empty?
68
+ promote_services_to_tag!(services, @opts[:tag], @opts[:promotetag])
69
+ else
70
+ raise "invalid stage #{@opts[:stage].inspect} for promotion"
71
+ end
72
+
73
+ exit 0
74
+
75
+ elsif services = @opts[:clean]
76
+ raise "tag not defined" if @opts[:tag].to_s.empty?
77
+ clean_services services, @opts[:tag]
78
+ exit 0
79
+
80
+ elsif deploy = @opts[:deploy]
81
+ raise "tag not defined" if @opts[:tag].to_s.empty?
82
+ raise "stage not defined" if @opts[:stage].to_s.empty?
83
+ ssh_deploy 'service-fns', deploy[:env], deploy[:server], @opts[:tag], @opts[:stage]
84
+ exit 0
85
+ end
86
+
87
+
88
+ STDERR.puts parser.help
89
+ exit 1
90
+
data/bin/aggmerge CHANGED
@@ -11,6 +11,8 @@ if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
11
11
  end
12
12
 
13
13
 
14
+ @skipbuild = ARGV.join('')['--skip-build']
15
+
14
16
  class Hash
15
17
 
16
18
  def deep_dup
@@ -99,6 +101,23 @@ def process_compose_hash(yml, dirname, parent = {})
99
101
  yml
100
102
  end
101
103
 
104
+ def remove_build_section hash
105
+ if hash.has_key?(:build) || hash.has_key?('build')
106
+ hash.delete(:build)
107
+ hash.delete('build')
108
+ end
109
+
110
+ hash.each_pair do |k, v|
111
+ if v.is_a?(Hash)
112
+ remove_build_section(v)
113
+ elsif v.is_a?(Array)
114
+ v.each do |vv|
115
+ remove_build_section(vv) if vv.is_a?(Hash)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
102
121
  if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
103
122
  result = ENV['COMPOSE_FILE'].split(':').reduce({}) do |parent, file|
104
123
  yml = process_compose_hash(YAML.load(File.read(file)), File.dirname(file), parent)
@@ -109,6 +128,9 @@ if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
109
128
  ret
110
129
  end
111
130
 
131
+
132
+ remove_build_section(result) if @skipbuild
133
+
112
134
  if ARGV[0].nil? || ARGV[0].strip == '-'
113
135
  puts YAML.dump(result)
114
136
  else
File without changes
data/docker/run_tests.sh CHANGED
@@ -2,6 +2,8 @@
2
2
  SELF=$(readlink -f $0)
3
3
  export ENV_ROOT=$(dirname "${SELF}")
4
4
 
5
+ export COMPOSE_PROJECT_NAME=agg-aggkit-${CI_JOB_ID}
6
+
5
7
  trap '$ENV_ROOT/down.sh' EXIT
6
8
 
7
9
  cd $ENV_ROOT
data/lib/aggkit/ci.rb ADDED
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'English'
4
+ require 'tempfile'
5
+ require 'json'
6
+ require 'aggkit/env'
7
+
8
+ module Aggkit
9
+
10
+ module Ci
11
+
12
+ def log_action action
13
+ puts " => #{action}"
14
+ end
15
+
16
+ def info message
17
+ puts " => #{message}"
18
+ end
19
+
20
+ def execute cmd
21
+ puts " - executing: #{cmd}"
22
+ result = `#{cmd}`
23
+ puts result
24
+ if !$?.success?
25
+ raise "Execution failed cmd: #{cmd.inspect}"
26
+ end
27
+ result
28
+ end
29
+
30
+ #Уровни образов
31
+ # 1. Без модификатора - не протестирован, отсутсвует в репозитории.
32
+ # 2. С модификатором dev - протестирован автотестами, ДОЛЖЕН быть в репозитории.
33
+ # 3. С модификатором stage - протестирован руками, ДОЛЖЕН быть в репозитории(как и его предшественник dev).
34
+ # 4. С модификатором release - помечен для релиза, ДОЛЖЕН быть в репозитории(как и его предшественник dev и stage).
35
+
36
+ #Работа должна вестить ТОЛЬКО с workimage
37
+
38
+ # Базовое имя образа
39
+ # Например docker.rnds.pro/aggredator-mq
40
+ def baseimage service
41
+ raise "Service name invalid: #{service.inspect}" if service.to_s.length < 2
42
+ "docker.rnds.pro/aggredator-#{service}"
43
+ end
44
+
45
+
46
+ # Текущее имя образа без модификаторов продвижения, но с тегом
47
+ # Например docker.rnds.pro/aggredator-mq:7e6420b5
48
+ def workimage service, tag
49
+ raise "Service tag invalid: #{tag.inspect}" if tag.to_s.length < 2
50
+ "#{baseimage(service)}:#{tag}"
51
+ end
52
+
53
+ # Образ с модификатором версии
54
+ # Например docker.rnds.pro/aggredator-mq-dev:7e6420b5
55
+ def promotedimage service, stage, tag
56
+ raise "Service stage invalid: #{stage.inspect}" if stage.to_s.length < 2
57
+ raise "Service tag invalid: #{tag.inspect}" if tag.to_s.length < 2
58
+ "#{baseimage(service)}-#{stage}:#{tag}"
59
+ end
60
+
61
+ # Образ с модификатором протестированной автотестами версии
62
+ # Например docker.rnds.pro/aggredator-mq-dev:7e6420b5
63
+ def devimage service, tag
64
+ promotedimage(service, 'dev', tag)
65
+ end
66
+
67
+ # Образ с модификатором стабильной версии
68
+ # Например docker.rnds.pro/aggredator-mq-stage:7e6420b5
69
+ def stageimage service, tag
70
+ promotedimage(service, 'stage', tag)
71
+ end
72
+
73
+ # Образ с модификатором релизной версии
74
+ #Например docker.rnds.pro/aggredator-mq-release:7e6420b5
75
+ def releaseimage service, tag
76
+ promotedimage(service, 'release', tag)
77
+ end
78
+
79
+ # Протегировать образ тегом
80
+ def tagimage! image, newimage, push: false, ignoreerror: false
81
+ log_action "Tag #{image.inspect} as #{newimage.inspect}"
82
+
83
+ if ignoreerror
84
+ execute("docker tag #{image} #{newimage} 2> /dev/null || true")
85
+ else
86
+ execute("docker tag #{image} #{newimage}")
87
+ end
88
+
89
+ if push
90
+ execute("docker push #{newimage}")
91
+ end
92
+
93
+ return newimage
94
+ end
95
+
96
+ #Удалить образ образ тегом
97
+ def rmimage! image
98
+ log_action "Remove #{image.inspect}"
99
+ execute("docker rmi #{image} &> /dev/null || true")
100
+ end
101
+
102
+ def label! image, label, value
103
+ raise "Image label invalid: #{label.inspect}" if label.to_s.length < 2
104
+ raise "Image label value invalid: #{value.inspect}" if value.to_s.length < 2
105
+
106
+ execute("echo #{image} | docker build -t #{image} --label=\"#{label.to_s}=#{value.to_s}\" - ")
107
+ end
108
+
109
+ # Растегировать все модификатора образа сервиса и возможно удалить его
110
+ def untagservice service, tag
111
+ if tag != 'latest'
112
+ rmimage! workimage(service, tag)
113
+ rmimage! devimage(service, tag)
114
+ rmimage! stageimage(service, tag)
115
+ rmimage! releaseimage(service, tag)
116
+ end
117
+ end
118
+
119
+ # Продвинуть текущий образ сервиса по уровню - добавить модификатор и залить в репозиторий
120
+ def promote! service, tag, stage, fromstage: nil
121
+ fromimage = if fromstage
122
+ promotedimage(service, fromstage, tag)
123
+ else
124
+ workimage(service, tag)
125
+ end
126
+
127
+ toimage = promotedimage(service, stage, tag)
128
+
129
+ log_action "Promote #{fromimage} to #{toimage}"
130
+ return tagimage! fromimage, toimage, push: true
131
+ end
132
+
133
+ # Продвинуть текущий образ по уровню - добавить модификатор dev(протестировано) и залить в репозиторий
134
+ def promote_to_dev! service, tag
135
+ promote!(service, tag, 'dev')
136
+ end
137
+
138
+ # Продвинуть текущий образ по уровню - добавить модификатор stage(стабильно) и залить в репозиторий
139
+ def promote_to_stage! service, tag
140
+ promoted = tagimage!(devimage(service, tag), stageimage(service, tag), push: true)
141
+ # Тегируем этот образ как latest
142
+ return tagimage!(promoted, stageimage(service, 'latest'), push: true)
143
+ end
144
+
145
+ # Продвинуть стабильный образ до указанного тега
146
+ def promote_to_tag! service, tag, newtag
147
+ return tagimage!(devimage(service, tag), stageimage(service, newtag), push: true)
148
+ end
149
+
150
+ # Продвинуть список образов или упасть с ошибкой
151
+ def promote_services_to_dev! services, tag
152
+ [services].flatten.each do |service|
153
+ promote_to_dev!(service, tag)
154
+ end
155
+ end
156
+
157
+ # Продвинуть список образов или упасть с ошибкой
158
+ def promote_services_to_stage! services, tag
159
+ [services].flatten.each do |service|
160
+ promote_to_stage!(service, tag)
161
+ end
162
+ end
163
+
164
+ #Протегировать образы до указанного тега
165
+ def promote_services_to_tag! services, tag, newtag
166
+ [services].flatten.each do |service|
167
+ promote_to_tag!(service, tag, newtag)
168
+ end
169
+ end
170
+
171
+
172
+
173
+ # Попытаться взять все модификаторы образов из репозитория
174
+ # Скачивает образы и тегирует их как workimage
175
+ def pull_images_cache services, tag
176
+ log_action "Puling images#{[services].flatten.inspect} from cache...."
177
+ [services].flatten.each do |service|
178
+ execute("docker pull #{devimage(service, tag)} 2> /dev/null || true")
179
+ execute("docker pull #{stageimage(service, tag)} 2> /dev/null || true")
180
+ execute("docker pull #{releaseimage(service, tag)} 2> /dev/null || true")
181
+ tagimage! devimage(service, tag), workimage(service, tag), ignoreerror: true
182
+ tagimage! stageimage(service, tag), workimage(service, tag), ignoreerror: true
183
+ tagimage! releaseimage(service, tag), workimage(service, tag), ignoreerror: true
184
+ end
185
+ end
186
+
187
+ #Растегировать все образы для дальнейшего удаления. Помечает как latest для поддержки кеширования docker
188
+ def clean_services services, tag
189
+ [services].flatten.each do |service|
190
+ tagimage! workimage(service, tag), workimage(service, 'latest'), ignoreerror: true
191
+ tagimage! devimage(service, tag), workimage(service, 'latest'), ignoreerror: true
192
+ tagimage! stageimage(service, tag), workimage(service, 'latest'), ignoreerror: true
193
+ tagimage! releaseimage(service, tag), workimage(service, 'latest'), ignoreerror: true
194
+ untagservice(service, tag)
195
+ end
196
+ end
197
+
198
+ def ssh_deploy services, env, sshserver, tag, stage
199
+ services = [services].flatten
200
+ log_action "Deploy [#{services.inspect}] tag:#{tag.inspect} stage:#{stage.inspect} to #{sshserver.inspect}"
201
+ with_ssh sshserver do |sockname|
202
+ env_config = JSON(`agg #{env} --show --release`)
203
+ envs = env_config['envs']
204
+ config_file = env_config['config_file'].to_s
205
+ envs['DOCKER_HOST'] = "unix://#{sockname}"
206
+ envs['CORE_TAG'] = tag
207
+ envs['CORE_SUFFIX'] = "-#{stage}"
208
+
209
+ localid = execute("docker info | grep ID").strip
210
+
211
+ Aggkit::Env.with_env(envs) do
212
+ remoteid = execute("docker info | grep ID").strip
213
+
214
+ if localid == remoteid
215
+ raise "Can't get access to remote Docker"
216
+ end
217
+
218
+ execute('docker-compose ps')
219
+ execute('docker-compose pull')
220
+
221
+ consul = execute('docker ps | grep consul').split(' ').first.strip
222
+ puts "CONSULT ID: #{consul}"
223
+
224
+ if !config_file.empty?
225
+ execute("docker exec -i #{consul} waiter.rb -t 20 --consul")
226
+ execute("docker exec -i #{consul} consul.rb --init --override --config - < #{config_file}")
227
+ end
228
+
229
+ execute('docker-compose up -d')
230
+
231
+ services.each do |service|
232
+ deployed = promotedimage(service, stage, tag)
233
+ current = promotedimage(service, stage, 'current')
234
+ tagimage! deployed, current
235
+ untagservice(service, tag)
236
+ end
237
+
238
+ end
239
+ end
240
+ end
241
+
242
+ def with_ssh server, user: 'root', &block
243
+ file = Tempfile.new
244
+ @ssh_sockname = file.path
245
+ file.unlink
246
+ @ssh_pid = spawn("ssh -NL #{@ssh_sockname}:/var/run/docker.sock root@#{server}")
247
+ sleep 5
248
+ yield(@ssh_sockname)
249
+ ensure
250
+ Process.kill(:SIGTERM, @ssh_pid)
251
+ sleep 5
252
+ Process.wait2(@ssh_pid, Process::WNOHANG)
253
+ FileUtils.rm_f(@ssh_sockname)
254
+ end
255
+
256
+ end
257
+
258
+ end
data/lib/aggkit/env.rb CHANGED
@@ -55,8 +55,10 @@ module Aggkit
55
55
  end
56
56
 
57
57
  @env_root = File.join(project_root, 'envs', @name)
58
+ end
58
59
 
59
- @environment = prepare_environment
60
+ def prepare release: false
61
+ @environment = prepare_environment(release: release)
60
62
  end
61
63
 
62
64
  def with_env &block
@@ -87,12 +89,17 @@ module Aggkit
87
89
  [core_dot_file, project_dot_file, dot_file].compact.select{|f| File.exists?(f)}.uniq
88
90
  end
89
91
 
92
+ def config_file
93
+ [File.join(env_root, 'config.yml'), File.join(env_root, 'config.yaml')].compact.select{|f| File.exists?(f)}.uniq.first
94
+ end
95
+
90
96
  def show
91
97
  {
92
98
  name: name,
93
99
  project_root: project_root,
94
100
  core_root: core_root,
95
101
  envfiles: env_files,
102
+ config_file: config_file,
96
103
  envs: environment,
97
104
  }
98
105
  end
@@ -100,7 +107,7 @@ module Aggkit
100
107
  private
101
108
 
102
109
 
103
- def prepare_environment
110
+ def prepare_environment release: false
104
111
  base_env = {
105
112
  'ENV_ROOT' => env_root,
106
113
  'PROJECT_ROOT' => project_root,
@@ -110,7 +117,7 @@ private
110
117
 
111
118
  dot_env = prepare_dot(base_env)
112
119
 
113
- compose_env = prepare_compose(dot_env.merge(base_env))
120
+ compose_env = prepare_compose(dot_env.merge(base_env), release: release)
114
121
 
115
122
  dot_env.merge(base_env).merge(compose_env)
116
123
  end
@@ -121,7 +128,7 @@ private
121
128
  end
122
129
  end
123
130
 
124
- def prepare_compose env
131
+ def prepare_compose env, release: false
125
132
  compose_files = if env['COMPOSE_FILE']
126
133
  env['COMPOSE_FILE']
127
134
  elsif File.exists?(File.join(env_root, "docker-compose.yml"))
@@ -133,8 +140,11 @@ private
133
140
  if compose_files
134
141
  result_file = File.join(env_root, "compose-result.yml")
135
142
  Env.with_env(env.merge('COMPOSE_FILE' => compose_files)) do
136
- result_content = `merger.rb`
137
- File.write(result_file, result_content)
143
+ if release
144
+ `aggmerge #{result_file} --skip-build`
145
+ else
146
+ `aggmerge #{result_file}`
147
+ end
138
148
  end
139
149
  {
140
150
  'COMPOSE_FILE' => result_file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aggkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6.8250
4
+ version: 0.2.6.8329
5
5
  platform: ruby
6
6
  authors:
7
7
  - Godko Ivan
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-02-06 00:00:00.000000000 Z
12
+ date: 2019-02-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: diplomat
@@ -101,15 +101,12 @@ email:
101
101
  - kinnalru@gmail.com
102
102
  executables:
103
103
  - agg
104
+ - aggci
104
105
  - aggconsul
105
106
  - agglock
106
107
  - aggmerge
108
+ - aggterm
107
109
  - aggwait
108
- - consul.rb
109
- - locker.rb
110
- - merger.rb
111
- - terminator.rb
112
- - waiter.rb
113
110
  extensions: []
114
111
  extra_rdoc_files: []
115
112
  files:
@@ -124,15 +121,12 @@ files:
124
121
  - README.md
125
122
  - aggkit.gemspec
126
123
  - bin/agg
124
+ - bin/aggci
127
125
  - bin/aggconsul
128
126
  - bin/agglock
129
127
  - bin/aggmerge
128
+ - bin/aggterm
130
129
  - bin/aggwait
131
- - bin/consul.rb
132
- - bin/locker.rb
133
- - bin/merger.rb
134
- - bin/terminator.rb
135
- - bin/waiter.rb
136
130
  - docker/Dockerfile
137
131
  - docker/docker-compose.yml
138
132
  - docker/down.sh
@@ -165,6 +159,7 @@ files:
165
159
  - lib/aggkit/childprocess/windows/process.rb
166
160
  - lib/aggkit/childprocess/windows/process_builder.rb
167
161
  - lib/aggkit/childprocess/windows/structs.rb
162
+ - lib/aggkit/ci.rb
168
163
  - lib/aggkit/env.rb
169
164
  - lib/aggkit/runner.rb
170
165
  - lib/aggkit/version.rb
data/bin/consul.rb DELETED
@@ -1,222 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'diplomat'
4
- require 'optparse'
5
- require 'English'
6
- require 'yaml'
7
-
8
- STDOUT.sync = true
9
- STDERR.sync = true
10
-
11
- @opts = {}
12
-
13
- @opts[:exec] = (begin
14
- ARGV.join(' ').split(' -- ')[1].strip
15
- rescue StandardError
16
- nil
17
- end)
18
-
19
- parser = OptionParser.new do |o|
20
- o.banner = 'Usage: consul.rb [options] -- exec'
21
-
22
- o.on('--consul url', 'Set up a custom Consul URL') do |url|
23
- Diplomat.configure do |config|
24
- config.url = url.strip
25
- end
26
- end
27
-
28
- o.on('--token token', 'Connect into consul with custom access token (ACL)') do |token|
29
- Diplomat.configure do |config|
30
- config.acl_token = token.strip
31
- end
32
- end
33
-
34
- o.on('--init [service]', 'Initialize Consul services from config') do |service|
35
- @opts[:service] = service
36
- @opts[:init] = true
37
- end
38
-
39
- o.on('--config file', 'Read service configulation from file') do |file|
40
- @opts[:config] = file.strip
41
- end
42
-
43
- o.on('--upload', 'Upload files to variables') do
44
- @opts[:upload] = true
45
- end
46
-
47
- o.on('--show [service]', 'Show service configulation from Consul') do |service|
48
- @opts[:service] = service
49
- @opts[:show] = true
50
- end
51
-
52
- o.on('--override', 'override existed keys') do
53
- @opts[:override] = true
54
- end
55
-
56
- o.on('-d', '--dereference', 'dereference consul values in form of "consul://key/subkey"') do
57
- @opts[:dereference] = true
58
- end
59
-
60
- o.on('--env prefix', 'export KV values from prefix as env varaibles') do |prefix|
61
- @opts[:env] = (prefix + '/').gsub('//', '/')
62
- end
63
-
64
- o.on('--export', 'add export to --env output') do
65
- @opts[:export] = true
66
- end
67
-
68
- o.on('--pristine', "not include the parent processes' environment when exec child process") do
69
- @opts[:pristine] = true
70
- end
71
-
72
- o.on('--put path:value', 'put value to path') do |path|
73
- @opts[:put] = path.strip
74
- end
75
-
76
- o.on('--get path', 'get value from') do |path|
77
- @opts[:get] = path.strip
78
- end
79
- end
80
- parser.parse!
81
-
82
- def die(message)
83
- STDERR.puts "Error: #{message}"
84
- exit 1
85
- end
86
-
87
- def key_to_consul(key)
88
- key.downcase.gsub(/[^0-9a-z]/i, '_')
89
- end
90
-
91
- def key_to_env(key)
92
- key.upcase.gsub(/[^0-9a-z]/i, '_')
93
- end
94
-
95
- def dereferenced_value(value)
96
- if @opts[:dereference] && value && value[/^consul:\/\//]
97
- reference_path = value.gsub(/^consul:\/\//, '')
98
- dereferenced_value(Diplomat::Kv.get(reference_path))
99
- else
100
- value
101
- end
102
- end
103
-
104
- if config = @opts[:config]
105
- @opts[:config] = YAML.load(config == '-' ? STDIN.read : File.read(config))
106
- end
107
-
108
- if @opts[:init]
109
- raise OptionParser::MissingArgument.new('config') unless @opts[:config]
110
-
111
- services = if service = @opts[:service]
112
- {
113
- service => @opts[:config][service]
114
- }
115
- else
116
- @opts[:config]
117
- end
118
-
119
- services.each_pair do |service, config|
120
- next unless config
121
- next if service[/^\./] # skip hidden keys
122
-
123
- path = "services/env/#{service}"
124
- config.each_pair do |env, item|
125
- key = "#{path}/#{key_to_consul(env)}"
126
- value = if @opts[:upload] && item['file']
127
- File.read(item['file'])
128
- else
129
- item['value'] || item['default'] || item['file']
130
- end
131
-
132
- empty = begin
133
- Diplomat::Kv.get(key)
134
- false
135
- rescue Diplomat::KeyNotFound
136
- true
137
- end
138
-
139
- Diplomat::Kv.put(key, value.to_s.strip) || die("Can't put #{key} to Consul") if empty || @opts[:override]
140
- end
141
- end
142
-
143
- exit 0
144
- end
145
-
146
- if @opts[:show]
147
- config = {}
148
-
149
- path = if service = @opts[:service]
150
- "services/env/#{service}/"
151
- else
152
- 'services/env/'
153
- end
154
-
155
- answer = Diplomat::Kv.get(path, recurse: true, convert_to_hash: true) || die("Can't get #{path} from Consul")
156
- answer['services']['env'].each_pair do |service, env|
157
- cfg = config[service] ||= {}
158
-
159
- env.each_pair do |key, value|
160
- value = dereferenced_value(value)
161
-
162
- cfg[key_to_env(key)] = {
163
- env: key_to_env(key),
164
- value: value
165
- }
166
- end
167
- end
168
-
169
- STDOUT.puts JSON.parse(config.to_json).to_yaml
170
-
171
- exit 0
172
- end
173
-
174
- if put = @opts[:put]
175
- path, *value = put.split(':').map(&:strip)
176
- value = value.join(':')
177
- value = File.read(value) if @opts[:upload] && value && File.exist?(value)
178
-
179
- Diplomat::Kv.put(path, value.to_s.strip) || die("Can't put #{path} to Consul")
180
-
181
- exit 0
182
- end
183
-
184
- if path = @opts[:get]
185
- value = dereferenced_value(Diplomat::Kv.get(path))
186
-
187
- STDOUT.puts value.to_s.strip
188
-
189
- exit 0
190
- end
191
-
192
- if prefix = @opts[:env]
193
- keys = begin
194
- Diplomat::Kv.get(prefix, keys: true)
195
- rescue Diplomat::KeyNotFound => e
196
- []
197
- rescue StandardError
198
- die("Can't get keys at #{prefix} from Consul")
199
- end
200
-
201
- env = keys.reduce({}) do |e, key|
202
- value = dereferenced_value(Diplomat::Kv.get(key))
203
-
204
- e.merge(key_to_env(key.gsub(prefix, '')) => value)
205
- end
206
-
207
- if cmd = @opts[:exec]
208
- env = ENV.to_h.merge(env) unless @opts[:pristine]
209
-
210
- exec(env, cmd, unsetenv_others: true)
211
- else
212
- env.each_pair do |k, v|
213
- STDOUT.puts "#{@opts[:export] ? 'export ' : ''}#{k}=\"#{v}\""
214
- end
215
- end
216
-
217
-
218
- exit 0
219
- end
220
-
221
- STDOUT.puts parser.help
222
- exit 1
data/bin/locker.rb DELETED
@@ -1,71 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'optparse'
4
- require 'diplomat'
5
-
6
- @opts = {
7
- url: 'http://localhost:8500',
8
- timeout: 10,
9
- ttl: 30 * 60
10
- }
11
-
12
- parser = OptionParser.new do |o|
13
- o.banner = 'Usage: locker.rb [options]'
14
-
15
- o.on("--consul url=#{@opts[:url]}", 'Set up a custom Consul URL') do |url|
16
- Diplomat.configure do |config|
17
- config.url = url.strip
18
- end
19
- end
20
-
21
- o.on('--lock resource', 'resource name to lock with Consul') do |resource|
22
- @opts[:lock] = resource.strip
23
- end
24
-
25
- o.on("--ttl seconds=#{@opts[:ttl]}", 'TTL to set when session created') do |seconds|
26
- @opts[:ttl] = Integer(seconds.strip)
27
- end
28
-
29
- o.on('--unlock session', 'session name from previous call lock') do |session|
30
- @opts[:unlock] = session.strip
31
- end
32
-
33
- o.on("--timeout seconds=#{@opts[:timeout]}", 'timeout to wait lock') do |seconds|
34
- @opts[:timeout] = Integer(seconds.strip)
35
- end
36
- end
37
- parser.parse!
38
-
39
-
40
- def lock(session, locker, timeout)
41
- Timeout.timeout(timeout) do
42
- return Diplomat::Lock.wait_to_acquire("resource/#{locker[:resource]}/lock", session, locker.to_json, 10)
43
- end
44
- rescue Timeout::Error => e
45
- false
46
- end
47
-
48
-
49
- if resource = @opts[:lock]
50
- locker = {
51
- Name: "#{resource}_locker_#{rand(999_999)}",
52
- Behavior: 'delete',
53
- TTL: "#{@opts[:ttl]}s",
54
- resource: resource
55
- }
56
- sessionid = Diplomat::Session.create(locker)
57
-
58
- if lock(sessionid, locker, @opts[:timeout])
59
- puts sessionid
60
- exit 0
61
- else
62
- STDERR.puts "Failed to lock resource: #{resource}"
63
- exit 1
64
- end
65
-
66
- elsif session = @opts[:unlock]
67
- Diplomat::Session.destroy(session)
68
- else
69
- STDERR.puts parser.help
70
- exit 1
71
- end
data/bin/merger.rb DELETED
@@ -1,118 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'English'
4
- require 'yaml'
5
-
6
- if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
7
- unless ENV['COMPOSE_FILE']
8
- STDERR.puts 'COMPOSE_FILE environment must point to one or more files'
9
- exit 1
10
- end
11
- end
12
-
13
-
14
- class Hash
15
-
16
- def deep_dup
17
- Marshal.load(Marshal.dump(self))
18
- end
19
-
20
- end
21
-
22
- class Array
23
-
24
- def deep_dup
25
- Marshal.load(Marshal.dump(self))
26
- end
27
-
28
- end
29
-
30
-
31
-
32
- def extend_hash(first, second)
33
- raise ArgumentError.new('First and second args equal nil') if [first, second].all? &:nil?
34
- return second if first.nil?
35
- return first if second.nil?
36
-
37
- first.each_pair do |fk, fv|
38
- next unless second.key?(fk)
39
-
40
- sv = second[fk]
41
- raise "Types of values not match(#{fv.class}, #{sv.class})" if fv.class != sv.class
42
-
43
- # Специальный случай потому что command не мерджится а заменяется
44
- if fk == 'command'
45
- first[fk] = sv
46
- elsif fv.is_a? Hash
47
- extend_hash(fv, sv)
48
- elsif fv.is_a? Array
49
- fv |= sv
50
- first[fk] = fv
51
- else
52
- first[fk] = sv
53
- end
54
- end
55
-
56
- second.each_pair do |sk, sv|
57
- next if first.key?(sk)
58
-
59
- first[sk] = sv
60
- end
61
-
62
- first
63
- end
64
-
65
- def process_compose_hash(yml, dirname, parent = {})
66
- (yml['services'] || {}).each_pair do |name, service|
67
- next unless ext = service['extends']
68
- base = if ext.is_a? String
69
- template = yml['services'][ext]
70
- parent_service = (parent['services'] || {})[ext] || {}
71
- extend_hash(parent_service.deep_dup, template)
72
- elsif file = ext['file']
73
- ENV.each_pair do |k, v|
74
- file.gsub!("$#{k}", v)
75
- file.gsub!("${#{k}}", v)
76
- end
77
-
78
- file_to_load = if File.exist?(dirname + '/' + file)
79
- dirname + '/' + file
80
- else
81
- file
82
- end
83
-
84
- tmp = process_compose_hash(YAML.load(File.read(file_to_load)), File.dirname(file_to_load), service)
85
-
86
- begin
87
- (tmp['services'][ext['service']] || {})
88
- rescue StandardError
89
- {}
90
- end
91
- else
92
- yml['services'][ext['service']]
93
- end.deep_dup
94
-
95
- service.delete 'extends'
96
-
97
- yml['services'][name] = extend_hash(base, service)
98
- end
99
- yml
100
- end
101
-
102
- if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
103
- result = ENV['COMPOSE_FILE'].split(':').reduce({}) do |parent, file|
104
- yml = process_compose_hash(YAML.load(File.read(file)), File.dirname(file), parent)
105
- if yml['version'] && parent['version'] && yml['version'] != parent['version']
106
- raise "version mismatch: #{file}"
107
- end
108
- ret = extend_hash(parent.deep_dup, yml)
109
- ret
110
- end
111
-
112
- if ARGV[0].nil? || ARGV[0].strip == '-'
113
- puts YAML.dump(result)
114
- else
115
- File.write(ARGV[0].strip, YAML.dump(result))
116
- end
117
-
118
- end
data/bin/waiter.rb DELETED
@@ -1,262 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'optparse'
4
- require 'securerandom'
5
- require 'English'
6
- require 'openssl'
7
- require 'tempfile'
8
- require 'json'
9
-
10
- STDOUT.sync = true
11
- STDERR.sync = true
12
-
13
- TIMEOUT = 15
14
- INTERVAL = 3
15
-
16
- @opts = {}
17
-
18
- @opts[:exec] = (begin
19
- ARGV.join(' ').split(' -- ')[1].strip
20
- rescue StandardError
21
- nil
22
- end)
23
- @opts[:timeout] = TIMEOUT
24
- @opts[:interval] = INTERVAL
25
- @opts[:consul_addr] = 'http://localhost:8500'
26
- @opts[:consul_service_count] = 1
27
-
28
-
29
- OptionParser.new do |o|
30
- o.banner = 'Usage: waiter.rb [options] -- exec'
31
-
32
- o.on('--tcp host:port', 'Wait for tcp accepts on host:port') do |addr|
33
- host, port = addr.split(':')
34
- @opts[:tcp] = addr.strip
35
- @opts[:host] = host.strip
36
- @opts[:port] = port.strip
37
- end
38
-
39
- o.on('--db dbname', 'Wait for PG database exists. Using --tcp to conenct PG') do |db|
40
- @opts[:db] = db.strip
41
- end
42
-
43
- o.on('--tb tablename', 'Wait for PG table exists. Using --tcp to conenct PG') do |tb|
44
- @opts[:tb] = tb.strip
45
- end
46
-
47
- o.on('-f', '--file filename', 'Wait for file exists.') do |file|
48
- @opts[:file] = file.strip
49
- end
50
-
51
- o.on("--consul-addr addr=#{@opts[:consul_addr]}", 'HTTP addres to connect to consul') do |addr|
52
- @opts[:consul_addr] = addr.strip
53
- end
54
-
55
- o.on('--consul', 'Wait for local consul agent to be ready') do
56
- @opts[:consul] = true
57
- end
58
-
59
- o.on('--consul-service service', 'Wait for service appear in consul') do |service|
60
- @opts[:consul_service] = service
61
- end
62
-
63
- o.on("--consul-service-count count=#{@opts[:consul_service_count]}", 'Wait for this count of service appear in consul') do |count|
64
- @opts[:consul_service_count] = count.to_i
65
- end
66
-
67
- o.on('--consul-tag tag', 'Filter consul service by tag') do |tag|
68
- @opts[:consul_tag] = tag.to_s
69
- end
70
-
71
- o.on('--user user', 'username') do |user|
72
- @opts[:user] = user.strip
73
- end
74
-
75
- o.on('--pass pass', 'password') do |pass|
76
- @opts[:pass] = pass.strip
77
- end
78
-
79
- o.on('-t', '--timeout secs=15', 'Total timeout') do |timeout|
80
- @opts[:timeout] = timeout.to_i
81
- end
82
-
83
- o.on('-i', '--interval secs=2', 'Interval between attempts') do |interval|
84
- @opts[:interval] = interval.to_i
85
- end
86
-
87
- o.on('-q', '--quiet', 'Do not output any status messages') do
88
- @opts[:quiet] = true
89
- end
90
- end.parse!
91
-
92
- def log(message)
93
- puts message unless @opts[:quiet]
94
- end
95
-
96
- @opts[:timeout] = @opts[:timeout].to_i
97
- @opts[:interval] = @opts[:interval].to_i
98
-
99
- if @opts[:db]
100
- @pg = {}
101
- @pg[:db] = "-d #{@opts[:db]}"
102
- @pg[:user] = "-U #{@opts[:user]}" if @opts[:user]
103
- @pg[:pass] = if @opts[:pass] && !@opts[:pass].empty?
104
- "PGPASSWORD=#{@opts[:pass]}"
105
- else
106
- ''
107
- end
108
-
109
- @pg[:host] = "-h #{@opts[:host]}" if @opts[:host]
110
- @pg[:port] = "-p #{@opts[:port]}" if @opts[:port]
111
-
112
- @pg[:tb] = @opts[:tb] if @opts[:tb]
113
- end
114
-
115
- def wait_for(timeout)
116
- starttime = Time.now
117
- loop do
118
- success = yield
119
-
120
- return success if success
121
-
122
- return false if (Time.now - starttime) > timeout
123
- sleep @opts[:interval]
124
- end
125
-
126
- false
127
- end
128
-
129
- def complete!(success)
130
- if success
131
- if @opts[:exec]
132
- exec @opts[:exec]
133
- else
134
- exit 0
135
- end
136
- end
137
-
138
- STDERR.puts 'Operation timed out'
139
- exit 1
140
- end
141
-
142
- def wait_for_consul
143
- log('Waiting for consul...')
144
- ret = wait_for @opts[:timeout] do
145
- cmd = "consul operator raft list-peers -http-addr=#{@opts[:consul_addr]} > /dev/null 2>&1"
146
- system(cmd)
147
- $?.success?
148
- end
149
-
150
- yield(ret)
151
- end
152
-
153
- def wait_for_consul_service(service)
154
- log("Waiting for consul service #{service}...")
155
- ret = wait_for @opts[:timeout] do
156
- cmd = "curl -s #{@opts[:consul_addr]}/v1/health/service/#{service}?passing"
157
- cmd += "&tag=#{@opts[:consul_tag]}" if @opts[:consul_tag]
158
- result = `#{cmd}`
159
- result = begin
160
- JSON.parse(result)
161
- rescue StandardError
162
- []
163
- end
164
- result.count >= @opts[:consul_service_count]
165
- end
166
-
167
- yield(ret)
168
- end
169
-
170
- def wait_for_tcp
171
- log("Waiting for TCP: #{@opts[:host]}:#{@opts[:port]}...")
172
- ret = wait_for @opts[:timeout] do
173
- cmd = "nc -z #{@opts[:host]} #{@opts[:port]} > /dev/null 2>&1"
174
- system(cmd)
175
- $?.success?
176
- end
177
-
178
- yield(ret)
179
- end
180
-
181
- def wait_for_db
182
- log("Waiting for DB: pg://#{@opts[:user]}:#{@opts[:pass]}@#{@opts[:host]}:#{@opts[:port]}/#{@opts[:db]}...")
183
- ret = wait_for @opts[:timeout] do
184
- cmd = "#{@pg[:pass]} psql -lqt #{@pg[:user]} #{@pg[:host]} #{@pg[:port]} #{@pg[:db]} 2>/dev/null | cut -d \\| -f 1 | grep -qw #{@opts[:db]} > /dev/null 2>&1"
185
- system(cmd)
186
- $?.success?
187
- end
188
-
189
- yield(ret)
190
- end
191
-
192
- def wait_for_tb
193
- log("Waiting for TABLE: pg://#{@opts[:user]}:#{@opts[:pass]}@#{@opts[:host]}:#{@opts[:port]}/#{@opts[:db]}##{@opts[:tb]}...")
194
- ret = wait_for @opts[:timeout] do
195
- cmd = "echo \"\\dt\" | psql -qt #{@pg[:user]} #{@pg[:pass]} #{@pg[:host]} #{@pg[:port]} #{@pg[:db]} 2>/dev/null | cut -d \\| -f 2 | grep -qw #{@pg[:tb]} > /dev/null 2>&1"
196
- system(cmd)
197
- $?.success?
198
- end
199
-
200
- yield(ret)
201
- end
202
-
203
- def wait_for_file(file = @opts[:file], timeout = @opts[:timeout])
204
- log("Waiting for FILE: #{file}")
205
- ret = wait_for timeout do
206
- File.exist? file
207
- end
208
- yield(ret)
209
- end
210
-
211
- if @opts[:tb]
212
- wait_for_tcp do |success|
213
- if success
214
- wait_for_db do |success|
215
- if success
216
- wait_for_tb do |success|
217
- complete!(success)
218
- end
219
- end
220
- end
221
- end
222
-
223
- complete!(false)
224
- end
225
- end
226
-
227
- if @opts[:db]
228
- wait_for_tcp do |success|
229
- if success
230
- wait_for_db do |success|
231
- complete!(success)
232
- end
233
- end
234
-
235
- complete!(false)
236
- end
237
- end
238
-
239
-
240
- if @opts[:consul]
241
- wait_for_consul do |success|
242
- complete!(success)
243
- end
244
- end
245
-
246
- if @opts[:consul_service]
247
- wait_for_consul_service(@opts[:consul_service]) do |success|
248
- complete!(success)
249
- end
250
- end
251
-
252
- if @opts[:tcp]
253
- wait_for_tcp do |success|
254
- complete!(success)
255
- end
256
- end
257
-
258
- if @opts[:file]
259
- wait_for_file(@opts[:file], @opts[:timeout]) do |success|
260
- complete!(success)
261
- end
262
- end