Skip to content

Commit 9747539

Browse files
authored
Merge pull request #45 from puppetlabs/cdpe-6808/add-podman-support
(CDPE-6808) Add podman support
2 parents 40bc850 + bcfa8ed commit 9747539

File tree

6 files changed

+354
-126
lines changed

6 files changed

+354
-126
lines changed

.github/workflows/unit-test.yml

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,36 @@ name: unit-test
22
on:
33
- pull_request
44
jobs:
5+
podman_tests:
6+
runs-on: ubuntu-22.04
7+
steps:
8+
- uses: actions/checkout@v2
9+
- uses: ruby/setup-ruby@v1
10+
with:
11+
ruby-version: 2.6
12+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
13+
- name: Uninstall docker
14+
run: |
15+
sudo apt-get update
16+
sudo apt-get remove -y docker-ce
17+
- name: Install podman
18+
run: |
19+
sudo apt-get update
20+
sudo apt-get install -y podman
21+
- run: bundle exec rspec spec/podman_spec.rb
22+
docker_tests:
23+
runs-on: ubuntu-22.04
24+
steps:
25+
- uses: actions/checkout@v2
26+
- uses: ruby/setup-ruby@v1
27+
with:
28+
ruby-version: 2.6
29+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
30+
- name: Uninstall podman
31+
run: |
32+
sudo apt-get update
33+
sudo apt-get remove -y podman
34+
- run: bundle exec rspec spec/docker_spec.rb
535
unix_tests:
636
runs-on: macos-latest
737
steps:
@@ -10,7 +40,7 @@ jobs:
1040
with:
1141
ruby-version: 2.6
1242
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
13-
- run: bundle exec rspec spec
43+
- run: bundle exec rspec spec/run_cd4pe_job_spec.rb
1444
windows_tests:
1545
runs-on: windows-latest
1646
steps:
@@ -19,4 +49,4 @@ jobs:
1949
with:
2050
ruby-version: 2.6
2151
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
22-
- run: $env:RUN_WINDOWS_UNIT_TESTS = "true"; bundle exec rspec spec
52+
- run: $env:RUN_WINDOWS_UNIT_TESTS = "true"; bundle exec rspec spec/run_cd4pe_job_spec.rb

spec/docker_spec.rb

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
require 'open3'
2+
require 'base64'
3+
require 'json'
4+
require 'fileutils'
5+
require_relative '../tasks/run_cd4pe_job.rb'
6+
7+
describe 'run_cd4pe_job' do
8+
before(:all) do
9+
@logger = Logger.new
10+
end
11+
12+
before(:each) do
13+
@working_dir = File.join(Dir.getwd, "test_working_dir")
14+
Dir.mkdir(@working_dir)
15+
16+
# Ensure tests don't write to /etc/containers/certs.d
17+
@certs_dir = File.join(@working_dir, "certs.d")
18+
CD4PEJobRunner.send(:remove_const, :DOCKER_CERTS)
19+
CD4PEJobRunner.const_set(:DOCKER_CERTS, @certs_dir)
20+
21+
@web_ui_endpoint = 'https://testtest.com'
22+
@job_token = 'alksjdbhfnadhsbf'
23+
@job_owner = 'carls cool carl'
24+
@job_instance_id = '17'
25+
@secrets = {
26+
secret1: "hello",
27+
secret2: "friend",
28+
}
29+
@windows_job = ENV['RUN_WINDOWS_UNIT_TESTS']
30+
end
31+
32+
after(:each) do
33+
FileUtils.remove_dir(@working_dir)
34+
$stdout = STDOUT
35+
end
36+
37+
describe 'cd4pe_job_helper::get_runtime' do
38+
it 'Detects docker as the available runtime.' do
39+
test_container_image = 'puppetlabs/test:10.0.1'
40+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
41+
expect(job_helper.get_runtime).to eq('docker')
42+
end
43+
end
44+
45+
describe 'cd4pe_job_helper::update_container_image' do
46+
let(:test_container_image) { 'puppetlabs/test:10.0.1' }
47+
it 'Generates a docker pull command.' do
48+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
49+
docker_pull_command = job_helper.get_image_pull_cmd
50+
expect(docker_pull_command).to eq("docker pull #{test_container_image}")
51+
end
52+
53+
context 'with config' do
54+
let(:hostname) { 'host1' }
55+
let(:creds_json) { {auths: {hostname => {}}}.to_json }
56+
let(:creds_b64) { Base64.encode64(creds_json) }
57+
let(:cert_txt) { 'junk' }
58+
let(:cert_b64) { Base64.encode64(cert_txt) }
59+
60+
it 'Uses config when present for docker.' do
61+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, image_pull_creds: creds_b64, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
62+
config_json = File.join(@working_dir, '.docker', 'config.json')
63+
expect(File.exist?(config_json)).to be(true)
64+
expect(File.read(config_json)).to eq(creds_json)
65+
66+
docker_pull_command = job_helper.get_image_pull_cmd
67+
expect(docker_pull_command).to eq("docker --config #{File.join(@working_dir, '.docker')} pull #{test_container_image}")
68+
end
69+
70+
it 'Registers the CA cert when provided.' do
71+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, image_pull_creds: creds_b64, base_64_ca_cert: cert_b64, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
72+
73+
cert_file = File.join(@certs_dir, hostname, 'ca.crt')
74+
expect(File.exist?(cert_file)).to be(true)
75+
expect(File.read(cert_file)).to eq(cert_txt)
76+
end
77+
end
78+
end
79+
80+
describe 'cd4pe_job_helper::get_container_run_cmd' do
81+
it 'Generates the correct docker run command.' do
82+
test_manifest_type = "AFTER_JOB_SUCCESS"
83+
test_container_image = 'puppetlabs/test:10.0.1'
84+
arg1 = '--testarg=woot'
85+
arg2 = '--otherarg=hello'
86+
arg3 = '--whatever=doesntmatter'
87+
user_specified_container_run_args = [arg1, arg2, arg3]
88+
job_type = 'unix'
89+
90+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, container_run_args: user_specified_container_run_args, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
91+
92+
docker_run_command = job_helper.get_container_run_cmd(test_manifest_type)
93+
cmd_parts = docker_run_command.split(' ')
94+
95+
expect(cmd_parts[0]).to eq('docker')
96+
expect(cmd_parts[1]).to eq('run')
97+
expect(cmd_parts[2]).to eq('--rm')
98+
expect(cmd_parts[3]).to eq(arg1)
99+
expect(cmd_parts[4]).to eq(arg2)
100+
expect(cmd_parts[5]).to eq(arg3)
101+
expect(cmd_parts[6]).to eq('-e')
102+
expect(cmd_parts[7]).to eq('secret1')
103+
expect(cmd_parts[8]).to eq('-e')
104+
expect(cmd_parts[9]).to eq('secret2')
105+
expect(cmd_parts[10]).to eq('-v')
106+
expect(cmd_parts[11].end_with?("/#{File.basename(@working_dir)}/cd4pe_job/repo:/repo\"")).to be(true)
107+
expect(cmd_parts[12]).to eq('-v')
108+
expect(cmd_parts[13].end_with?("/#{File.basename(@working_dir)}/cd4pe_job/jobs/#{job_type}:/cd4pe_job\"")).to be(true)
109+
expect(cmd_parts[14]).to eq(test_container_image)
110+
expect(cmd_parts[15]).to eq('"/cd4pe_job/AFTER_JOB_SUCCESS"')
111+
end
112+
end
113+
end

spec/podman_spec.rb

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
require 'open3'
2+
require 'base64'
3+
require 'json'
4+
require 'fileutils'
5+
require_relative '../tasks/run_cd4pe_job.rb'
6+
7+
describe 'run_cd4pe_job' do
8+
before(:all) do
9+
@logger = Logger.new
10+
end
11+
12+
before(:each) do
13+
@working_dir = File.join(Dir.getwd, "test_working_dir")
14+
Dir.mkdir(@working_dir)
15+
16+
# Ensure tests don't write to /etc/containers/certs.d
17+
@certs_dir = File.join(@working_dir, "certs.d")
18+
CD4PEJobRunner.send(:remove_const, :PODMAN_CERTS)
19+
CD4PEJobRunner.const_set(:PODMAN_CERTS, @certs_dir)
20+
21+
@web_ui_endpoint = 'https://testtest.com'
22+
@job_token = 'alksjdbhfnadhsbf'
23+
@job_owner = 'carls cool carl'
24+
@job_instance_id = '17'
25+
@secrets = {
26+
secret1: "hello",
27+
secret2: "friend",
28+
}
29+
@windows_job = ENV['RUN_WINDOWS_UNIT_TESTS']
30+
end
31+
32+
after(:each) do
33+
FileUtils.remove_dir(@working_dir)
34+
$stdout = STDOUT
35+
end
36+
37+
describe 'cd4pe_job_helper::get_runtime' do
38+
it 'Detects podman as the available runtime.' do
39+
test_container_image = 'puppetlabs/test:10.0.1'
40+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
41+
expect(job_helper.get_runtime).to eq('podman')
42+
end
43+
end
44+
45+
describe 'cd4pe_job_helper::update_container_image' do
46+
let(:test_container_image) { 'puppetlabs/test:10.0.1' }
47+
it 'Generates a podman pull command.' do
48+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
49+
podman_pull_command = job_helper.get_image_pull_cmd
50+
expect(podman_pull_command).to eq("podman pull #{test_container_image}")
51+
end
52+
53+
context 'with config' do
54+
let(:hostname) { 'host1' }
55+
let(:creds_json) { {auths: {hostname => {}}}.to_json }
56+
let(:creds_b64) { Base64.encode64(creds_json) }
57+
let(:cert_txt) { 'junk' }
58+
let(:cert_b64) { Base64.encode64(cert_txt) }
59+
60+
it 'Uses config when present for podman.' do
61+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, image_pull_creds: creds_b64, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
62+
config_json = File.join(@working_dir, '.docker', 'config.json')
63+
expect(File.exist?(config_json)).to be(true)
64+
expect(File.read(config_json)).to eq(creds_json)
65+
66+
podman_pull_command = job_helper.get_image_pull_cmd
67+
expect(podman_pull_command).to eq("podman --config #{File.join(@working_dir, '.docker')} pull #{test_container_image}")
68+
end
69+
70+
it 'Registers the CA cert when provided.' do
71+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, image_pull_creds: creds_b64, base_64_ca_cert: cert_b64, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
72+
73+
cert_file = File.join(@certs_dir, hostname, 'ca.crt')
74+
expect(File.exist?(cert_file)).to be(true)
75+
expect(File.read(cert_file)).to eq(cert_txt)
76+
end
77+
end
78+
end
79+
80+
describe 'cd4pe_job_helper::get_container_run_cmd' do
81+
it 'Generates the correct podman run command.' do
82+
test_manifest_type = "AFTER_JOB_SUCCESS"
83+
test_container_image = 'puppetlabs/test:10.0.1'
84+
arg1 = '--testarg=woot'
85+
arg2 = '--otherarg=hello'
86+
arg3 = '--whatever=doesntmatter'
87+
user_specified_container_run_args = [arg1, arg2, arg3]
88+
job_type = 'unix'
89+
90+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_image: test_container_image, container_run_args: user_specified_container_run_args, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
91+
92+
podman_run_command = job_helper.get_container_run_cmd(test_manifest_type)
93+
cmd_parts = podman_run_command.split(' ')
94+
95+
expect(cmd_parts[0]).to eq('podman')
96+
expect(cmd_parts[1]).to eq('run')
97+
expect(cmd_parts[2]).to eq('--rm')
98+
expect(cmd_parts[3]).to eq(arg1)
99+
expect(cmd_parts[4]).to eq(arg2)
100+
expect(cmd_parts[5]).to eq(arg3)
101+
expect(cmd_parts[6]).to eq('-e')
102+
expect(cmd_parts[7]).to eq('secret1')
103+
expect(cmd_parts[8]).to eq('-e')
104+
expect(cmd_parts[9]).to eq('secret2')
105+
expect(cmd_parts[10]).to eq('-v')
106+
expect(cmd_parts[11].end_with?("/#{File.basename(@working_dir)}/cd4pe_job/repo:/repo:z\"")).to be(true)
107+
expect(cmd_parts[12]).to eq('-v')
108+
expect(cmd_parts[13].end_with?("/#{File.basename(@working_dir)}/cd4pe_job/jobs/#{job_type}:/cd4pe_job:z\"")).to be(true)
109+
expect(cmd_parts[14]).to eq(test_container_image)
110+
expect(cmd_parts[15]).to eq('"/cd4pe_job/AFTER_JOB_SUCCESS"')
111+
end
112+
end
113+
end

spec/run_cd4pe_job_spec.rb

Lines changed: 4 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,15 @@
139139
end
140140

141141
describe 'cd4pe_job_helper::initialize' do
142-
it 'Passes the docker run args through without modifying the structure.' do
142+
it 'Passes the container run args through without modifying the structure.' do
143143
arg1 = '--testarg=woot'
144144
arg2 = '--otherarg=hello'
145145
arg3 = '--whatever=isclever'
146-
user_specified_docker_run_args = [arg1, arg2, arg3]
146+
user_specified_container_run_args = [arg1, arg2, arg3]
147147

148-
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, docker_run_args: user_specified_docker_run_args, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
148+
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, container_run_args: user_specified_container_run_args, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
149149

150-
expect(job_helper.docker_run_args).to eq("#{arg1} #{arg2} #{arg3}")
150+
expect(job_helper.container_run_args).to eq("#{arg1} #{arg2} #{arg3}")
151151
end
152152

153153
it 'Sets the HOME and REPO_DIR env vars' do
@@ -157,75 +157,6 @@
157157
expect(ENV['REPO_DIR']).to eq("#{@working_dir}/cd4pe_job/repo")
158158
end
159159
end
160-
161-
describe 'cd4pe_job_helper::update_docker_image' do
162-
let(:test_docker_image) { 'puppetlabs/test:10.0.1' }
163-
it 'Generates a docker pull command.' do
164-
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, docker_image: test_docker_image, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
165-
docker_pull_command = job_helper.get_docker_pull_cmd
166-
expect(docker_pull_command).to eq("docker pull #{test_docker_image}")
167-
end
168-
169-
context 'with config' do
170-
let(:hostname) { 'host1' }
171-
let(:creds_json) { {auths: {hostname => {}}}.to_json }
172-
let(:creds_b64) { Base64.encode64(creds_json) }
173-
let(:cert_txt) { 'junk' }
174-
let(:cert_b64) { Base64.encode64(cert_txt) }
175-
176-
it 'Uses config when present.' do
177-
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, docker_image: test_docker_image, docker_pull_creds: creds_b64, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
178-
config_json = File.join(@working_dir, '.docker', 'config.json')
179-
expect(File.exist?(config_json)).to be(true)
180-
expect(File.read(config_json)).to eq(creds_json)
181-
182-
docker_pull_command = job_helper.get_docker_pull_cmd
183-
expect(docker_pull_command).to eq("docker --config #{File.join(@working_dir, '.docker')} pull #{test_docker_image}")
184-
end
185-
186-
it 'Registers the CA cert when provided.' do
187-
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, docker_image: test_docker_image, docker_pull_creds: creds_b64, base_64_ca_cert: cert_b64, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
188-
189-
cert_file = File.join(@certs_dir, hostname, 'ca.crt')
190-
expect(File.exist?(cert_file)).to be(true)
191-
expect(File.read(cert_file)).to eq(cert_txt)
192-
end
193-
end
194-
end
195-
196-
describe 'cd4pe_job_helper::get_docker_run_cmd' do
197-
it 'Generates the correct docker run command.' do
198-
test_manifest_type = "AFTER_JOB_SUCCESS"
199-
test_docker_image = 'puppetlabs/test:10.0.1'
200-
arg1 = '--testarg=woot'
201-
arg2 = '--otherarg=hello'
202-
arg3 = '--whatever=doesntmatter'
203-
user_specified_docker_run_args = [arg1, arg2, arg3]
204-
job_type = @windows_job ? 'windows' : 'unix'
205-
206-
job_helper = CD4PEJobRunner.new(windows_job: @windows_job, working_dir: @working_dir, docker_image: test_docker_image, docker_run_args: user_specified_docker_run_args, job_token: @job_token, web_ui_endpoint: @web_ui_endpoint, job_owner: @job_owner, job_instance_id: @job_instance_id, logger: @logger, secrets: @secrets)
207-
208-
docker_run_command = job_helper.get_docker_run_cmd(test_manifest_type)
209-
cmd_parts = docker_run_command.split(' ')
210-
211-
expect(cmd_parts[0]).to eq('docker')
212-
expect(cmd_parts[1]).to eq('run')
213-
expect(cmd_parts[2]).to eq('--rm')
214-
expect(cmd_parts[3]).to eq(arg1)
215-
expect(cmd_parts[4]).to eq(arg2)
216-
expect(cmd_parts[5]).to eq(arg3)
217-
expect(cmd_parts[6]).to eq('-e')
218-
expect(cmd_parts[7]).to eq('secret1')
219-
expect(cmd_parts[8]).to eq('-e')
220-
expect(cmd_parts[9]).to eq('secret2')
221-
expect(cmd_parts[10]).to eq('-v')
222-
expect(cmd_parts[11].end_with?("/#{File.basename(@working_dir)}/cd4pe_job/repo:/repo\"")).to be(true)
223-
expect(cmd_parts[12]).to eq('-v')
224-
expect(cmd_parts[13].end_with?("/#{File.basename(@working_dir)}/cd4pe_job/jobs/#{job_type}:/cd4pe_job\"")).to be(true)
225-
expect(cmd_parts[14]).to eq(test_docker_image)
226-
expect(cmd_parts[15]).to eq('"/cd4pe_job/AFTER_JOB_SUCCESS"')
227-
end
228-
end
229160
end
230161

231162
describe 'cd4pe_job_helper::run_job' do

tasks/run_cd4pe_job.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@
2424
},
2525
"docker_image": {
2626
"type": "Optional[String[1]]",
27-
"description": "If specified, the job will attempt to run inside the docker container."
27+
"description": "If specified, the job will attempt to run inside a container."
2828
},
2929
"docker_run_args": {
3030
"type": "Optional[Array[String[1]]]",
31-
"description": "If specified, the arguements will be passed to docker if docker image is specified."
31+
"description": "The arguments to pass to the container runtime."
3232
},
3333
"docker_pull_creds": {
3434
"type": "Optional[String[1]]",
35-
"description": "Base64-encoded docker config.json to use when pulling the specified docker image.",
35+
"description": "Base64-encoded config.json to use when pulling the specified container image.",
3636
"sensitive": true
3737
},
3838
"base_64_ca_cert": {

0 commit comments

Comments
 (0)