dply 0.0.2

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,24 @@
1
+ require 'dply/helper'
2
+ module Dply
3
+ class Lock
4
+
5
+ include Helper
6
+ attr_accessor :deploy_dir
7
+
8
+ def initialize(deploy_dir)
9
+ @deploy_dir = deploy_dir
10
+ end
11
+
12
+ def lock_file
13
+ @lock_file ||= Dir.chdir(deploy_dir) do
14
+ File.open(".dply.lock", "w+")
15
+ end
16
+ end
17
+
18
+ def acquire
19
+ logger.debug "acquiring exclusive lock"
20
+ lock_file.flock(File::LOCK_EX)
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ require 'logger'
2
+ require 'dply/custom_logger'
3
+ module Dply
4
+ module Logger
5
+
6
+ def self.logger
7
+ @logger ||= ::Dply::CustomLogger.new(STDOUT)
8
+ end
9
+
10
+ def self.stderr
11
+ @stderr ||= ::Logger.new(STDERR)
12
+ end
13
+
14
+ def logger
15
+ ::Dply::Logger.logger
16
+ end
17
+
18
+ def stderr
19
+ ::Dply::Logger.stderr
20
+ end
21
+
22
+ def debug?
23
+ logger.level == ::Logger::DEBUG
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ module Dply
2
+ class Release
3
+
4
+ attr_reader release_dir
5
+
6
+ def initialize(release_dir)
7
+ @release_dir = release_dir
8
+ end
9
+
10
+ def create(repo_cache, branch)
11
+ return if exists?
12
+ FileUtils.mkdir uncommited_release_dir
13
+ copy_source_code(repo_cache, branch)
14
+ end
15
+
16
+ def copy_source_code(repo_cache, branch)
17
+ cmd "git archive #{branch} | tar -x -f - -C #{uncommited_release_dir}"
18
+ end
19
+
20
+ def exists?
21
+ File.exists? (release_dir) || File.exists? (uncommited_release_dir)
22
+ end
23
+
24
+ def uncommited_release_dir
25
+ "#{release_dir}.uncommited"
26
+ end
27
+
28
+ def commit
29
+ return if File.exists? (release_dir)
30
+ FileUtils.mv uncommited_release_dir, release_dir
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,139 @@
1
+ require 'pty'
2
+ require 'dply/report'
3
+ require 'dply/logger'
4
+
5
+ module Dply
6
+ class RemoteTask
7
+
8
+ include Logger
9
+
10
+ attr_reader :hosts, :parallel_jobs, :task
11
+
12
+ def initialize(hosts, task, parallel_jobs: 1)
13
+ @hosts = hosts
14
+ @parallel_jobs = parallel_jobs
15
+ @task = task
16
+ end
17
+
18
+ def run
19
+ if parallel_jobs > 1 && hosts.count > 1
20
+ run_in_parallel
21
+ else
22
+ run_serially
23
+ end
24
+ end
25
+
26
+ def run_in_parallel
27
+ init_run
28
+ parallel_jobs.times do
29
+ spawn_queued_job
30
+ end
31
+ loop do
32
+ break if @queue.empty?
33
+ @mq.pop
34
+ spawn_queued_job
35
+ end
36
+ report.print_full
37
+ end
38
+
39
+ def run_serially
40
+ hosts.each do |host_info|
41
+ puts "=== Running on #{host_info[:id]} ==="
42
+ run_cmd = remote_cmd host_info
43
+ system run_cmd
44
+ puts
45
+ raise ::Dply::Error, "remote deploy failed on #{host_info[:id]}" if $? != 0
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def init_run
52
+ queue_all_hosts
53
+ job_output_template
54
+ @threads = {}
55
+ @mq = Queue.new
56
+ end
57
+
58
+ def queue_all_hosts
59
+ @queue = Queue.new
60
+ hosts.each { |h| @queue << h }
61
+ end
62
+
63
+ def spawn_job(host_info)
64
+ run_cmd = remote_cmd host_info
65
+ thread = popen(run_cmd, host_info)
66
+ @threads[host_info] = thread
67
+ end
68
+
69
+ def spawn_queued_job
70
+ return if @queue.empty?
71
+ host = @queue.pop(false)
72
+ spawn_job host
73
+ rescue ThreadError
74
+ end
75
+
76
+ def remote_cmd(host_info)
77
+ user = host_info[:user]
78
+ host = host_info[:host]
79
+ deploy_dir = host_info[:deploy_dir]
80
+ if logger.debug?
81
+ %(ssh -tt -oBatchMode=yes -l #{user} #{host} "drake --debug -d #{deploy_dir} #{task} 2>&1")
82
+ else
83
+ %(ssh -tt -oBatchMode=yes -l #{user} #{host} "drake -d #{deploy_dir} #{task} 2>&1" 2>/dev/null)
84
+ end
85
+ end
86
+
87
+ def pty_read(file, id)
88
+ file.each do |line|
89
+ printf @job_output_template, id, line
90
+ end
91
+ rescue EOFError,Errno::ECONNRESET, Errno::EPIPE, Errno::EIO => e
92
+ end
93
+
94
+ def popen(cmd, host_info)
95
+ t = Thread.new do
96
+ begin
97
+ r, w, pid = PTY.spawn(cmd)
98
+ pty_read(r, host_info[:id])
99
+ exit_status pid
100
+ ensure
101
+ @mq << true
102
+ end
103
+ end
104
+ t.abort_on_exception = true
105
+ t.run
106
+ return t
107
+ end
108
+
109
+ def host_id_max_width
110
+ hosts.map {|h| h[:id].size }.max
111
+ end
112
+
113
+ def job_output_template
114
+ @job_output_template ||= begin
115
+ id_template = "%-#{host_id_max_width}s".bold.grey
116
+ template = "#{id_template} %s"
117
+ end
118
+ end
119
+
120
+ def report
121
+ @report ||= Report.new(hosts, exit_statuses)
122
+ end
123
+
124
+ def exit_statuses
125
+ return @exit_statuses if @exit_statuses
126
+ @exit_statuses = {}
127
+ @threads.each do |host, thread|
128
+ @exit_statuses[host] = thread.value
129
+ end
130
+ @exit_statuses
131
+ end
132
+
133
+ def exit_status(pid)
134
+ pid, status = Process.waitpid2(pid)
135
+ return status
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,31 @@
1
+ require 'dply/git'
2
+ module Dply
3
+ class Repo
4
+
5
+ attr_reader :dir, :upstream, :mirror
6
+
7
+ def initialize(dir, upstream, mirror: nil)
8
+ @dir = dir
9
+ @upstream = upstream
10
+ @mirror = mirror
11
+ end
12
+
13
+ def create
14
+ if Dir.exist? "#{dir}/.git"
15
+ raise "unable to create repo" if not verify_remote_url
16
+ else
17
+ Git.clone upstream, dir
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def verify_remote_url
24
+ remote_url = Dir.chdir(dir) do
25
+ Git.get_remote_url
26
+ end
27
+ remote_url == upstream
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,65 @@
1
+ module Dply
2
+ class Report
3
+
4
+ attr_reader :hosts, :exit_statuses
5
+
6
+ def initialize(hosts, exit_statuses)
7
+ @hosts = hosts
8
+ @exit_statuses = exit_statuses
9
+ end
10
+
11
+
12
+ def print_full
13
+ print_successful_jobs
14
+ print_failed_jobs
15
+ print_summary
16
+ end
17
+
18
+ def succeeded
19
+ @succeeded ||= exit_statuses.keys.select do |k|
20
+ exit_statuses[k] == 0
21
+ end
22
+ end
23
+
24
+ def failed
25
+ @failed ||= exit_statuses.keys.select do |k|
26
+ exit_statuses[k] != 0
27
+ end
28
+ end
29
+
30
+ def print_successful_jobs
31
+ puts "succeeded".green
32
+ succeeded.each do |host|
33
+ puts " - #{host[:id]}"
34
+ end
35
+ end
36
+
37
+ def print_failed_jobs
38
+ return if failed.count == 0
39
+ puts "failed".red
40
+ failed.each do |host|
41
+ puts " - #{host[:id]}"
42
+ end
43
+ end
44
+
45
+
46
+ def print_summary
47
+ total_hosts = hosts.count
48
+ run_count = @exit_statuses.count
49
+ not_run = total_hosts - run_count
50
+ if (failed.count > 0 || not_run > 0 )
51
+ not_run_error = "not run on #{not_run}/#{total_hosts}" if not_run > 0
52
+ failed_error = "failed on #{failed.count} of #{total_hosts} hosts" if failed.count > 0
53
+
54
+ errors = []
55
+ errors << not_run_error if not_run_error
56
+ errors << failed_error if failed_error
57
+
58
+ error_str = "task #{errors.join(", ")}"
59
+ raise ::Dply::Error, error_str
60
+ end
61
+ puts "tasks ran successfully on #{succeeded.count}/#{total_hosts} hosts"
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,12 @@
1
+ module Dply
2
+ module Setup
3
+
4
+ def self.load(strategy, config)
5
+ require_relative "setup/#{strategy}"
6
+ const = "::Dply::Setup::#{strategy.capitalize}"
7
+ const = Module.const_get(const)
8
+ return const.new(config)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ require 'fileutils'
2
+ require 'dply/helper'
3
+ require 'dply/shared_dirs'
4
+
5
+ module Dply
6
+ module Setup
7
+ class Default
8
+
9
+ include Helper
10
+
11
+ attr_reader :config
12
+
13
+ def initialize(config)
14
+ @config = config
15
+ end
16
+
17
+ def run
18
+ Dir.chdir config.deploy_dir do
19
+ shared_dirs.create
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def shared_dirs
26
+ @shared_dirs ||= SharedDirs.new
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ require 'fileutils'
2
+ require 'dply/helper'
3
+ require 'dply/repo'
4
+ require 'dply/shared_dirs'
5
+
6
+ module Dply
7
+ module Setup
8
+ class Git
9
+
10
+ include Helper
11
+
12
+ attr_reader :config
13
+
14
+ def initialize(config)
15
+ @config = config
16
+ end
17
+
18
+ def run
19
+ Dir.chdir setup_dir do
20
+ repo.create
21
+ symlink "repo", "current"
22
+ create_dirs
23
+ shared_dirs.create_in "shared"
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def repo
30
+ @repo ||= ::Dply::Repo.new(repo_dir, config.repo)
31
+ end
32
+
33
+ def create_dirs
34
+ dirs = ["config", "shared"]
35
+ FileUtils.mkdir_p dirs
36
+ end
37
+
38
+ def shared_dirs
39
+ @shared_dirs ||= SharedDirs.new(config.shared_dirs)
40
+ end
41
+
42
+ def setup_dir
43
+ config.deploy_dir
44
+ end
45
+
46
+ def repo_dir
47
+ "#{setup_dir}/repo"
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,58 @@
1
+ require 'fileutils'
2
+ require 'dply/helper'
3
+
4
+ module Dply
5
+ class Setup
6
+
7
+ include Helper
8
+
9
+ attr_reader :config
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ def run
16
+ method = "#{config.mode}_mode".to_sym
17
+ send method
18
+ end
19
+
20
+ def git_mode
21
+ Dir.chdir deploy_dir do
22
+ git.clone config.repo, "repo"
23
+ FileUtils.mkdir_p "config"
24
+ symlink "repo", "current"
25
+ end
26
+ end
27
+
28
+ def release_mode
29
+ Dir.chdir deploy_dir do
30
+ git.clone "repo_cache"
31
+ FileUtils.mkdir_p "shared", "releases", "config"
32
+ create_tmp_dirs "shared"
33
+ create_extra_shared_dirs "shared"
34
+ end
35
+ end
36
+
37
+ def default_mode
38
+ create_tmp_dirs deploy_dir
39
+ end
40
+
41
+ private
42
+
43
+ def create_tmp_dirs(dir)
44
+ dirs = "tmp/sockets", "tmp/pids", "log"
45
+ Dir.chdir(dir) { FileUtils.mkdir_p dirs }
46
+ end
47
+
48
+ def create_extra_shared_dirs(dir)
49
+ dirs = config.shared_dirs
50
+ Dir.chdir dir { FileUtils.mkdir_p dirs }
51
+ end
52
+
53
+ def deploy_dir
54
+ config.deploy_dir
55
+ end
56
+
57
+ end
58
+ end