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.
@@ -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