docker-cli 0.3.1 → 0.5.0
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 +4 -4
- data/.release_history.yml +2 -0
- data/.rubocop.yml +2 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +24 -27
- data/Rakefile +2 -1
- data/docker-cli.gemspec +5 -2
- data/exe/_gemdocker +225 -0
- data/exe/dc-update +24 -0
- data/exe/gemdocker +52 -0
- data/exe/gemdocker_test +40 -0
- data/lib/docker/cli/command.rb +37 -31
- data/lib/docker/cli/command_factory.rb +49 -25
- data/lib/docker/cli/command_result.rb +16 -0
- data/lib/docker/cli/docker_composer.rb +113 -0
- data/lib/docker/cli/docker_container.rb +216 -0
- data/lib/docker/cli/docker_image.rb +205 -0
- data/lib/docker/cli/docker_run_log.rb +169 -0
- data/lib/docker/cli/dockerfile.rb +62 -0
- data/lib/docker/cli/dockerfile_template/dup_gem_bundler_env.rb +73 -0
- data/lib/docker/cli/dockerfile_template/match_user.rb +38 -0
- data/lib/docker/cli/dockerfile_template.rb +69 -0
- data/lib/docker/cli/gem/bundler_helper.rb +25 -0
- data/lib/docker/cli/image_helper.rb +43 -0
- data/lib/docker/cli/operations/args_parser.rb +53 -0
- data/lib/docker/cli/operations/run.rb +203 -0
- data/lib/docker/cli/operations/run_del.rb +67 -0
- data/lib/docker/cli/operations/run_keep.rb +118 -0
- data/lib/docker/cli/user_info.rb +39 -0
- data/lib/docker/cli/version.rb +1 -1
- data/lib/docker/cli.rb +64 -0
- data/scripts/create_user.sh.erb +10 -0
- metadata +51 -12
@@ -0,0 +1,169 @@
|
|
1
|
+
|
2
|
+
require 'singleton'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Docker
|
6
|
+
module Cli
|
7
|
+
|
8
|
+
class NonEmptyRecord < StandardError; end
|
9
|
+
|
10
|
+
class DockerRunLog
|
11
|
+
include TR::CondUtils
|
12
|
+
include Singleton
|
13
|
+
|
14
|
+
def log(image, container, opts = {})
|
15
|
+
if not_empty?(image) and not_empty?(container)
|
16
|
+
logfile[image] = [] if logfile[image].nil?
|
17
|
+
cont = { container: container, created_at: Time.now.to_i, last_run: Time.now.to_i }
|
18
|
+
cont = cont.merge(opts) if not_empty?(opts)
|
19
|
+
logfile[image] << cont
|
20
|
+
write
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#def log_dockerfile(df)
|
25
|
+
# if not_empty?(df) and File.exist?(df)
|
26
|
+
# logfile[:dockerfile_signature] = [] if logfile[:dockerfile_signature].nil?
|
27
|
+
# d = digest_file(df)
|
28
|
+
# if not logfile[:dockerfile_signature].include?(d)
|
29
|
+
# logfile[:dockerfile_signature] << digest.hexdigest(File.read(df))
|
30
|
+
# end
|
31
|
+
# write
|
32
|
+
# end
|
33
|
+
#end
|
34
|
+
|
35
|
+
def log_dockerfile_image(df, image)
|
36
|
+
if not_empty?(df) and File.exist?(df) and not_empty?(image)
|
37
|
+
logfile[:dockerfile_images] = { } if logfile[:dockerfile_images].nil?
|
38
|
+
d = digest_file(df)
|
39
|
+
logfile[:dockerfile_images][d] = [] if logfile[:dockerfile_images][d].nil?
|
40
|
+
logfile[:dockerfile_images][d] << image
|
41
|
+
write
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def has_dockerfile_built_to_image?(df)
|
46
|
+
if not_empty?(df)
|
47
|
+
if File.exist?(df)
|
48
|
+
d = digest_file(df)
|
49
|
+
else
|
50
|
+
d = df
|
51
|
+
end
|
52
|
+
|
53
|
+
not (logfile[:dockerfile_images].nil? or logfile[:dockerfile_images][d].nil?)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def dockerfile_images(df)
|
58
|
+
if File.exist?(df)
|
59
|
+
d = digest_file(df)
|
60
|
+
else
|
61
|
+
d = df
|
62
|
+
end
|
63
|
+
|
64
|
+
if (logfile[:dockerfile_images].nil? or logfile[:dockerfile_images][d].nil?)
|
65
|
+
[]
|
66
|
+
else
|
67
|
+
logfile[:dockerfile_images][d]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def has_dockerfile_seen_before?(df)
|
72
|
+
logger.debug "dockerfile_seen_before? #{df}"
|
73
|
+
if not_empty?(df) and File.exist?(df)
|
74
|
+
d = Cli.digest_bin(File.read(df))
|
75
|
+
logger.debug "Digest : #{d}"
|
76
|
+
logger.debug "Record : #{logfile[:dockerfile_images]}"
|
77
|
+
if not logfile[:dockerfile_images].nil?
|
78
|
+
logfile[:dockerfile_images].include?(d)
|
79
|
+
else
|
80
|
+
false
|
81
|
+
end
|
82
|
+
else
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def digest_file(path)
|
88
|
+
if not_empty?(path) and File.exist?(path)
|
89
|
+
Cli.digest_bin(File.read(path))
|
90
|
+
else
|
91
|
+
""
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def image_has_containers?(image)
|
96
|
+
not logfile[image].nil? and logfile[image].length > 0
|
97
|
+
end
|
98
|
+
|
99
|
+
def delete_image(image, opts = {})
|
100
|
+
if logfile[image].nil? or is_empty?(logfile[image])
|
101
|
+
logfile.delete(image)
|
102
|
+
elsif not_empty?(opts) and opts[:force] == true
|
103
|
+
logfile.delete(image)
|
104
|
+
else
|
105
|
+
raise NonEmptyRecord, "Image #{image} has #{logfile[image].length} container(s). Remove image failed."
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def update_last_run(image, cont)
|
110
|
+
if not logfile[image].nil? and not logfile[image][cont].nil?
|
111
|
+
logfile[image][cont][:last_run] = Time.now.to_i
|
112
|
+
write
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def image_containers(image)
|
117
|
+
if not logfile[image].nil?
|
118
|
+
logfile[image]
|
119
|
+
else
|
120
|
+
[]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def all_logs
|
125
|
+
logfile.freeze
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
def logfile
|
130
|
+
if @_logfile.nil?
|
131
|
+
if File.exist?(log_path)
|
132
|
+
@_logfile = YAML.load(File.read(log_path))
|
133
|
+
else
|
134
|
+
@_logfile = {}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
@_logfile
|
138
|
+
end
|
139
|
+
|
140
|
+
def write
|
141
|
+
File.open(log_path,"w") do |f|
|
142
|
+
f.write YAML.dump(logfile)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def log_path
|
147
|
+
if @_logPath.nil?
|
148
|
+
@_logPath = File.join(Dir.getwd, ".docker_run_log")
|
149
|
+
end
|
150
|
+
@_logPath
|
151
|
+
end
|
152
|
+
|
153
|
+
def digest
|
154
|
+
if @_digest.nil?
|
155
|
+
@_digest = Cli.digest
|
156
|
+
end
|
157
|
+
@_digest
|
158
|
+
end
|
159
|
+
|
160
|
+
def logger
|
161
|
+
if @_logger.nil?
|
162
|
+
@_logger = Cli.logger(:drLog)
|
163
|
+
end
|
164
|
+
@_logger
|
165
|
+
end
|
166
|
+
|
167
|
+
end # class DockerRunLog
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
module Docker
|
5
|
+
module Cli
|
6
|
+
class Dockerfile
|
7
|
+
|
8
|
+
class NoDockerfileFound < StandardError; end
|
9
|
+
|
10
|
+
def self.find_available(root = Dir.getwd)
|
11
|
+
Dir.glob("**/Dockerfile*")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.run_before?(dockerfile_path)
|
15
|
+
DockerRunLog.instance.has_dockerfile_seen_before?(dockerfile_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
# expect dockerfile is CONTENT, not file path
|
19
|
+
def self.images(dockerfile)
|
20
|
+
DockerRunLog.instance.dockerfile_images(dockerfile)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(file)
|
24
|
+
@dfile = file
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_dockerfile(vals = {}, &block)
|
28
|
+
if @_df.nil?
|
29
|
+
if @dfile.nil?
|
30
|
+
@_df = nil
|
31
|
+
else
|
32
|
+
if File.exist?(@dfile)
|
33
|
+
if is_erb_template?
|
34
|
+
@_df = process_dockerfile_template(@dfile, vals)
|
35
|
+
else
|
36
|
+
@_df = File.read(@dfile)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
@_df = nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@_df
|
44
|
+
end
|
45
|
+
|
46
|
+
def is_erb_template?
|
47
|
+
if File.exist?(@dfile)
|
48
|
+
cont = File.read(@dfile)
|
49
|
+
(cont =~ /<%=/ and cont =~ /%>/) != nil
|
50
|
+
else
|
51
|
+
false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def process_dockerfile_template(file, values = {})
|
56
|
+
raise Error, "Given dockerfile to process as template not found" if not File.exist?(file)
|
57
|
+
DockerfileTemplate::TemplateEngine.new.process(File.read(file), values)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
module Docker
|
3
|
+
module Cli
|
4
|
+
module DockerfileTemplate
|
5
|
+
module DupGemBundlerEnv
|
6
|
+
|
7
|
+
# DSL entry by including into DockerfileTemplate module
|
8
|
+
def dup_gem_bundler_env(&block)
|
9
|
+
if has_dev_gems?
|
10
|
+
logger.debug "Detected development gems"
|
11
|
+
|
12
|
+
#add_mandatory_key(:docker_root)
|
13
|
+
#if not has_mandatory_keys? and block
|
14
|
+
# @docker_root = block.call(:docker_root)
|
15
|
+
#else
|
16
|
+
raise TemplateKeyRequired, "docker_root is required for dup_gem_bundler_env to function" if is_empty?(@docker_root)
|
17
|
+
#end
|
18
|
+
|
19
|
+
# gen shell script
|
20
|
+
res = gen_script
|
21
|
+
block.call(:script_output, res) if block
|
22
|
+
localPath = File.join(Dir.getwd,"dup_gem_bundler_env.rb")
|
23
|
+
File.open(localPath,"w") do |f|
|
24
|
+
f.write res
|
25
|
+
end
|
26
|
+
|
27
|
+
inst = []
|
28
|
+
# copy inside docker
|
29
|
+
inst << "COPY #{File.basename(localPath)} /tmp/dup_gem_bundler_env.rb"
|
30
|
+
# run the script
|
31
|
+
inst << "RUN ruby /tmp/dup_gem_bundler_env.rb"
|
32
|
+
# for the docker just this two lines
|
33
|
+
# but the localPath must be there first before this two
|
34
|
+
# lines can come into effect
|
35
|
+
inst.join("\n")
|
36
|
+
else
|
37
|
+
""
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def has_dev_gems?
|
43
|
+
not Cli.find_dev_gems.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def gen_script
|
47
|
+
|
48
|
+
res = %Q(
|
49
|
+
#!/usr/bin/env ruby
|
50
|
+
|
51
|
+
## This file is auto-generated.
|
52
|
+
|
53
|
+
<% Docker::Cli.find_dev_gems.each do |name, pa| %>
|
54
|
+
`bundle config --global local.<%= name %> <%= File.join(@docker_root, File.basename(pa)) %>`
|
55
|
+
<% end %>
|
56
|
+
)
|
57
|
+
|
58
|
+
ERB.new(res).result(binding)
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def logger
|
63
|
+
if @_logger
|
64
|
+
@_logger = Cli.logger(:temp_dup_gem_bundler_env)
|
65
|
+
end
|
66
|
+
@_logger
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
require 'erb'
|
3
|
+
require_relative '../user_info'
|
4
|
+
|
5
|
+
module Docker
|
6
|
+
module Cli
|
7
|
+
module DockerfileTemplate
|
8
|
+
module MatchUser
|
9
|
+
|
10
|
+
# DSL entry
|
11
|
+
def match_user
|
12
|
+
|
13
|
+
logger.debug "match_user called"
|
14
|
+
ui = UserInfo.user_info
|
15
|
+
gi = UserInfo.group_info
|
16
|
+
|
17
|
+
ERB.new(user_template).result_with_hash({ user_group_id: gi[:gid], user_group_name: gi[:group_name], user_id: ui[:uid], user_login: ui[:login] })
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def user_template
|
23
|
+
if @_ut.nil?
|
24
|
+
@_ut = []
|
25
|
+
@_ut << "RUN apt-get install -y sudo && groupadd -f -g <%= user_group_id %> <%= user_group_name %> && useradd -u <%= user_id %> -g <%= user_group_id %> -m <%= user_login %> && usermod -aG sudo <%= user_login %> && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers"
|
26
|
+
@_ut << "USER <%= user_login %>"
|
27
|
+
end
|
28
|
+
@_ut.join("\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
def logger
|
32
|
+
Cli.logger(:temp_match_user)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
|
2
|
+
Dir.glob(File.join(File.dirname(__FILE__),"dockerfile_template","*.rb")).each do |f|
|
3
|
+
require_relative f
|
4
|
+
end
|
5
|
+
|
6
|
+
module Docker
|
7
|
+
module Cli
|
8
|
+
module DockerfileTemplate
|
9
|
+
class TemplateKeyRequired < StandardError; end
|
10
|
+
class TemplateEngine
|
11
|
+
include MatchUser
|
12
|
+
include DupGemBundlerEnv
|
13
|
+
|
14
|
+
def process(cont, values = {}, &block)
|
15
|
+
logger.debug "Got values : #{values}"
|
16
|
+
values.each do |k,v|
|
17
|
+
|
18
|
+
logger.debug "Creating field #{k}"
|
19
|
+
|
20
|
+
self.class.class_eval <<-END
|
21
|
+
if not (Class.instance_methods.include?(:#{k}) and Class.instace_methods.include?(:#{k}=))
|
22
|
+
attr_accessor :#{k}
|
23
|
+
elsif not Class.instance_methods.include?(:#{k})
|
24
|
+
attr_reader :#{k}
|
25
|
+
elsif not Class.instance_methods.include?(:#{k}=)
|
26
|
+
attr_writer :#{k}
|
27
|
+
end
|
28
|
+
END
|
29
|
+
|
30
|
+
self.send("#{k}=", v)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
ERB.new(cont).result(binding)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def logger
|
39
|
+
if @_logger.nil?
|
40
|
+
@_logger = Cli.logger(:df_template)
|
41
|
+
end
|
42
|
+
@_logger
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_mandatory_key(key)
|
46
|
+
if @_man.nil?
|
47
|
+
@_man = []
|
48
|
+
end
|
49
|
+
@_man << key if not_empty?(key)
|
50
|
+
end
|
51
|
+
|
52
|
+
def mandatory_keys
|
53
|
+
@_man
|
54
|
+
end
|
55
|
+
|
56
|
+
def has_mandatory_keys?
|
57
|
+
given = true
|
58
|
+
mandatory_keys.each do |mk|
|
59
|
+
given = Class.instance_methods.include?(mk.to_sym) and Class.instance_methods.include?("#{mk}=".to_sym)
|
60
|
+
break if not given
|
61
|
+
end
|
62
|
+
|
63
|
+
given
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
module Docker
|
5
|
+
module Cli
|
6
|
+
module BundlerHelper
|
7
|
+
|
8
|
+
def find_local_dev_gems
|
9
|
+
|
10
|
+
res = {}
|
11
|
+
Bundler.load.dependencies.each do |d|
|
12
|
+
if not d.source.nil?
|
13
|
+
src = d.source
|
14
|
+
if src.path.to_s != "."
|
15
|
+
res[d.name] = src.path.expand_path.to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
res
|
20
|
+
|
21
|
+
end # find_local_dev_gem
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Docker
|
4
|
+
module Cli
|
5
|
+
module ImageHelper
|
6
|
+
|
7
|
+
def build_image(pmt, cmdFact)
|
8
|
+
|
9
|
+
root = Dir.getwd
|
10
|
+
dockerfile = File.join(root, "Dockerfile")
|
11
|
+
|
12
|
+
again = true
|
13
|
+
while again
|
14
|
+
if not File.exist?(dockerfile)
|
15
|
+
dockerfile = pmt.ask(" #{dockerfile} does not exist. Please provide new location of Dockerfile: ", required: true)
|
16
|
+
else
|
17
|
+
again = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
again = true
|
22
|
+
while again
|
23
|
+
dname = pmt.ask(" Please provide name of image at local : ", required: true)
|
24
|
+
if cmdFact.is_image_exist?(dname)
|
25
|
+
|
26
|
+
reuse = pmt.yes?(" Given local image name '#{dname}' already taken. Use back the same image? 'No' to retry with new name : ")
|
27
|
+
if reuse
|
28
|
+
again = false
|
29
|
+
end
|
30
|
+
else
|
31
|
+
rv = cmdFact.build_image(dname, dockerfile: dockerfile)
|
32
|
+
raise CommandFailed, "Build image command failed. Error was : #{rv.err_stream}"
|
33
|
+
again = false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
dname
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
require 'toolrack'
|
3
|
+
require_relative 'run_keep'
|
4
|
+
require_relative 'run_del'
|
5
|
+
require_relative 'run'
|
6
|
+
|
7
|
+
module Docker
|
8
|
+
module Cli
|
9
|
+
class ArgsParser
|
10
|
+
include TR::ArgUtils
|
11
|
+
|
12
|
+
class ArgsParserException < StandardError; end
|
13
|
+
|
14
|
+
OpsOption = [
|
15
|
+
"run-keep", "rk",
|
16
|
+
"run-del","rd",
|
17
|
+
"run","r"
|
18
|
+
]
|
19
|
+
|
20
|
+
arg_spec do
|
21
|
+
|
22
|
+
callback :pre_processing do |argv|
|
23
|
+
select_runner(argv)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def select_runner(argv)
|
29
|
+
ops = argv.first
|
30
|
+
if is_empty?(ops)
|
31
|
+
raise ArgsParserException, "\n Operation is empty. First parameter is operation. Supported operations including : #{OpsOption.join(", ")}\n\n"
|
32
|
+
else
|
33
|
+
case ops
|
34
|
+
when "run-keep", "rk"
|
35
|
+
Docker::Cli::Operations::RunKeep.new.parse_argv(argv[1..-1])
|
36
|
+
|
37
|
+
when "run-del", "rd"
|
38
|
+
Docker::Cli::Operations::RunDel.new.parse_argv(argv[1..-1])
|
39
|
+
|
40
|
+
when "run", "r"
|
41
|
+
Docker::Cli::Operations::Run.new.run
|
42
|
+
|
43
|
+
else
|
44
|
+
raise ArgsParserException, " Unknown operation '#{ops}'. First parameter is operation. Supported operations including : #{OpsOption.join(", ")}\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
[true, argv[1..-1]]
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|