capistrano-init-exporter 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2da48c75cdd13276bf3cdf9ce8324fb08ecdb2a8
4
+ data.tar.gz: 50d3d4a55a17021a98e96f78a9e20979884d9e02
5
+ SHA512:
6
+ metadata.gz: f4da4a52107e43b79e90d21c160ba5940edced8f9855cc8894ba282d8e6f321fc02e77d66fac74008b78a584bd9401ecc56766bd8b9254927c27e91fae4fa536
7
+ data.tar.gz: b39a0c53c91a17e065daabd90a3763ea35d09fe8558e0bb743293a5c08f8e516e4025dbfe33c4c6b772a72c1b21aa79fa32def91723125deac89b797a09ce380
@@ -0,0 +1,55 @@
1
+ .idea
2
+
3
+ # Created by .ignore support plugin (hsz.mobi)
4
+ ### Ruby template
5
+ *.gem
6
+ *.rbc
7
+ /.config
8
+ /coverage/
9
+ /InstalledFiles
10
+ /pkg/
11
+ /spec/reports/
12
+ /spec/examples.txt
13
+ /test/tmp/
14
+ /test/version_tmp/
15
+ /tmp/
16
+
17
+ # Used by dotenv library to load environment variables.
18
+ # .env
19
+
20
+ ## Specific to RubyMotion:
21
+ .dat*
22
+ .repl_history
23
+ build/
24
+ *.bridgesupport
25
+ build-iPhoneOS/
26
+ build-iPhoneSimulator/
27
+
28
+ ## Specific to RubyMotion (use of CocoaPods):
29
+ #
30
+ # We recommend against adding the Pods directory to your .gitignore. However
31
+ # you should judge for yourself, the pros and cons are mentioned at:
32
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
33
+ #
34
+ # vendor/Pods/
35
+
36
+ ## Documentation cache and generated files:
37
+ /.yardoc/
38
+ /_yardoc/
39
+ /doc/
40
+ /rdoc/
41
+
42
+ ## Environment normalization:
43
+ /.bundle/
44
+ /vendor/bundle
45
+ /lib/bundler/man/
46
+
47
+ # for a library or gem, you might want to ignore these files since the code is
48
+ # intended to run in multiple environments; otherwise, check them in:
49
+ # Gemfile.lock
50
+ # .ruby-version
51
+ # .ruby-gemset
52
+
53
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
54
+ .rvmrc
55
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
File without changes
@@ -0,0 +1,31 @@
1
+ ## `capistrano-init-exporter`
2
+
3
+ Capistrano bindings for the [init-exporter utility](https://github.com/funbox/init-exporter).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'capistrano-init-exporter'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install capistrano-init-exporter
20
+
21
+ ## Usage
22
+
23
+ Capify your project as described [here](https://github.com/capistrano/capistrano#capify-your-project).
24
+
25
+ Configure the project as a normal project which uses capistrano for deployment. Then add to your ``Capfile`` :
26
+
27
+ require 'capistrano/init_exporter'
28
+
29
+ Install [init-exporter utility](https://github.com/funbox/init-exporter) on your server and configure it.
30
+
31
+ Use [procfiles version 2](https://github.com/funbox/init-exporter#procfile-v2) to describe how your project should be started .
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+ Bundler.setup(:default, :development)
5
+ require 'rake'
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task default: :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.6.0
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'init_exporter/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'capistrano-init-exporter'
7
+ spec.version = InitExporter::VERSION
8
+ spec.authors = ['funbox']
9
+ spec.email = ['a.ilyin@fun-box.ru', 'i.kushmantsev@fun-box.ru']
10
+
11
+ spec.summary = 'Capistrano bindings for exporting services described by Procfile to init system'
12
+ spec.homepage = 'https://github.com/funbox/capistrano-init-exporter'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency('capistrano', '~> 3.0')
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.15'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.0'
27
+ end
@@ -0,0 +1,23 @@
1
+ require 'capistrano/setup'
2
+ require 'capistrano/deploy'
3
+ # require 'capistrano/rbenv'
4
+ # require 'capistrano/bundler'
5
+ # require 'capistrano/overrides/dsl'
6
+
7
+ # require 'capistrano/funbox/access'
8
+ # require 'capistrano/funbox/bundler'
9
+ # require 'capistrano/funbox/config'
10
+ # require 'capistrano/funbox/secrets'
11
+ # require 'capistrano/funbox/database'
12
+ # require 'capistrano/funbox/deploy'
13
+ # require 'capistrano/funbox/folders'
14
+ # require 'capistrano/funbox/git'
15
+ # require 'capistrano/funbox/nginx'
16
+ # require 'capistrano/funbox/release'
17
+ # require 'capistrano/funbox/unicorn'
18
+ # require 'capistrano/funbox/unicorn_god'
19
+ # require 'capistrano/funbox/puma_god'
20
+ require 'capistrano/init_exporter/init'
21
+ # require 'capistrano/funbox/whenever'
22
+ # require 'capistrano/funbox/logrotator'
23
+ # require 'capistrano/funbox/slack'
@@ -0,0 +1 @@
1
+ load File.expand_path('../../tasks/init.rake', __FILE__)
@@ -0,0 +1,121 @@
1
+ require 'init_exporter/helpers/init'
2
+
3
+ include InitExporter::Helpers::Init
4
+
5
+ namespace :init do
6
+ desc 'Ensure all procfiles are correct'
7
+ task :ensure_correct_procfiles do
8
+ current_procfiles = procfiles_for(fetch(:stage))
9
+
10
+ ambiguous_procfiles = current_procfiles.group_by { |p| p[:role] }
11
+ ambiguous_procfiles.each do |role, procfiles|
12
+ next if procfiles.size == 1
13
+
14
+ paths = procfiles.map { |p| p[:path] }
15
+
16
+ warn "Several procfiles for role '#{role}' and stage '#{fetch(:stage)}' have been detected:"
17
+ paths.each do |path|
18
+ warn " #{path}"
19
+ end
20
+ warn "Don't know which to use. You must solve this disambiguation prior to deploy."
21
+
22
+ raise 'Ambiguous procfiles'
23
+ end
24
+
25
+ on release_roles(init_roles.first || []) do
26
+ within release_path do
27
+ empty_procfiles = current_procfiles.map { |p| p[:path] }.select do |path|
28
+ contents = capture(:cat, path).gsub(/\s/, '')
29
+ contents.empty?
30
+ end
31
+
32
+ unless empty_procfiles.empty?
33
+ warn 'Empty procfiles have been detected:'
34
+ empty_procfiles.each do |path|
35
+ warn " - #{path}"
36
+ end
37
+ warn 'Empty procfiles are not allowed.'
38
+
39
+ raise 'Empty procfiles'
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ desc 'Stop init jobs'
46
+ task :stop do
47
+ init_roles.each do |role|
48
+ next unless procfile_exists?(fetch(:stage), role)
49
+ on release_roles(role) do
50
+ backend = detect_backend(self)
51
+ job_name = init_job_name(fetch(:application), role)
52
+ backend.stop(job_name) if backend.running?(job_name)
53
+ end
54
+ end
55
+ end
56
+
57
+ desc 'Start init jobs'
58
+ task :start do
59
+ init_roles.each do |role|
60
+ next unless procfile_exists?(fetch(:stage), role)
61
+ on release_roles(role) do
62
+ backend = detect_backend(self)
63
+ job_name = init_job_name(fetch(:application), role)
64
+ backend.start(job_name) unless backend.running?(job_name)
65
+ end
66
+ end
67
+ end
68
+
69
+ desc 'Install init jobs'
70
+ task :install do
71
+ procfiles_for(fetch(:stage)).each do |procfile|
72
+ on release_roles(procfile[:role]) do
73
+ backend = detect_backend(self)
74
+
75
+ procfile_path = release_path.join(procfile[:path])
76
+ if test "[ -f #{procfile_path} ]"
77
+ app_name = application_init_name(fetch(:application), procfile[:role])
78
+ backend.install(app_name, procfile_path)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ desc 'Uninstall init jobs'
85
+ task :uninstall => :stop do
86
+ init_roles.each do |role|
87
+ on release_roles role do
88
+ backend = detect_backend(self)
89
+
90
+ app_name = application_init_name(fetch(:application), role)
91
+ backend.uninstall(app_name)
92
+ end
93
+ end
94
+ end
95
+
96
+ desc 'Dry run init-exporter'
97
+ task :dry_run do
98
+ procfiles_for(fetch(:stage)).each do |procfile|
99
+ on release_roles(procfile[:role]) do
100
+ backend = detect_backend(self)
101
+
102
+ procfile_path = release_path.join(procfile[:path])
103
+ if test "[ -f #{procfile_path} ]"
104
+ app_name = application_init_name(fetch(:application), procfile[:role])
105
+ backend.dry_run(app_name, procfile_path)
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ desc 'Publish init jobs (install && run)'
112
+ task :publish do
113
+ invoke 'init:ensure_correct_procfiles'
114
+ invoke 'init:install'
115
+ invoke 'init:start'
116
+ end
117
+ end
118
+
119
+ before 'deploy:publishing', 'init:dry_run'
120
+ before 'deploy:publishing', 'init:stop'
121
+ before 'deploy:published', 'init:publish'
File without changes
@@ -0,0 +1,121 @@
1
+ require 'init_exporter/helpers/init/upstart_backend'
2
+ require 'init_exporter/helpers/init/systemd_backend'
3
+
4
+ module InitExporter
5
+ module Helpers
6
+ module Init
7
+ module_function
8
+
9
+ def detect_backend(ssh)
10
+ if ssh.test 'which', 'systemctl'
11
+ InitExporter::Helpers::Init::SystemdBackend.new(ssh)
12
+ elsif ssh.test 'which', 'initctl'
13
+ InitExporter::Helpers::Init::UpstartBackend.new(ssh)
14
+ else
15
+ raise 'No init system found (both systemctl and initctl are not found in path)'
16
+ end
17
+ end
18
+
19
+ def application_init_name(application, role)
20
+ suffix = role.to_s == 'all' ? '' : "_#{role}"
21
+
22
+ application.tr('-', '_').gsub(/\W/, '') + suffix
23
+ end
24
+
25
+ def init_job_name(application, role)
26
+ init_job_prefix + application_init_name(application, role)
27
+ end
28
+
29
+ def all_procfiles
30
+ @all_procfiles ||= begin
31
+ list_procfiles.map do |path|
32
+ stage, role = extract_stage_and_role(path)
33
+ {stage: stage, role: role, path: path}
34
+ end
35
+ end
36
+ end
37
+
38
+ def known_procfiles
39
+ all_procfiles.reject do |p|
40
+ p[:stage] == :unknown || p[:role] == :unknown
41
+ end
42
+ end
43
+
44
+ def procfile_exists?(stage, role)
45
+ all_procfiles.find { |p| p[:stage] == stage && p[:role] == role }
46
+ end
47
+
48
+ def procfiles_for(stage)
49
+ known_procfiles.select { |p| p[:stage] == stage }
50
+ end
51
+
52
+ def init_roles
53
+ known_procfiles.map { |p| p[:role] }.uniq
54
+ end
55
+
56
+ protected
57
+
58
+ def list_procfiles
59
+ paths = []
60
+ on release_roles :all do
61
+ within release_path do
62
+ paths = procfile_paths('config/init') + procfile_paths('config/upstart')
63
+ break
64
+ end
65
+ end
66
+ paths
67
+ end
68
+
69
+ def procfile_paths(root)
70
+ capture(:find, root, '-type f', '-name "Procfile.*"', '2>/dev/null').lines.map(&:strip)
71
+ rescue SSHKit::Command::Failed
72
+ []
73
+ end
74
+
75
+ def extract_stage_and_role(procfile_path)
76
+ case procfile_path
77
+ when %r{\Aconfig/(?:init|upstart)/Procfile\.(\w+)\z}
78
+ [Regexp.last_match(1), :all]
79
+ when %r{\Aconfig/(?:init|upstart)/Procfile\.(\w+)\.(\w+)\z}
80
+ [Regexp.last_match(2), Regexp.last_match(1)]
81
+ when %r{\Aconfig/(?:init|upstart)/(\w+)/Procfile\.(\w+)\z}
82
+ [Regexp.last_match(1), Regexp.last_match(2)]
83
+ else
84
+ [:unknown, :unknown]
85
+ end.map(&:to_sym)
86
+ end
87
+
88
+ def init_job_prefix
89
+ config = nil
90
+ on release_roles :all do
91
+ config = capture(:cat, '/etc/init-exporter.conf').split("\n").map(&:strip)
92
+ end
93
+ get_prefix(config)
94
+ end
95
+
96
+ COMMENT_SYMBOL = '#'.freeze
97
+ SECTION_SYMBOL = '['.freeze
98
+ DELIMITER = ':'.freeze
99
+ MAIN_SECTION = '[main]'.freeze
100
+ PREFIX_KEY = 'prefix'.freeze
101
+
102
+ def get_prefix(config_lines)
103
+ main_section = false
104
+ prefix = nil
105
+ config_lines.each do |line|
106
+ next if line == '' || line[0] == COMMENT_SYMBOL
107
+ if line[0] == SECTION_SYMBOL
108
+ main_section = (line == MAIN_SECTION)
109
+ next
110
+ end
111
+ next unless main_section
112
+ key, value = line.split(DELIMITER)
113
+ next unless key == PREFIX_KEY
114
+ prefix = value.strip!
115
+ break
116
+ end
117
+ prefix
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,33 @@
1
+ module InitExporter
2
+ module Helpers
3
+ module Init
4
+ class BaseBackend
5
+ def initialize(ssh)
6
+ @ssh = ssh
7
+ end
8
+
9
+ def install(app_name, procfile_path)
10
+ run_init_exporter(app_name, '-p', procfile_path)
11
+ end
12
+
13
+ def dry_run(app_name, procfile_path)
14
+ run_init_exporter(app_name, '-p', procfile_path, '--dry-start')
15
+ end
16
+
17
+ def uninstall(app_name)
18
+ run_init_exporter(app_name, '-c')
19
+ end
20
+
21
+ def init_type
22
+ raise NotImplementedError
23
+ end
24
+
25
+ private
26
+
27
+ def run_init_exporter(app_name, *args)
28
+ @ssh.execute :sudo, 'init-exporter', '-n', app_name, '-f', init_type, *args
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ require 'init_exporter/helpers/init/base_backend'
2
+
3
+ module InitExporter
4
+ module Helpers
5
+ module Init
6
+ class SystemdBackend < BaseBackend
7
+ def initialize(ssh)
8
+ @ssh = ssh
9
+ end
10
+
11
+ def init_type
12
+ 'systemd'
13
+ end
14
+
15
+ def start(job_name)
16
+ @ssh.execute :sudo, 'systemctl', 'start', job_name
17
+ end
18
+
19
+ def stop(job_name)
20
+ @ssh.execute :sudo, 'systemctl', 'stop', job_name
21
+ end
22
+
23
+ def running?(job_name)
24
+ @ssh.test :sudo, 'systemctl', 'status', job_name
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,51 @@
1
+ require 'init_exporter/helpers/init/base_backend'
2
+
3
+ module InitExporter
4
+ module Helpers
5
+ module Init
6
+ class UpstartBackend < BaseBackend
7
+ def initialize(ssh)
8
+ @ssh = ssh
9
+ end
10
+
11
+ def init_type
12
+ 'upstart'
13
+ end
14
+
15
+ def install(app_name, procfile_path)
16
+ init_exporter_installed? ? super : run_upstart_exporter(app_name, '-p', procfile_path)
17
+ end
18
+
19
+ def dry_run(app_name, procfile_path)
20
+ init_exporter_installed? ? super : true
21
+ end
22
+
23
+ def uninstall(app_name, procfile_path)
24
+ init_exporter_installed? ? super : run_upstart_exporter(app_name, '-c')
25
+ end
26
+
27
+ def start(job_name)
28
+ @ssh.execute :sudo, 'start', job_name
29
+ end
30
+
31
+ def stop(job_name)
32
+ @ssh.execute :sudo, 'stop', job_name
33
+ end
34
+
35
+ def running?(job_name)
36
+ @ssh.test :sudo, "/sbin/initctl status #{job_name} | grep start"
37
+ end
38
+
39
+ private
40
+
41
+ def init_exporter_installed?
42
+ @ssh.test 'which', 'init-exporter'
43
+ end
44
+
45
+ def run_upstart_exporter(app_name, *args)
46
+ @ssh.execute :sudo, 'upstart-export', '-n', app_name, *args
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module InitExporter
2
+ VERSION = '0.1.0'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capistrano-init-exporter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - funbox
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-01-17 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.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
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.15'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.15'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description:
70
+ email:
71
+ - a.ilyin@fun-box.ru
72
+ - i.kushmantsev@fun-box.ru
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - VERSION
83
+ - capistrano-init-exporter.gemspec
84
+ - lib/capistrano/init_exporter.rb
85
+ - lib/capistrano/init_exporter/init.rb
86
+ - lib/capistrano/tasks/init.rake
87
+ - lib/init_exporter.rb
88
+ - lib/init_exporter/helpers/init.rb
89
+ - lib/init_exporter/helpers/init/base_backend.rb
90
+ - lib/init_exporter/helpers/init/systemd_backend.rb
91
+ - lib/init_exporter/helpers/init/upstart_backend.rb
92
+ - lib/init_exporter/version.rb
93
+ homepage: https://github.com/funbox/capistrano-init-exporter
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.5.2
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Capistrano bindings for exporting services described by Procfile to init
117
+ system
118
+ test_files: []