foreman_openbolt 1.1.0 → 1.1.1
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 +4 -4
- data/README.md +9 -15
- data/Rakefile +0 -0
- data/lib/foreman_openbolt/version.rb +1 -1
- data/package.json +1 -1
- data/test/acceptance/acceptance_helper.rb +146 -0
- data/test/acceptance/docker/docker-compose.yml +69 -0
- data/test/acceptance/docker/foreman/Dockerfile +45 -0
- data/test/acceptance/docker/foreman/entrypoint.sh +26 -0
- data/test/acceptance/docker/target/Dockerfile +29 -0
- data/test/acceptance/docker/target/entrypoint.sh +11 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/complex_params.json +30 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/complex_params.sh +16 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/echo.json +13 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/echo.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/failing_task.json +8 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/failing_task.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/noop_task.json +8 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/noop_task.sh +2 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/slow_task.json +14 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/slow_task.sh +3 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.json +13 -0
- data/test/acceptance/fixtures/modules/acceptance/tasks/target_conditional.sh +9 -0
- data/test/acceptance/fixtures/openbolt.yml +7 -0
- data/test/acceptance/tests/error_handling_test.rb +40 -0
- data/test/acceptance/tests/host_selector_test.rb +31 -0
- data/test/acceptance/tests/launch_task_test.rb +96 -0
- data/test/acceptance/tests/parameter_table_test.rb +61 -0
- data/test/acceptance/tests/settings_test.rb +95 -0
- data/test/acceptance/tests/ssh_options_test.rb +77 -0
- data/test/acceptance/tests/task_execution_test.rb +40 -0
- data/test/acceptance/tests/task_history_test.rb +84 -0
- data/test/acceptance/tests/transport_options_test.rb +121 -0
- data/test/test_plugin_helper.rb +17 -0
- data/test/unit/controllers/task_controller_test.rb +351 -0
- data/test/unit/docker/Dockerfile +47 -0
- data/test/unit/docker/docker-compose.yml +33 -0
- data/test/unit/docker/entrypoint.sh +4 -0
- data/test/unit/factories/foreman_openbolt_factories.rb +39 -0
- data/test/unit/lib/actions/cleanup_proxy_artifacts_test.rb +51 -0
- data/test/unit/lib/actions/poll_task_status_test.rb +141 -0
- data/test/unit/lib/proxy_api/openbolt_test.rb +174 -0
- data/test/unit/models/task_job_test.rb +278 -0
- metadata +39 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3747d57bc5198327d01255ec665724e38a46dbfd91fd1e5bd764d6cf3ddd3bb3
|
|
4
|
+
data.tar.gz: ad67d388a524680a567ffdf020e35bbb364471bd190cbdfc597c25d88928a4d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1570a5623355cd514276d9e25aece89f2b3f5970bde5efa80db4f9cbe1c4e59eb030b4b5194906d9e9b93f6ec62ac3059e9ed1671cfd612b025d805e3cff7f8e
|
|
7
|
+
data.tar.gz: b335d7b62ff7c6a6d93e39d2ae12c8357ca32b36299313ecc28a23ecc9190d74016a60f03e61f8bacbbf2ba7583cfb15b96b31acf5c21590d59dafc70934fb84
|
data/README.md
CHANGED
|
@@ -252,7 +252,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
252
252
|
|
|
253
253
|
### Version locations
|
|
254
254
|
|
|
255
|
-
|
|
255
|
+
The version is maintained in two files:
|
|
256
256
|
|
|
257
257
|
1. `lib/foreman_openbolt/version.rb` -- the gem version (authoritative source)
|
|
258
258
|
2. `package.json` -- the npm package version (must match)
|
|
@@ -260,23 +260,17 @@ Update the version in these files:
|
|
|
260
260
|
If the minimum Foreman version changes, also update:
|
|
261
261
|
|
|
262
262
|
3. `lib/foreman_openbolt/engine.rb` -- `requires_foreman '>= X.Y.Z'`
|
|
263
|
-
4.
|
|
264
|
-
5. `.github/workflows/build.yml` -- default `foreman_version` and `foreman_packaging_ref` inputs
|
|
263
|
+
4. `.github/workflows/build.yml` -- default `foreman_version` and `foreman_packaging_ref` inputs
|
|
265
264
|
|
|
266
265
|
### Release steps
|
|
267
266
|
|
|
268
|
-
1.
|
|
269
|
-
2.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
```bash
|
|
276
|
-
git tag 1.1.0
|
|
277
|
-
git push origin 1.1.0
|
|
278
|
-
```
|
|
279
|
-
5. The [release workflow](.github/workflows/release.yml) runs automatically on tag push and:
|
|
267
|
+
1. Go to [Actions > Prepare Release](../../actions/workflows/prepare_release.yml) and run the workflow with the version to release (e.g. `1.2.0`)
|
|
268
|
+
2. The workflow bumps the version in `version.rb` and `package.json`, generates the changelog, and opens a PR with the `skip-changelog` label
|
|
269
|
+
3. Review and merge the PR
|
|
270
|
+
4. Go to [Actions > Release](../../actions/workflows/release.yml) and run the workflow with the same version
|
|
271
|
+
5. The release workflow:
|
|
272
|
+
- Verifies the version in `version.rb` matches the input
|
|
273
|
+
- Creates and pushes a git tag
|
|
280
274
|
- Builds the gem
|
|
281
275
|
- Creates a GitHub Release with auto-generated notes and the gem attached
|
|
282
276
|
- Publishes the gem to GitHub Packages
|
data/Rakefile
CHANGED
|
File without changes
|
data/package.json
CHANGED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'capybara'
|
|
4
|
+
require 'capybara/dsl'
|
|
5
|
+
require 'selenium-webdriver'
|
|
6
|
+
require 'test/unit'
|
|
7
|
+
|
|
8
|
+
# Base class for acceptance tests using Capybara + Selenium Chrome.
|
|
9
|
+
# Connects to Foreman through a remote ChromeDriver container and
|
|
10
|
+
# exercises the plugin UI as a real user would.
|
|
11
|
+
class AcceptanceTestCase < Test::Unit::TestCase
|
|
12
|
+
include Capybara::DSL
|
|
13
|
+
|
|
14
|
+
# Chrome runs in a separate container and reaches Foreman via the Docker
|
|
15
|
+
# network service name. The test runner connects to ChromeDriver via the
|
|
16
|
+
# exposed port 4444 on the host.
|
|
17
|
+
FOREMAN_URL = ENV.fetch('FOREMAN_URL', 'https://foreman')
|
|
18
|
+
FOREMAN_USER = ENV.fetch('FOREMAN_USER', 'admin')
|
|
19
|
+
FOREMAN_PASS = ENV.fetch('FOREMAN_PASS', 'changeme')
|
|
20
|
+
CHROMEDRIVER_URL = ENV.fetch('CHROMEDRIVER_URL', 'http://localhost:4444')
|
|
21
|
+
|
|
22
|
+
def setup
|
|
23
|
+
Capybara.app_host = FOREMAN_URL
|
|
24
|
+
Capybara.run_server = false
|
|
25
|
+
Capybara.default_max_wait_time = 15
|
|
26
|
+
|
|
27
|
+
Capybara.register_driver :remote_chrome do |app|
|
|
28
|
+
options = Selenium::WebDriver::Chrome::Options.new
|
|
29
|
+
options.add_argument('--headless') unless ENV['HEADFUL']
|
|
30
|
+
options.add_argument('--no-sandbox')
|
|
31
|
+
options.add_argument('--disable-dev-shm-usage')
|
|
32
|
+
options.add_argument('--disable-gpu')
|
|
33
|
+
options.add_argument('--window-size=1280,720')
|
|
34
|
+
options.add_argument('--ignore-certificate-errors')
|
|
35
|
+
|
|
36
|
+
Capybara::Selenium::Driver.new(
|
|
37
|
+
app,
|
|
38
|
+
browser: :remote,
|
|
39
|
+
url: CHROMEDRIVER_URL,
|
|
40
|
+
options: options
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Capybara.default_driver = :remote_chrome
|
|
45
|
+
Capybara.javascript_driver = :remote_chrome
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def teardown
|
|
49
|
+
visit '/users/logout'
|
|
50
|
+
Capybara.reset_sessions!
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def foreman_login(user: FOREMAN_USER, password: FOREMAN_PASS)
|
|
54
|
+
visit '/users/login'
|
|
55
|
+
fill_in 'login_login', with: user
|
|
56
|
+
fill_in 'login_password', with: password
|
|
57
|
+
click_button 'Log In'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# --- Launch page helpers ---
|
|
61
|
+
|
|
62
|
+
def select_first_proxy
|
|
63
|
+
assert_selector '#smart-proxy-input option', minimum: 2, wait: 15
|
|
64
|
+
proxy_option = first('#smart-proxy-input option:not([value=""])')
|
|
65
|
+
select proxy_option.text, from: 'smart-proxy-input'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def select_hosts_via_search(query)
|
|
69
|
+
find('[aria-label="Select host targeting method"]').click
|
|
70
|
+
find('[data-ouia-component-id="host_methods"]').find('li', text: 'Search query').click
|
|
71
|
+
search_input = find('.foreman-search-field input[type="text"]', wait: 10)
|
|
72
|
+
search_input.fill_in with: query
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def launch_task_via_ui(task_name, targets: 'target', params: {})
|
|
76
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
77
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
78
|
+
|
|
79
|
+
select_first_proxy
|
|
80
|
+
select_hosts_via_search(targets)
|
|
81
|
+
|
|
82
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
83
|
+
select task_name, from: 'task-name-input'
|
|
84
|
+
|
|
85
|
+
params.each do |name, value|
|
|
86
|
+
assert_selector "#param_#{name}", wait: 10
|
|
87
|
+
fill_in "param_#{name}", with: value
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
click_button 'Launch Task'
|
|
91
|
+
assert_selector 'h1', text: 'Task Execution', wait: 15
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def assert_task_completed
|
|
95
|
+
assert_selector '.pf-v5-c-label', text: /Success|Complete/i, wait: 120
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def assert_task_failed
|
|
99
|
+
assert_selector '.pf-v5-c-label', text: /Failed|Failure|Error/i, wait: 120
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def assert_result_contains(text)
|
|
103
|
+
assert_selector '.pf-v5-c-code-block__code', text: text, wait: 15
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def assert_result_has_content
|
|
107
|
+
assert_no_selector '.pf-v5-c-empty-state', text: 'No result data', wait: 15
|
|
108
|
+
assert_selector '.pf-v5-c-code-block__code', wait: 15
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def assert_log_contains(text)
|
|
112
|
+
# Click the "Log Output" tab to see the bolt command and log
|
|
113
|
+
find('.pf-v5-c-tabs__link', text: 'Log Output').click
|
|
114
|
+
assert_selector '.pf-v5-c-code-block__code', text: text, wait: 15
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# OpenBolt options and task parameters both use param_ prefix for field IDs
|
|
118
|
+
def set_openbolt_option(name, value)
|
|
119
|
+
field = find("#param_#{name}", wait: 10)
|
|
120
|
+
if field.tag_name == 'select'
|
|
121
|
+
field.select value
|
|
122
|
+
elsif field['type'] == 'checkbox'
|
|
123
|
+
value ? field.check : field.uncheck
|
|
124
|
+
else
|
|
125
|
+
field.fill_in with: value
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# --- Settings page helpers ---
|
|
130
|
+
|
|
131
|
+
# Update a Foreman setting through the /settings UI. Each setting row
|
|
132
|
+
# has an inline edit button with id=<setting_name> that reveals an
|
|
133
|
+
# input with id=setting-input-<setting_name> and a submit button with
|
|
134
|
+
# ouiaId=submit-edit-btn (see Foreman's SettingValueCell /
|
|
135
|
+
# SettingValueEdit components).
|
|
136
|
+
def update_foreman_setting(setting_name, new_value, category: 'openbolt')
|
|
137
|
+
visit '/settings'
|
|
138
|
+
find("a[href='##{category}_settings_tab']", wait: 10).click
|
|
139
|
+
|
|
140
|
+
within("##{category}_settings_tab", wait: 10) do
|
|
141
|
+
find("button##{setting_name}", wait: 10).click
|
|
142
|
+
find("#setting-input-#{setting_name}", wait: 10).fill_in with: new_value
|
|
143
|
+
find("[data-ouia-component-id='submit-edit-btn']").click
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: foreman-openbolt-acceptance
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
foreman:
|
|
5
|
+
container_name: foreman-openbolt-test
|
|
6
|
+
hostname: foreman.example.com
|
|
7
|
+
image: ${FOREMAN_IMAGE:-foreman-openbolt:3.18}
|
|
8
|
+
pull_policy: never
|
|
9
|
+
platform: linux/amd64
|
|
10
|
+
privileged: true
|
|
11
|
+
ports:
|
|
12
|
+
- "443:443"
|
|
13
|
+
- "8443:8443"
|
|
14
|
+
entrypoint: /entrypoint.sh
|
|
15
|
+
volumes:
|
|
16
|
+
# Entrypoint script (prepares SSH keys, fixtures, then starts systemd)
|
|
17
|
+
- ./foreman/entrypoint.sh:/entrypoint.sh:ro
|
|
18
|
+
# Plugin RPMs built by rake build:rpm
|
|
19
|
+
- ../../../pkg:/opt/pkg:ro
|
|
20
|
+
# Fixture modules for acceptance tasks
|
|
21
|
+
- ../fixtures/modules:/opt/fixtures/modules:ro
|
|
22
|
+
# SSH private key for bolt to connect to targets
|
|
23
|
+
- ../fixtures/keys/id_rsa:/tmp/ssh/id_rsa:ro
|
|
24
|
+
# OpenBolt plugin config for smart-proxy
|
|
25
|
+
- ../fixtures/openbolt.yml:/etc/foreman-proxy/settings.d/openbolt.yml:ro
|
|
26
|
+
depends_on:
|
|
27
|
+
- target1
|
|
28
|
+
- target2
|
|
29
|
+
tmpfs:
|
|
30
|
+
- /run
|
|
31
|
+
- /tmp:rw,exec,nosuid
|
|
32
|
+
healthcheck:
|
|
33
|
+
test: ["CMD", "curl", "-sk", "https://localhost/api/v2/status"]
|
|
34
|
+
interval: 10s
|
|
35
|
+
timeout: 10s
|
|
36
|
+
retries: 30
|
|
37
|
+
start_period: 60s
|
|
38
|
+
|
|
39
|
+
chrome:
|
|
40
|
+
container_name: foreman-openbolt-chrome
|
|
41
|
+
image: ${SELENIUM_IMAGE:-selenium/standalone-chrome:latest}
|
|
42
|
+
shm_size: 2g
|
|
43
|
+
ports:
|
|
44
|
+
- "4444:4444"
|
|
45
|
+
- "7900:7900"
|
|
46
|
+
depends_on:
|
|
47
|
+
- foreman
|
|
48
|
+
|
|
49
|
+
target1:
|
|
50
|
+
container_name: foreman-openbolt-target1
|
|
51
|
+
hostname: target1.example.com
|
|
52
|
+
image: foreman-openbolt-target
|
|
53
|
+
pull_policy: never
|
|
54
|
+
build:
|
|
55
|
+
context: .
|
|
56
|
+
dockerfile: target/Dockerfile
|
|
57
|
+
volumes:
|
|
58
|
+
- ../fixtures/keys/id_rsa.pub:/tmp/id_rsa.pub:ro
|
|
59
|
+
|
|
60
|
+
target2:
|
|
61
|
+
container_name: foreman-openbolt-target2
|
|
62
|
+
hostname: target2.example.com
|
|
63
|
+
image: foreman-openbolt-target
|
|
64
|
+
pull_policy: never
|
|
65
|
+
build:
|
|
66
|
+
context: .
|
|
67
|
+
dockerfile: target/Dockerfile
|
|
68
|
+
volumes:
|
|
69
|
+
- ../fixtures/keys/id_rsa.pub:/tmp/id_rsa.pub:ro
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Foreman base image for acceptance testing.
|
|
2
|
+
# This Dockerfile only installs packages. foreman-installer is run
|
|
3
|
+
# separately via `docker run --hostname` (see Rakefile) because it
|
|
4
|
+
# requires a valid FQDN and systemd, neither of which are available
|
|
5
|
+
# during `docker build`.
|
|
6
|
+
#
|
|
7
|
+
# The final image is created by committing the container after
|
|
8
|
+
# foreman-installer completes. Plugin RPMs are installed at runtime
|
|
9
|
+
# via setup.sh.
|
|
10
|
+
FROM --platform=linux/amd64 rockylinux:9
|
|
11
|
+
|
|
12
|
+
ARG FOREMAN_VERSION=3.18
|
|
13
|
+
|
|
14
|
+
# Locale
|
|
15
|
+
RUN dnf install -y glibc-langpack-en && dnf clean all
|
|
16
|
+
ENV LANG=en_US.UTF-8
|
|
17
|
+
ENV PATH="$PATH:/opt/puppetlabs/bin:/opt/puppetlabs/server/bin"
|
|
18
|
+
|
|
19
|
+
# Enable module streams
|
|
20
|
+
RUN dnf -y module enable nodejs:22 postgresql:16
|
|
21
|
+
|
|
22
|
+
# Add Foreman and OpenVox repos
|
|
23
|
+
RUN dnf install -y \
|
|
24
|
+
"https://yum.theforeman.org/releases/${FOREMAN_VERSION}/el9/x86_64/foreman-release.rpm" \
|
|
25
|
+
https://yum.voxpupuli.org/openvox8-release-el-9.noarch.rpm && \
|
|
26
|
+
dnf clean all
|
|
27
|
+
|
|
28
|
+
# Install foreman-installer, OpenBolt, and OpenVox (agent + server)
|
|
29
|
+
RUN dnf install -y \
|
|
30
|
+
foreman-installer \
|
|
31
|
+
jq \
|
|
32
|
+
openbolt \
|
|
33
|
+
openssh-clients \
|
|
34
|
+
openvox-agent \
|
|
35
|
+
openvox-server && \
|
|
36
|
+
dnf clean all
|
|
37
|
+
|
|
38
|
+
# Fix NSS for container environment. Rocky 9 defaults to "sss files systemd"
|
|
39
|
+
# but sssd isn't running, causing getpwuid(0) to fail in JRuby/puppetserver.
|
|
40
|
+
RUN sed -i 's/^passwd:.*/passwd: files/' /etc/nsswitch.conf && \
|
|
41
|
+
sed -i 's/^shadow:.*/shadow: files/' /etc/nsswitch.conf && \
|
|
42
|
+
sed -i 's/^group:.*/group: files/' /etc/nsswitch.conf
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
CMD ["/usr/sbin/init"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Prepare the Foreman container for acceptance testing.
|
|
3
|
+
# This runs before systemd (PID 1) takes over.
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
# Proxy log directory
|
|
7
|
+
mkdir -p /var/log/foreman-proxy/openbolt
|
|
8
|
+
chown -R foreman-proxy:foreman-proxy /var/log/foreman-proxy
|
|
9
|
+
|
|
10
|
+
# SSH key for proxy to reach targets
|
|
11
|
+
if [ -f /tmp/ssh/id_rsa ]; then
|
|
12
|
+
mkdir -p /opt/foreman-proxy/.ssh
|
|
13
|
+
chown foreman-proxy:foreman-proxy /opt/foreman-proxy /opt/foreman-proxy/.ssh
|
|
14
|
+
cp /tmp/ssh/id_rsa /opt/foreman-proxy/.ssh/id_rsa
|
|
15
|
+
chown foreman-proxy:foreman-proxy /opt/foreman-proxy/.ssh/id_rsa
|
|
16
|
+
chmod 600 /opt/foreman-proxy/.ssh/id_rsa
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Deploy fixture modules for acceptance tasks
|
|
20
|
+
if [ -d /opt/fixtures/modules ]; then
|
|
21
|
+
mkdir -p /etc/puppetlabs/code/environments/production/modules
|
|
22
|
+
cp -r /opt/fixtures/modules/* /etc/puppetlabs/code/environments/production/modules/
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Hand off to systemd
|
|
26
|
+
exec /usr/sbin/init
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
FROM rockylinux:9
|
|
2
|
+
|
|
3
|
+
ARG OPENVOX_RELEASE=8
|
|
4
|
+
|
|
5
|
+
RUN dnf install -y \
|
|
6
|
+
openssh-server \
|
|
7
|
+
openssh-clients \
|
|
8
|
+
sudo \
|
|
9
|
+
jq \
|
|
10
|
+
https://yum.voxpupuli.org/openvox${OPENVOX_RELEASE}-release-el-9.noarch.rpm && \
|
|
11
|
+
dnf install -y openvox-agent && \
|
|
12
|
+
dnf clean all
|
|
13
|
+
|
|
14
|
+
RUN useradd -m openbolt && \
|
|
15
|
+
echo 'openbolt:openbolt' | chpasswd && \
|
|
16
|
+
usermod -aG wheel openbolt && \
|
|
17
|
+
echo 'openbolt ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/openbolt
|
|
18
|
+
|
|
19
|
+
RUN mkdir -p /home/openbolt/.ssh && \
|
|
20
|
+
chmod 700 /home/openbolt/.ssh && \
|
|
21
|
+
chown -R openbolt:openbolt /home/openbolt
|
|
22
|
+
|
|
23
|
+
RUN ssh-keygen -A
|
|
24
|
+
|
|
25
|
+
COPY target/entrypoint.sh /entrypoint.sh
|
|
26
|
+
RUN chmod +x /entrypoint.sh
|
|
27
|
+
|
|
28
|
+
EXPOSE 22
|
|
29
|
+
ENTRYPOINT ["/entrypoint.sh"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Copy the bind-mounted public key into the openbolt user's authorized_keys
|
|
3
|
+
# with correct ownership and permissions. sshd refuses keys when the
|
|
4
|
+
# file or parent directory is writable by anyone other than the owner.
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
cp /tmp/id_rsa.pub /home/openbolt/.ssh/authorized_keys
|
|
8
|
+
chown openbolt:openbolt /home/openbolt/.ssh/authorized_keys
|
|
9
|
+
chmod 600 /home/openbolt/.ssh/authorized_keys
|
|
10
|
+
|
|
11
|
+
exec /usr/sbin/sshd -D
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"input_method": "environment",
|
|
3
|
+
"description": "A task with multiple parameter types for testing command building",
|
|
4
|
+
"parameters": {
|
|
5
|
+
"required_string": {
|
|
6
|
+
"type": "String",
|
|
7
|
+
"description": "A required string parameter"
|
|
8
|
+
},
|
|
9
|
+
"optional_string": {
|
|
10
|
+
"type": "Optional[String]",
|
|
11
|
+
"description": "An optional string parameter"
|
|
12
|
+
},
|
|
13
|
+
"array_param": {
|
|
14
|
+
"type": "Array",
|
|
15
|
+
"description": "An array parameter"
|
|
16
|
+
},
|
|
17
|
+
"with_default": {
|
|
18
|
+
"type": "String",
|
|
19
|
+
"description": "A parameter with a default value",
|
|
20
|
+
"default": "default_value"
|
|
21
|
+
},
|
|
22
|
+
"hash_param": {
|
|
23
|
+
"type": "Optional[Hash]",
|
|
24
|
+
"description": "An optional hash parameter"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"implementations": [
|
|
28
|
+
{"name": "complex_params.sh", "requirements": ["shell"]}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Echo back all parameters as JSON so the test can verify they arrived correctly.
|
|
3
|
+
# PT_array_param arrives as a JSON string from bolt, so we pass it through as raw JSON.
|
|
4
|
+
jq -n \
|
|
5
|
+
--arg required "$PT_required_string" \
|
|
6
|
+
--arg optional "$PT_optional_string" \
|
|
7
|
+
--argjson array "${PT_array_param:-null}" \
|
|
8
|
+
--arg default_val "$PT_with_default" \
|
|
9
|
+
--argjson hash "${PT_hash_param:-null}" \
|
|
10
|
+
'{
|
|
11
|
+
required_string: $required,
|
|
12
|
+
optional_string: (if $optional == "" then null else $optional end),
|
|
13
|
+
array_param: $array,
|
|
14
|
+
with_default: $default_val,
|
|
15
|
+
hash_param: $hash
|
|
16
|
+
}'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"input_method": "environment",
|
|
3
|
+
"description": "Echo a message back with the hostname",
|
|
4
|
+
"parameters": {
|
|
5
|
+
"message": {
|
|
6
|
+
"type": "String",
|
|
7
|
+
"description": "The message to echo back"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"implementations": [
|
|
11
|
+
{"name": "echo.sh", "requirements": ["shell"]}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"input_method": "environment",
|
|
3
|
+
"description": "A task that sleeps for a specified number of seconds",
|
|
4
|
+
"parameters": {
|
|
5
|
+
"seconds": {
|
|
6
|
+
"type": "Integer",
|
|
7
|
+
"description": "Number of seconds to sleep",
|
|
8
|
+
"default": 5
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"implementations": [
|
|
12
|
+
{"name": "slow_task.sh", "requirements": ["shell"]}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"input_method": "environment",
|
|
3
|
+
"description": "A task that succeeds on target1 and fails on target2",
|
|
4
|
+
"parameters": {
|
|
5
|
+
"succeed_on": {
|
|
6
|
+
"type": "String",
|
|
7
|
+
"description": "Hostname prefix that should succeed"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"implementations": [
|
|
11
|
+
{"name": "target_conditional.sh", "requirements": ["shell"]}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests that errors are handled gracefully through the full stack.
|
|
6
|
+
class ErrorHandlingTest < AcceptanceTestCase
|
|
7
|
+
def setup
|
|
8
|
+
super
|
|
9
|
+
foreman_login
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_launch_button_disabled_with_no_matching_hosts
|
|
13
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
14
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
15
|
+
select_first_proxy
|
|
16
|
+
select_hosts_via_search('nonexistent.example.com')
|
|
17
|
+
|
|
18
|
+
assert_selector '#task-name-input option', minimum: 2, wait: 15
|
|
19
|
+
select 'acceptance::echo', from: 'task-name-input'
|
|
20
|
+
|
|
21
|
+
assert_selector 'button[type="submit"][disabled]', text: 'Launch Task'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_failing_task_shows_error_in_result
|
|
25
|
+
launch_task_via_ui('acceptance::failing_task')
|
|
26
|
+
assert_task_failed
|
|
27
|
+
assert_result_contains 'This task always fails'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_mixed_target_results_show_per_host_status
|
|
31
|
+
launch_task_via_ui('acceptance::target_conditional',
|
|
32
|
+
params: { 'succeed_on' => 'target1' })
|
|
33
|
+
|
|
34
|
+
assert_task_failed
|
|
35
|
+
assert_result_has_content
|
|
36
|
+
# Result should contain output from both targets
|
|
37
|
+
assert_result_contains 'target1'
|
|
38
|
+
assert_result_contains 'target2'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../acceptance_helper'
|
|
4
|
+
|
|
5
|
+
# Tests for HostSelector-specific affordances: the search query chip
|
|
6
|
+
# and the "Clear all target selections" link.
|
|
7
|
+
class HostSelectorTest < AcceptanceTestCase
|
|
8
|
+
def setup
|
|
9
|
+
super
|
|
10
|
+
foreman_login
|
|
11
|
+
visit '/foreman_openbolt/page_launch_task'
|
|
12
|
+
assert_selector '#smart-proxy-input', wait: 15
|
|
13
|
+
select_first_proxy
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_clear_chips_link_empties_targets_and_removes_chip
|
|
17
|
+
select_hosts_via_search('target1')
|
|
18
|
+
|
|
19
|
+
# The Targets label is conditional on targets.length > 0
|
|
20
|
+
# (LaunchTask/index.js:230).
|
|
21
|
+
assert_selector '.pf-v5-c-label', text: /Targets:\s*\d+/, wait: 15
|
|
22
|
+
|
|
23
|
+
# Click the "Clear all target selections" button (ouiaId=clear-chips).
|
|
24
|
+
find('[data-ouia-component-id="clear-chips"]').click
|
|
25
|
+
|
|
26
|
+
# Targets label disappears and the clear-chips button itself
|
|
27
|
+
# unmounts once there are no selections.
|
|
28
|
+
assert_no_selector '.pf-v5-c-label', text: /Targets:/, wait: 10
|
|
29
|
+
assert_no_selector '[data-ouia-component-id="clear-chips"]', wait: 5
|
|
30
|
+
end
|
|
31
|
+
end
|