puppet_docker_tools 0.1.5 → 0.2.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 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