indocker 0.0.6 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +10 -2
  3. data/.rspec +2 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +5 -2
  6. data/Gemfile.lock +19 -49
  7. data/LICENSE.txt +17 -18
  8. data/README.md +20 -1
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/example/indocker/bin/deploy +120 -0
  13. data/example/indocker/bin/remote/compile +72 -0
  14. data/example/indocker/bin/remote/run +66 -0
  15. data/example/indocker/bin/utils/configurations.rb +3 -0
  16. data/example/indocker/bounded_contexts/shared/ruby/Dockerfile +10 -0
  17. data/example/indocker/bounded_contexts/shared/ruby/container.rb +5 -0
  18. data/example/indocker/bounded_contexts/shared/ruby/image.rb +3 -0
  19. data/example/indocker/configurations/dev.rb +9 -0
  20. data/example/indocker/infrastructure/build_servers.rb +8 -0
  21. data/example/indocker/infrastructure/networks.rb +1 -0
  22. data/example/indocker/infrastructure/registries.rb +3 -0
  23. data/example/indocker/infrastructure/servers.rb +8 -0
  24. data/example/indocker/setup.rb +24 -0
  25. data/indocker.gemspec +27 -25
  26. data/lib/indocker.rb +437 -130
  27. data/lib/indocker/artifacts/git.rb +25 -0
  28. data/lib/indocker/build_context.rb +87 -0
  29. data/lib/indocker/build_context_helper.rb +109 -0
  30. data/lib/indocker/build_context_pool.rb +36 -0
  31. data/lib/indocker/build_server.rb +2 -0
  32. data/lib/indocker/colored_string.rb +33 -0
  33. data/lib/indocker/concerns/inspectable.rb +12 -0
  34. data/lib/indocker/configuration_deployer.rb +525 -0
  35. data/lib/indocker/configurations/configuration.rb +122 -0
  36. data/lib/indocker/configurations/configuration_builder.rb +250 -0
  37. data/lib/indocker/configurations/formatters/stdout.rb +36 -0
  38. data/lib/indocker/container_deployer.rb +73 -0
  39. data/lib/indocker/container_helper.rb +13 -0
  40. data/lib/indocker/container_runner.rb +23 -0
  41. data/lib/indocker/containers/container.rb +118 -0
  42. data/lib/indocker/containers/container_builder.rb +186 -0
  43. data/lib/indocker/containers/restart_policy.rb +36 -0
  44. data/lib/indocker/context_args.rb +43 -0
  45. data/lib/indocker/crontab_redeploy_rules_builder.rb +54 -0
  46. data/lib/indocker/deploy_context.rb +126 -0
  47. data/lib/indocker/deployment_checker.rb +121 -0
  48. data/lib/indocker/deployment_progress.rb +495 -0
  49. data/lib/indocker/docker.rb +87 -0
  50. data/lib/indocker/docker_run_args.rb +183 -0
  51. data/lib/indocker/env_file_helper.rb +13 -0
  52. data/lib/indocker/env_files/local.rb +8 -0
  53. data/lib/indocker/env_files/remote.rb +8 -0
  54. data/lib/indocker/hash_merger.rb +9 -0
  55. data/lib/indocker/images/image.rb +86 -0
  56. data/lib/indocker/images/image_builder.rb +98 -0
  57. data/lib/indocker/images/image_compiler.rb +68 -0
  58. data/lib/indocker/images/template_compiler.rb +15 -0
  59. data/lib/indocker/images/templates_compiler.rb +28 -0
  60. data/lib/indocker/images_compiler.rb +38 -0
  61. data/lib/indocker/indocker_helper.rb +9 -0
  62. data/lib/indocker/network.rb +12 -0
  63. data/lib/indocker/network_helper.rb +7 -0
  64. data/lib/indocker/registries/abstract.rb +17 -0
  65. data/lib/indocker/registries/local.rb +5 -0
  66. data/lib/indocker/registries/remote.rb +12 -0
  67. data/lib/indocker/repositories/abstract.rb +25 -0
  68. data/lib/indocker/repositories/clonner.rb +17 -0
  69. data/lib/indocker/repositories/git.rb +24 -0
  70. data/lib/indocker/repositories/local.rb +20 -0
  71. data/lib/indocker/repositories/no_sync.rb +20 -0
  72. data/lib/indocker/rsync.rb +52 -0
  73. data/lib/indocker/server.rb +20 -0
  74. data/lib/indocker/server_pool.rb +30 -0
  75. data/lib/indocker/shell.rb +60 -0
  76. data/lib/indocker/ssh_result_logger.rb +18 -0
  77. data/lib/indocker/ssh_session.rb +92 -0
  78. data/lib/indocker/version.rb +1 -1
  79. data/lib/indocker/volume_helper.rb +7 -0
  80. data/lib/indocker/volumes/external.rb +8 -0
  81. data/lib/indocker/volumes/local.rb +9 -0
  82. data/lib/indocker/volumes/repository.rb +9 -0
  83. metadata +107 -222
  84. data/bin/indocker +0 -9
  85. data/lib/indocker/application_initializer.rb +0 -19
  86. data/lib/indocker/cli.rb +0 -27
  87. data/lib/indocker/configs/config.rb +0 -147
  88. data/lib/indocker/configs/config_factory.rb +0 -37
  89. data/lib/indocker/configs/config_initializer.rb +0 -9
  90. data/lib/indocker/configs/locator.rb +0 -23
  91. data/lib/indocker/container/container_builder.rb +0 -29
  92. data/lib/indocker/container/container_directives_runner.rb +0 -54
  93. data/lib/indocker/container/container_dsl.rb +0 -83
  94. data/lib/indocker/container/container_evaluator.rb +0 -13
  95. data/lib/indocker/container/container_inspector.rb +0 -26
  96. data/lib/indocker/container/container_manager.rb +0 -122
  97. data/lib/indocker/container/container_metadata.rb +0 -112
  98. data/lib/indocker/container/container_metadata_factory.rb +0 -30
  99. data/lib/indocker/container/container_metadata_repository.rb +0 -27
  100. data/lib/indocker/directives/base.rb +0 -17
  101. data/lib/indocker/directives/container_directives/base.rb +0 -23
  102. data/lib/indocker/directives/container_directives/cmd.rb +0 -7
  103. data/lib/indocker/directives/container_directives/depends_on.rb +0 -7
  104. data/lib/indocker/directives/container_directives/env.rb +0 -7
  105. data/lib/indocker/directives/container_directives/env_file.rb +0 -7
  106. data/lib/indocker/directives/container_directives/expose.rb +0 -7
  107. data/lib/indocker/directives/container_directives/from.rb +0 -14
  108. data/lib/indocker/directives/container_directives/network.rb +0 -12
  109. data/lib/indocker/directives/container_directives/ports.rb +0 -15
  110. data/lib/indocker/directives/container_directives/ready.rb +0 -16
  111. data/lib/indocker/directives/container_directives/volume.rb +0 -23
  112. data/lib/indocker/directives/image_directives/base.rb +0 -27
  113. data/lib/indocker/directives/image_directives/cmd.rb +0 -19
  114. data/lib/indocker/directives/image_directives/copy.rb +0 -32
  115. data/lib/indocker/directives/image_directives/docker_cp.rb +0 -20
  116. data/lib/indocker/directives/image_directives/entrypoint.rb +0 -19
  117. data/lib/indocker/directives/image_directives/env.rb +0 -9
  118. data/lib/indocker/directives/image_directives/env_file.rb +0 -19
  119. data/lib/indocker/directives/image_directives/expose.rb +0 -9
  120. data/lib/indocker/directives/image_directives/from.rb +0 -36
  121. data/lib/indocker/directives/image_directives/registry.rb +0 -30
  122. data/lib/indocker/directives/image_directives/run.rb +0 -17
  123. data/lib/indocker/directives/image_directives/workdir.rb +0 -9
  124. data/lib/indocker/directives/partial.rb +0 -21
  125. data/lib/indocker/docker_api/container_config.rb +0 -121
  126. data/lib/indocker/docker_api/docker_api.rb +0 -207
  127. data/lib/indocker/dsl_context.rb +0 -21
  128. data/lib/indocker/envs/env_metadata.rb +0 -39
  129. data/lib/indocker/envs/loader.rb +0 -19
  130. data/lib/indocker/errors.rb +0 -21
  131. data/lib/indocker/git/git_api.rb +0 -32
  132. data/lib/indocker/git/git_helper.rb +0 -34
  133. data/lib/indocker/git/git_service.rb +0 -21
  134. data/lib/indocker/handlers/container_run.rb +0 -20
  135. data/lib/indocker/handlers/container_stop.rb +0 -17
  136. data/lib/indocker/handlers/performable.rb +0 -22
  137. data/lib/indocker/image/image_builder.rb +0 -54
  138. data/lib/indocker/image/image_dependencies_manager.rb +0 -47
  139. data/lib/indocker/image/image_directives_runner.rb +0 -99
  140. data/lib/indocker/image/image_dockerfile_builder.rb +0 -24
  141. data/lib/indocker/image/image_dsl.rb +0 -89
  142. data/lib/indocker/image/image_evaluator.rb +0 -21
  143. data/lib/indocker/image/image_helper.rb +0 -9
  144. data/lib/indocker/image/image_metadata.rb +0 -50
  145. data/lib/indocker/image/image_metadata_factory.rb +0 -31
  146. data/lib/indocker/image/image_metadata_repository.rb +0 -29
  147. data/lib/indocker/networks/network_metadata.rb +0 -9
  148. data/lib/indocker/networks/network_metadata_factory.rb +0 -9
  149. data/lib/indocker/networks/network_metadata_repository.rb +0 -34
  150. data/lib/indocker/partial/partial_metadata.rb +0 -8
  151. data/lib/indocker/partial/partial_metadata_repository.rb +0 -26
  152. data/lib/indocker/registry/registry_api.rb +0 -46
  153. data/lib/indocker/registry/registry_helper.rb +0 -20
  154. data/lib/indocker/registry/registry_service.rb +0 -28
  155. data/lib/indocker/utils/ioc_container.rb +0 -17
  156. data/lib/indocker/utils/logger.rb +0 -62
  157. data/lib/indocker/utils/logger_factory.rb +0 -13
  158. data/lib/indocker/utils/registry_authenticator.rb +0 -19
  159. data/lib/indocker/utils/render_namespace.rb +0 -11
  160. data/lib/indocker/utils/render_util.rb +0 -15
  161. data/lib/indocker/utils/string_utils.rb +0 -11
  162. data/lib/indocker/utils/tar_helper.rb +0 -40
  163. data/lib/indocker/utils/test_logger_factory.rb +0 -9
  164. data/lib/indocker/volumes/volume_metadata.rb +0 -9
  165. data/lib/indocker/volumes/volume_metadata_factory.rb +0 -9
  166. data/lib/indocker/volumes/volume_metadata_repository.rb +0 -34
  167. data/spec/example/.indocker/config.rb +0 -30
  168. data/spec/example/.indocker/images_and_containers.rb +0 -25
  169. data/spec/example/assets/index.css +0 -1
  170. data/spec/example/assets/index.js +0 -1
  171. data/spec/fixtures/spec.env +0 -2
  172. data/spec/indocker/configs/config_factory_spec.rb +0 -18
  173. data/spec/indocker/configs/config_spec.rb +0 -88
  174. data/spec/indocker/container/container_builder_spec.rb +0 -67
  175. data/spec/indocker/container/container_manager_spec.rb +0 -278
  176. data/spec/indocker/docker_api/container_config_spec.rb +0 -64
  177. data/spec/indocker/docker_api/docker_api_spec.rb +0 -112
  178. data/spec/indocker/handlers/container_run_spec.rb +0 -60
  179. data/spec/indocker/image/image_builder_spec.rb +0 -153
  180. data/spec/indocker/image/image_directives_runner_spec.rb +0 -141
  181. data/spec/indocker/image/image_dockerfile_builder_spec.rb +0 -25
  182. data/spec/indocker/image/image_evaluator_spec.rb +0 -85
  183. data/spec/spec_helper.rb +0 -68
@@ -0,0 +1,25 @@
1
+ class Indocker::Artifacts::Git
2
+ attr_reader :name, :remote_name, :remote_url, :branch, :source_path, :target_path
3
+
4
+ def initialize(name:, remote_name:, remote_url:, branch:, source_path:, target_path:)
5
+ @name = name
6
+ @remote_name = remote_name
7
+ @remote_url = remote_url
8
+ @branch = branch
9
+ @source_path = source_path
10
+ @target_path = target_path
11
+ end
12
+
13
+ def repository
14
+ @repository ||= Indocker::Repositories::Git.new(@name).setup(
15
+ remote_name: remote_name,
16
+ remote_url: remote_url,
17
+ branch: branch,
18
+ clone_path: "/tmp/#{Indocker.configuration.name}/artifacts/git/#{project_name(remote_url)}/#{branch}"
19
+ )
20
+ end
21
+
22
+ def project_name(url)
23
+ url.split('/').last.gsub('.git', '')
24
+ end
25
+ end
@@ -0,0 +1,87 @@
1
+ require 'fileutils'
2
+
3
+ class Indocker::BuildContext
4
+ attr_reader :session, :server, :configuration, :helper, :logger
5
+
6
+ def initialize(configuration:, build_server:, logger:)
7
+ @configuration = configuration
8
+ @logger = logger
9
+ @helper = Indocker::BuildContextHelper.new(@configuration, @build_server)
10
+ @server = build_server
11
+
12
+ if build_server
13
+ @session = Indocker::SshSession.new(
14
+ host: build_server.host,
15
+ user: build_server.user,
16
+ port: build_server.port,
17
+ logger: @logger
18
+ )
19
+ end
20
+
21
+ @compiled_images = Hash.new(false)
22
+ end
23
+
24
+ def exec!(command)
25
+ @session.exec!(command)
26
+ end
27
+
28
+ def close_session
29
+ @session.close if @session
30
+ end
31
+
32
+ def image_compiled?(image)
33
+ @compiled_images[image]
34
+ end
35
+
36
+ def set_busy(flag)
37
+ @busy = !!flag
38
+ end
39
+
40
+ def busy?
41
+ !!@busy
42
+ end
43
+
44
+ def set_compiled(image)
45
+ @compiled_images[image] = true
46
+ end
47
+
48
+ def build_image(image, build_dir, args: [])
49
+ image_name = image.image_name
50
+ registry = image.registry
51
+ tag = image.tag
52
+
53
+ FileUtils.cd(build_dir) do
54
+ if @logger.debug?
55
+ @logger.debug("#{"Docker image content:".yellow}")
56
+
57
+ Dir[File.join(build_dir, '*')]
58
+ .map {|path|
59
+ file = path.gsub(build_dir, '')
60
+ @logger.debug(" .#{file}".yellow)
61
+ }
62
+ .join("\n")
63
+ end
64
+
65
+ if !@logger.debug?
66
+ args = args.push('-q')
67
+ end
68
+
69
+ build_args = args.join(' ')
70
+
71
+ res = Indocker::Docker.build(image.local_registry_url, build_args)
72
+
73
+ if res.exit_status != 0
74
+ @logger.error("image compilation :#{image.name} failed")
75
+ @logger.error(res.stdout)
76
+ exit 1
77
+ end
78
+
79
+ Indocker::Docker.tag(image.local_registry_url, image.registry_url)
80
+ Indocker::Docker.tag(image.local_registry_url, image.local_registry_url)
81
+
82
+ if !image.registry.is_local?
83
+ Indocker::Docker.push(image.registry_url)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,109 @@
1
+ class Indocker::BuildContextHelper
2
+ attr_reader :configuration, :build_server
3
+
4
+ def initialize(configuration, build_server)
5
+ @configuration = configuration
6
+ @build_server = build_server
7
+ @clonned_repositories = Hash.new(false)
8
+ end
9
+
10
+ def image_url(image_sym)
11
+ path = Indocker.image_files.fetch(image_sym) do
12
+ Indocker.logger.error("image :#{image_sym} was not found in configuration :#{@configuration.name}")
13
+ exit 1
14
+ end
15
+
16
+ require path
17
+
18
+ image = @configuration.images.detect do |i|
19
+ i.name == image_sym
20
+ end
21
+
22
+ if image.nil?
23
+ raise ArgumentError.new("image :#{image_sym} was not found in configuration")
24
+ end
25
+
26
+ image.registry_url
27
+ end
28
+
29
+ def repository_path(name)
30
+ repo = repository(name)
31
+ repo.clone_path
32
+ end
33
+
34
+ def repository(name)
35
+ @configuration.repositories.fetch(name) do
36
+ raise ArgumentError.new("repository :#{name} is not defined in configuration")
37
+ end
38
+ end
39
+
40
+ def global_build_args
41
+ @global_build_args = Indocker::ContextArgs.new(nil, @configuration.global_build_args, nil)
42
+ end
43
+
44
+ def container_enabled?(container)
45
+ @configuration.enabled_containers.include?(container.name)
46
+ end
47
+
48
+ class Containers
49
+ def initialize(configuration)
50
+ @configuration = configuration
51
+ end
52
+
53
+ def method_missing(name, *args)
54
+ if args.size > 0
55
+ raise ArgumentError.new("containers.#{name} does not accept arguments")
56
+ end
57
+
58
+ if Indocker.container_files.has_key?(name)
59
+ require Indocker.container_files.fetch(name)
60
+ end
61
+
62
+ container = @configuration.containers.detect do |container|
63
+ container.name == name
64
+ end
65
+
66
+ if !container
67
+ raise ArgumentError.new("container :#{name} was not found in configuration")
68
+ end
69
+
70
+ Container.new(@configuration, container)
71
+ end
72
+ end
73
+
74
+ class Container
75
+ def initialize(configuration, container)
76
+ @configuration = configuration
77
+ @container = container
78
+ end
79
+
80
+ def build_args
81
+ Indocker::ContextArgs.new(
82
+ nil,
83
+ Indocker::HashMerger.deep_merge(@container.build_args, @container.image.build_args),
84
+ nil,
85
+ @container
86
+ )
87
+ end
88
+
89
+ def hostname(number = nil)
90
+ Indocker::ContainerHelper.hostname(@configuration.name, @container, number)
91
+ end
92
+
93
+ def count
94
+ @container.get_start_option(:scale) || 1
95
+ end
96
+
97
+ def method_missing(name, *args)
98
+ @container.send(name, *args)
99
+ end
100
+ end
101
+
102
+ def containers
103
+ @containers ||= Containers.new(@configuration)
104
+ end
105
+
106
+ def get_binding
107
+ binding
108
+ end
109
+ end
@@ -0,0 +1,36 @@
1
+ class Indocker::BuildContextPool
2
+ def initialize(configuration:, logger:)
3
+ @logger = logger
4
+ @configuration = configuration
5
+
6
+ @contexts = configuration.build_servers.map do |build_server|
7
+ Indocker::BuildContext.new(
8
+ logger: @logger,
9
+ configuration: configuration,
10
+ build_server: build_server,
11
+ )
12
+ end
13
+ end
14
+
15
+ def get
16
+ context = nil
17
+
18
+ loop do
19
+ context = @contexts.detect {|c| !c.busy?}
20
+ sleep(0.1)
21
+ break if context
22
+ end
23
+
24
+ context
25
+ end
26
+
27
+ def each(&proc)
28
+ @contexts.each(&proc)
29
+ end
30
+
31
+ def close_sessions
32
+ @contexts.each(&:close_session)
33
+ rescue => e
34
+ @logger.error("error during session close: #{e.inspect}")
35
+ end
36
+ end
@@ -0,0 +1,2 @@
1
+ class Indocker::BuildServer < Indocker::Server
2
+ end
@@ -0,0 +1,33 @@
1
+ class String
2
+ def red
3
+ colorize(31)
4
+ end
5
+
6
+ def green
7
+ colorize(32)
8
+ end
9
+
10
+ def yellow
11
+ colorize(33)
12
+ end
13
+
14
+ def blue
15
+ colorize(34)
16
+ end
17
+
18
+ def purple
19
+ colorize(35)
20
+ end
21
+
22
+ def cyan
23
+ colorize(36)
24
+ end
25
+
26
+ def grey
27
+ colorize(37)
28
+ end
29
+
30
+ def colorize(color_code)
31
+ "\e[#{color_code}m#{self}\e[0m"
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ module Indocker::Concerns::Inspectable
2
+ def inspect
3
+ data = {}
4
+ data[:type] = self.class.to_s.split('::').last.downcase
5
+
6
+ instance_variables.each do |variable|
7
+ data[variable.to_s.gsub('@', '').to_sym] = instance_variable_get(variable)
8
+ end
9
+
10
+ data.inspect
11
+ end
12
+ end
@@ -0,0 +1,525 @@
1
+ require 'timeout'
2
+ require 'benchmark'
3
+ require 'tempfile'
4
+
5
+ class Indocker::ConfigurationDeployer
6
+ REMOTE_OPERATION_TIMEOUT = 60
7
+
8
+ def initialize(logger)
9
+ Thread.abort_on_exception = true # abort all threads if exception occurs
10
+
11
+ @logger = logger
12
+ @global_logger = Logger.new(STDOUT)
13
+ @global_logger.formatter = logger.formatter
14
+
15
+ @progress = Indocker::DeploymentProgress.new(
16
+ Indocker.logger.level == Logger::DEBUG ? nil : Logger.new(STDOUT)
17
+ )
18
+ end
19
+
20
+ def run(configuration:, deploy_containers:, skip_tags:, deploy_tags:, skip_dependent:,
21
+ skip_containers:, servers:, skip_build:, force_restart:, skip_force_restart:, auto_confirm:, require_confirmation:)
22
+ build_context_pool = nil
23
+ deployer = nil
24
+
25
+ time = Benchmark.realtime do
26
+ if force_restart
27
+ @logger.warn("WARNING. All containers will be forced to restart.")
28
+ end
29
+
30
+ if skip_build
31
+ @logger.warn("WARNING. Images build step will be skipped")
32
+ end
33
+
34
+ preload_containers(configuration)
35
+
36
+ containers = find_deploy_containers(configuration, deploy_containers, deploy_tags, skip_dependent, skip_containers, servers, skip_tags, auto_confirm, require_confirmation)
37
+ containers = containers.uniq {|c| c.name}
38
+
39
+ clonner = Indocker::Repositories::Clonner.new(configuration, @logger)
40
+
41
+ @global_logger.info("Establishing ssh sessions to all servers...")
42
+
43
+ build_context_pool = Indocker::BuildContextPool.new(configuration: configuration, logger: @logger)
44
+ deployer = Indocker::ContainerDeployer.new(configuration: configuration, logger: @logger)
45
+
46
+ build_servers = configuration
47
+ .build_servers
48
+ .uniq { |s| s.host }
49
+
50
+ deploy_servers = containers
51
+ .map(&:servers)
52
+ .flatten
53
+ .uniq { |s| s.host }
54
+
55
+ servers = (deploy_servers + build_servers).uniq { |s| s.host }
56
+
57
+ @progress.setup(
58
+ binaries_servers: servers,
59
+ build_servers: build_servers,
60
+ deploy_servers: deploy_servers,
61
+ env_files: configuration.env_files.keys,
62
+ repositories: configuration.repositories.keys,
63
+ force_restart: force_restart,
64
+ skip_build: skip_build,
65
+ containers: containers,
66
+ artifact_servers: configuration.artifact_servers,
67
+ )
68
+
69
+ remote_operations = sync_indocker(servers)
70
+
71
+ wait_remote_operations(remote_operations)
72
+
73
+ remote_operations += sync_env_files(deploy_servers, configuration.env_files)
74
+ remote_operations += pull_repositories(clonner, build_servers, configuration.repositories)
75
+ remote_operations += sync_artifacts(clonner, configuration.artifact_servers)
76
+
77
+ wait_remote_operations(remote_operations)
78
+
79
+ update_crontab_redeploy_rules(configuration, build_servers.first)
80
+
81
+ containers.uniq.each do |container|
82
+ recursively_deploy_container(configuration, deployer, build_context_pool, container, containers, skip_build, force_restart, skip_force_restart)
83
+ end
84
+
85
+ Thread
86
+ .list
87
+ .each { |t| t.join if t != Thread.current }
88
+ end
89
+
90
+ @global_logger.info("Deployment finished".green)
91
+ @global_logger.info("Total time taken: #{time.round}s".green)
92
+ ensure
93
+ build_context_pool.close_sessions if build_context_pool
94
+ deployer.close_sessions if deployer
95
+ end
96
+
97
+ private
98
+
99
+ def wait_remote_operations(remote_operations)
100
+ remote_operations.each do |remote_operation|
101
+ begin
102
+ Timeout::timeout(REMOTE_OPERATION_TIMEOUT) do
103
+ remote_operation.thread.join
104
+ end
105
+ rescue Timeout::Error
106
+ @global_logger.error("Deployment aborted. Remote operation :#{remote_operation.operation} on server #{remote_operation.server.user}@#{remote_operation.server.host} did not finish during #{REMOTE_OPERATION_TIMEOUT} seconds.")
107
+ exit 1
108
+ end
109
+ end
110
+ end
111
+
112
+ def preload_containers(configuration)
113
+ configuration.enabled_containers.each do |container_name|
114
+ path = Indocker.container_files[container_name]
115
+ require path
116
+ end
117
+ end
118
+
119
+ def find_deploy_containers(configuration, deploy_containers, deploy_tags, skip_dependent, skip_containers, servers, skip_tags, auto_confirm, require_confirmation)
120
+ containers = []
121
+
122
+ deploy_tags.each do |tag|
123
+ containers += configuration.containers.select do |container|
124
+ container.tags.include?(tag)
125
+ end
126
+ end
127
+
128
+ skip_containers.each do |name|
129
+ container = configuration.containers.detect do |container|
130
+ container.name == name
131
+ end
132
+
133
+ if !container
134
+ @global_logger.error("Invalid --skip container :#{name} for configuration :#{configuration.name}")
135
+ @global_logger.info("Available containers:")
136
+
137
+ configuration.containers.sort_by(&:name).each do |container|
138
+ @global_logger.info(" - #{container.name}")
139
+ end
140
+
141
+ exit 1
142
+ end
143
+ end
144
+
145
+ deploy_containers.each do |name|
146
+ container = configuration.containers.detect do |container|
147
+ container.name == name
148
+ end
149
+
150
+ if container
151
+ containers.push(container)
152
+ else
153
+ @global_logger.error("container :#{name} was not found in configuration :#{configuration.name}")
154
+
155
+ exit 1
156
+ end
157
+ end
158
+
159
+ if deploy_tags.empty? && deploy_containers.empty?
160
+ containers = configuration.containers.select do |container|
161
+ configuration.enabled_containers.include?(container.name)
162
+ end
163
+ end
164
+
165
+ if !skip_dependent
166
+ containers = collect_dependent_containers(containers)
167
+ end
168
+
169
+ if !skip_dependent
170
+ containers = collect_soft_dependent_containers(containers, configuration)
171
+ end
172
+
173
+ extra_containers = containers.map(&:name) - configuration.enabled_containers
174
+
175
+ if !extra_containers.empty?
176
+ @global_logger.warn("configuration :#{configuration.name} does not include following containers: #{extra_containers.inspect}")
177
+ @global_logger.warn("they will be skipped during deployment")
178
+ end
179
+
180
+ containers = containers
181
+ .select { |container| configuration.container_enabled?(container) }
182
+ .select { |container| !skip_containers.include?(container.name) }
183
+ .select { |container|
184
+ (skip_tags & container.tags).empty?
185
+ }
186
+
187
+ if !servers.empty?
188
+ containers = containers.select {|c| !(c.servers.map(&:name) & servers).empty? }
189
+ end
190
+
191
+ if containers.empty?
192
+ @global_logger.error("at least one container should be specified for deployment")
193
+ exit 1
194
+ else
195
+ @global_logger.info("Following containers will be deployed:")
196
+
197
+ if servers.empty?
198
+ servers = containers.map(&:servers).flatten.uniq.map(&:name)
199
+ end
200
+
201
+ servers.each do |server_name|
202
+ @global_logger.info("")
203
+ @global_logger.info("List of containers for server #{server_name.to_s.yellow}")
204
+
205
+ containers.each do |container|
206
+ if container.servers.map(&:name).include?(server_name)
207
+ scale = container.get_start_option(:scale)
208
+ scale_msg = scale > 1 ? " (#{container.get_start_option(:scale)})" : ''
209
+ @global_logger.info(" - #{container.name}#{scale_msg}")
210
+ end
211
+ end
212
+ end
213
+
214
+ if (require_confirmation || configuration.confirm_deployment) && !auto_confirm
215
+ @global_logger.info("\n")
216
+ @global_logger.info("Do you want to continue deployment? (y or n)")
217
+ result = gets.chomp
218
+
219
+ if result.downcase != 'y'
220
+ @global_logger.info("Deployment aborted")
221
+ exit 0
222
+ end
223
+ end
224
+
225
+ containers
226
+ end
227
+ end
228
+
229
+ def collect_dependent_containers(containers)
230
+ result = containers
231
+
232
+ result += containers.map do |container|
233
+ collect_dependent_containers(container.dependent_containers)
234
+ end.flatten
235
+
236
+ result.uniq
237
+ end
238
+
239
+ def collect_soft_dependent_containers(containers, configuration)
240
+ result = containers
241
+
242
+ result += containers.map do |container|
243
+ container.soft_dependent_containers.reject do |soft_dependent_container|
244
+ configuration.enabled_containers.include?(soft_dependent_container.name) &&
245
+ Indocker.launched?(soft_dependent_container.name)
246
+ end
247
+ end.flatten
248
+
249
+
250
+ result.uniq
251
+ end
252
+
253
+ def compile_image(configuration, image, build_context)
254
+ return if build_context.image_compiled?(image)
255
+
256
+ image.dependent_images.each do |dependent_image|
257
+ next if build_context.image_compiled?(dependent_image)
258
+ compile_image(configuration, dependent_image, build_context)
259
+ end
260
+
261
+ compiler = Indocker::Images::ImageCompiler.new
262
+
263
+ @logger.info("Image compilation started #{image.name.to_s.green}")
264
+
265
+ result = nil
266
+
267
+ time = Benchmark.realtime do
268
+ result = build_context
269
+ .session
270
+ .exec!(
271
+ "cd #{Indocker::IndockerHelper.indocker_dir} && ./bin/remote/compile -C #{Indocker.configuration_name} -i #{image.name} -s #{@logger.debug? ? '-d' : ''}"
272
+ )
273
+ end
274
+
275
+ Indocker::SshResultLogger
276
+ .new(@logger)
277
+ .log(result, "#{image.name.to_s.green} image compilation failed")
278
+
279
+ exit 1 if result.exit_code != 0
280
+
281
+ @logger.info("Image compilation completed #{image.name.to_s.green}. Time taken: #{time}")
282
+
283
+ build_context.set_compiled(image)
284
+ end
285
+
286
+ def recursively_deploy_container(configuration, deployer, build_context_pool, container, containers, skip_build, force_restart, skip_force_restart)
287
+ container.dependent_containers.each do |container|
288
+ recursively_deploy_container(configuration, deployer, build_context_pool, container, containers, skip_build, force_restart, skip_force_restart)
289
+ end
290
+
291
+ return if !containers.include?(container)
292
+
293
+ @progress.start_building_container(container)
294
+
295
+ if !skip_build
296
+ build_context = build_context_pool.get
297
+
298
+ build_context.set_busy(true)
299
+ compile_image(configuration, container.image, build_context)
300
+ build_context.set_busy(false)
301
+ end
302
+
303
+ @progress.finish_building_container(container)
304
+
305
+ deployer.deploy(container, force_restart, skip_force_restart, @progress)
306
+ end
307
+
308
+ class RemoteOperation
309
+ attr_reader :thread, :server, :operation, :message
310
+
311
+ def initialize(thread, server, operation, message = nil)
312
+ @thread = thread
313
+ @server = server
314
+ @operation = operation
315
+ @message = message
316
+ end
317
+ end
318
+
319
+ def pull_repositories(clonner, servers, repositories)
320
+ @logger.info("{timestamp}")
321
+ @logger.info("Clonning/pulling repositories")
322
+
323
+ remote_operations = []
324
+
325
+ servers.each do |server|
326
+ remote_operations += repositories.map do |alias_name, repository|
327
+ @progress.start_syncing_repository(server, alias_name)
328
+
329
+ thread = Thread.new do
330
+ session = Indocker::SshSession.new(
331
+ host: server.host,
332
+ user: server.user,
333
+ port: server.port,
334
+ logger: @logger
335
+ )
336
+
337
+ if repository.is_local?
338
+ @logger.info("Rsyncing repository :#{alias_name} from #{repository.root_path} to #{server.user}@#{server.host}:#{repository.clone_path}")
339
+
340
+ Indocker::Rsync.sync(
341
+ session,
342
+ File.join(repository.root_path, '.'),
343
+ repository.clone_path,
344
+ create_path: repository.clone_path,
345
+ raise_on_error: true
346
+ )
347
+ elsif repository.is_git?
348
+ @logger.info("Pulling repository #{alias_name.to_s.green} for #{server.user}@#{server.host}")
349
+ result = clonner.clone(session, repository)
350
+
351
+ if result.exit_code != 0
352
+ @logger.error("Repository :#{repository.name} was not clonned")
353
+ @logger.error(result.stderr_data)
354
+ exit 1
355
+ end
356
+ elsif repository.is_no_sync?
357
+ @logger.info("Skipping pull/sync operation for no_sync repository :#{alias_name} #{repository.clone_path}")
358
+ else
359
+ raise NotImplementedError.new("unsupported repository type: #{repository.inspect}")
360
+ end
361
+
362
+ @progress.finish_syncing_repository(server, alias_name)
363
+ end
364
+
365
+ RemoteOperation.new(thread, server, :repository_pull)
366
+ end
367
+ end
368
+
369
+ remote_operations
370
+ end
371
+
372
+ def sync_artifacts(clonner, artifact_servers)
373
+ @logger.info("{timestamp}")
374
+ @logger.info("Syncing git artifacts")
375
+
376
+ remote_operations = []
377
+
378
+ artifact_servers.each do |artifact, servers|
379
+ remote_operations += servers.map do |server|
380
+ @progress.start_syncing_artifact(server, artifact)
381
+
382
+ thread = Thread.new do
383
+ session = Indocker::SshSession.new(
384
+ host: server.host,
385
+ user: server.user,
386
+ port: server.port,
387
+ logger: @logger
388
+ )
389
+
390
+ @logger.info("Pulling git artifact #{artifact.name.to_s.green} for #{server.user}@#{server.host}")
391
+ result = clonner.clone(session, artifact.repository)
392
+
393
+ if result.exit_code != 0
394
+ @logger.error("Artifact repository :#{artifact.repository.name} was not clonned")
395
+ @logger.error(result.stderr_data)
396
+ exit 1
397
+ end
398
+
399
+ source_path = File.join(artifact.repository.clone_path, artifact.source_path)
400
+ result = session.exec!("mkdir -p #{artifact.target_path}")
401
+ result = session.exec!("cp -r #{source_path} #{artifact.target_path}")
402
+
403
+ if !result.success?
404
+ @logger.error(result.stdout_data)
405
+ @logger.error(result.stderr_data)
406
+ exit 1
407
+ end
408
+
409
+ @progress.finish_syncing_artifact(server, artifact)
410
+ end
411
+
412
+ RemoteOperation.new(thread, server, :artifact_sync)
413
+ end
414
+ end
415
+
416
+ remote_operations
417
+ end
418
+
419
+ def update_crontab_redeploy_rules(configuration, server)
420
+ redeploy_containers = configuration.containers.select {|c| c.redeploy_schedule}.uniq
421
+ return if redeploy_containers.empty?
422
+
423
+ @logger.info("{timestamp}")
424
+
425
+ deploy_user = "#{server.user}@#{server.host}"
426
+ crontab_filepath = Indocker.redeploy_crontab_path
427
+
428
+ crontab = Indocker::CrontabRedeployRulesBuilder
429
+ .new(
430
+ configuration: configuration,
431
+ logger: @logger,
432
+ )
433
+ .call(redeploy_containers)
434
+
435
+ tmp_crontab_file = Tempfile.new('crontab')
436
+ tmp_crontab_file.write(crontab)
437
+ tmp_crontab_file.close
438
+
439
+ @logger.info("Updating crontab file #{deploy_user}:#{crontab_filepath}")
440
+
441
+ Indocker::Shell.command("scp #{tmp_crontab_file.path} #{deploy_user}:#{crontab_filepath}", @logger)
442
+
443
+ tmp_crontab_file.unlink
444
+
445
+ Indocker::Shell.command("ssh #{deploy_user} 'crontab #{crontab_filepath}'", @logger)
446
+ end
447
+
448
+ def sync_indocker(servers)
449
+ @logger.info("{timestamp}")
450
+
451
+ servers.map do |server|
452
+ @progress.start_syncing_binaries(server)
453
+
454
+ thread = Thread.new do
455
+ session = Indocker::SshSession.new(
456
+ host: server.host,
457
+ user: server.user,
458
+ port: server.port,
459
+ logger: @logger
460
+ )
461
+
462
+ sync_path = Indocker::IndockerHelper.indocker_dir
463
+ @logger.info("Syncing indocker to #{server.user}@#{server.host}:#{sync_path}")
464
+
465
+ Indocker::Rsync.sync(
466
+ session,
467
+ File.join(Indocker.root_dir, '.'),
468
+ sync_path,
469
+ create_path: sync_path,
470
+ raise_on_error: true,
471
+ )
472
+
473
+ @progress.finish_syncing_binaries(server)
474
+ end
475
+
476
+ RemoteOperation.new(thread, server, :indocker_sync)
477
+ end
478
+ end
479
+
480
+ def sync_env_files(servers, env_files)
481
+ @logger.info("{timestamp}")
482
+
483
+ remote_operations = []
484
+
485
+ servers.map do |server|
486
+ remote_operations += env_files.map do |alias_name, env_file|
487
+ @progress.start_syncing_env_file(server, alias_name)
488
+
489
+ thread = Thread.new do
490
+ if env_file.is_a?(Indocker::EnvFiles::Local)
491
+ sync_path = File.join(Indocker.deploy_dir, 'env_files', File.basename(env_file.path))
492
+ @logger.info("Syncing env file :#{alias_name} from #{env_file.path} to #{server.user}@#{server.host}:#{sync_path}")
493
+
494
+ session = Indocker::SshSession.new(
495
+ host: server.host,
496
+ user: server.user,
497
+ port: server.port,
498
+ logger: @logger
499
+ )
500
+
501
+ session.exec!("mkdir -p #{Indocker::EnvFileHelper.folder}")
502
+
503
+ Indocker::Rsync.sync(
504
+ session,
505
+ env_file.path,
506
+ sync_path,
507
+ raise_on_error: true
508
+ )
509
+ elsif env_file.is_a?(Indocker::EnvFiles::Remote)
510
+ @logger.warn("Sync operation for remote env file :#{alias_name} is skipped")
511
+ else
512
+ @logger.error("unsupported env file type: #{env_file.inspect}")
513
+ raise "unsupported env file type: #{env_file.inspect}"
514
+ end
515
+
516
+ @progress.finish_syncing_env_file(server, alias_name)
517
+ end
518
+
519
+ RemoteOperation.new(thread, server, :env_file_sync)
520
+ end
521
+ end
522
+
523
+ remote_operations
524
+ end
525
+ end