stackose 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 78e1ae2368a649e93bc733155190ea791acd36ec
4
+ data.tar.gz: d3e0aec71ec3afbcce1b78b4cbe3c2412d25ae87
5
+ SHA512:
6
+ metadata.gz: 0c75721a352ae19820b45e462dbff3c93f17e5f9eba8d79db22245946c222e70728855b428023f16150de96a6e855fba4747b254e3cd824dd3f245c6fe6bd488
7
+ data.tar.gz: c6358e7705080b4e46d16aa8eea19371851a04332e5ac6576155f68b6c059eedca22d8fc50b419301e2e046b4c3641c10b9c5a4be3054683b698f7434471a0f3
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+
3
+ \.idea/
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in capistrano.gemspec
4
+ gemspec
5
+
6
+ gem "rspec"
7
+ gem "rspec-core"
8
+ gem "pry"
data/Gemfile.lock ADDED
@@ -0,0 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ capose (0.1.0)
5
+ capistrano (>= 3.7)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ airbrussh (1.1.2)
11
+ sshkit (>= 1.6.1, != 1.7.0)
12
+ capistrano (3.7.2)
13
+ airbrussh (>= 1.0.0)
14
+ capistrano-harrow
15
+ i18n
16
+ rake (>= 10.0.0)
17
+ sshkit (>= 1.9.0)
18
+ capistrano-harrow (0.5.3)
19
+ coderay (1.1.1)
20
+ diff-lcs (1.3)
21
+ i18n (0.8.0)
22
+ method_source (0.8.2)
23
+ net-scp (1.2.1)
24
+ net-ssh (>= 2.6.5)
25
+ net-ssh (4.0.1)
26
+ pry (0.10.4)
27
+ coderay (~> 1.1.0)
28
+ method_source (~> 0.8.1)
29
+ slop (~> 3.4)
30
+ rake (12.0.0)
31
+ rspec (3.5.0)
32
+ rspec-core (~> 3.5.0)
33
+ rspec-expectations (~> 3.5.0)
34
+ rspec-mocks (~> 3.5.0)
35
+ rspec-core (3.5.4)
36
+ rspec-support (~> 3.5.0)
37
+ rspec-expectations (3.5.0)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.5.0)
40
+ rspec-mocks (3.5.0)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.5.0)
43
+ rspec-support (3.5.0)
44
+ slop (3.6.0)
45
+ sshkit (1.11.5)
46
+ net-scp (>= 1.1.2)
47
+ net-ssh (>= 2.8.0)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ bundler (~> 1.4)
54
+ capose!
55
+ pry
56
+ rake
57
+ rspec
58
+ rspec-core
59
+
60
+ BUNDLED WITH
61
+ 1.12.5
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # Stackose - A capistrano addon to use with docker stack [WIP]
2
+
3
+ This gem is a refactoring to use the [capose](https://github.com/netguru/capose) gem, but with docker stack implementation.
4
+ **The Documentation is owned from capose, and not so far complete for the docker stack, must be rewritten and cleared**
5
+
6
+ This gem is a lighter version of docker-compose strategy found in [capistrano-docker](https://github.com/netguru/capistrano-docker) gem.
7
+ The idea is to be much simplier and more custom to use.
8
+
9
+
10
+ ### Installation
11
+
12
+ 1. Ensure you already have `capistrano` gem in your project, with version at least `3.7`
13
+ 2. Add the following line to your `Gemfile`: `gem 'stackose', require: false`
14
+ 3. Add the following file to `Capfile`: `require 'stackose'`
15
+
16
+ This gem will automatically hook up to the capistrano after `deploy:updated` hook to perform the deployment via `stackose:deploy` hook
17
+
18
+ ### Changelog
19
+
20
+
21
+ ### Defaults
22
+
23
+ This gem uses couple variables which can be modified inside your deploy scripts, these are:
24
+
25
+ set :stackose_role - capistrano role for stackose, defaults to :web
26
+ set :stackose_copy - list of files/dirs to be copied from shared_path before first command (replaces :link_dirs/files), defaults to []
27
+ set :stackose_project - docker-compose project name, defaults to fetch(:application)
28
+ set :stackose_file - list of files for the docker-compose -f parameter (defauls to ["docker-compose.yml", "docker-compose-#{fetch(:stage)}.yml"]) and finaly the generated docker-compose-override
29
+ set :stackose_commands - list of commands to be run with docker-compose, defaults to []
30
+ set :stackose_docker_mount_point - mount point inside the application container, defaults to "/usr/share/www/"
31
+ set :stackose_linked_folders - list of folders to link inside de image
32
+ set :stackose_service_to_build - name of the service that contains the application to run (default: app)
33
+
34
+ ### Folders to be linked
35
+ If you need to link shared folders to the root of your application path, like capistrano standard linked_folders do,
36
+ you can setup this like:
37
+
38
+ set :capose_linked_folders, [ 'tmp/pids',
39
+ 'tmp/cache',
40
+ 'tmp/sockets',
41
+ 'public/system',
42
+ 'public/pictures',
43
+ 'public/attachments',
44
+ 'public/pages',
45
+ 'public/assets',
46
+ 'uploads']
47
+
48
+ For custom linking you can provide inside the array a hash with key->value of source->destination
49
+
50
+ set :capose_linked_folders, [
51
+ 'tmp/pids',
52
+ 'tmp/cache',
53
+ ....,
54
+ 'uploads',
55
+ "/custom_path/#{fetch(:application)}/":"#{fetch(:capose_docker_mount_point)}/log",
56
+ ]
57
+
58
+ if you need the shared_path url in the key you can set `__shared_path__` as placeholder
59
+
60
+
61
+
62
+ ### Additional files before first command
63
+ If you need to throw in to the "release" folder couple files (say secret files) before image is build, you can use `capose_copy` for that, ex:
64
+
65
+ set :capose_copy, %w(config/secrets.yml certs/google.crt certs/google.key)
66
+
67
+ This command will copy the files from shared/ path before first command is executed, such as:
68
+
69
+ cp -aR [shared_path]/config/secrets.yml [release_path]/config/secrets.yml
70
+ cp -aR [shared_path]/config/google.crt [release_path]/config/google.crt
71
+ cp -aR [shared_path]/config/google.key [release_path]/config/google.key
72
+ docker-compose -p [application] -f ... build
73
+ docker-compose ...
74
+
75
+ ### Additional commands to run
76
+ If your use-case contains more commands than `build` and `up -d`, then you can modify `capose_commands` to achieve that, ex:
77
+
78
+ set :capose_commands, ["build", "run --rm web rake db:migrate", "run --rm web rake assets:precompile", "up -d"]
79
+
80
+ This will tell capistrano to run following commands:
81
+
82
+ docker-compose -p [application] -f docker-compose-[stage].yml -f docker-compose-image-override.yml build
83
+ docker-compose -p [application] -f docker-compose-[stage].yml -f docker-compose-image-override.yml run --rm web rake db:migrate
84
+ docker-compose -p [application] -f docker-compose-[stage].yml -f docker-compose-image-override.yml run --rm web rake assets:precompile
85
+ docker-compose -p [application] -f docker-compose-[stage].yml -f docker-compose-image-override.yml up -d
86
+
87
+ ### Custom docker-compose file paths
88
+
89
+ If you want to add more compose files or change the name, then modify `capose_file` variable, ex:
90
+
91
+ set :stackose_file, ["docker-compose.yml", "docker-compose-override.yml"]
92
+
93
+ This will tell capistrano to run commands as:
94
+
95
+ docker-compose -p [application] -f docker-compose.yml -f docker-compose-override.yml -f docker-compose-image-override.yml [command]
96
+
97
+ ### Docker-compose image override
98
+
99
+ This file is generated on deploy, it will create on the "app" service configurations to make it work more correctly:
100
+ It append the image builded for the release (docker stack don't build images) and the user configuration, so the files generated
101
+ from inside of the container will have the user oth the host system.
@@ -0,0 +1,24 @@
1
+
2
+ ### Template Per Rails e Alchemy
3
+
4
+
5
+ `
6
+ set :assets_dir, %w(public/system/. uploads/. public/pages/. public/noimage/.)
7
+ set :local_assets_dir, %w(../../shared/public/system ../../shared/uploads ../../shared/public/pages ../../shared/public/noimage)
8
+
9
+ set :stackose_copy, %w(config/secrets.yml)
10
+
11
+ set :stackose_commands, ['run --rm --no-deps app rails assets:precompile', 'run --rm --no-deps app rails db:migrate']
12
+
13
+ set :stackose_linked_folders, [ 'tmp/pids',
14
+ 'tmp/cache',
15
+ 'tmp/sockets',
16
+ 'public/system',
17
+ 'public/pictures',
18
+ 'public/attachments',
19
+ 'public/pages',
20
+ 'public/assets',
21
+ 'uploads',
22
+ :"__shared_path__/db_volume"=>"/usr/share/application_storage"
23
+ ]
24
+ `
data/lib/stackose.rb ADDED
@@ -0,0 +1,257 @@
1
+ require 'json'
2
+ namespace :deploy do
3
+ after :updated, 'stackose:deploy'
4
+ after :cleanup, 'stackose:cleanup'
5
+ before :check, 'stackose:create_linked_dirs'
6
+ end
7
+
8
+ namespace :stackose do
9
+ def _project
10
+ return "" if fetch(:stackose_project).nil?
11
+ "-p #{fetch(:stackose_project)}_tmp"
12
+ end
13
+
14
+ def _files
15
+ cmd = []
16
+
17
+ Array(fetch(:stackose_file)).each {|file| cmd << ["-f #{file}"]}
18
+
19
+ cmd.join(" ")
20
+ end
21
+
22
+ def _command(command)
23
+ [_project, _files, command].join(" ")
24
+ end
25
+
26
+ def normalized_stackose_linked_folders
27
+ files = []
28
+ fetch(:stackose_linked_folders).each do |f|
29
+
30
+ if f.is_a? Hash
31
+ f.each {|k, v| files << ["#{k.to_s.gsub('__shared_path__', shared_path.to_s)}", v]}
32
+ elsif f.is_a? String
33
+ files << ["#{shared_path}/#{f}", "#{fetch(:stackose_docker_mount_point)}/#{f}"]
34
+ end
35
+
36
+ end
37
+ files.to_h
38
+ end
39
+
40
+ # task :command do
41
+ # _cmd = ENV["STACKOSE_COMMAND"]
42
+ # if _cmd.nil?
43
+ # puts "Usage: STACKOSE_COMMAND='command' bundle exec cap #{fetch(:stage)} stackose:command"
44
+ # exit 1
45
+ # end
46
+ #
47
+ # on roles(fetch(:stackose_role)) do
48
+ # within current_path do
49
+ # execute :"docker-compose", _command(ENV["STACKOSE_COMMAND"])
50
+ # end
51
+ # end
52
+ # end
53
+ #
54
+ # task :stop do
55
+ # on roles(fetch(:stackose_role)) do
56
+ # within current_path do
57
+ # execute :"docker-compose", _command("stop")
58
+ # end
59
+ # end
60
+ # end
61
+ #
62
+
63
+ desc "check linked dirs, and create this if there are not present"
64
+ task :create_linked_dirs do
65
+ on roles(fetch(:stackose_role)) do
66
+ execute :mkdir,'-p',normalized_stackose_linked_folders.keys.join(' ')
67
+ end
68
+ end
69
+
70
+ task :deploy do
71
+ on roles(fetch(:stackose_role)) do
72
+ within release_path do
73
+ fetch(:stackose_copy).each do |file|
74
+ execute :cp, " -aR #{shared_path}/#{file} #{release_path}/#{file}"
75
+ end
76
+ end
77
+ end
78
+
79
+ base_image_name = "#{fetch(:stackose_project)}:#{fetch(:stackose_image_tag)}"
80
+ compose_override = "docker-compose-image-override.yml"
81
+
82
+ ## costruiamo l'immagine
83
+ on roles(fetch(:stackose_role)) do
84
+ within release_path do
85
+ with fetch(:stackose_env) do
86
+
87
+ user_id = capture :id, '-u'
88
+ group_id = capture :id, '-g'
89
+
90
+ execute :docker, :build, ". -t #{base_image_name}"
91
+
92
+ compose_production = {
93
+ version: '3',
94
+ services: {
95
+ fetch(:stackose_service_to_build, 'app').to_sym => {
96
+ image: base_image_name,
97
+ user: "#{user_id}:#{group_id}"
98
+ }
99
+ }
100
+ }
101
+
102
+ contents = StringIO.new(JSON[compose_production.to_json].to_yaml)
103
+ upload! contents, "#{release_path}/#{compose_override}"
104
+
105
+ set :stackose_file, fetch(:stackose_file) + [compose_override]
106
+
107
+
108
+ end
109
+ end
110
+ end
111
+
112
+
113
+ on roles(fetch(:stackose_role)) do
114
+ within release_path do
115
+ with fetch(:stackose_env) do
116
+ fetch(:stackose_commands).each do |command|
117
+ execute :"docker-compose", _command(command)
118
+ end
119
+
120
+ execute :docker, :stack, :deploy, "-c #{fetch(:stackose_file).join(' -c ')} #{fetch(:stackose_project)}"
121
+
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+
128
+ task :cleanup do
129
+ on roles(fetch(:stackose_role)) do
130
+ within release_path do
131
+ with fetch(:stackose_env) do
132
+ tags_list = capture :docker, :images, fetch(:stackose_project).to_sym, '--format "{{.Tag}}"'
133
+
134
+ list = tags_list.split
135
+ list.delete('latest')
136
+
137
+ releases = capture(:ls, "-x", releases_path).split
138
+
139
+ (list - releases).each do |r|
140
+ #delete image without release path
141
+ execute :docker, :image, :rm, "#{fetch(:stackose_project)}:#{r}"
142
+ end
143
+
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ desc "Create online docker-compose file, use the linked_folders parameter to generate the hash form the compose file"
150
+ task :create_online_docker_compose_file do
151
+
152
+ ask(:exposed_port, 30000)
153
+ ask(:server_name, 'example.tld')
154
+
155
+ ask(:need_redis_service, true)
156
+
157
+
158
+ compose_production = {
159
+ version: '3',
160
+ services: {
161
+ app: {
162
+ restart: 'unless-stopped',
163
+ environment: {:RAILS_ENV => fetch(:rails_env).to_s,
164
+ :RAILS_SERVE_STATIC_FILES => 'true',
165
+ :RAILS_MAX_THREADS => 5,
166
+ :WEB_CONCURRENCY => 1},
167
+ deploy: {
168
+ replicas: 1,
169
+ resources: {
170
+ limits: {
171
+ cpus: '0.50',
172
+ memory: '250M'
173
+ }
174
+ }
175
+ },
176
+ volumes:
177
+ normalized_stackose_linked_folders.collect do |k|
178
+ k.join(':')
179
+ end,
180
+ ports: ["#{fetch(:exposed_port)}:3000"]
181
+ }
182
+ }
183
+ }
184
+
185
+ if fetch(:need_redis_service)
186
+ compose_production[:services][:redis] = {
187
+ restart: 'unless-stopped',
188
+ image: 'redis',
189
+ volumes: ["./config/redis.conf:/usr/local/etc/redis/redis.conf"],
190
+ deploy: {
191
+ resources: {
192
+ limits: {
193
+ memory: '50M'
194
+ }
195
+ }
196
+ }
197
+ }
198
+
199
+ File.open("config/redis.conf", 'w') {|file| file.write("maxmemory 50mb\nmaxmemory-policy allkeys-lfu")}
200
+ end
201
+
202
+ nginx_config = "server {
203
+ listen 80;
204
+ server_name #{fetch(:server_name)};
205
+ location / {
206
+ proxy_pass http://0.0.0.0:#{fetch(:exposed_port)}/;
207
+ proxy_redirect off;
208
+
209
+ proxy_set_header Host $host;
210
+ proxy_set_header X-Real-IP $remote_addr;
211
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
212
+
213
+ client_max_body_size 10m;
214
+ client_body_buffer_size 128k;
215
+
216
+ proxy_connect_timeout 90;
217
+ proxy_send_timeout 90;
218
+ proxy_read_timeout 90;
219
+
220
+ proxy_buffer_size 4k;
221
+ proxy_buffers 4 32k;
222
+ proxy_busy_buffers_size 64k;
223
+ proxy_temp_file_write_size 64k;
224
+ }
225
+ }"
226
+
227
+ puts "Configurazione NGINX:"
228
+ puts nginx_config
229
+ puts "Ricorda di cambiare anche la configurazione del database.yml per l'env specificato"
230
+ puts "
231
+ production:
232
+ <<: *default
233
+ pool: <%= ENV.fetch(\"RAILS_MAX_THREADS\") {5} %>
234
+ database: /usr/share/application_storage/production.sqlite3
235
+ "
236
+
237
+ File.open("docker-compose-production.yml", 'w') {|file| file.write(JSON[compose_production.to_json].to_yaml)}
238
+ end
239
+
240
+ end
241
+
242
+ namespace :load do
243
+ task :defaults do
244
+ set :stackose_role, -> {:web}
245
+ set :stackose_copy, -> {[]}
246
+ set :stackose_project, -> {fetch(:application)}
247
+ set :stackose_file, -> {["docker-compose.yml", "docker-compose-#{fetch(:stage)}.yml"]}
248
+ set :stackose_env, -> {{}}
249
+ set :stackose_image_tag, -> {fetch(:release_timestamp)}
250
+ set :stackose_service_to_build, -> {'app'}
251
+
252
+ set :stackose_commands, -> {[]}
253
+
254
+ set :stackose_linked_folders, -> {[]}
255
+ set :stackose_docker_mount_point, -> {'/usr/share/www'}
256
+ end
257
+ end
data/stackose.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'stackose'
6
+ spec.version = '0.1.0'
7
+ spec.authors = ["Marino Bonetti"]
8
+ spec.email = ["marinobonetti@gmail.com"]
9
+ spec.description = %q{Docker-Stack support for Capistrano 3.x}
10
+ spec.summary = %q{Docker-Stack support for Capistrano 3.x}
11
+ spec.homepage = 'https://github.com/oniram88/stackose'
12
+ spec.license = 'MIT'
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.add_runtime_dependency 'capistrano', '~> 3.7'
20
+ spec.add_development_dependency 'bundler', '~> 1.4'
21
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stackose
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Marino Bonetti
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: capistrano
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ description: Docker-Stack support for Capistrano 3.x
42
+ email:
43
+ - marinobonetti@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - README.md
52
+ - RailsSqliteTemplate.md
53
+ - lib/stackose.rb
54
+ - stackose.gemspec
55
+ homepage: https://github.com/oniram88/stackose
56
+ licenses:
57
+ - MIT
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 2.5.2
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Docker-Stack support for Capistrano 3.x
79
+ test_files: []