docker-janitor 0.0.3

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: 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: []