docker-cli 0.3.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|