indocker 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) 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 +17 -49
  7. data/LICENSE.txt +17 -18
  8. data/README.md +33 -1
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/indocker.gemspec +26 -25
  13. data/lib/indocker.rb +429 -130
  14. data/lib/indocker/artifacts/git.rb +25 -0
  15. data/lib/indocker/build_context.rb +87 -0
  16. data/lib/indocker/build_context_helper.rb +109 -0
  17. data/lib/indocker/build_context_pool.rb +36 -0
  18. data/lib/indocker/build_server.rb +2 -0
  19. data/lib/indocker/colored_string.rb +33 -0
  20. data/lib/indocker/concerns/inspectable.rb +12 -0
  21. data/lib/indocker/configuration_deployer.rb +525 -0
  22. data/lib/indocker/configurations/configuration.rb +122 -0
  23. data/lib/indocker/configurations/configuration_builder.rb +250 -0
  24. data/lib/indocker/configurations/formatters/stdout.rb +36 -0
  25. data/lib/indocker/container_deployer.rb +73 -0
  26. data/lib/indocker/container_helper.rb +13 -0
  27. data/lib/indocker/container_runner.rb +23 -0
  28. data/lib/indocker/containers/container.rb +118 -0
  29. data/lib/indocker/containers/container_builder.rb +186 -0
  30. data/lib/indocker/containers/restart_policy.rb +36 -0
  31. data/lib/indocker/context_args.rb +44 -0
  32. data/lib/indocker/crontab_redeploy_rules_builder.rb +54 -0
  33. data/lib/indocker/deploy_context.rb +126 -0
  34. data/lib/indocker/deployment_checker.rb +121 -0
  35. data/lib/indocker/deployment_progress.rb +495 -0
  36. data/lib/indocker/docker.rb +87 -0
  37. data/lib/indocker/docker_run_args.rb +175 -0
  38. data/lib/indocker/env_file_helper.rb +13 -0
  39. data/lib/indocker/env_files/local.rb +8 -0
  40. data/lib/indocker/env_files/remote.rb +8 -0
  41. data/lib/indocker/hash_merger.rb +9 -0
  42. data/lib/indocker/images/image.rb +86 -0
  43. data/lib/indocker/images/image_builder.rb +98 -0
  44. data/lib/indocker/images/image_compiler.rb +68 -0
  45. data/lib/indocker/images/template_compiler.rb +15 -0
  46. data/lib/indocker/images/templates_compiler.rb +28 -0
  47. data/lib/indocker/images_compiler.rb +38 -0
  48. data/lib/indocker/indocker_helper.rb +9 -0
  49. data/lib/indocker/network.rb +12 -0
  50. data/lib/indocker/network_helper.rb +7 -0
  51. data/lib/indocker/registries/abstract.rb +17 -0
  52. data/lib/indocker/registries/local.rb +5 -0
  53. data/lib/indocker/registries/remote.rb +12 -0
  54. data/lib/indocker/repositories/abstract.rb +25 -0
  55. data/lib/indocker/repositories/clonner.rb +15 -0
  56. data/lib/indocker/repositories/git.rb +21 -0
  57. data/lib/indocker/repositories/local.rb +20 -0
  58. data/lib/indocker/repositories/no_sync.rb +20 -0
  59. data/lib/indocker/rsync.rb +52 -0
  60. data/lib/indocker/server.rb +20 -0
  61. data/lib/indocker/server_pool.rb +30 -0
  62. data/lib/indocker/shell.rb +60 -0
  63. data/lib/indocker/ssh_result_logger.rb +18 -0
  64. data/lib/indocker/ssh_session.rb +92 -0
  65. data/lib/indocker/version.rb +1 -1
  66. data/lib/indocker/volume_helper.rb +7 -0
  67. data/lib/indocker/volumes/external.rb +8 -0
  68. data/lib/indocker/volumes/local.rb +9 -0
  69. data/lib/indocker/volumes/repository.rb +9 -0
  70. metadata +88 -230
  71. data/bin/indocker +0 -9
  72. data/lib/indocker/application_initializer.rb +0 -19
  73. data/lib/indocker/cli.rb +0 -27
  74. data/lib/indocker/configs/config.rb +0 -147
  75. data/lib/indocker/configs/config_factory.rb +0 -37
  76. data/lib/indocker/configs/config_initializer.rb +0 -9
  77. data/lib/indocker/configs/locator.rb +0 -23
  78. data/lib/indocker/container/container_builder.rb +0 -29
  79. data/lib/indocker/container/container_directives_runner.rb +0 -54
  80. data/lib/indocker/container/container_dsl.rb +0 -83
  81. data/lib/indocker/container/container_evaluator.rb +0 -13
  82. data/lib/indocker/container/container_inspector.rb +0 -26
  83. data/lib/indocker/container/container_manager.rb +0 -122
  84. data/lib/indocker/container/container_metadata.rb +0 -112
  85. data/lib/indocker/container/container_metadata_factory.rb +0 -30
  86. data/lib/indocker/container/container_metadata_repository.rb +0 -27
  87. data/lib/indocker/directives/base.rb +0 -17
  88. data/lib/indocker/directives/container_directives/base.rb +0 -23
  89. data/lib/indocker/directives/container_directives/cmd.rb +0 -7
  90. data/lib/indocker/directives/container_directives/depends_on.rb +0 -7
  91. data/lib/indocker/directives/container_directives/env.rb +0 -7
  92. data/lib/indocker/directives/container_directives/env_file.rb +0 -7
  93. data/lib/indocker/directives/container_directives/expose.rb +0 -7
  94. data/lib/indocker/directives/container_directives/from.rb +0 -14
  95. data/lib/indocker/directives/container_directives/network.rb +0 -12
  96. data/lib/indocker/directives/container_directives/ports.rb +0 -15
  97. data/lib/indocker/directives/container_directives/ready.rb +0 -16
  98. data/lib/indocker/directives/container_directives/volume.rb +0 -23
  99. data/lib/indocker/directives/image_directives/base.rb +0 -27
  100. data/lib/indocker/directives/image_directives/cmd.rb +0 -19
  101. data/lib/indocker/directives/image_directives/copy.rb +0 -32
  102. data/lib/indocker/directives/image_directives/docker_cp.rb +0 -20
  103. data/lib/indocker/directives/image_directives/entrypoint.rb +0 -19
  104. data/lib/indocker/directives/image_directives/env.rb +0 -9
  105. data/lib/indocker/directives/image_directives/env_file.rb +0 -19
  106. data/lib/indocker/directives/image_directives/expose.rb +0 -9
  107. data/lib/indocker/directives/image_directives/from.rb +0 -36
  108. data/lib/indocker/directives/image_directives/registry.rb +0 -30
  109. data/lib/indocker/directives/image_directives/run.rb +0 -17
  110. data/lib/indocker/directives/image_directives/workdir.rb +0 -9
  111. data/lib/indocker/directives/partial.rb +0 -21
  112. data/lib/indocker/docker_api/container_config.rb +0 -121
  113. data/lib/indocker/docker_api/docker_api.rb +0 -207
  114. data/lib/indocker/dsl_context.rb +0 -21
  115. data/lib/indocker/envs/env_metadata.rb +0 -39
  116. data/lib/indocker/envs/loader.rb +0 -19
  117. data/lib/indocker/errors.rb +0 -21
  118. data/lib/indocker/git/git_api.rb +0 -32
  119. data/lib/indocker/git/git_helper.rb +0 -34
  120. data/lib/indocker/git/git_service.rb +0 -21
  121. data/lib/indocker/handlers/container_run.rb +0 -20
  122. data/lib/indocker/handlers/container_stop.rb +0 -17
  123. data/lib/indocker/handlers/performable.rb +0 -22
  124. data/lib/indocker/image/image_builder.rb +0 -54
  125. data/lib/indocker/image/image_dependencies_manager.rb +0 -47
  126. data/lib/indocker/image/image_directives_runner.rb +0 -99
  127. data/lib/indocker/image/image_dockerfile_builder.rb +0 -24
  128. data/lib/indocker/image/image_dsl.rb +0 -89
  129. data/lib/indocker/image/image_evaluator.rb +0 -21
  130. data/lib/indocker/image/image_helper.rb +0 -9
  131. data/lib/indocker/image/image_metadata.rb +0 -50
  132. data/lib/indocker/image/image_metadata_factory.rb +0 -31
  133. data/lib/indocker/image/image_metadata_repository.rb +0 -29
  134. data/lib/indocker/networks/network_metadata.rb +0 -9
  135. data/lib/indocker/networks/network_metadata_factory.rb +0 -9
  136. data/lib/indocker/networks/network_metadata_repository.rb +0 -34
  137. data/lib/indocker/partial/partial_metadata.rb +0 -8
  138. data/lib/indocker/partial/partial_metadata_repository.rb +0 -26
  139. data/lib/indocker/registry/registry_api.rb +0 -46
  140. data/lib/indocker/registry/registry_helper.rb +0 -20
  141. data/lib/indocker/registry/registry_service.rb +0 -28
  142. data/lib/indocker/utils/ioc_container.rb +0 -17
  143. data/lib/indocker/utils/logger.rb +0 -62
  144. data/lib/indocker/utils/logger_factory.rb +0 -13
  145. data/lib/indocker/utils/registry_authenticator.rb +0 -19
  146. data/lib/indocker/utils/render_namespace.rb +0 -11
  147. data/lib/indocker/utils/render_util.rb +0 -15
  148. data/lib/indocker/utils/string_utils.rb +0 -11
  149. data/lib/indocker/utils/tar_helper.rb +0 -40
  150. data/lib/indocker/utils/test_logger_factory.rb +0 -9
  151. data/lib/indocker/volumes/volume_metadata.rb +0 -9
  152. data/lib/indocker/volumes/volume_metadata_factory.rb +0 -9
  153. data/lib/indocker/volumes/volume_metadata_repository.rb +0 -34
  154. data/spec/example/.indocker/config.rb +0 -30
  155. data/spec/example/.indocker/images_and_containers.rb +0 -25
  156. data/spec/example/assets/index.css +0 -1
  157. data/spec/example/assets/index.js +0 -1
  158. data/spec/fixtures/spec.env +0 -2
  159. data/spec/indocker/configs/config_factory_spec.rb +0 -18
  160. data/spec/indocker/configs/config_spec.rb +0 -88
  161. data/spec/indocker/container/container_builder_spec.rb +0 -67
  162. data/spec/indocker/container/container_manager_spec.rb +0 -278
  163. data/spec/indocker/docker_api/container_config_spec.rb +0 -64
  164. data/spec/indocker/docker_api/docker_api_spec.rb +0 -112
  165. data/spec/indocker/handlers/container_run_spec.rb +0 -60
  166. data/spec/indocker/image/image_builder_spec.rb +0 -153
  167. data/spec/indocker/image/image_directives_runner_spec.rb +0 -141
  168. data/spec/indocker/image/image_dockerfile_builder_spec.rb +0 -25
  169. data/spec/indocker/image/image_evaluator_spec.rb +0 -85
  170. 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 = "~/#{server.user}/crontab"
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