puppet_docker_tools 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9a052ffa04d431161f3d63267430416c2857234
4
- data.tar.gz: bad61b2a4a18d434760e620c7dfe3fe531f63a1d
3
+ metadata.gz: 9b6627e1ff33d2a5c6b9f302ad8268f3206f083b
4
+ data.tar.gz: 135420d71d73f036a552fa4619fbf0d7ac02b435
5
5
  SHA512:
6
- metadata.gz: ab37a2fc5f60e35a88dd59a447c72d45d461083a77edda97a868c6d02abe519565b994e5925f1b3d6cb6599e0d2a0c279e07b166c2919bca6fc07cdf94361ef0
7
- data.tar.gz: bc64d63f6cd8bc8285021b1746057a7236c9669217559517044e2016d23ec11fe4ae14faf8355a332b5293c90dd849d3adc57dbd3cf80f17f7c27c510c6a4833
6
+ metadata.gz: 291b7624f5478dd96a4e29be0516202ae01f453c1af520db31331b020b4a26b130ac542aa4ed7971e8d503f4cc8365a56ef8297c474236ab5dcb93ee3351f0a9
7
+ data.tar.gz: ddc9c5af3e64adfd1a5cb2ddb48bb07c07fa9534beb18fb4a837557054a717cb3de3b3fef893531eb072aa96df1e840e5efe4c2d8e4860762936ba14d8d66ef3
data/README.md CHANGED
@@ -21,7 +21,7 @@ Usage:
21
21
  puppet-docker pull [IMAGE] [--repository=<repo>]
22
22
  puppet-docker push [DIRECTORY] [--dockerfile=<dockerfile>] [--repository=<repo>] [--namespace=<namespace>] [--version=<version>] [--no-latest]
23
23
  puppet-docker rev-labels [DIRECTORY] [--dockerfile=<dockerfile>] [--namespace=<namespace>]
24
- puppet-docker spec [DIRECTORY]
24
+ puppet-docker spec [DIRECTORY] [--image=<image>]
25
25
  puppet-docker test [DIRECTORY] [--dockerfile=<dockerfile>]
26
26
  puppet-docker version [DIRECTORY] [--dockerfile=<dockerfile>] [--namespace=<namespace>]
27
27
  puppet-docker update-base-images [TAG]...
@@ -39,6 +39,7 @@ Options:
39
39
  --dockerfile=<dockerfile> File name for your dockerfile [default: Dockerfile]
40
40
  --version=<version> Version to build. This field will be used to determine the label and will be passed as the version build arg.
41
41
  **NOTE** `--build-arg version='<version>'` overrides `--version <version>`
42
+ --image=<image> Docker image to use for spec testing
42
43
  --build-arg=<buildarg> Build arg to pass to container build, can be passed multiple times.
43
44
  --no-latest Do not include the 'latest' tag when building and shipping images. By default, the 'latest' tag is built and shipped with the versioned tag.
44
45
  ```
@@ -13,7 +13,7 @@ Usage:
13
13
  puppet-docker pull [IMAGE] [--repository=<repo>]
14
14
  puppet-docker push [DIRECTORY] [--dockerfile=<dockerfile>] [--repository=<repo>] [--namespace=<namespace>] [--version=<version>] [--no-latest]
15
15
  puppet-docker rev-labels [DIRECTORY] [--dockerfile=<dockerfile>] [--namespace=<namespace>]
16
- puppet-docker spec [DIRECTORY]
16
+ puppet-docker spec [DIRECTORY] [--image=<image>]
17
17
  puppet-docker test [DIRECTORY] [--dockerfile=<dockerfile>]
18
18
  puppet-docker version [DIRECTORY] [--dockerfile=<dockerfile>] [--namespace=<namespace>]
19
19
  puppet-docker update-base-images [TAG]...
@@ -31,6 +31,7 @@ Options:
31
31
  --dockerfile=<dockerfile> File name for your dockerfile [default: Dockerfile]
32
32
  --version=<version> Version to build. This field will be used to determine the label and will be passed as the version build arg.
33
33
  **NOTE** `--build-arg version='<version>'` overrides `--version <version>`
34
+ --image=<image> Docker image to use for spec testing
34
35
  --build-arg=<buildarg> Build arg to pass to container build, can be passed multiple times.
35
36
  --no-latest Do not include the 'latest' tag when building and shipping images. By default, the 'latest' tag is built and shipped with the versioned tag.
36
37
  DOC
@@ -39,7 +40,7 @@ begin
39
40
  options = Docopt::docopt(doc)
40
41
  rescue Docopt::Exit
41
42
  puts doc
42
- exit
43
+ exit 1
43
44
  end
44
45
 
45
46
  defaults = {
@@ -51,6 +52,7 @@ defaults = {
51
52
  '--version' => nil,
52
53
  '--build-arg' => [],
53
54
  '--no-latest' => false,
55
+ '--image' => nil,
54
56
  }
55
57
 
56
58
  options.merge!(defaults) do |key, option, default|
@@ -91,7 +93,7 @@ begin
91
93
  elsif options['rev-labels']
92
94
  command_runner.rev_labels
93
95
  elsif options['spec']
94
- command_runner.spec
96
+ command_runner.spec(image: options['--image'])
95
97
  elsif options['test']
96
98
  command_runner.lint
97
99
  command_runner.spec
@@ -1,5 +1,4 @@
1
1
  require 'date'
2
- require 'docker'
3
2
  require 'json'
4
3
  require 'rspec/core'
5
4
  require 'time'
@@ -16,7 +15,7 @@ class PuppetDockerTools
16
15
  @namespace = namespace
17
16
  @dockerfile = dockerfile
18
17
 
19
- file = "#{directory}/#{dockerfile}"
18
+ file = File.join(directory, dockerfile)
20
19
  fail "File #{file} doesn't exist!" unless File.exist? file
21
20
  end
22
21
 
@@ -31,7 +30,7 @@ class PuppetDockerTools
31
30
  # 'arg=value'.
32
31
  # @param latest Whether or not to build the latest tag along with the
33
32
  # versioned image build.
34
- def build(no_cache: false, version: nil, build_args: [], latest: true)
33
+ def build(no_cache: false, version: nil, build_args: [], latest: true, stream_output: true)
35
34
  image_name = File.basename(directory)
36
35
  build_args_hash = {
37
36
  'vcs_ref' => PuppetDockerTools::Utilities.current_git_sha(directory),
@@ -49,7 +48,7 @@ class PuppetDockerTools
49
48
  build_args_hash.merge!(PuppetDockerTools::Utilities.parse_build_args(Array(build_args)))
50
49
  end
51
50
 
52
- build_args_hash = PuppetDockerTools::Utilities.filter_build_args(build_args: build_args_hash, dockerfile: "#{directory}/#{dockerfile}")
51
+ build_args_hash = PuppetDockerTools::Utilities.filter_build_args(build_args: build_args_hash, dockerfile: File.join(directory, dockerfile))
53
52
 
54
53
  # This variable is meant to be used for building the non-latest tagged build
55
54
  # If the version was set via `version` or `build_args`, use that. If not,
@@ -61,29 +60,47 @@ class PuppetDockerTools
61
60
  # and versions passed in to the dockerfile with an `ARG`
62
61
  version = build_args_hash['version'] || PuppetDockerTools::Utilities.get_value_from_env('version', namespace: namespace, directory: directory, dockerfile: dockerfile)
63
62
 
64
- path = "#{repository}/#{image_name}"
65
-
66
- build_options = {'dockerfile' => dockerfile, 'buildargs' => "#{build_args_hash.to_json}"}
63
+ path = File.join(repository, image_name)
67
64
 
65
+ build_options = []
68
66
  if no_cache
69
67
  puts "Ignoring cache for #{path}"
70
- build_options['nocache'] = true
68
+ build_options << '--no-cache'
71
69
  end
72
70
 
71
+ if dockerfile != "Dockerfile"
72
+ build_options << ['--file', dockerfile]
73
+ end
74
+
75
+ tags = []
73
76
  if latest
74
- puts "Building #{path}:latest"
77
+ tags << ['--tag', "#{path}:latest"]
78
+ end
75
79
 
76
- # 't' in the build_options sets the tag for the image we're building
77
- build_options['t'] = "#{path}:latest"
80
+ if version
81
+ tags << ['--tag', "#{path}:#{version}"]
82
+ end
78
83
 
79
- Docker::Image.build_from_dir(directory, build_options)
84
+ if tags.empty?
85
+ return nil
80
86
  end
81
87
 
82
- if version
83
- puts "Building #{path}:#{version}"
84
88
 
85
- build_options['t'] = "#{path}:#{version}"
86
- Docker::Image.build_from_dir(directory, build_options)
89
+ build_args = []
90
+ build_args_hash.map{ |k,v| "#{k}=#{v}" }.each do |val|
91
+ build_args << ['--build-arg', val]
92
+ end
93
+
94
+ build_command = ['docker', 'build', build_args, build_options, tags, directory].flatten
95
+
96
+ Open3.popen2e(*build_command) do |stdin, output_stream, wait_thread|
97
+ output=''
98
+ output_stream.each_line do |line|
99
+ stream_output ? (puts line) : (output += line)
100
+ end
101
+ exit_status = wait_thread.value.exitstatus
102
+ puts output unless stream_output
103
+ fail unless exit_status == 0
87
104
  end
88
105
  end
89
106
 
@@ -96,21 +113,15 @@ class PuppetDockerTools
96
113
 
97
114
  # make sure we have the container locally
98
115
  PuppetDockerTools::Utilities.pull("#{hadolint_container}:latest")
99
- container = Docker::Container.create('Cmd' => ['/bin/sh', '-c', "#{PuppetDockerTools::Utilities.get_hadolint_command}"], 'Image' => hadolint_container, 'OpenStdin' => true, 'StdinOnce' => true)
100
- # This container.tap startes the container created above, and passes directory/Dockerfile to the container
101
- container.tap(&:start).attach(stdin: "#{directory}/#{dockerfile}")
102
- # Wait for the run to finish
103
- container.wait
104
- exit_status = container.json['State']['ExitCode']
105
- unless exit_status == 0
106
- fail container.logs(stdout: true, stderr: true)
107
- end
116
+ docker_run = ['docker', 'run', '--rm', '-v', "#{File.join(Dir.pwd, directory, dockerfile)}:/Dockerfile:ro", '-i', 'hadolint/hadolint', PuppetDockerTools::Utilities.get_hadolint_command('Dockerfile')].flatten
117
+ output, status = Open3.capture2e(*docker_run)
118
+ fail output unless status == 0
108
119
  end
109
120
 
110
121
  # Run hadolint Dockerfile linting using a local hadolint executable. Executable
111
122
  # found based on your path.
112
123
  def local_lint
113
- output, status = Open3.capture2e(PuppetDockerTools::Utilities.get_hadolint_command("#{directory}/#{dockerfile}"))
124
+ output, status = Open3.capture2e(*PuppetDockerTools::Utilities.get_hadolint_command(File.join(directory,dockerfile)))
114
125
  fail output unless status == 0
115
126
  end
116
127
 
@@ -120,7 +131,7 @@ class PuppetDockerTools
120
131
  # versioned image build.
121
132
  def push(latest: true, version: nil)
122
133
  image_name = File.basename(directory)
123
- path = "#{repository}/#{image_name}"
134
+ path = File.join(repository, image_name)
124
135
 
125
136
  # only check for version from the label if we didn't pass it in
126
137
  if version.nil?
@@ -169,15 +180,20 @@ class PuppetDockerTools
169
180
 
170
181
  # Run spec tests
171
182
  #
172
- def spec
173
- tests = Dir.glob("#{directory}/spec/*_spec.rb")
183
+ def spec(image: nil)
184
+ if image
185
+ fail 'Oh no! You have PUPPET_TEST_DOCKER_IMAGE set! Please unset!' if ENV['PUPPET_TEST_DOCKER_IMAGE']
186
+ ENV['PUPPET_TEST_DOCKER_IMAGE'] = image
187
+ end
188
+
189
+ tests = Dir.glob(File.join(directory,'spec','*_spec.rb'))
174
190
  test_files = tests.map { |test| File.basename(test, '.rb') }
175
191
 
176
- puts "Running RSpec tests from #{File.expand_path("#{directory}/spec")} (#{test_files.join ","}), this may take some time"
192
+ puts "Running RSpec tests from #{File.expand_path(File.join(directory,'spec'))} (#{test_files.join ","}), this may take some time"
177
193
  success = true
178
194
  tests.each do |test|
179
- Open3.popen2e("rspec spec #{test}") do |stdin, output_stream, wait_thread|
180
- while line = output_stream.gets
195
+ Open3.popen2e('rspec', 'spec', test) do |stdin, output_stream, wait_thread|
196
+ output_stream.each_line do |line|
181
197
  puts line
182
198
  end
183
199
  exit_status = wait_thread.value.exitstatus
@@ -185,6 +201,10 @@ class PuppetDockerTools
185
201
  end
186
202
  end
187
203
 
204
+ if image
205
+ ENV['PUPPET_TEST_DOCKER_IMAGE'] = nil
206
+ end
207
+
188
208
  fail "Running RSpec tests for #{directory} failed!" unless success
189
209
  end
190
210
 
@@ -1,8 +1,2 @@
1
- require 'serverspec'
2
- require 'docker'
3
-
4
- # Travis builds can take time
5
- Docker.options[:read_timeout] = 7200
6
-
7
1
  # Load any shared examples or context helpers
8
2
  Dir[File.join(File.dirname(__FILE__), '..', '..', 'spec', 'support', '**', '*.rb')].sort.each { |f| require f }
@@ -1,5 +1,5 @@
1
- require 'docker'
2
1
  require 'open3'
2
+ require 'json'
3
3
 
4
4
  class PuppetDockerTools
5
5
  module Utilities
@@ -15,13 +15,10 @@ class PuppetDockerTools
15
15
  # command and a string containing the combined stdout and stderr
16
16
  # from the push
17
17
  def push_to_docker_repo(image_name, stream_output=true)
18
- Open3.popen2e("docker push #{image_name}") do |stdin, output_stream, wait_thread|
18
+ Open3.popen2e('docker', 'push', "#{image_name}") do |stdin, output_stream, wait_thread|
19
19
  output=''
20
- while line = output_stream.gets
21
- if stream_output
22
- puts line
23
- end
24
- output += line
20
+ output_stream.each_line do |line|
21
+ stream_output ? (puts line) : (output += line)
25
22
  end
26
23
  exit_status = wait_thread.value.exitstatus
27
24
  return exit_status, output
@@ -79,7 +76,8 @@ class PuppetDockerTools
79
76
  # @param value The value you want to get from the labels, e.g. 'version'
80
77
  # @param namespace The namespace for the value, e.g. 'org.label-schema'
81
78
  def get_value_from_label(image, value: , namespace: )
82
- labels = Docker::Image.get(image).json["Config"]["Labels"]
79
+ output, _ = Open3.capture2('docker', 'inspect', '-f', '"{{json .Config.Labels }}"', "#{image}")
80
+ labels = JSON.parse(output)
83
81
  labels["#{namespace}.#{value.tr('_', '-')}"]
84
82
  rescue
85
83
  nil
@@ -142,40 +140,19 @@ class PuppetDockerTools
142
140
  # Pull a docker image
143
141
  #
144
142
  # @param image The image to pull. If the image does not include the tag to
145
- # pull, it will pull all tags for that image
146
- def pull(image)
147
- if image.include?(':')
148
- puts "Pulling #{image}"
149
- PuppetDockerTools::Utilities.pull_single_tag(image)
150
- else
151
- puts "Pulling all tags for #{image}"
152
- PuppetDockerTools::Utilities.pull_all_tags(image)
153
- end
154
- end
155
-
156
- # Pull all tags for a docker image
157
- #
158
- # @param image The image to pull, e.g. puppet/puppetserver
159
- def pull_all_tags(image)
160
- Docker::Image.create('fromImage' => image)
161
-
162
- # Filter through existing tags of that image so we can output what we pulled
163
- images = Docker::Image.all('filter' => image)
164
- images.each do |img|
165
- timestamp = PuppetDockerTools::Utilities.format_timestamp(img.info["Created"])
166
- puts "Pulled #{img.info["RepoTags"].join(', ')}, last updated #{timestamp}"
143
+ # pull, it will pull the 'latest' tag for that image
144
+ def pull(image, stream_output = true)
145
+ Open3.popen2e('docker', 'pull', "#{image}") do |stdin, output_stream, wait_thread|
146
+ output=''
147
+ output_stream.each_line do |line|
148
+ stream_output ? (puts line) : (output += line)
149
+ end
150
+ exit_status = wait_thread.value.exitstatus
151
+ puts output unless stream_output
152
+ fail unless exit_status == 0
167
153
  end
168
154
  end
169
155
 
170
- # Pull a single tag of a docker image
171
- #
172
- # @param tag The image/tag to pull, e.g. puppet/puppetserver:latest
173
- def pull_single_tag(tag)
174
- image = Docker::Image.create('fromImage' => tag)
175
- timestamp = PuppetDockerTools::Utilities.format_timestamp(image.info["Created"])
176
- puts "Pulled #{image.info["RepoTags"].first}, last updated #{timestamp}"
177
- end
178
-
179
156
  # Pull the specified tags
180
157
  #
181
158
  # @param tags [Array] A list of tags to pull, e.g. ['centos:7', 'ubuntu:16.04']
@@ -197,9 +174,12 @@ class PuppetDockerTools
197
174
  'DL4000',
198
175
  'DL4001',
199
176
  ]
200
- ignore_string = ignore_rules.map { |x| "--ignore #{x}" }.join(' ')
201
-
202
- "hadolint #{ignore_string} #{file}"
177
+ hadolint_command = ['hadolint']
178
+ ignore_rules.each do |rule|
179
+ hadolint_command << ['--ignore', rule]
180
+ end
181
+ hadolint_command << file
182
+ hadolint_command.flatten
203
183
  end
204
184
 
205
185
  # Get a value from a Dockerfile
@@ -1,6 +1,6 @@
1
1
  require 'puppet_docker_tools'
2
2
  require 'puppet_docker_tools/runner'
3
- require 'docker'
3
+ require 'tmpdir'
4
4
 
5
5
  describe PuppetDockerTools::Runner do
6
6
  def create_runner(directory:, repository:, namespace:, dockerfile:)
@@ -32,55 +32,50 @@ describe PuppetDockerTools::Runner do
32
32
  end
33
33
 
34
34
  describe '#build' do
35
- let(:image) { double(Docker::Image) }
36
-
37
35
  it 'builds a latest and version tag if version is found' do
38
36
  expect(File).to receive(:read).with("#{runner.directory}/#{runner.dockerfile}").and_return(read_dockerfile)
39
37
  expect(PuppetDockerTools::Utilities).to receive(:get_value_from_env).with('version', namespace: runner.namespace, directory: runner.directory, dockerfile: runner.dockerfile).and_return('1.2.3')
40
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:1.2.3', 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs }).and_return(image)
41
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:latest', 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs }).and_return(image)
38
+ expect(Open3).to receive(:popen2e).with('docker', 'build', '--build-arg', 'vcs_ref=b0c5ead01b6cabdb3f01871bce699be165c3288f', '--build-arg', 'build_date=2018-06-08T17:18:13Z', '--tag', 'test/test-image:latest', '--tag', 'test/test-image:1.2.3', runner.directory)
42
39
  runner.build
43
40
  end
44
41
 
45
42
  it 'builds just a latest tag if no version is found' do
46
43
  expect(File).to receive(:read).with("#{runner.directory}/#{runner.dockerfile}").and_return(read_dockerfile)
47
44
  expect(PuppetDockerTools::Utilities).to receive(:get_value_from_env).with('version', namespace: runner.namespace, directory: runner.directory, dockerfile: runner.dockerfile).and_return(nil)
48
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:latest', 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs }).and_return(image)
45
+ expect(Open3).to receive(:popen2e).with('docker', 'build', '--build-arg', 'vcs_ref=b0c5ead01b6cabdb3f01871bce699be165c3288f', '--build-arg', 'build_date=2018-06-08T17:18:13Z', '--tag', 'test/test-image:latest', runner.directory)
49
46
  runner.build
50
47
  end
51
48
 
52
49
  it 'does not build a latest tag if latest is set to false' do
53
50
  expect(File).to receive(:read).with("#{runner.directory}/#{runner.dockerfile}").and_return(read_dockerfile)
54
51
  expect(PuppetDockerTools::Utilities).to receive(:get_value_from_env).with('version', namespace: runner.namespace, directory: runner.directory, dockerfile: runner.dockerfile).and_return('1.2.3')
55
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:1.2.3', 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs }).and_return(image)
52
+ expect(Open3).to receive(:popen2e).with('docker', 'build', '--build-arg', 'vcs_ref=b0c5ead01b6cabdb3f01871bce699be165c3288f', '--build-arg', 'build_date=2018-06-08T17:18:13Z', '--tag', 'test/test-image:1.2.3', runner.directory)
56
53
  runner.build(latest: false)
57
54
  end
58
55
 
59
56
  it 'ignores the cache when that parameter is set' do
60
57
  expect(File).to receive(:read).with("#{runner.directory}/#{runner.dockerfile}").and_return(read_dockerfile)
61
58
  expect(PuppetDockerTools::Utilities).to receive(:get_value_from_env).with('version', namespace: runner.namespace, directory: runner.directory, dockerfile: runner.dockerfile).and_return(nil)
62
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:latest', 'nocache' => true, 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs }).and_return(image)
59
+ expect(Open3).to receive(:popen2e).with('docker', 'build', '--build-arg', 'vcs_ref=b0c5ead01b6cabdb3f01871bce699be165c3288f', '--build-arg', 'build_date=2018-06-08T17:18:13Z', '--no-cache', '--tag', 'test/test-image:latest', runner.directory)
63
60
  runner.build(no_cache: true)
64
61
  end
65
62
 
66
63
  it 'passes the version when that parameter is set' do
67
64
  expect(File).to receive(:read).with("#{runner.directory}/#{runner.dockerfile}").and_return(read_dockerfile_with_version)
68
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:latest', 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs_with_version }).and_return(image)
69
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:1.2.3', 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs_with_version }).and_return(image)
65
+ expect(Open3).to receive(:popen2e).with('docker', 'build', '--build-arg', 'vcs_ref=b0c5ead01b6cabdb3f01871bce699be165c3288f', '--build-arg', 'build_date=2018-06-08T17:18:13Z', '--build-arg', 'version=1.2.3', '--tag', 'test/test-image:latest', '--tag', 'test/test-image:1.2.3', runner.directory)
70
66
  runner.build(version: '1.2.3')
71
67
  end
72
68
 
73
69
  it 'passes arbitrary build args' do
74
70
  expect(File).to receive(:read).with("#{runner.directory}/#{runner.dockerfile}").and_return(read_dockerfile_with_arbitrary_args)
75
71
  expect(PuppetDockerTools::Utilities).to receive(:get_value_from_env).with('version', namespace: runner.namespace, directory: runner.directory, dockerfile: runner.dockerfile).and_return(nil)
76
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:latest', 'dockerfile' => runner.dockerfile, 'buildargs' => extra_buildargs }).and_return(image)
72
+ expect(Open3).to receive(:popen2e).with('docker', 'build', '--build-arg', 'vcs_ref=b0c5ead01b6cabdb3f01871bce699be165c3288f', '--build-arg', 'build_date=2018-06-08T17:18:13Z', '--build-arg', 'foo=bar', '--build-arg', 'baz=test=with=equals', '--tag', 'test/test-image:latest', runner.directory)
77
73
  runner.build(build_args: ['foo=bar', 'baz=test=with=equals'])
78
74
  end
79
75
 
80
76
  it 'prioritizes version as a build arg over regular version' do
81
77
  expect(File).to receive(:read).with("#{runner.directory}/#{runner.dockerfile}").and_return(read_dockerfile_with_version)
82
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:latest', 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs_with_version }).and_return(image)
83
- expect(Docker::Image).to receive(:build_from_dir).with(runner.directory, { 't' => 'test/test-image:1.2.3', 'dockerfile' => runner.dockerfile, 'buildargs' => buildargs_with_version }).and_return(image)
78
+ expect(Open3).to receive(:popen2e).with('docker', 'build', '--build-arg', 'vcs_ref=b0c5ead01b6cabdb3f01871bce699be165c3288f', '--build-arg', 'build_date=2018-06-08T17:18:13Z', '--build-arg', 'version=1.2.3', '--tag', 'test/test-image:latest', '--tag', 'test/test-image:1.2.3', runner.directory)
84
79
  runner.build(version: '1.2.4', build_args: ['version=1.2.3'])
85
80
  end
86
81
 
@@ -88,53 +83,31 @@ describe PuppetDockerTools::Runner do
88
83
  allow(File).to receive(:exist?).with('/tmp/test-image/Dockerfile.test').and_return(true)
89
84
  expect(File).to receive(:read).with('/tmp/test-image/Dockerfile.test').and_return(read_dockerfile)
90
85
  expect(PuppetDockerTools::Utilities).to receive(:get_value_from_env).with('version', namespace: 'org.label-schema', directory: '/tmp/test-image', dockerfile: 'Dockerfile.test').and_return(nil)
91
- expect(Docker::Image).to receive(:build_from_dir).with('/tmp/test-image', { 't' => 'test/test-image:latest', 'dockerfile' => 'Dockerfile.test', 'buildargs' => buildargs }).and_return(image)
86
+ expect(Open3).to receive(:popen2e).with('docker', 'build', '--build-arg', 'vcs_ref=b0c5ead01b6cabdb3f01871bce699be165c3288f', '--build-arg', 'build_date=2018-06-08T17:18:13Z', '--no-cache', '--file', 'Dockerfile.test', '--tag', 'test/test-image:latest', runner.directory)
92
87
  local_runner = create_runner(directory: '/tmp/test-image', repository: 'test', namespace: 'org.label-schema', dockerfile: 'Dockerfile.test')
93
- local_runner.build
88
+ local_runner.build(no_cache: true)
94
89
  end
95
90
  end
96
91
 
97
92
  describe '#lint' do
98
- let(:passing_exit) {
99
- {
100
- 'State' => {
101
- 'ExitCode' => 0
102
- }
103
- }
104
- }
105
- let(:failing_exit) {
106
- {
107
- 'State' => {
108
- 'ExitCode' => 1
109
- }
110
- }
111
- }
112
-
113
- let(:container) { double(Docker::Container).as_null_object }
114
-
115
93
  before do
116
- allow(PuppetDockerTools::Utilities).to receive(:pull).and_return(double(Docker::Image))
117
- allow(Docker::Container).to receive(:create).and_return(container)
118
- allow(container).to receive(:tap).and_return(container)
119
- allow(container).to receive(:attach)
120
- allow(container).to receive(:wait)
121
- allow(container).to receive(:logs).and_return('container logs')
94
+ allow(PuppetDockerTools::Utilities).to receive(:pull)
122
95
  end
123
96
 
124
97
  it "should lint the container" do
125
- allow(container).to receive(:json).and_return(passing_exit)
98
+ allow(Open3).to receive(:capture2e).and_return(['', 0])
126
99
  runner.lint
127
100
  end
128
101
 
129
102
  it "should exit with exit status if something went wrong" do
130
- allow(container).to receive(:json).and_return(failing_exit)
103
+ allow(Open3).to receive(:capture2e).and_return(['container logs', 1])
131
104
  expect { runner.lint }.to raise_error(RuntimeError, /container logs/)
132
105
  end
133
106
  end
134
107
 
135
108
  describe '#local_lint' do
136
109
  it "should fail with logs if linting fails" do
137
- allow(Open3).to receive(:capture2e).with(PuppetDockerTools::Utilities.get_hadolint_command('/tmp/test-image/Dockerfile')).and_return('container logs', 1)
110
+ allow(Open3).to receive(:capture2e).with(*PuppetDockerTools::Utilities.get_hadolint_command('/tmp/test-image/Dockerfile')).and_return('container logs', 1)
138
111
  expect { runner.local_lint }.to raise_error(RuntimeError, /container logs/)
139
112
  end
140
113
  end
@@ -223,8 +196,8 @@ HERE
223
196
  it "runs tests under the 'spec' directory" do
224
197
  tests=["/tmp/test-image/spec/test1_spec.rb", "/tmp/test-image/spec/test2_spec.rb"]
225
198
  expect(Dir).to receive(:glob).with("/tmp/test-image/spec/*_spec.rb").and_return(tests)
226
- expect(Open3).to receive(:popen2e).with('rspec spec /tmp/test-image/spec/test1_spec.rb')
227
- expect(Open3).to receive(:popen2e).with('rspec spec /tmp/test-image/spec/test2_spec.rb')
199
+ expect(Open3).to receive(:popen2e).with('rspec', 'spec', '/tmp/test-image/spec/test1_spec.rb')
200
+ expect(Open3).to receive(:popen2e).with('rspec', 'spec', '/tmp/test-image/spec/test2_spec.rb')
228
201
  runner.spec
229
202
  end
230
203
  end
@@ -81,15 +81,20 @@ HERE
81
81
  }
82
82
  }
83
83
 
84
- let(:config_labels) { {
85
- 'Config' => {
86
- 'Labels' => {
87
- 'org.label-schema.vendor' => 'Puppet',
88
- 'org.label-schema.version' => '1.2.3',
89
- 'org.label-schema.vcs-ref' => 'b75674e1fbf52f7821f7900ab22a19f1a10cafdb'
90
- }
91
- }
92
- }
84
+ let(:labels) {
85
+ '{
86
+ "org.label-schema.build-date":"2018-08-24T21:31:54Z",
87
+ "org.label-schema.dockerfile":"/Dockerfile",
88
+ "org.label-schema.license":"Apache-2.0",
89
+ "org.label-schema.maintainer":"Puppet Release Team <release@puppet.com>",
90
+ "org.label-schema.name":"Puppet Server",
91
+ "org.label-schema.schema-version":"1.0",
92
+ "org.label-schema.url":"https://github.com/puppetlabs/puppetserver",
93
+ "org.label-schema.vcs-ref":"5296a6a86b141c9c1aeab63258205ae664d4108d",
94
+ "org.label-schema.vcs-url":"https://github.com/puppetlabs/puppetserver",
95
+ "org.label-schema.vendor":"Puppet",
96
+ "org.label-schema.version":"5.3.5"
97
+ }'
93
98
  }
94
99
 
95
100
  describe "#filter_build_args" do
@@ -107,11 +112,8 @@ HERE
107
112
  end
108
113
 
109
114
  describe "#get_value_from_label" do
110
- let(:image) { double(Docker::Image).as_null_object }
111
-
112
115
  before do
113
- allow(Docker::Image).to receive(:get).and_return(image)
114
- allow(image).to receive(:json).and_return(config_labels)
116
+ allow(Open3).to receive(:capture2).and_return(labels)
115
117
  end
116
118
 
117
119
  it "returns the value of a label" do
@@ -119,7 +121,7 @@ HERE
119
121
  end
120
122
 
121
123
  it "replaces '_' with '-' in the label name" do
122
- expect(PuppetDockerTools::Utilities.get_value_from_label('puppet/puppetserver-test', value: 'vcs_ref', namespace: 'org.label-schema')).to eq('b75674e1fbf52f7821f7900ab22a19f1a10cafdb')
124
+ expect(PuppetDockerTools::Utilities.get_value_from_label('puppet/puppetserver-test', value: 'vcs_ref', namespace: 'org.label-schema')).to eq('5296a6a86b141c9c1aeab63258205ae664d4108d')
123
125
  end
124
126
 
125
127
  it "returns nil if you ask for a value that isn't there" do
@@ -213,58 +215,18 @@ HERE
213
215
 
214
216
  describe '#pull' do
215
217
  it 'will pull a single image if the image has a tag' do
216
- expect(PuppetDockerTools::Utilities).to receive(:pull_single_tag).with('test/test-dir:latest')
218
+ expect(Open3).to receive(:popen2e).with('docker', 'pull', 'test/test-dir:latest')
217
219
  PuppetDockerTools::Utilities.pull('test/test-dir:latest')
218
220
  end
219
-
220
- it 'will pull all the images if no tag is passed' do
221
- expect(PuppetDockerTools::Utilities).to receive(:pull_all_tags).with('test/test-dir')
222
- PuppetDockerTools::Utilities.pull('test/test-dir')
223
- end
224
- end
225
-
226
- describe '#pull_all_tags' do
227
- let(:image_info) {
228
- {
229
- 'Created' => '2018-05-11T20:09:32Z',
230
- 'RepoTags' => ['latest', '1.2.3'],
231
- }
232
- }
233
-
234
- let(:image) { double(Docker::Image) }
235
- let(:images) { [image] }
236
-
237
- it 'pulls the tags' do
238
- expect(Docker::Image).to receive(:create).with('fromImage' => 'test/test-dir')
239
- expect(Docker::Image).to receive(:all).and_return(images)
240
- expect(image).to receive(:info).and_return(image_info).twice
241
- PuppetDockerTools::Utilities.pull_all_tags('test/test-dir')
242
- end
243
- end
244
-
245
- describe '#pull_single_tag' do
246
- let(:image_info) {
247
- {
248
- 'Created' => '2018-05-11T20:09:32Z',
249
- 'RepoTags' => ['1.2.3'],
250
- }
251
- }
252
- let(:image) { double(Docker::Image) }
253
-
254
- it 'pulls the single tag' do
255
- expect(Docker::Image).to receive(:create).with('fromImage' => 'test/test-dir:1.2.3').and_return(image)
256
- expect(image).to receive(:info).and_return(image_info).twice
257
- PuppetDockerTools::Utilities.pull_single_tag('test/test-dir:1.2.3')
258
- end
259
221
  end
260
222
 
261
223
  describe '#get_hadolint_command' do
262
224
  it 'generates a commmand with a dockerfile' do
263
- expect(PuppetDockerTools::Utilities.get_hadolint_command('test/Dockerfile')).to eq('hadolint --ignore DL3008 --ignore DL3018 --ignore DL4000 --ignore DL4001 test/Dockerfile')
225
+ expect(PuppetDockerTools::Utilities.get_hadolint_command('test/Dockerfile')).to eq(['hadolint', '--ignore', 'DL3008', '--ignore', 'DL3018', '--ignore', 'DL4000', '--ignore', 'DL4001', 'test/Dockerfile'])
264
226
  end
265
227
 
266
228
  it 'defaults to generating a command that reads from stdin' do
267
- expect(PuppetDockerTools::Utilities.get_hadolint_command).to eq('hadolint --ignore DL3008 --ignore DL3018 --ignore DL4000 --ignore DL4001 -')
229
+ expect(PuppetDockerTools::Utilities.get_hadolint_command).to eq(['hadolint', '--ignore', 'DL3008', '--ignore', 'DL3018', '--ignore', 'DL4000', '--ignore', 'DL4001', '-'])
268
230
  end
269
231
  end
270
232
 
@@ -1,11 +1,44 @@
1
+ require 'open3'
2
+
1
3
  shared_context 'with a docker container' do
4
+ def is_running?(container)
5
+ is_running = false
6
+ output, _ = Open3.capture2e('docker', 'inspect', '--format', "'{{ .Config.Healthcheck }}'", "#{container}")
7
+ output.chomp!
8
+ output.gsub!(/'/, '')
9
+
10
+ # no configured healthcheck
11
+ if output.chomp == "<nil>"
12
+ command = ['docker', 'inspect', '-f', "'{{.State.Status}}'", "#{container}"]
13
+ else
14
+ command = ['docker', 'inspect', '-f', "'{{.State.Health.Status}}'", "#{container}"]
15
+ end
16
+
17
+ while ! is_running
18
+ output, _ = Open3.capture2e(*command)
19
+ output.chomp!
20
+ output.gsub!(/'/, '')
21
+
22
+ case output
23
+ when 'healthy', 'running'
24
+ return true
25
+ when 'unhealthy', 'removing', 'paused', 'exited', 'dead'
26
+ return false
27
+ end
28
+
29
+ puts "Container is not running yet, will try again in 5 seconds..."
30
+ sleep(5)
31
+ end
32
+ end
33
+
2
34
  before(:all) do
3
- @container = Docker::Container.create('Image' => @image.id)
4
- @container.start
35
+ @container = %x(docker run --detach --rm -i #{@image}).chomp
36
+ unless is_running?(@container)
37
+ fail "something went wrong with container startup!"
38
+ end
5
39
  end
6
40
 
7
41
  after(:all) do
8
- @container.kill
9
- @container.delete(force: true)
42
+ %x(docker container kill #{@container})
10
43
  end
11
44
  end
@@ -1,9 +1,19 @@
1
+ require 'json'
1
2
  shared_context 'with a docker image' do
2
3
  before(:all) do
3
- @image = Docker::Image.build_from_dir(CURRENT_DIRECTORY)
4
+ if ENV['PUPPET_TEST_DOCKER_IMAGE'] && !ENV['PUPPET_TEST_DOCKER_IMAGE'].empty?
5
+ @image=ENV['PUPPET_TEST_DOCKER_IMAGE']
6
+ else
7
+ @image = "test/#{File.basename(CURRENT_DIRECTORY)}:#{Random.rand(1000)}"
8
+ %x(docker image build --tag #{@image} #{CURRENT_DIRECTORY})
9
+ end
10
+ puts "Running tests on #{@image}..."
11
+ @image_json = JSON.parse(%x(docker inspect #{@image}))
12
+ end
4
13
 
5
- set :os, family: @os || :debian
6
- set :backend, :docker
7
- set :docker_image, @image.id
14
+ after(:all) do
15
+ if !ENV['PUPPET_TEST_DOCKER_IMAGE'] || ENV['PUPPET_TEST_DOCKER_IMAGE'].empty?
16
+ %x(docker image rm --force #{@image})
17
+ end
8
18
  end
9
19
  end
@@ -0,0 +1,9 @@
1
+ shared_context 'with a transient docker container' do
2
+ before(:each) do
3
+ @container = %x(docker run --detach --rm -i #{@image}).chomp
4
+ end
5
+
6
+ after(:each) do
7
+ %x(docker container kill #{@container})
8
+ end
9
+ end
@@ -1,11 +1,45 @@
1
- shared_examples "a running container" do |command, exit_status|
2
- it "should run #{command} with exit status #{exit_status}" do
3
- container = Docker::Container.create('Image' => @image.id, 'Cmd' => command)
4
- container.start
5
- container.wait
6
- exit_status = container.json['State']['ExitCode']
7
- expect(exit_status).to eq(exit_status)
8
- container.kill
9
- container.delete(force: true)
1
+ require 'open3'
2
+
3
+ shared_examples "a running container" do |command, exit_code, expected_output|
4
+ unless command.is_a? Array
5
+ command = command.split(' ')
6
+ end
7
+
8
+ if expected_output && exit_code
9
+ it "should run #{command} with output matching #{expected_output} and exit code #{exit_code}" do
10
+ output, status = Open3.capture2e('docker', 'exec', @container, *command)
11
+ expect(output).to match(/#{expected_output}/)
12
+ expect(status.exitstatus).to eq(exit_code)
13
+ end
14
+ elsif expected_output
15
+ it "should run #{command} with output matching #{expected_output}" do
16
+ output, _ = Open3.capture2e('docker', 'exec', @container, *command)
17
+ expect(output).to match(/#{expected_output}/)
18
+ end
19
+ elsif exit_code
20
+ it "should run #{command} with exit code #{exit_code}" do
21
+ _, status = Open3.capture2e('docker', 'exec', @container, *command)
22
+ expect(status.exitstatus).to eq(exit_code)
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ shared_examples "a service in a container" do |service, user, arg, pid|
29
+ if service && user
30
+ it "should run #{service} as #{user}" do
31
+ if pid
32
+ output, status = Open3.capture2e('docker', 'exec', @container, 'ps', '-f', '--quick-pid', pid)
33
+ else
34
+ output, status = Open3.capture2e('docker', 'exec', @container, 'ps', '-f', '-u', user)
35
+ output = output.split("\n").select { |proc| proc[/#{service}/] }.join('')
36
+ end
37
+ expect(status.exitstatus).to eq(0)
38
+ expect(output).to match(/#{service}/)
39
+ expect(output).to match(/#{user}/)
40
+ if arg
41
+ expect(output).to match(/#{arg}/)
42
+ end
43
+ end
10
44
  end
11
45
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet_docker_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-05 00:00:00.000000000 Z
11
+ date: 2018-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: docker-api
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.34'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.34'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rspec
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -38,20 +24,6 @@ dependencies:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
26
  version: '3.0'
41
- - !ruby/object:Gem::Dependency
42
- name: serverspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2.41'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '2.41'
55
27
  - !ruby/object:Gem::Dependency
56
28
  name: docopt
57
29
  requirement: !ruby/object:Gem::Requirement
@@ -82,11 +54,9 @@ files:
82
54
  - lib/puppet_docker_tools/utilities.rb
83
55
  - spec/lib/puppet_docker_tools/runner_spec.rb
84
56
  - spec/lib/puppet_docker_tools/utilities_spec.rb
85
- - spec/support/context/using_alpine.rb
86
- - spec/support/context/using_centos.rb
87
57
  - spec/support/context/with_docker_container.rb
88
- - spec/support/context/with_docker_container_dummy_cmd.rb
89
58
  - spec/support/context/with_docker_image.rb
59
+ - spec/support/context/with_transient_docker_container.rb
90
60
  - spec/support/examples/running_container.rb
91
61
  homepage: https://github.com/puppetlabs/puppet_docker_tools
92
62
  licenses:
@@ -108,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
78
  version: '0'
109
79
  requirements: []
110
80
  rubyforge_project:
111
- rubygems_version: 2.2.5
81
+ rubygems_version: 2.6.14
112
82
  signing_key:
113
83
  specification_version: 3
114
84
  summary: Puppet tools for building docker images
@@ -1,5 +0,0 @@
1
- shared_context 'using alpine' do
2
- before(:all) do
3
- @os = :alpine
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- shared_context 'using centos' do
2
- before(:all) do
3
- @os = :redhat
4
- end
5
- end
@@ -1,18 +0,0 @@
1
- shared_context 'with a docker container with a dummy cmd' do
2
- before(:all) do
3
- @image = Docker::Image.build_from_dir(CURRENT_DIRECTORY)
4
- @container = Docker::Container.create(
5
- 'Image' => @image.id,
6
- 'Cmd' => ['sh', '-c', 'while true; do sleep 1; done']
7
- )
8
- @container.start
9
-
10
- set :backend, :docker
11
- set :docker_container, @container.id
12
- end
13
-
14
- after(:all) do
15
- @container.kill
16
- @container.delete(force: true)
17
- end
18
- end