swarm_orca 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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