capistrano-init-exporter 0.1.0

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