concourse-technician 0.3.4 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.org +36 -3
- data/bin/technician +8 -4
- data/concourse-technician.gemspec +2 -0
- data/lib/concourse-technician.rb +1 -0
- data/lib/concourse-technician/volume_reaper.rb +133 -0
- metadata +43 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c97c608157d2c56434e208f79d548d58fd591c7
|
4
|
+
data.tar.gz: 5717fe2194475f37fa01e47ff57d0c9af67eefbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 194f576c50f1459eb1b1a48b5387f9281256e8efee93877c928e7fd684202a7cdcc325aaed2a16091d298c65440d530f857ebc6fca08df446c5d8cca31a67dce
|
7
|
+
data.tar.gz: 94af80ccab86ad6dc1dd36b941453f086aa86c518f00e9bc75d8c03b7f670e807e94204f54fab295438c564c8a67b293a8cb9ac11f3ae007f494f50e31c8019e
|
data/README.org
CHANGED
@@ -11,23 +11,56 @@
|
|
11
11
|
gem install concourse-technician
|
12
12
|
#+END_SRC
|
13
13
|
|
14
|
+
* Configuration
|
15
|
+
|
16
|
+
~concourse-technician~ needs to be pointed at your Concourse database. It
|
17
|
+
looks for a configuration file at =~/.config/concourse-technician.yaml=. To
|
18
|
+
tell it to look somewhere else, set =CONCOURSE_TECHNICIAN_CONFIG= in the
|
19
|
+
environment.
|
20
|
+
|
21
|
+
** Example
|
22
|
+
|
23
|
+
#+BEGIN_SRC yaml
|
24
|
+
---
|
25
|
+
adapter: postgres
|
26
|
+
database: my-awesome-database
|
27
|
+
host: localhost
|
28
|
+
password: MyVerySecurePasswordStoredUnencryptedOnDisk
|
29
|
+
port: 5432
|
30
|
+
user: me
|
31
|
+
|
32
|
+
volumes_root: /concourse/volumes
|
33
|
+
#+END_SRC
|
34
|
+
|
14
35
|
* Usage
|
15
36
|
|
16
37
|
To get a list of abandoned volumes...
|
17
38
|
|
18
39
|
#+BEGIN_SRC shell
|
19
|
-
technician abandoned_volumes
|
40
|
+
technician database abandoned_volumes
|
20
41
|
#+END_SRC
|
21
42
|
|
22
43
|
To clean up all abandoned volumes...
|
23
44
|
|
24
45
|
#+BEGIN_SRC shell
|
25
|
-
technician delete_abandoned_volumes
|
46
|
+
technician database delete_abandoned_volumes
|
47
|
+
#+END_SRC
|
48
|
+
|
49
|
+
To check if a worker is failing due to issues with volume reaping...
|
50
|
+
|
51
|
+
#+BEGIN_SRC shell
|
52
|
+
technician volume_reaper damaged
|
53
|
+
#+END_SRC
|
54
|
+
|
55
|
+
To resolve issues with volume reaping...
|
56
|
+
|
57
|
+
#+BEGIN_SRC shell
|
58
|
+
technician volume_reaper repair
|
26
59
|
#+END_SRC
|
27
60
|
|
28
61
|
* License
|
29
62
|
|
30
|
-
~
|
63
|
+
~concourse-technician~ is available under the [[https://tldrlegal.com/license/mit-license][MIT License]]. See ~LICENSE.txt~ for the full text.
|
31
64
|
|
32
65
|
* Contributors
|
33
66
|
|
data/bin/technician
CHANGED
@@ -3,9 +3,13 @@
|
|
3
3
|
require 'json'
|
4
4
|
require 'concourse-technician/cli'
|
5
5
|
require 'concourse-technician/database'
|
6
|
+
require 'concourse-technician/volume_reaper'
|
6
7
|
|
7
|
-
|
8
|
-
ConcourseTechnician::
|
9
|
-
|
10
|
-
|
8
|
+
commands = {
|
9
|
+
database: ConcourseTechnician::Database.new,
|
10
|
+
volume_reaper: ConcourseTechnician::VolumeReaper.new
|
11
|
+
}
|
12
|
+
|
13
|
+
ConcourseTechnician::CLI.new(commands).execute(*ARGV).tap do |out|
|
14
|
+
puts JSON.dump(out.respond_to?(:to_i) ? out.to_i : out.to_a) if out
|
11
15
|
end
|
@@ -12,7 +12,9 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
13
13
|
gem.require_paths = ['lib']
|
14
14
|
|
15
|
+
gem.add_runtime_dependency 'btrfs', '~> 0.1', '>= 0.1.5'
|
15
16
|
gem.add_runtime_dependency 'contracts', '~> 0.14', '>= 0.14.0'
|
16
17
|
gem.add_runtime_dependency 'instacli', '~> 1.1', '>= 1.1.2'
|
17
18
|
gem.add_runtime_dependency 'sequel', '~> 4.36', '>= 4.36.0'
|
19
|
+
gem.add_runtime_dependency 'systemized', '~> 0.2', '>= 0.2.4'
|
18
20
|
end
|
data/lib/concourse-technician.rb
CHANGED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'btrfs'
|
2
|
+
require 'contracts'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'systemized'
|
5
|
+
require 'time'
|
6
|
+
require_relative 'settings'
|
7
|
+
|
8
|
+
module ConcourseTechnician
|
9
|
+
class VolumeReaper
|
10
|
+
include ::Contracts::Core
|
11
|
+
include ::Contracts::Builtin
|
12
|
+
include Settings
|
13
|
+
|
14
|
+
Contract None => ArrayOf[Time]
|
15
|
+
def timestamps
|
16
|
+
@timestamps ||= worker.journal.read(1000).map do |entry|
|
17
|
+
Time.parse entry.split.first
|
18
|
+
end.sort
|
19
|
+
end
|
20
|
+
|
21
|
+
Contract None => ArrayOf[Hash]
|
22
|
+
def recent_events
|
23
|
+
@recent_events ||= logs.read(100).map do |event|
|
24
|
+
begin
|
25
|
+
JSON.load event
|
26
|
+
rescue JSON::ParserError
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end.compact
|
30
|
+
end
|
31
|
+
|
32
|
+
Contract None => Any
|
33
|
+
def damaged
|
34
|
+
detected? ? exit : abort
|
35
|
+
end
|
36
|
+
|
37
|
+
Contract None => Any
|
38
|
+
def repair
|
39
|
+
detected ? repair! : abort
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
Contract RespondTo[:to_s] => Bool
|
45
|
+
def env?(key)
|
46
|
+
if ![nil, 'nil', 'false'].include? ENV[key.to_s.upcase]
|
47
|
+
true
|
48
|
+
else
|
49
|
+
block_given? ? yield : false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Contract None => Bool
|
54
|
+
def stagnant_logs?
|
55
|
+
env?(:stagnant_logs) do
|
56
|
+
(Time.now - timestamps.last) > (timestamps.last - timestamps.first)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Contract None => Bool
|
61
|
+
def reaping_failure?
|
62
|
+
env?(:reaping_failure) do
|
63
|
+
recent_events.any? do |event|
|
64
|
+
event['message'] == 'baggageclaim.tick.failed-to-reap'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
Contract RespondTo[:to_s] => nil
|
70
|
+
def report(message)
|
71
|
+
STDERR.puts message.to_s unless env?(:QUIET)
|
72
|
+
end
|
73
|
+
|
74
|
+
Contract None => Bool
|
75
|
+
def detected?
|
76
|
+
stagnant_logs?.tap do |status|
|
77
|
+
report "Most recent log entry @ #{timestamps.last}"
|
78
|
+
report "Logs seem stagnant? #{status}"
|
79
|
+
end && reaping_failure?.tap do |status|
|
80
|
+
report "Recently volume reaping failure? #{status}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Contract None => ::Systemized::Service
|
85
|
+
def worker
|
86
|
+
@worker = ::Systemized::Service.new('concourse-worker')
|
87
|
+
end
|
88
|
+
|
89
|
+
Contract None => ::Systemized::Journal
|
90
|
+
def logs
|
91
|
+
@logs ||= ::Systemized::Journal.new('concourse-worker', output: 'cat')
|
92
|
+
end
|
93
|
+
|
94
|
+
Contract None => String
|
95
|
+
def volumes_root
|
96
|
+
@volumes_root ||= ENV.fetch('VOLUMES_ROOT') do
|
97
|
+
settings.fetch('volumes_root') { '/concourse/volumes' }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
Contract None => ArrayOf[::Btrfs::Subvolume]
|
102
|
+
def subvolumes
|
103
|
+
@subvolumes ||= ::Btrfs::Volume.new(volumes_root).subvolumes
|
104
|
+
end
|
105
|
+
|
106
|
+
Contract None => Maybe[Bool]
|
107
|
+
def reap_subvolumes
|
108
|
+
report "Reaping #{subvolumes.size} subvolumes..."
|
109
|
+
subvolumes.each(&:delete).all?(&:deleted?) unless env?(:NOOP)
|
110
|
+
end
|
111
|
+
|
112
|
+
Contract None => ArrayOf[String]
|
113
|
+
def dead_volumes
|
114
|
+
Dir.glob("#{volumes_root}/dead/*")
|
115
|
+
end
|
116
|
+
|
117
|
+
Contract None => Any
|
118
|
+
def reap_dead_volumes
|
119
|
+
report "Reaping #{dead_volumes.size} dead volumes..."
|
120
|
+
dead_volumes.each do |volume|
|
121
|
+
FileUtils.rmtree volume, verbose: env?(:QUIET), noop: env?(:NOOP)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
Contract None => Any
|
126
|
+
def repair!
|
127
|
+
worker.stop if worker.active?
|
128
|
+
reap_subvolumes
|
129
|
+
reap_dead_volumes
|
130
|
+
worker.start
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concourse-technician
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Olstrom
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: btrfs
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.1.5
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.1.5
|
13
33
|
- !ruby/object:Gem::Dependency
|
14
34
|
name: contracts
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,6 +90,26 @@ dependencies:
|
|
70
90
|
- - ">="
|
71
91
|
- !ruby/object:Gem::Version
|
72
92
|
version: 4.36.0
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: systemized
|
95
|
+
requirement: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0.2'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.2.4
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.2'
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 0.2.4
|
73
113
|
description:
|
74
114
|
email: chris@olstrom.com
|
75
115
|
executables:
|
@@ -86,6 +126,7 @@ files:
|
|
86
126
|
- lib/concourse-technician/cli.rb
|
87
127
|
- lib/concourse-technician/database.rb
|
88
128
|
- lib/concourse-technician/settings.rb
|
129
|
+
- lib/concourse-technician/volume_reaper.rb
|
89
130
|
homepage: https://github.com/colstrom/concourse-technician
|
90
131
|
licenses:
|
91
132
|
- MIT
|
@@ -111,4 +152,3 @@ signing_key:
|
|
111
152
|
specification_version: 4
|
112
153
|
summary: Troubleshoot Concourse without fly
|
113
154
|
test_files: []
|
114
|
-
has_rdoc:
|