docker-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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 660f8254eeffba306d7a7b33be76730913a3436d
4
+ data.tar.gz: a18ced013b698ad73798e4eb4d40cc63508331e3
5
+ SHA512:
6
+ metadata.gz: 2fc2864783ba673f7ed5eea77a30f997842f3c66e0573f81a76d9a551b0cc9a8a7334fb2ae6bc6216fc3572224d55e85a4485d254d5817f875af24165438e5c0
7
+ data.tar.gz: f2e2ba806e48f9ca7affb0fa63e584eb6f1ac5d27b1d96c06948948ae3adbd994ea5b97059c3cdba9aa3b3b5e1ad4e94f7ac12118e98e648ca59a9fea1b7e1b6
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thor'
4
+ require 'sys/filesystem'
5
+ require 'ostruct'
6
+ require 'colorize'
7
+
8
+ require 'docker_janitor'
9
+
10
+ class DockerJanitorBin < Thor
11
+ class_option :dryrun, type: :boolean, aliases: ['dry-run', 'd'], default: false
12
+
13
+ desc 'clean', 'Clean up leftover docker cruft (does containers, images, volumes)'
14
+ long_desc <<-LONGDESC
15
+ Clean up leftover docker cruft
16
+
17
+ This command will clean up the saved cache items that docker
18
+ fills up your disk with. This includes containers that have
19
+ exited, images that are unlabeled, and old volumes.
20
+
21
+ To restrict what is cleaned up, you can specify [-c|--containers],
22
+ [-i|--images], and/or [-v|--volumes]. If nothing is specified,
23
+ all three will be cleaned.
24
+
25
+ Named volumes are not included by default to avoid data loss.
26
+ If you want to include them, pass --include-named or -n or --named
27
+
28
+ > $ docker-janitor [-c|--containers] [-i|--images] [-v|--volumes]
29
+ LONGDESC
30
+ option :containers, type: :boolean, aliases: ['c', 'container'], default: false
31
+ option :images, type: :boolean, aliases: ['i', 'image'], default: false
32
+ option :volumes, type: :boolean, aliases: ['v', 'volumes'], default: false
33
+ option :'include-named', type: :boolean, aliases: ['n', 'named'], default: false
34
+ def clean
35
+ args = options.dup
36
+ if !args[:containers] && !args[:images] && !args[:volumes]
37
+ args[:containers] = true
38
+ args[:images] = true
39
+ args[:volumes] = true
40
+ end
41
+ exec_clean(args)
42
+ end
43
+
44
+ desc 'containers', 'Delete exited containers'
45
+ def containers
46
+ exec_clean({
47
+ dryrun: options[:dryrun],
48
+ containers: true,
49
+ images: false,
50
+ volumes: false
51
+ })
52
+ end
53
+
54
+ desc 'images', 'Delete unlabeled imaged'
55
+ def images
56
+ exec_clean({
57
+ dryrun: options[:dryrun],
58
+ containers: false,
59
+ images: true,
60
+ volumes: false
61
+ })
62
+ end
63
+
64
+ desc 'volumes', 'Delete orphaned volumes'
65
+ def volumes
66
+ exec_clean({
67
+ dryrun: options[:dryrun],
68
+ containers: false,
69
+ images: false,
70
+ volumes: true
71
+ })
72
+ end
73
+
74
+ desc 'config-file', 'Write a default config file'
75
+ long_desc <<-LONGDESC
76
+ Writes a dory config file to #{DockerJanitor::Config.filename}
77
+ containing the default settings. This can then be configured
78
+ as preferred.
79
+ LONGDESC
80
+ def config_file
81
+ exec_config_file(options)
82
+ end
83
+
84
+ private
85
+
86
+ def exec_clean(options)
87
+ mounts = Sys::Filesystem.mounts.select do |m|
88
+ %w[ntfs vfat xfs].include?(m) ||
89
+ m.mount_type =~ /^ext[234]$/i ||
90
+ m.mount_type =~ /^reiser/i
91
+ end
92
+ mounts.map! do |m|
93
+ OpenStruct.new({
94
+ mount: m,
95
+ mbfree: mb_free(m.mount_point)
96
+ })
97
+ end
98
+
99
+ if options[:containers]
100
+ puts "[*] Deleting stopped containers".cyan
101
+ containers = DockerJanitor::Sh.run_command(
102
+ "docker ps -a | grep -E 'Exited|Created'"
103
+ ).stdout.split("\n")
104
+ puts "[*] #{containers.count} containers to delete".green
105
+ containers.each do |cont|
106
+ id_sha, image, command, created, status, ports, name = cont.split(/\s{2,}/)
107
+ unless name
108
+ name = ports
109
+ ports = ''
110
+ end
111
+ if options[:dryrun]
112
+ puts "[*] Not actually deleting containers (dry run)".yellow
113
+ else
114
+ if DockerJanitor::Config.settings[:"docker-janitor"][:safe_containers].any?{ |safe_container|
115
+ name =~ /#{safe_container}/ ||
116
+ id_sha =~ /#{safe_container}/ ||
117
+ image =~ /#{safe_container}/
118
+ }
119
+ puts "[*] Container '#{name}' with ID '#{id_sha}' is protected. " \
120
+ "Not deleting.".yellow
121
+ else
122
+ puts "[*] Deleting container '#{name}' with ID '#{id_sha}' created #{created}...".blue
123
+ unless DockerJanitor::Sh.run_command("docker rm #{id_sha}").success?
124
+ puts "[*] Failed deleting container '#{name}' with ID " \
125
+ "'#{id_sha}' with command '#{command}' created " \
126
+ "#{created}. Status: '#{status}'".red
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ if options[:images]
134
+ puts "[*] Deleting all unlabeled images".cyan
135
+ to_del = DockerJanitor::Sh.run_command(
136
+ "docker images | grep -iE '^<none>' | awk '{print $3}'"
137
+ ).stdout.split("\n")
138
+ puts "[*] #{to_del.count} images to delete".green
139
+ puts "[*] #{to_del.join(' ')}".blue
140
+ if to_del.empty?
141
+ puts "[*] No unlabeled images to delete!".green
142
+ else
143
+ if options[:dryrun]
144
+ puts "[*] Not actually deleting images (dry run)".yellow
145
+ else
146
+ if del_images.success?
147
+ puts "[*] Unlabeled images successfully delete".green
148
+ else
149
+ puts "[*] Error deleting unlabeled images".red
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ if options[:volumes]
156
+ puts "[*] Deleting all dangling docker volumes".cyan
157
+ if options[:'include-named']
158
+ command = 'docker volume ls -qf dangling=true'
159
+ else
160
+ command = "docker volume ls -qf dangling=true | grep -E --color=none '^[A-Fa-f0-9]{64}$'"
161
+ end
162
+ volumes = DockerJanitor::Sh.run_command(command).stdout.split("\n")
163
+ if volumes.empty?
164
+ puts "[*] No dangling volumes to delete!".green
165
+ else
166
+ puts "[*] Removing these volumes:".green
167
+ volumes.each{ |v| puts v.blue }
168
+ if options[:dryrun]
169
+ puts "[*] Not actually deleting volumes (dry run)".yellow
170
+ else
171
+ if del_volumes(volumes).success?
172
+ puts "[*] Succeeded removing dangling volumes".green
173
+ else
174
+ puts "[*] Error removing dangling volumes".red
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ puts "Mount Name:\tMount Point:\tFree Before:\tFree After:\tTotal freed:".cyan
181
+ mounts.each do |m|
182
+ m.mount.name
183
+ m.mount.mount_point
184
+ m.mbfree
185
+ puts "#{m.mount.name}\t" \
186
+ "#{m.mount.mount_point}\t\t" \
187
+ "#{free_space(m.mbfree)}\t\t" \
188
+ "#{free_space(mb_free(m.mount.mount_point))}\t\t" \
189
+ "#{free_space(mb_free(m.mount.mount_point) - m.mbfree)}"
190
+ end
191
+ end
192
+
193
+ def del_images
194
+ DockerJanitor::Sh.run_command(
195
+ "docker rmi $(docker images | grep -iE '^<none>' " \
196
+ "| awk '{print $3}' | xargs)"
197
+ )
198
+ end
199
+
200
+ def del_volumes(volumes)
201
+ DockerJanitor::Sh.run_command(
202
+ "docker volume rm #{volumes.join(' ')}"
203
+ )
204
+ end
205
+
206
+ def free_space(megabytes)
207
+ if megabytes / 1024 > 9
208
+ "#{megabytes / 1024} GB"
209
+ else
210
+ "#{megabytes} MB"
211
+ end
212
+ end
213
+
214
+ def mb_free(mount_point)
215
+ Sys::Filesystem.stat(mount_point).bytes_free / 1024 / 1024
216
+ end
217
+
218
+ def exec_config_file(options)
219
+ if File.exist?(DockerJanitor::Config.filename)
220
+ print "A config file already exists at #{DockerJanitor::Config.filename}" \
221
+ ". Overwrite with default settings? (Y/N): ".yellow
222
+ conf = STDIN.gets.chomp
223
+ unless conf =~ /y/i
224
+ puts "User declined over-writing. Not writing config file".red
225
+ return
226
+ end
227
+ end
228
+ puts "Writing config file to #{DockerJanitor::Config.filename}".green
229
+ DockerJanitor::Config.write_default_settings_file
230
+ end
231
+
232
+ end
233
+
234
+ if !ARGV.empty? && %w[-v --version].include?(ARGV.first)
235
+ puts "Docker Janitor - Version: #{DockerJanitor::VERSION}"
236
+ else
237
+ DockerJanitorBin.start(ARGV)
238
+ end
@@ -0,0 +1,3 @@
1
+ Gem.find_files('docker_janitor/**/*.rb').each do |path|
2
+ require path.gsub(/\.rb$/, '') unless path =~ /bot.*cli/
3
+ end
@@ -0,0 +1,52 @@
1
+ require 'yaml'
2
+
3
+ module DockerJanitor
4
+ class Config
5
+ def self.filename
6
+ "#{Dir.home}/.docker-janitor.yml"
7
+ end
8
+
9
+ def self.default_yaml
10
+ %q(---
11
+ :docker-janitor:
12
+ # safe-containers is an array of regular expressions
13
+ # against which container names will be checked to see if
14
+ # they are protected. This can help to make sure that
15
+ # data-only containers for example are not
16
+ # unintentionally deleted. These are ruby flavored
17
+ # regular expressions, check with the ruby operator =~
18
+ # rubular.com is a helpful site for testing your regex
19
+ :safe_containers:
20
+ - db$
21
+ - postgres
22
+ - mongo
23
+ - dynamo
24
+ - mysql
25
+ :safe_images:
26
+ - a-sha-of-some-sort
27
+ :safe_volumes:
28
+ - a-sha-of-some-sort
29
+ ).split("\n").map{|s| s.sub(' ' * 8, '')}.join("\n")
30
+ end
31
+
32
+ def self.default_settings
33
+ YAML.load(self.default_yaml)
34
+ end
35
+
36
+ def self.settings(filename = self.filename)
37
+ if File.exist?(filename)
38
+ self.default_settings.merge(YAML.load_file(filename))
39
+ else
40
+ self.default_settings
41
+ end
42
+ end
43
+
44
+ def self.write_settings(settings, filename = self.filename)
45
+ File.write(filename, settings)
46
+ end
47
+
48
+ def self.write_default_settings_file(filename = self.filename)
49
+ self.write_settings(self.default_yaml, filename)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ require 'ostruct'
2
+
3
+ module DockerJanitor
4
+ module Sh
5
+ def self.run_command(command)
6
+ stdout = `#{command}`
7
+ OpenStruct.new({
8
+ success?: $?.exitstatus == 0,
9
+ exitstatus: $?.exitstatus,
10
+ stdout: stdout
11
+ })
12
+ end
13
+ end
14
+
15
+ module Bash
16
+ def self.run_command(command)
17
+ stdout = `bash -c "#{command}"`
18
+ OpenStruct.new({
19
+ success?: $?.exitstatus == 0,
20
+ exitstatus: $?.exitstatus,
21
+ stdout: stdout
22
+ })
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module DockerJanitor
2
+ VERSION = "0.0.3"
3
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docker-janitor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Ben Porter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.19'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.19'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sys-filesystem
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.5'
97
+ description: 'The slack web api is good, but very raw. '
98
+ email: BenjaminPorter86@gmail.com
99
+ executables:
100
+ - docker-janitor
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - bin/docker-janitor
105
+ - lib/docker_janitor.rb
106
+ - lib/docker_janitor/config.rb
107
+ - lib/docker_janitor/shell.rb
108
+ - lib/docker_janitor/version.rb
109
+ homepage: https://github.com/FreedomBen/docker-janitor
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 2.5.2
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: docker janitor helps you clean up after the sometimes messy house guest known
133
+ as docker
134
+ test_files: []