gitlab-janitor 0.0.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 20a74b385cc01ef9b3fcaf380a1186f3f8bb24b278aa496d38cfc859889301bc
4
+ data.tar.gz: 2488753090d283ed78422395dded9e8935f060285987b032aae9350b3aecb3b4
5
+ SHA512:
6
+ metadata.gz: 39f830eb28f777462808abea43295d2e7297eed919028661bbfae9d213eb45f2c2e66ed3daea021fb48a790186bb2c678d287b0881c8580f82e5fc203ee650fa
7
+ data.tar.gz: cdbbeaa361c77f3aeb5deb707000c2ca93e0c0d26381f5c6b2be63c24917e95586b834fa17bcddfe15a80607c736be1961048c18ce75a7606fe306927877e82f
data/Dockerfile ADDED
@@ -0,0 +1,33 @@
1
+ ARG RUBY_VERSION=3.1
2
+
3
+ FROM ruby:${RUBY_VERSION}-alpine
4
+
5
+ WORKDIR /home/app
6
+
7
+ RUN mkdir -p /usr/local/etc \
8
+ && { \
9
+ echo 'install: --no-document'; \
10
+ echo 'update: --no-document'; \
11
+ } >> /usr/local/etc/gemrc \
12
+ && echo 'gem: --no-document' > ~/.gemrc
13
+
14
+ # RUN set -ex \
15
+ # && apk add --no-cache build-base git curl
16
+
17
+ ADD Gemfile Gemfile.lock /home/app/
18
+
19
+ RUN set -ex \
20
+ && gem install bundler && gem update bundler \
21
+ && bundle config set --local system 'true' \
22
+ && bundle install --jobs=3 \
23
+ && rm -rf /tmp/* /var/tmp/* /usr/src/ruby /root/.gem /usr/local/bundle/cache
24
+
25
+ ADD . /home/app/
26
+
27
+ RUN set -ex \
28
+ && bundle install --jobs=3 \
29
+ && rm -rf /tmp/* /var/tmp/* /usr/src/ruby /root/.gem /usr/local/bundle/cache
30
+
31
+ CMD ["bundle", "exec", "giltab-janitor"]
32
+
33
+
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+
6
+
7
+
data/Gemfile.lock ADDED
@@ -0,0 +1,86 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gitlab-janitor (0.0.3)
5
+ activesupport (~> 6.0)
6
+ docker-api
7
+ fugit
8
+ optparse
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activesupport (6.1.6.1)
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ i18n (>= 1.6, < 2)
16
+ minitest (>= 5.1)
17
+ tzinfo (~> 2.0)
18
+ zeitwerk (~> 2.3)
19
+ ansi (1.5.0)
20
+ awesome_print (1.9.2)
21
+ concurrent-ruby (1.1.10)
22
+ diff-lcs (1.5.0)
23
+ docile (1.4.0)
24
+ docker-api (2.2.0)
25
+ excon (>= 0.47.0)
26
+ multi_json
27
+ et-orbi (1.2.7)
28
+ tzinfo
29
+ excon (0.92.4)
30
+ fugit (1.5.3)
31
+ et-orbi (~> 1, >= 1.2.7)
32
+ raabro (~> 1.4)
33
+ i18n (1.12.0)
34
+ concurrent-ruby (~> 1.0)
35
+ minitest (5.16.2)
36
+ multi_json (1.15.0)
37
+ optparse (0.2.0)
38
+ raabro (1.4.0)
39
+ rake (13.0.6)
40
+ rspec (3.11.0)
41
+ rspec-core (~> 3.11.0)
42
+ rspec-expectations (~> 3.11.0)
43
+ rspec-mocks (~> 3.11.0)
44
+ rspec-core (3.11.0)
45
+ rspec-support (~> 3.11.0)
46
+ rspec-expectations (3.11.0)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.11.0)
49
+ rspec-mocks (3.11.1)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.11.0)
52
+ rspec-support (3.11.0)
53
+ rspec_junit_formatter (0.5.1)
54
+ rspec-core (>= 2, < 4, != 2.12.0)
55
+ simplecov (0.21.2)
56
+ docile (~> 1.1)
57
+ simplecov-html (~> 0.11)
58
+ simplecov_json_formatter (~> 0.1)
59
+ simplecov-console (0.9.1)
60
+ ansi
61
+ simplecov
62
+ terminal-table
63
+ simplecov-html (0.12.3)
64
+ simplecov_json_formatter (0.1.4)
65
+ terminal-table (3.0.2)
66
+ unicode-display_width (>= 1.1.1, < 3)
67
+ tzinfo (2.0.5)
68
+ concurrent-ruby (~> 1.0)
69
+ unicode-display_width (2.2.0)
70
+ zeitwerk (2.6.0)
71
+
72
+ PLATFORMS
73
+ x86_64-linux
74
+
75
+ DEPENDENCIES
76
+ awesome_print
77
+ bundler (~> 2.0, >= 2.0.1)
78
+ gitlab-janitor!
79
+ rake
80
+ rspec
81
+ rspec_junit_formatter
82
+ simplecov
83
+ simplecov-console
84
+
85
+ BUNDLED WITH
86
+ 2.3.15
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014-2019 Рнд Софт
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # gitlab-janitor
2
+
3
+ GitLab Janitor is a tool to automatically manage stalled containers when using Docker.
4
+
5
+ Commain line options and default valuee:
6
+
7
+ ```bash
8
+ $ ./gitlab-janitor.rb --help
9
+
10
+ Usage: gitlab-janitor.rb [options]
11
+ --clean-delay=30m Delay between clean operation ENV[CLEAN_DELAY]
12
+ --include=*units* <List> Include container for removal. ENV[INCLUDE]
13
+ --exclude=*gitlab* <List> Exclude container from removal by name. ENV[EXCLUDE]
14
+ --container-deadline=1s Maximum container run duration. ENV[CONTAINER_DEADLINE]
15
+ --volume-deadline=2d6h Maximum volume life dudation. ENV[VOLUME_DEADLINE]
16
+ --image-deadline=20d Maximum image life duration. ENV[IMAGE_DEADLINE]
17
+ --remove Real remove instead of dry run. ENV[REMOVE]
18
+ --docker=unix:///var/run/docker.sock
19
+ Docker api endpoint. ENV[DOCKER_HOST]
20
+ ```
21
+
22
+
23
+ ## Удаление зависших контейнеров
24
+
25
+ Порядок определения контейнреов для удаления:
26
+
27
+ - `include=*units*` - в список на удаление включаются контейнеры удовлетворябющие шаблону;
28
+ - `exclude=*gitlab*` - из спсика исключаются контейнеры по шаблону;
29
+ - `container-deadline=[1h10m]` - результирующий список проверяется на длительность запуска контенйра;
30
+
31
+ ## Удаление ненужных volumes
32
+
33
+ Порядок определения вольюмов для удалени:
34
+
35
+ - на удаление попадают только вольюмы, не являющиеся именованными;
36
+ - `volume-deadline=[2d6h]` - результирующий список проверяется на длительность существования вольюма;
37
+
38
+ ## Удаление образов
39
+
40
+ Docker не сохраняет временную метку образа при скачивании (pull), там образом используя средства Docker API невозможно понять как давно образ был скачан и когда его поря удалять. Для решения этой задачи сервис сохраняет информацию о скачанных образах, отслеживая там образом интервалы устаревания.
41
+
42
+ Порядок определения образов для удалени:
43
+
44
+ - на удаление попадают только образы, не имеющие тэг `latest`;
45
+ - `image-deadline=[20d]` - результирующий список проверяется на длительность существования образа;
46
+
47
+
48
+ ## Пример запуска
49
+
50
+ ```bash
51
+ REMOVE=true INCLUDE="*integr*, *units*" EXCLUDE="*gitlab*" CONTAINER_DEADLINE="1h10m" VOLUME_DEADLINE="3d" IMAGE_DEADLINE="20d" ./main.rb
52
+ ```
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #cwd = __dir__
4
+ #$root = "#{cwd}/"
5
+ #$: << $root
6
+
7
+ #lib = File.expand_path('lib', __dir__)
8
+ #$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
9
+
10
+
11
+ #require 'rubygems'
12
+ #require 'bundler'
13
+ #require 'bundler/setup'
14
+ #Bundler.require(:default)
15
+
16
+ require 'active_support/all'
17
+ require 'docker-api'
18
+ require 'fugit'
19
+ require 'optparse'
20
+
21
+ require 'gitlab-janitor'
22
+
23
+ UTIL = File.basename(__FILE__)
24
+
25
+ GitlabJanitor::Util.setup
26
+
27
+ @opts = {
28
+ includes: [ENV.fetch('INCLUDE', '*units*')],
29
+ excludes: [ENV.fetch('EXCLUDE', '*gitlab*')],
30
+ clean_delay: ENV.fetch('CLEAN_DELAY', '30m'),
31
+ container_deadline: ENV.fetch('CONTAINER_DEADLINE', '1h10m'),
32
+ volume_deadline: ENV.fetch('VOLUME_DEADLINE', '2d6h'),
33
+ image_deadline: ENV.fetch('IMAGE_DEADLINE', '20d'),
34
+ remove: ENV.fetch('REMOVE', 'false').to_bool,
35
+ docker_host: ENV.fetch('DOCKER_HOST', 'unix:///var/run/docker.sock')
36
+ }
37
+
38
+ parser = OptionParser.new do |o|
39
+ o.banner = "Usage: #{UTIL} [options] "
40
+
41
+ o.on("--clean-delay=#{@opts[:clean_delay]}", 'Delay between clean operation ENV[CLEAN_DELAY]') do |pattern|
42
+ @opts[:clean_delay] = pattern.strip
43
+ end
44
+
45
+ o.on("--include=#{@opts[:includes].join(',')}", '<List> Include container for removal. ENV[INCLUDE]') do |pattern|
46
+ @opts[:includes] += pattern.split(/[,;]/)
47
+ end
48
+
49
+ o.on("--exclude=#{@opts[:excludes].join(',')}", '<List> Exclude container from removal by name. ENV[EXCLUDE]') do |pattern|
50
+ @opts[:excludes] += pattern.split(/[,;]/)
51
+ end
52
+
53
+ o.on("--container-deadline=#{@opts[:container_deadline]}", 'Maximum container run duration. ENV[CONTAINER_DEADLINE]') do |pattern|
54
+ @opts[:container_deadline] = pattern.strip
55
+ end
56
+
57
+ o.on("--volume-deadline=#{@opts[:volume_deadline]}", 'Maximum volume life dudation. ENV[VOLUME_DEADLINE]') do |pattern|
58
+ @opts[:volume_deadline] = pattern.strip
59
+ end
60
+
61
+ o.on("--image-deadline=#{@opts[:image_deadline]}", 'Maximum image life duration. ENV[IMAGE_DEADLINE]') do |pattern|
62
+ @opts[:image_deadline] = pattern.strip
63
+ end
64
+
65
+ o.on("--remove", 'Real remove instead of dry run. ENV[REMOVE]') do |value|
66
+ @opts[:remove] = value.strip.to_bool
67
+ end
68
+
69
+ o.on("--docker=#{@opts[:docker_host]}", 'Docker api endpoint. ENV[DOCKER_HOST]') do |url|
70
+ @opts[:docker_host] = value.strip
71
+ end
72
+
73
+ end
74
+ parser.parse!
75
+
76
+ Docker.url = @opts[:docker_host]
77
+
78
+ GitlabJanitor::Util::logger.debug do
79
+ "Config: #{JSON.pretty_generate(@opts)}"
80
+ end
81
+
82
+ containers = GitlabJanitor::ContainerCleaner.new(
83
+ delay: Fugit::Duration.parse(@opts[:clean_delay]).to_sec,
84
+ includes: @opts[:includes],
85
+ excludes: @opts[:excludes],
86
+ deadline: Fugit::Duration.parse(@opts[:container_deadline]).to_sec
87
+ )
88
+
89
+ volumes = GitlabJanitor::VolumeCleaner.new(
90
+ delay: Fugit::Duration.parse(@opts[:clean_delay]).to_sec,
91
+ deadline: Fugit::Duration.parse(@opts[:volume_deadline]).to_sec
92
+ )
93
+
94
+
95
+ while !$exiting do
96
+ containers.clean(remove: @opts[:remove])
97
+ volumes.clean(remove: @opts[:remove])
98
+
99
+ sleep 3
100
+ end
101
+
102
+
103
+
104
+
@@ -0,0 +1,21 @@
1
+ version: "2.3"
2
+
3
+ services:
4
+ gitlab-janitor:
5
+ restart: 'unless-stopped'
6
+ healthcheck:
7
+ disable: true
8
+ build:
9
+ context: .
10
+ args:
11
+ RUBY_VERSION: 3.1
12
+ image: ${SERVICE_IMAGE-rnds/gitlab-janitor}:${SERVICE_TAG-latest}
13
+ working_dir: /home/app
14
+ tmpfs: /tmp
15
+
16
+
17
+
18
+
19
+
20
+
21
+
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ # Maintain your gem's version:
5
+ require 'gitlab-janitor/version'
6
+
7
+ Gem::Specification.new 'gitlab-janitor' do |spec|
8
+ spec.version = ENV['BUILDVERSION'].to_i > 0 ? "#{Lusnoc::VERSION}.#{ENV['BUILDVERSION'].to_i}" : GitlabJanitor::VERSION
9
+ spec.authors = ['Samoilenko Yuri']
10
+ spec.email = ['kinnalru@gmail.com']
11
+ spec.description = spec.summary = 'GitLab Janitor is a tool to automatically manage stalled containers when using Docker.'
12
+ spec.homepage = 'https://github.com/RnD-Soft/gitlab-janitor'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = Dir['bin/**/*', 'lib/**/*', 'Gemfile*', 'LICENSE', 'README.md', 'Dockerfile*', 'docker-compose.yml', '*.gemspec']
16
+ spec.bindir = 'bin'
17
+ spec.executables = spec.files.grep(%r{^bin/gitlab-janitor}) {|f| File.basename(f) }
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'bundler', '~> 2.0', '>= 2.0.1'
21
+ spec.add_development_dependency 'rake'
22
+ spec.add_development_dependency 'rspec'
23
+ spec.add_development_dependency 'rspec_junit_formatter'
24
+ spec.add_development_dependency 'simplecov'
25
+ spec.add_development_dependency 'simplecov-console'
26
+ spec.add_development_dependency 'awesome_print'
27
+
28
+ spec.add_runtime_dependency 'activesupport', '~> 6.0'
29
+ spec.add_runtime_dependency 'docker-api'
30
+ spec.add_runtime_dependency 'fugit'
31
+ spec.add_runtime_dependency 'optparse'
32
+ end
33
+
@@ -0,0 +1,50 @@
1
+ module GitlabJanitor
2
+ class BaseCleaner
3
+
4
+ class Model
5
+ attr_reader :model
6
+ def initialize(m)
7
+ @model = m
8
+ end
9
+
10
+ def method_missing(method, *args, &block)
11
+ super unless model.respond_to?(method)
12
+
13
+ model.send(method, *args, &block)
14
+ end
15
+
16
+ def respond_to?(*args)
17
+ model.send(:respond_to?, *args) || super
18
+ end
19
+
20
+ def respond_to_missing?(method_name, include_private = false)
21
+ model.send(:respond_to_missing?, method_name, include_private) || super
22
+ end
23
+ end
24
+
25
+ attr_reader :delay, :deadline, :logger
26
+
27
+ def initialize delay: 10, deadline: 1.second, logger: GitlabJanitor::Util::logger, **args
28
+ @delay = delay
29
+ @deadline = deadline
30
+ @logger = logger.tagged(self.class.to_s)
31
+ end
32
+
33
+ def clean(remove: false)
34
+ return nil if @cleaned_at && (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @cleaned_at) < @delay.seconds
35
+
36
+ do_clean(remove: remove)
37
+
38
+ @cleaned_at = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
39
+ return true
40
+ end
41
+
42
+ def log_exception(text)
43
+ yield
44
+ rescue StandardError => e
45
+ logger.error("Exception in #{text}: #{e}")
46
+ logger.error e.backtrace
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,114 @@
1
+ module GitlabJanitor
2
+ class ContainerCleaner < BaseCleaner
3
+
4
+ class Model < BaseCleaner::Model
5
+ def initialize(v)
6
+ super(v)
7
+
8
+ info['_Age'] = (Time.now - Time.at(created_at)).round(0)
9
+ end
10
+
11
+ def created_at
12
+ info['Created']
13
+ end
14
+
15
+ def name
16
+ @anme ||= info['Names'].first.sub(/^\//, '')
17
+ end
18
+
19
+ def age
20
+ info['_Age']
21
+ end
22
+ end
23
+
24
+ attr_reader :excludes, :includes
25
+
26
+ def initialize includes: [''], excludes: [''], **args
27
+ super(**args)
28
+ @includes = includes
29
+ @excludes = excludes
30
+ @deadline = deadline
31
+ end
32
+
33
+ def do_clean(remove: false)
34
+ to_remove, keep = prepare(Docker::Container.all(all: true).map{|m| Model.new(m)})
35
+
36
+ if !to_remove.empty?
37
+ keep.each do |c|
38
+ logger.debug(" KEEP #{c.name}")
39
+ end
40
+
41
+ if remove
42
+ logger.info "Removing containers..."
43
+ to_remove.each do |c|
44
+ logger.tagged(c.name) do
45
+ logger.debug " Removing..."
46
+ log_exception("Stop") {c.stop}
47
+ log_exception("Wait") {c.wait(15)}
48
+ log_exception("Remove") {c.remove}
49
+ logger.debug " Removing COMPLETED"
50
+ end
51
+ end
52
+ else
53
+ logger.info "Skip removal due to dry run"
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ def prepare containers
60
+ @logger.debug("Selecting containers by includes #{@includes}...")
61
+ to_remove = select_by_name(containers)
62
+ if to_remove.empty?
63
+ @logger.info("Noting to remove.")
64
+ return [], containers
65
+ end
66
+ @logger.info("Selected containers: \n#{to_remove.map{|c| " + #{format_item(c)}"}.join("\n")}")
67
+
68
+ @logger.debug("Filtering containers by excludes #{@excludes}...")
69
+ to_remove = reject_by_name(to_remove)
70
+ if to_remove.empty?
71
+ @logger.info("Noting to remove.")
72
+ return [], containers
73
+ end
74
+ @logger.info("Filtered containers: \n#{to_remove.map{|c| " + #{format_item(c)}"}.join("\n")}")
75
+
76
+ @logger.debug("Filtering containers by deadline: older than #{Fugit::Duration.parse(@deadline).deflate.to_plain_s}...")
77
+ to_remove = select_by_deadline(to_remove)
78
+ if to_remove.empty?
79
+ @logger.info("Noting to remove.")
80
+ return [], containers
81
+ end
82
+ @logger.info("Filtered containers: \n#{to_remove.map{|c| " + #{format_item(c)}"}.join("\n")}")
83
+
84
+ [to_remove, containers - to_remove]
85
+ end
86
+
87
+ def format_item c
88
+ "#{Time.at(c.created_at)} Age:#{Fugit::Duration.parse(c.age).deflate.to_plain_s.ljust(10)} #{c.name.first(60).ljust(60)}"
89
+ end
90
+
91
+ def select_by_name containers
92
+ containers.select do |c|
93
+ @includes.any? do |pattern|
94
+ File.fnmatch(pattern, c.name)
95
+ end
96
+ end
97
+ end
98
+
99
+ def reject_by_name containers
100
+ containers.reject do |c|
101
+ @excludes.any? do |pattern|
102
+ File.fnmatch(pattern, c.name)
103
+ end
104
+ end
105
+ end
106
+
107
+ def select_by_deadline containers
108
+ containers.select do |c|
109
+ c.age > deadline
110
+ end
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,69 @@
1
+ module GitlabJanitor
2
+
3
+ class Fmt < ActiveSupport::Logger::SimpleFormatter
4
+ def call(severity, timestamp, progname, msg)
5
+ super
6
+ end
7
+ end
8
+
9
+ class Util
10
+
11
+ TERM_SIGNALS = %w[INT TERM].freeze
12
+
13
+ class << self
14
+
15
+ def exiting?
16
+ $exiting
17
+ end
18
+
19
+ def exit!
20
+ $exiting = true
21
+ end
22
+
23
+ def logger
24
+ $logger
25
+ end
26
+
27
+ def setup
28
+ STDOUT.sync = true
29
+ STDERR.sync = true
30
+
31
+ $logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
32
+ $logger.level = ENV.fetch('LOG_LEVEL', Logger::INFO)
33
+ formatter = Logger::Formatter.new
34
+ formatter.extend ActiveSupport::TaggedLogging::Formatter
35
+ $logger.formatter = formatter
36
+
37
+
38
+
39
+ initialize_signal_handlers
40
+
41
+ String.class_eval do
42
+ def to_bool
43
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
44
+ return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
45
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
46
+ end
47
+ end
48
+ end
49
+
50
+ def initialize_signal_handlers
51
+ TERM_SIGNALS.each do |sig|
52
+ trap(sig) do |*_args|
53
+ TERM_SIGNALS.each do |sig|
54
+ trap(sig) do |*_args|
55
+ STDERR.puts 'Forcing exit!'
56
+ Kernel::exit!(1)
57
+ end
58
+ end
59
+
60
+ STDOUT.puts "Caught signal[#{sig}]: exiting...."
61
+ GitlabJanitor::Util.exit!
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,5 @@
1
+ module GitlabJanitor
2
+
3
+ VERSION = '0.0.3'.freeze
4
+
5
+ end
@@ -0,0 +1,90 @@
1
+ module GitlabJanitor
2
+ class VolumeCleaner < BaseCleaner
3
+
4
+ class Model < BaseCleaner::Model
5
+ def initialize(v)
6
+ super(v)
7
+
8
+ info['_Age'] = (Time.now - Time.parse(created_at)).round(0)
9
+ end
10
+
11
+ def created_at
12
+ info['CreatedAt']
13
+ end
14
+
15
+ def name
16
+ info['Name']
17
+ end
18
+
19
+ def age
20
+ info['_Age']
21
+ end
22
+
23
+ def mountpoint
24
+ info['Mountpoint']
25
+ end
26
+ end
27
+
28
+ def do_clean(remove: false)
29
+ to_remove, keep = prepare(Docker::Volume.all.map{|m| Model.new(m)})
30
+
31
+ if !to_remove.empty?
32
+ keep.each do |c|
33
+ logger.debug(" KEEP #{c.name}")
34
+ end
35
+ if remove
36
+ logger.info "Removing volumes..."
37
+ to_remove.each do |c|
38
+ logger.tagged(c.name.first(10)) do
39
+ logger.debug " Removing..."
40
+ log_exception("Remove") {c.remove}
41
+ logger.debug " Removing COMPLETED"
42
+ end
43
+ end
44
+ else
45
+ logger.info "Skip removal due to dry run"
46
+ end
47
+ end
48
+ end
49
+
50
+
51
+ def prepare volumes
52
+ @logger.debug("Selecting unnamed volumes...")
53
+ to_remove = select_unnamed(volumes)
54
+ if to_remove.empty?
55
+ @logger.info("Noting to remove.")
56
+ return [], volumes
57
+ end
58
+ @logger.info("Selected volumes: \n#{to_remove.map{|c| " + #{format_item(c)}"}.join("\n")}")
59
+
60
+ @logger.debug("Filtering volumes by deadline: older than #{@deadline} seconds...")
61
+ to_remove = select_by_deadline(to_remove)
62
+ if to_remove.empty?
63
+ @logger.info("Noting to remove.")
64
+ return [], volumes
65
+ end
66
+ @logger.info("Filtered volumes: \n#{to_remove.map{|c| " !! #{format_item(c)}"}.join("\n")}")
67
+
68
+ return to_remove, (volumes - to_remove)
69
+ end
70
+
71
+ def format_item c
72
+ "#{Time.parse(c.created_at)} Age:#{Fugit::Duration.parse(c.age).deflate.to_plain_s.ljust(13)} #{c.name.first(10).ljust(10)} #{c.mountpoint}"
73
+ end
74
+
75
+ SHA_RX = /^[a-zA-Z0-9]{64}$/
76
+
77
+ def select_unnamed volumes
78
+ volumes.select do |c|
79
+ SHA_RX.match(c.name)
80
+ end
81
+ end
82
+
83
+ def select_by_deadline containers
84
+ containers.select do |c|
85
+ c.age > deadline
86
+ end
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,5 @@
1
+ require_relative 'gitlab-janitor/version'
2
+ require_relative 'gitlab-janitor/utils'
3
+ require_relative 'gitlab-janitor/base-cleaner'
4
+ require_relative 'gitlab-janitor/container-cleaner'
5
+ require_relative 'gitlab-janitor/volume-cleaner'
metadata ADDED
@@ -0,0 +1,220 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitlab-janitor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Samoilenko Yuri
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-08-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.1
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.0.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec_junit_formatter
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: simplecov
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: simplecov-console
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: awesome_print
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: activesupport
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '6.0'
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '6.0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: docker-api
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :runtime
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: fugit
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ type: :runtime
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: optparse
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ description: GitLab Janitor is a tool to automatically manage stalled containers when
174
+ using Docker.
175
+ email:
176
+ - kinnalru@gmail.com
177
+ executables:
178
+ - gitlab-janitor
179
+ extensions: []
180
+ extra_rdoc_files: []
181
+ files:
182
+ - Dockerfile
183
+ - Gemfile
184
+ - Gemfile.lock
185
+ - LICENSE
186
+ - README.md
187
+ - bin/gitlab-janitor
188
+ - docker-compose.yml
189
+ - gitlab-janitor.gemspec
190
+ - lib/gitlab-janitor.rb
191
+ - lib/gitlab-janitor/base-cleaner.rb
192
+ - lib/gitlab-janitor/container-cleaner.rb
193
+ - lib/gitlab-janitor/utils.rb
194
+ - lib/gitlab-janitor/version.rb
195
+ - lib/gitlab-janitor/volume-cleaner.rb
196
+ homepage: https://github.com/RnD-Soft/gitlab-janitor
197
+ licenses:
198
+ - MIT
199
+ metadata: {}
200
+ post_install_message:
201
+ rdoc_options: []
202
+ require_paths:
203
+ - lib
204
+ required_ruby_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ required_rubygems_version: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ requirements: []
215
+ rubygems_version: 3.3.8
216
+ signing_key:
217
+ specification_version: 4
218
+ summary: GitLab Janitor is a tool to automatically manage stalled containers when
219
+ using Docker.
220
+ test_files: []