luban 0.7.15 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79e1f167e693836da903a95a79e5ccf3d4019c40
4
- data.tar.gz: cdd786aa212e17818ae3107ebc9382697a343972
3
+ metadata.gz: b76fee1999addcf718abd92f2c2f49ad36e0bc01
4
+ data.tar.gz: 21654c1dac641251e5dfcf7dbd643dd4b474f683
5
5
  SHA512:
6
- metadata.gz: 23d244aff4a62075d23589325b9510e776f76bc38c1d1e26f99b2a98825a4a67bd0719513ec35d1926a8b4de8fd6c94781efa57f19ea140dec6cbade52d20be8
7
- data.tar.gz: 205dd39de95af822c32fa50b635a95d360a5d16e3f0d077b487cdd095349af6b8f5e67b0e52767dca37e5ba469392383d3cce876e442d01255f78735d972d9c9
6
+ metadata.gz: c7bc4215dd7b267c2910448510f93990b2d01f3d07466e06bbd07ab1fd6c5856ea15be2443ac4033c311b1f791c8e6c7284dcfd340ab877add02cb2e8dd9b429
7
+ data.tar.gz: ae7e62c0cafce61eca81d3b041014c65e18550cdaca7f0800a52d0c3568c8fde901579f52895adffae1b3e696e085447ff4e5827517d7130f29d6d0194a6dd80
data/CHANGELOG.md CHANGED
@@ -1,9 +1,22 @@
1
1
  # Change log
2
2
 
3
+ ## Version 0.8.0 (Sept 19, 2016)
4
+
5
+ # New features:
6
+ * Supported cronjob deployment
7
+
8
+ Minor enhancements:
9
+ * Refactored Service::Worker#compose_command to be more flexible
10
+ * Enhanced util method #upload_by_template to handle header and footer template rendering
11
+ * Added convenient method #bundle_command to compose command running within the bundler context
12
+ * Refactored the way of composing shell commands including cronjob commands
13
+ * Minor code cleanup
14
+
3
15
  ## Version 0.7.15 (Sept 07, 2016)
4
16
 
5
17
  Minor enhancements:
6
18
  * During app packaging, extracted vendor/gems from the source code if any along with Gemfile/Gemfile.lock
19
+ * As a result, bump up gem dependency on luban-cli to version 0.4.5 or above
7
20
 
8
21
  ## Version 0.7.14 (Sept 06, 2016)
9
22
 
@@ -7,6 +7,7 @@ module Luban
7
7
  include Luban::Deployment::Command::Tasks::Install
8
8
  include Luban::Deployment::Command::Tasks::Deploy
9
9
  include Luban::Deployment::Command::Tasks::Control
10
+ include Luban::Deployment::Command::Tasks::Crontab
10
11
 
11
12
  attr_reader :packages
12
13
  attr_reader :services
@@ -54,7 +55,7 @@ module Luban
54
55
  def has_services?; !services.empty?; end
55
56
 
56
57
  def installable?; has_packages?; end
57
- def deployable?; has_source? or has_profile? end
58
+ def deployable?; has_source? or has_profile? end
58
59
  def controllable?; has_source? or has_services?; end
59
60
 
60
61
  def use_package?(package_name, package_version, servers: [])
@@ -191,7 +192,9 @@ module Luban
191
192
  show_app_environment
192
193
  deploy_release(args: args, opts: opts) if has_source?
193
194
  deploy_profile(args: args, opts: opts) if has_profile?
195
+ deploy_cronjobs(args: args, opts: opts)
194
196
  end
197
+ dispatch_task :deploy_cronjobs, to: :crontab, as: :deploy_cronjobs
195
198
 
196
199
  Luban::Deployment::Command::Tasks::Control::Actions.each do |action|
197
200
  define_method(action) do |args:, opts:|
@@ -233,6 +236,16 @@ module Luban
233
236
  end
234
237
  end
235
238
 
239
+ Luban::Deployment::Command::Tasks::Crontab::Actions.each do |action|
240
+ define_method(action) do |args:, opts:|
241
+ show_app_environment
242
+ opts = opts.merge(version: current_app) if current_app
243
+ send("#{action}!", args: args, opts: opts)
244
+ end
245
+ dispatch_task "#{action}!", to: :crontab, as: action
246
+ protected "#{action}!"
247
+ end
248
+
236
249
  protected
237
250
 
238
251
  def set_parameters
@@ -285,6 +298,7 @@ module Luban
285
298
  def setup_tasks
286
299
  setup_init_profiles
287
300
  super
301
+ setup_crontab_tasks
288
302
  end
289
303
 
290
304
  def setup_init_profiles
@@ -374,4 +388,4 @@ module Luban
374
388
  end
375
389
  end
376
390
  end
377
- end
391
+ end
@@ -0,0 +1,147 @@
1
+ module Luban
2
+ module Deployment
3
+ class Application
4
+ class Crontab < Worker
5
+ def cronjobs; backend.host.cronjobs; end
6
+ def has_cronjobs?; !cronjobs.empty?; end
7
+
8
+ def crontab_file_path
9
+ @crontab_file_path ||= shared_path.join(crontab_file_name)
10
+ end
11
+
12
+ def tmp_crontab_file_path
13
+ @tmp_crontab_file_path ||= shared_path.join(".tmp.#{crontab_file_name}")
14
+ end
15
+
16
+ def crontab_file_name
17
+ @crontab_file_name ||= "crontab"
18
+ end
19
+
20
+ def crontab_template_file
21
+ @crontab_template_file ||= find_template_file("crontab.erb")
22
+ end
23
+
24
+ def crontab_header_template_file
25
+ @crontab_header_template_file ||= find_template_file("crontab_header.erb")
26
+ end
27
+
28
+ def crontab_footer_template_file
29
+ @crontab_footer_template_file ||= find_template_file("crontab_footer.erb")
30
+ end
31
+
32
+ def crontab_open
33
+ @crontab_open ||= "# CRONTAB BEGIN : #{env_name}"
34
+ end
35
+
36
+ def crontab_close
37
+ @crontab_close ||= "# CRONTAB END : #{env_name}"
38
+ end
39
+
40
+ def updated?
41
+ extract_crontab == capture(:cat, crontab_file_path, "2>/dev/null")
42
+ end
43
+
44
+ def deploy_cronjobs
45
+ if deploy_cronjobs!
46
+ update_result "Successfully published #{crontab_file_name}."
47
+ else
48
+ update_result "Skipped! ALREADY published #{crontab_file_name}.", status: :skipped
49
+ end
50
+ end
51
+
52
+ def update_cronjobs
53
+ unless file?(crontab_file_path)
54
+ update_result "FAILED to update crontab: missing #{crontab_file_path}.", status: :failed, level: :error
55
+ return
56
+ end
57
+ if updated?
58
+ update_result "Skipped! ALREADY updated crontab.", status: :skipped
59
+ return
60
+ end
61
+
62
+ update_cronjobs!
63
+ if updated?
64
+ update_result "Successfully updated crontab."
65
+ else
66
+ update_result "FAILED to update crontab.", status: :failed, level: :error
67
+ end
68
+ end
69
+
70
+ def list_cronjobs
71
+ crontab = extract_crontab(task.opts.all)
72
+ if crontab.empty?
73
+ update_result "No crontab for #{user}."
74
+ else
75
+ update_result crontab
76
+ end
77
+ end
78
+
79
+ def shell_setup
80
+ @shell_setup ||= task.opts.release.nil? ? ["source #{envrc_file}"] : super
81
+ end
82
+
83
+ def shell_delimiter; @shell_delimiter ||= '&&'; end
84
+
85
+ protected
86
+
87
+ def deploy_cronjobs!
88
+ rm(crontab_file_path) if force?
89
+ upload_by_template(file_to_upload: crontab_file_path,
90
+ template_file: crontab_template_file,
91
+ header_file: crontab_header_template_file,
92
+ footer_file: crontab_footer_template_file,
93
+ auto_revision: true)
94
+ end
95
+
96
+ def crontab_entry(command:, schedule:, output: "", type: :shell, disabled: false)
97
+ if output.is_a?(String) and !output.empty?
98
+ output = log_path.join("cron.#{output}")
99
+ end
100
+ command_composer = "#{type}_command"
101
+ unless respond_to?(command_composer)
102
+ abort "Aborted! Unknown cronjob type: #{type.inspect}"
103
+ end
104
+ command = send(command_composer, command, output: output)
105
+ entry = "#{schedule} #{command}"
106
+ disabled ? "# DISABLED - #{entry}" : entry
107
+ end
108
+
109
+ def update_cronjobs!
110
+ crontab = capture(:crontab, "-l")
111
+ new_crontab = capture(:cat, crontab_file_path, "2>/dev/null")
112
+ old = false
113
+ crontab = crontab.split("\n").inject([]) do |lines, line|
114
+ if old || line == crontab_open
115
+ lines << new_crontab unless (old = line != crontab_close)
116
+ else
117
+ lines << line
118
+ end
119
+ lines
120
+ end
121
+ if crontab.empty?
122
+ test(:crontab, crontab_file_path, "2>&1")
123
+ else
124
+ upload!(StringIO.new(crontab.join("\n")), tmp_crontab_file_path)
125
+ test(:crontab, tmp_crontab_file_path, "2>&1")
126
+ end
127
+ ensure
128
+ rm(tmp_crontab_file_path)
129
+ end
130
+
131
+ def extract_crontab(all = false)
132
+ crontab = capture(:crontab, "-l")
133
+ return crontab if all
134
+
135
+ old = false
136
+ crontab.split("\n").inject([]) do |lines, line|
137
+ if old || line == crontab_open
138
+ lines << line
139
+ old = line != crontab_close
140
+ end
141
+ lines
142
+ end.join("\n")
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -11,8 +11,8 @@ module Luban
11
11
 
12
12
  def has_gemfile?; file?(gemfile); end
13
13
 
14
- def shell_setup_commands
15
- @shell_setup_commands ||= super << "cd #{release_path}"
14
+ def shell_setup
15
+ @shell_setup ||= super << "cd #{release_path}"
16
16
  end
17
17
 
18
18
  %i(name full_name version major_version patch_level).each do |method|
@@ -38,6 +38,10 @@ module Luban
38
38
  def bundle_executable
39
39
  @bundle_executable ||= ruby_bin_path.join('bundle')
40
40
  end
41
+
42
+ def bundle_command(cmd, **opts)
43
+ shell_command("#{bundle_executable} exec #{cmd}", **opts)
44
+ end
41
45
  end
42
46
  end
43
47
  end
@@ -6,4 +6,5 @@ require_relative 'application/repository'
6
6
  require_relative 'application/publisher'
7
7
  require_relative 'application/configurator'
8
8
  require_relative 'application/controller'
9
+ require_relative 'application/crontab'
9
10
 
@@ -141,6 +141,55 @@ module Luban
141
141
  end
142
142
  end
143
143
  end
144
+
145
+ module Crontab
146
+ Actions = %i(update_cronjobs list_cronjobs)
147
+ Actions.each do |action|
148
+ define_method(action) do |args:, opts:|
149
+ raise NotImplementedError, "#{self.class.name}##{__method__} is an abstract method."
150
+ end
151
+ end
152
+
153
+ def cronjobs; @cronjobs ||= []; end
154
+
155
+ def has_cronjobs?; !cronjobs.empty?; end
156
+
157
+ def cronjob(roles: nil, hosts: nil, **job)
158
+ validate_cronjob(job)
159
+ roles = Array(roles)
160
+ hosts = Array(hosts)
161
+ servers = select_servers(roles, hosts)
162
+ servers.each { |s| server(s, cronjob: job) }
163
+ cronjobs << job
164
+ end
165
+
166
+ protected
167
+
168
+ def validate_cronjob(job)
169
+ if job[:command].nil?
170
+ abort "Aborted! Cron job command is MISSING."
171
+ end
172
+ if job[:schedule].nil?
173
+ abort "Aborted! Cron job schedule is MISSING for command: #{job[:command]}"
174
+ end
175
+ if cronjobs.any? { |j| j[:command] == job[:command] }
176
+ abort "Aborted! Duplicate command is FOUND: #{job[:command]}"
177
+ end
178
+ end
179
+
180
+ def setup_crontab_tasks
181
+ task :cronjobs_update do
182
+ desc 'Update cron jobs'
183
+ action! :update_cronjobs
184
+ end
185
+
186
+ task :cronjobs_list do
187
+ desc 'List cron jobs'
188
+ switch :all, "List all cron jobs"
189
+ action! :list_cronjobs
190
+ end
191
+ end
192
+ end
144
193
  end
145
194
 
146
195
  using Luban::CLI::CoreRefinements
@@ -38,15 +38,15 @@ module Luban
38
38
  end
39
39
 
40
40
  def monitor_command
41
- @monitor_command ||= "#{monitor_executable} monitor #{service_entry}"
41
+ @monitor_command ||= shell_command("#{monitor_executable} monitor #{service_entry}")
42
42
  end
43
43
 
44
44
  def unmonitor_command
45
- @unmonitor_command ||= "#{monitor_executable} unmonitor #{service_entry}"
45
+ @unmonitor_command ||= shell_command("#{monitor_executable} unmonitor #{service_entry}")
46
46
  end
47
47
 
48
48
  def reload_monitor_command
49
- @reload_monitor_command ||= "#{monitor_executable} reload"
49
+ @reload_monitor_command ||= shell_command("#{monitor_executable} reload")
50
50
  end
51
51
 
52
52
  def start_process
@@ -186,13 +186,8 @@ module Luban
186
186
  succeeded
187
187
  end
188
188
 
189
- def start_process!
190
- capture(compose_command(start_command))
191
- end
192
-
193
- def stop_process!
194
- capture(compose_command(stop_command))
195
- end
189
+ def start_process!; capture(start_command); end
190
+ def stop_process!; capture(stop_command); end
196
191
 
197
192
  def check_process!
198
193
  if pid_file_missing?
@@ -230,15 +225,15 @@ module Luban
230
225
  end
231
226
 
232
227
  def monitor_process!
233
- test(compose_command(monitor_command))
228
+ test(monitor_command)
234
229
  end
235
230
 
236
231
  def unmonitor_process!
237
- test(compose_command(unmonitor_command))
232
+ test(unmonitor_command)
238
233
  end
239
234
 
240
235
  def reload_monitor_process!
241
- test(compose_command(reload_monitor_command))
236
+ test(reload_monitor_command)
242
237
  end
243
238
  end
244
239
 
@@ -3,24 +3,31 @@ module Luban
3
3
  module Service
4
4
  class Worker < Luban::Deployment::Package::Worker
5
5
  module Base
6
- def shell_setup_commands
7
- @shell_setup_commands ||= ["source #{envrc_file}"]
6
+ def shell_setup; @shell_setup ||= ["source #{envrc_file}"]; end
7
+ def shell_prefix; @shell_prefix ||= []; end
8
+ def shell_output; @shell_output ||= :stdout; end
9
+ def shell_delimiter; @shell_delimiter ||= ';'; end
10
+
11
+ def shell_command(cmd, setup: shell_setup, prefix: shell_prefix,
12
+ output: shell_output, delimiter: shell_delimiter)
13
+ cmd = "#{prefix.join(' ')} #{cmd}" unless prefix.empty?
14
+ cmd = "#{cmd} #{output_redirection(output)}"
15
+ "#{setup.join(' ' + delimiter + ' ')} #{delimiter} #{cmd}"
16
+ end
17
+
18
+ def output_redirection(output)
19
+ case output
20
+ when :stdout
21
+ "2>&1"
22
+ when nil
23
+ ">> /dev/null 2>&1"
24
+ when ""
25
+ ""
26
+ else
27
+ ">> #{output} 2>&1"
28
+ end
8
29
  end
9
30
 
10
- def shell_command_prefix
11
- @shell_command_prefix ||= []
12
- end
13
-
14
- def shell_command_output
15
- @shell_command_output ||= '2>&1'
16
- end
17
-
18
- def compose_command(cmd, delimiter: ';')
19
- cmd = "#{shell_command_prefix.join(' ')} #{cmd}" unless shell_command_prefix.empty?
20
- cmd = "#{cmd} #{shell_command_output}" unless shell_command_output.empty?
21
- "#{shell_setup_commands.join(' ' + delimiter + ' ')} #{delimiter} #{cmd}"
22
- end
23
-
24
31
  %i(name full_name version major_version patch_level).each do |method|
25
32
  define_method("service_#{method}") { send("target_#{method}") }
26
33
  end
@@ -31,7 +31,7 @@ module Luban
31
31
  end
32
32
 
33
33
  def properties
34
- @properties ||= { :roles => Set.new }
34
+ @properties ||= { :roles => Set.new, :cronjobs => Set.new }
35
35
  end
36
36
 
37
37
  def [](key)
@@ -57,13 +57,27 @@ module Luban
57
57
  end
58
58
  alias_method :set, :[]=
59
59
 
60
- def add_properties(_properties)
60
+ def add_properties(_properties)
61
61
  _properties.each { |k, v| self[k] = v }
62
62
  end
63
63
 
64
64
  def primary?
65
65
  self[:primary]
66
66
  end
67
+
68
+ def cronjobs
69
+ self[:cronjobs]
70
+ end
71
+
72
+ def add_cronjobs(cronjobs)
73
+ cronjobs.each { |cronjob| add_cronjob(cronjob) }
74
+ end
75
+ alias_method :cronjobs=, :add_cronjobs
76
+
77
+ def add_cronjob(cronjob)
78
+ cronjobs.add(cronjob)
79
+ end
80
+ alias_method :cronjob=, :add_cronjob
67
81
  end
68
82
  end
69
83
  end
@@ -120,6 +120,7 @@ module Luban
120
120
 
121
121
  def upload_by_template(file_to_upload:, template_file:,
122
122
  header_file: find_template_file('header.erb'),
123
+ footer_file: nil,
123
124
  auto_revision: false, **opts)
124
125
  content = render_template(template_file, context: binding)
125
126
 
@@ -127,13 +128,15 @@ module Luban
127
128
  if auto_revision
128
129
  require 'digest/md5'
129
130
  revision = Digest::MD5.hexdigest(content)
130
- return if revision_match?(file_to_upload, revision)
131
+ return false if revision_match?(file_to_upload, revision)
131
132
  end
132
133
 
133
- header = render_template(header_file, context: binding)
134
+ header = header_file.nil? ? '' : render_template(header_file, context: binding)
135
+ footer = footer_file.nil? ? '' : render_template(footer_file, context: binding)
134
136
 
135
- upload!(StringIO.new(header + content), file_to_upload)
137
+ upload!(StringIO.new(header + content + footer), file_to_upload)
136
138
  yield file_to_upload if block_given?
139
+ true
137
140
  end
138
141
 
139
142
  def render_template(template_file, context: binding)
@@ -0,0 +1,6 @@
1
+ <% unless has_cronjobs? -%>
2
+ # !!! No cronjobs for <%= user %> !!!
3
+ <% end -%>
4
+ <% cronjobs.each do |cronjob| -%>
5
+ <%= "#{crontab_entry(**cronjob)}" %>
6
+ <% end -%>
@@ -0,0 +1 @@
1
+ <%= crontab_close %>
@@ -0,0 +1,3 @@
1
+ <%= crontab_open %>
2
+ # Revision: <%= revision %>
3
+ # Created at <%= now %>
@@ -1,5 +1,5 @@
1
1
  module Luban
2
2
  module Deployment
3
- VERSION = "0.7.15"
3
+ VERSION = "0.8.0"
4
4
  end
5
5
  end
data/luban.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.required_ruby_version = ">= 2.1.0"
23
- spec.add_runtime_dependency 'luban-cli'
23
+ spec.add_runtime_dependency 'luban-cli', ">=0.4.5"
24
24
  spec.add_runtime_dependency 'sshkit'
25
25
 
26
26
  spec.add_development_dependency "bundler", "~> 1.9"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: luban
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.15
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rubyist Lei
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-06 00:00:00.000000000 Z
11
+ date: 2016-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: luban-cli
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.4.5
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.4.5
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sshkit
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -106,6 +106,7 @@ files:
106
106
  - lib/luban/deployment/cli/application/configurator.rb
107
107
  - lib/luban/deployment/cli/application/constructor.rb
108
108
  - lib/luban/deployment/cli/application/controller.rb
109
+ - lib/luban/deployment/cli/application/crontab.rb
109
110
  - lib/luban/deployment/cli/application/publisher.rb
110
111
  - lib/luban/deployment/cli/application/repository.rb
111
112
  - lib/luban/deployment/cli/application/scm/git.rb
@@ -156,6 +157,9 @@ files:
156
157
  - lib/luban/deployment/templates/application/config/deploy/__stage/templates/.gitkeep
157
158
  - lib/luban/deployment/templates/application/config/templates/profile/.gitkeep
158
159
  - lib/luban/deployment/templates/application/lib/application.rb.erb
160
+ - lib/luban/deployment/templates/crontab.erb
161
+ - lib/luban/deployment/templates/crontab_footer.erb
162
+ - lib/luban/deployment/templates/crontab_header.erb
159
163
  - lib/luban/deployment/templates/envrc.erb
160
164
  - lib/luban/deployment/templates/header.erb
161
165
  - lib/luban/deployment/templates/project/.gitignore