swarm_orca 0.1.1

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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.overcommit.yml +33 -0
  4. data/.rubocop.yml +1 -0
  5. data/.rubocop_todo.yml +17 -0
  6. data/.ruby-gemset +1 -0
  7. data/.ruby-version +1 -0
  8. data/CHANGELOG.md +15 -0
  9. data/Gemfile +5 -0
  10. data/Gemfile.lock +57 -0
  11. data/README.md +335 -0
  12. data/Rakefile +3 -0
  13. data/bin/orca +7 -0
  14. data/lib/capistrano/scm/copy.rb +62 -0
  15. data/lib/capistrano/scm/tasks/copy.cap +33 -0
  16. data/lib/capistrano/scm/tasks/deploy.rb +16 -0
  17. data/lib/capistrano/swarm_orca/bash/crypt +1 -0
  18. data/lib/capistrano/swarm_orca/deploy.rb +3 -0
  19. data/lib/capistrano/swarm_orca/docker.rb +3 -0
  20. data/lib/capistrano/swarm_orca/helpers/fetch_config.rb +134 -0
  21. data/lib/capistrano/swarm_orca/local.rb +43 -0
  22. data/lib/capistrano/swarm_orca/set_global_config.rb +3 -0
  23. data/lib/capistrano/swarm_orca/tasks/deploy.cap +200 -0
  24. data/lib/capistrano/swarm_orca/tasks/docker.cap +318 -0
  25. data/lib/capistrano/swarm_orca/tasks/set_global_config.cap +28 -0
  26. data/lib/swarm_orca.rb +11 -0
  27. data/lib/swarm_orca/encrypt.rb +40 -0
  28. data/lib/swarm_orca/new.rb +73 -0
  29. data/lib/swarm_orca/orca_cli.rb +41 -0
  30. data/lib/swarm_orca/templates/orca/.gitignore +50 -0
  31. data/lib/swarm_orca/templates/orca/.ruby-gemset.tt +1 -0
  32. data/lib/swarm_orca/templates/orca/.ruby-version +1 -0
  33. data/lib/swarm_orca/templates/orca/README.md +93 -0
  34. data/lib/swarm_orca/templates/orca/application_stack/docker-stack-elasticsearch.yml.erb.tt +22 -0
  35. data/lib/swarm_orca/templates/orca/application_stack/docker-stack-errbit.yml.erb.tt +43 -0
  36. data/lib/swarm_orca/templates/orca/application_stack/docker-stack-mysql.yml.erb.tt +17 -0
  37. data/lib/swarm_orca/templates/orca/application_stack/docker-stack-nginx.yml.erb.tt +22 -0
  38. data/lib/swarm_orca/templates/orca/application_stack/docker-stack-rabbitmq.yml.erb.tt +19 -0
  39. data/lib/swarm_orca/templates/orca/application_stack/docker-stack-redis.yml.erb.tt +19 -0
  40. data/lib/swarm_orca/templates/orca/capistrano/Capfile +45 -0
  41. data/lib/swarm_orca/templates/orca/capistrano/Gemfile.tt +4 -0
  42. data/lib/swarm_orca/templates/orca/capistrano/config/deploy.rb.tt +30 -0
  43. data/lib/swarm_orca/templates/orca/capistrano/config/deploy/template_stage.rb +64 -0
  44. data/lib/swarm_orca/templates/orca/nginx/Dockerfile +5 -0
  45. data/lib/swarm_orca/templates/orca/nginx/nginx.conf +31 -0
  46. data/lib/swarm_orca/templates/orca/redis/Dockerfile +5 -0
  47. data/lib/swarm_orca/templates/orca/redis/redis.conf +2 -0
  48. data/lib/swarm_orca/version.rb +5 -0
  49. data/swarm_orca.gemspec +26 -0
  50. metadata +174 -0
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'swarm_orca'
5
+ require 'swarm_orca/orca_cli'
6
+
7
+ SwarmOrca::Cli::OrcaCli.start(ARGV)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capistrano/scm/plugin'
4
+ require 'cgi'
5
+ require 'shellwords'
6
+ require 'uri'
7
+ require_relative 'tasks/deploy.rb'
8
+
9
+ module Capistrano
10
+ class SCM
11
+ # Capistrano Copy stretegy
12
+ class Copy < Capistrano::SCM::Plugin
13
+ def set_defaults
14
+ set_if_empty :app_release_id, Time.now.to_i
15
+ set_if_empty :tar_extension, '.tar.gz'
16
+ set_if_empty :app_source_file_path, "/tmp/s_#{fetch(:app_release_id)}#{fetch(:tar_extension)}"
17
+ set_if_empty :app_destination_file_path, "/tmp/#{fetch(:app_release_id)}#{fetch(:tar_extension)}"
18
+ end
19
+
20
+ def working_dir
21
+ File.dirname(Dir.pwd)
22
+ end
23
+
24
+ def register_hooks
25
+ after 'deploy:new_release_path', 'copy:create_release'
26
+ before 'deploy:set_current_revision', 'copy:set_current_revision'
27
+ end
28
+
29
+ def define_tasks
30
+ eval_rakefile File.expand_path('tasks/copy.cap', __dir__)
31
+ end
32
+
33
+ def tar_exists?
34
+ backend.test " [ -f #{fetch(:app_destination_file_path)} ] "
35
+ end
36
+
37
+ def create_tar
38
+ source_path = Shellwords.escape(working_dir)
39
+ run_locally do
40
+ execute "tar -C #{source_path} -czf #{fetch(:app_source_file_path)} ."
41
+ end
42
+ end
43
+
44
+ def remove_tar
45
+ run_locally do
46
+ execute "rm -f #{fetch(:app_source_file_path)}"
47
+ end
48
+ end
49
+
50
+ def archive_to_release_path
51
+ # Unpack the tar uploaded by deploy:upload_tar task.
52
+ backend.execute "tar -xzmf #{fetch(:app_destination_file_path)} -C #{release_path}"
53
+ # Remove it just to keep things clean.
54
+ backend.execute :rm, fetch(:app_destination_file_path)
55
+ end
56
+
57
+ def fetch_revision
58
+ fetch(:app_release_id)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,33 @@
1
+ copy_plugin = self
2
+
3
+ namespace :copy do
4
+ desc "Clone the repo to the cache"
5
+ task :clone do
6
+ copy_plugin.create_tar
7
+ on release_roles(:all) do |host|
8
+ info "Upload packages to #{host.to_s}"
9
+ upload! fetch(:app_source_file_path), fetch(:app_destination_file_path)
10
+ end
11
+ copy_plugin.remove_tar
12
+ end
13
+
14
+ desc "Copy repo to releases"
15
+ task create_release: :'copy:clone' do
16
+ on release_roles :all do
17
+ if copy_plugin.tar_exists?
18
+ info "Create Release folder"
19
+ execute :mkdir, "-p", release_path
20
+ copy_plugin.archive_to_release_path
21
+ else
22
+ error t(:deploy_failed, ex: "#{fetch(:app_destination_file_path)} does not exists")
23
+ end
24
+ end
25
+ end
26
+
27
+ desc "Determine the revision that will be deployed"
28
+ task :set_current_revision do
29
+ on release_roles :all do
30
+ set :current_revision, copy_plugin.fetch_revision
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :deploy do
4
+ def revision_log_message
5
+ "Copy deploy (at #{fetch(:current_revision)}) deployed as release #{fetch(:release_timestamp)} by #{local_user}"
6
+ end
7
+
8
+ desc 'Log details of the deploy'
9
+ task :log_revision do
10
+ on release_roles(:all) do
11
+ within releases_path do
12
+ execute :echo, %("#{revision_log_message}" >> #{revision_log})
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1 @@
1
+ if [[ -z "$ENCRYPTION_KEY" ]]; then echo "Please define the ENCRYPTION_KEY"; exit 1; fi;function decrypt() { local value="$1";echo "$value" | openssl enc -d -aes-256-cbc -base64 -K "$ENCRYPTION_KEY" -iv 2adae58101d71b14cbfa3bdaf17d26c8; }
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('tasks/deploy.cap', File.dirname(__FILE__))
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('tasks/docker.cap', File.dirname(__FILE__))
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capistrano
4
+ # SwarmOrca helpers
5
+ module SwarmOrca
6
+ def stacks
7
+ fetch(:service_stacks) + fetch(:service_stacks_with_build_image) + application_stacks
8
+ end
9
+
10
+ def all_service_stacks
11
+ fetch(:service_stacks) + fetch(:service_stacks_with_build_image)
12
+ end
13
+
14
+ def db_apps
15
+ fetch(:db_apps_stacks_mapping, {}).values.flatten
16
+ end
17
+
18
+ def elasticsearch_apps
19
+ fetch(:elasticsearch_apps)
20
+ end
21
+
22
+ def docker_command
23
+ "#{fetch(:docker_path)}docker"
24
+ end
25
+
26
+ def application_stacks
27
+ fetch(:db_apps_stacks_mapping, {}).keys.map(&:to_s)
28
+ end
29
+
30
+ def service_stacks_with_build_image
31
+ fetch(:service_stacks_with_build_image)
32
+ end
33
+
34
+ def fetch_config(stack, env_var)
35
+ fetch_application_config(stack)[env_var.to_sym] || ''
36
+ end
37
+
38
+ def fetch_application_config(stack)
39
+ included_shared_config = fetch(stack.to_s.to_sym).fetch(:include_shared_config, '')
40
+ fetch(:shared, {}).merge(fetch(included_shared_config.to_sym, {})).merge(fetch(stack.to_s.to_sym, {}))
41
+ end
42
+
43
+ def fetch_stack_db_apps(stack)
44
+ fetch(:db_apps_stacks_mapping, {})[stack.to_sym] || []
45
+ end
46
+
47
+ def env_vars_command_for_docker(stack)
48
+ decrypt_env(stack).map { |k, _v| "-e #{k.to_s.upcase}=\"$#{k.to_s.upcase}\"" }.join(' ')
49
+ end
50
+
51
+ def env_vars_command(stack)
52
+ export_lines = decrypt_env(stack).map { |k, v| "export #{k.to_s.upcase}=#{v}" }.join(';')
53
+ encrypted_vars?(stack) ? "#{load_encrypt_lib};#{export_lines}" : export_lines
54
+ end
55
+
56
+ def erb_vars_command(stack)
57
+ decrypt_env(stack).map { |k, _v| "#{k.to_s.upcase}='$#{k.to_s.upcase}'" }.join(';')
58
+ end
59
+
60
+ def load_encrypt_lib
61
+ File.read("#{bash_source_root}/crypt").strip
62
+ end
63
+
64
+ def encrypted_vars?(stack)
65
+ !fetch_application_config(stack).select { |k, _| encrypted?(k) }.empty?
66
+ end
67
+
68
+ def decrypt_env(stack)
69
+ encrypted_vars = fetch_application_config(stack).select { |k, _| encrypted?(k) }
70
+ unencrypted_vars = fetch_application_config(stack).reject { |k, _| encrypted?(k) }
71
+ unencrypted_vars.transform_values! { |v| Shellwords.escape(v) }
72
+ configs = unencrypted_vars.merge(encrypted_vars.transform_values { |v| decrypt_var(v) })
73
+ configs.transform_keys { |k| k.to_s.sub(/^#{encrypted_prefix}/, '') }
74
+ end
75
+
76
+ def decrypt_var(var)
77
+ "$(decrypt '#{var}')"
78
+ end
79
+
80
+ def docker_erb_templates
81
+ fetch(:docker_erb_templates)
82
+ end
83
+
84
+ def docker_cleanup
85
+ fetch(:docker_cleanup)
86
+ end
87
+
88
+ def build_docker_images
89
+ fetch(:auto_image_build)
90
+ end
91
+
92
+ def encrypted_prefix
93
+ 'encrypted_'
94
+ end
95
+
96
+ def encrypted?(key)
97
+ key.to_s.start_with?(encrypted_prefix)
98
+ end
99
+
100
+ def bash_source_root
101
+ "#{File.dirname(__FILE__)}/../bash"
102
+ end
103
+
104
+ def upload_script(shell_command, description)
105
+ filename = capture("mktemp /tmp/#{script_prefix}#{description}_XXXXXXX")
106
+ upload! StringIO.new(shell_command), filename
107
+ filename
108
+ end
109
+
110
+ def script_prefix
111
+ 'ORCA_SSH_SCRIPT_'
112
+ end
113
+
114
+ def debug_mode?
115
+ %(true yes 1).include? ENV.fetch('ORCA_DEBUG', 'false')
116
+ end
117
+
118
+ def show_debug_messages(host, shell_command, description)
119
+ $stdout.puts <<~MESSAGE
120
+
121
+ \e[33mExecuting: #{description} ON #{host}
122
+ Bash Script:\n#{shell_command}\e[0m
123
+ MESSAGE
124
+ end
125
+
126
+ def execute_orca_script(host, shell_command, description)
127
+ show_debug_messages(host, shell_command, description) if debug_mode?
128
+ with ENCRYPTION_KEY: ENV.fetch('ENCRYPTION_KEY', nil) do
129
+ execute :bash, upload_script(shell_command, description)
130
+ end
131
+ execute :rm, "/tmp/#{script_prefix}*"
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+ require 'sshkit'
5
+ require 'capistrano/dsl'
6
+
7
+ module Capistrano
8
+ # Capistrano Local
9
+ module Local
10
+ end
11
+ end
12
+
13
+ module Capistrano
14
+ # Capistrano DSL
15
+ module DSL
16
+ alias original_on on
17
+
18
+ def on(hosts, options = {}, &block)
19
+ return unless hosts
20
+
21
+ localhosts, remotehosts = Array(hosts).partition { |h| h.hostname.to_s =~ /local|127.0.0.1/ }
22
+ localhost = Configuration.env.filter(localhosts).first
23
+
24
+ ssh_backend.new(localhost, &block).run unless localhost.nil?
25
+
26
+ original_on(remotehosts, options, &block)
27
+ end
28
+
29
+ private
30
+
31
+ def ssh_backend
32
+ if dry_run?
33
+ SSHKit::Backend::Printer
34
+ else
35
+ SSHKit::Backend::Local
36
+ end
37
+ end
38
+
39
+ def dry_run?
40
+ fetch(:sshkit_backend) == SSHKit::Backend::Printer
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ load File.expand_path('tasks/set_global_config.cap', File.dirname(__FILE__))
@@ -0,0 +1,200 @@
1
+ namespace :deploy do
2
+ include Capistrano::SwarmOrca
3
+
4
+ namespace :stop do
5
+ desc "Stop all stacks"
6
+ task :all do
7
+ stacks.each do |stack|
8
+ invoke "deploy:stop:#{stack}"
9
+ end
10
+ end
11
+
12
+ stacks.each do |stack|
13
+ desc "Stop #{stack}"
14
+ task stack do
15
+ invoke "docker:deploy:stop_#{stack}"
16
+ end
17
+ end
18
+ end
19
+
20
+ namespace :scale do
21
+ desc "scale all stacks to 0 instance"
22
+ task :all_zero do
23
+ stacks.each do |stack|
24
+ invoke "docker:deploy:scale_down_#{stack}"
25
+ end
26
+ end
27
+
28
+ desc "scale all stacks to 1 instance"
29
+ task :all_one do
30
+ stacks.each do |stack|
31
+ invoke "docker:deploy:scale_up_#{stack}"
32
+ end
33
+ end
34
+ end
35
+
36
+ namespace :start do
37
+ desc "Start all stacks"
38
+ task :all do
39
+ stacks.each do |stack|
40
+ invoke "deploy:start:#{stack}"
41
+ end
42
+ end
43
+
44
+ stacks.each do |stack|
45
+ desc "Start #{stack}"
46
+ task stack do
47
+ invoke "docker:deploy:#{stack}"
48
+ end
49
+ end
50
+ end
51
+
52
+ stacks.each do |stack|
53
+ desc "Deploy #{stack}"
54
+ task stack do
55
+ pre_stack_specific_tasks stack if build_docker_images
56
+ invoke "docker:pull:#{stack}"
57
+ post_stack_specific_tasks stack
58
+ invoke "docker:deploy:#{stack}"
59
+ end
60
+ end
61
+
62
+ desc "Deploy all stacks and services"
63
+ task :all do
64
+ invoke "docker:deploy:cleanup" if docker_cleanup
65
+ stacks.each do |stack|
66
+ invoke "deploy:#{stack}"
67
+ end
68
+ end
69
+
70
+ desc "Deploy all stacks Databases"
71
+ task :all_dbs do
72
+ application_stacks.each do |stack|
73
+ invoke "deploy:create_#{stack}_dbs"
74
+ invoke "deploy:seed_#{stack}_dbs"
75
+ end
76
+ end
77
+
78
+
79
+ desc "Build all docker custom docker images"
80
+ task :build_images do
81
+ stacks.each do |stack|
82
+ pre_stack_specific_tasks stack
83
+ end
84
+ end
85
+
86
+ desc "Seed all stacks Databases defined in SEEDED_STACKS ENV var"
87
+ task :seed_auto do
88
+ seeded_stacks = ENV.fetch('SEEDED_STACKS', '').split(' ') & application_stacks
89
+ seeded_stacks.each do |stack|
90
+ invoke "deploy:seed_#{stack}_dbs"
91
+ end
92
+ end
93
+
94
+ desc "Recreate all Databases"
95
+ task :recreate_all_dbs do
96
+ application_stacks.each do |stack|
97
+ invoke "deploy:drop_#{stack}_dbs"
98
+ invoke "deploy:create_#{stack}_dbs"
99
+ end
100
+ end
101
+
102
+ desc "Deploy all stacks and services defined in DEPLOYED_STACKS ENV var"
103
+ task :auto do
104
+ invoke "deploy:setup"
105
+ invoke "docker:deploy:cleanup" if docker_cleanup
106
+ deployed_stacks = ENV.fetch('DEPLOYED_STACKS', stacks.join(' ')).split(' ') & stacks
107
+ deployed_stacks.each do |stack|
108
+ invoke "deploy:#{stack}"
109
+ end
110
+ end
111
+
112
+ def pre_stack_specific_tasks(stack)
113
+ task = "docker:deploy:build_#{stack}"
114
+ invoke task if Rake::Task.task_defined?(task)
115
+ end
116
+
117
+ def post_stack_specific_tasks(stack)
118
+ stack_migrate_apps(stack) if application_stacks.include? stack
119
+ stack_reindex_apps(stack) if stack.eql? 'community'
120
+ end
121
+
122
+ def stack_migrate_apps(stack)
123
+ Rake::Task["deploy:migrate_#{stack}_dbs"].invoke(stack)
124
+ end
125
+
126
+ def stack_seed_apps(stack)
127
+ Rake::Task["deploy:seed_#{stack}_dbs"].invoke(stack)
128
+ end
129
+
130
+ def stack_reindex_apps(stack)
131
+ fetch_config(stack, 'reindex_apps').split.each do |app|
132
+ Rake::Task["docker:deploy:reindex_elasticsearch_#{app}"].invoke(stack)
133
+ end
134
+ end
135
+
136
+ application_stacks.each do |stack|
137
+ desc "Create #{stack} Databases"
138
+ task "create_#{stack}_dbs".to_sym do
139
+ fetch_stack_db_apps(stack).each do |app|
140
+ Rake::Task["docker:deploy:create_#{app}_db"].invoke(stack)
141
+ end
142
+ end
143
+ end
144
+
145
+ application_stacks.each do |stack|
146
+ fetch_stack_db_apps(stack).each do |app|
147
+ desc "Execute #{app} one Time Data Migration"
148
+ task "otm_#{app}".to_sym, :task_name do |t, args|
149
+ raise "Please provide migration name. ie. deploy:otm_#{app}[MIGRATION]" unless args.key?(:task_name)
150
+ Rake::Task["docker:deploy:otm_#{app}"].invoke(stack, args[:task_name])
151
+ end
152
+ end
153
+ end
154
+
155
+ application_stacks.each do |stack|
156
+ desc "Drop #{stack} Databases"
157
+ task "drop_#{stack}_dbs".to_sym do
158
+ fetch_stack_db_apps(stack).each do |app|
159
+ Rake::Task["docker:deploy:drop_#{app}_db"].invoke(stack)
160
+ end
161
+ end
162
+ end
163
+
164
+ application_stacks.each do |stack|
165
+ desc "Seed #{stack} Databases"
166
+ task "seed_#{stack}_dbs".to_sym do
167
+ fetch_stack_db_apps(stack).each do |app|
168
+ Rake::Task["docker:deploy:seed_#{app}_db"].invoke(stack)
169
+ end
170
+ end
171
+ end
172
+
173
+ application_stacks.each do |stack|
174
+ desc "Migrate #{stack} Databases"
175
+ task "migrate_#{stack}_dbs".to_sym do
176
+ fetch_stack_db_apps(stack).each do |app|
177
+ Rake::Task["docker:deploy:migrate_#{app}"].invoke(stack)
178
+ end
179
+ end
180
+ end
181
+
182
+ desc "Setup Deployment Project"
183
+ task :setup do
184
+ invoke "deploy"
185
+ invoke "docker:deploy:create_network"
186
+ end
187
+
188
+ desc "Setup Deployment Project"
189
+ task :development_setup do
190
+ invoke "deploy"
191
+ invoke "docker:deploy:create_network"
192
+ all_service_stacks.each do |stack|
193
+ invoke "deploy:#{stack}"
194
+ end
195
+ invoke "deploy:all_dbs"
196
+ application_stacks.each do |stack|
197
+ invoke "deploy:#{stack}"
198
+ end
199
+ end
200
+ end