luban 0.7.15 → 0.8.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.
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