foreman_openbolt 1.1.1 → 1.2.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 +14 -246
- data/Rakefile +32 -0
- data/app/controllers/foreman_openbolt/task_controller.rb +4 -1
- data/lib/foreman_openbolt/engine.rb +128 -24
- data/lib/foreman_openbolt/version.rb +1 -1
- data/lib/proxy_api/openbolt.rb +13 -3
- data/package.json +1 -1
- data/test/acceptance/acceptance_helper.rb +13 -0
- data/test/acceptance/tests/transport_options_test.rb +44 -4
- data/test/unit/controllers/task_controller_test.rb +96 -21
- data/webpack/src/Components/LaunchTask/__tests__/ParameterField.test.js +45 -0
- data/webpack/src/Components/LaunchTask/hooks/__tests__/useOpenBoltOptions.test.js +1 -0
- data/webpack/src/Components/TaskExecution/__tests__/LoadingIndicator.test.js +1 -1
- data/webpack/src/Components/TaskExecution/hooks/__tests__/useJobPolling.test.js +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a6aae14c90bc7a4af6435767a84d174b69309e9f98ae59a221477067be2492b8
|
|
4
|
+
data.tar.gz: 2d3e9188b01cf3c03bc132440106271b541f8ab753df060984e559b624058631
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f2f8a142ca2e214836b36c5f22be10975c322b5df20830110edc09ef49ac6b1445bccb22ffbe7b6d3252efb140d9ce45b1fa11d12987df27270fbd014e497ca
|
|
7
|
+
data.tar.gz: b05c9442560d6acc1720c7495c30fe213f2b59ea72f5aefe37f3b7cab98b863b6c22add8fa3fd010b6b3803075b4c46ad130232c7b804d2dfffd6f4f118f17fe
|
data/README.md
CHANGED
|
@@ -23,14 +23,14 @@ A task is copied to N targets and executed there.
|
|
|
23
23
|
|
|
24
24
|
## Plans
|
|
25
25
|
|
|
26
|
-
Plans provide complex logic options, written in Puppet
|
|
27
|
-
Besides the usual Puppet
|
|
26
|
+
Plans provide complex logic options, written in the Puppet language.
|
|
27
|
+
Besides the usual Puppet language functions, it's also possible to execute tasks and evaluate their responses.
|
|
28
28
|
|
|
29
29
|
## OpenBolt in Foreman!
|
|
30
30
|
|
|
31
|
-
OpenBolt is the Ansible counterpart and OpenBolt is Puppet
|
|
32
|
-
OpenBolt and Puppet integrate very well together and OpenBolt can reuse your existing
|
|
33
|
-
Since OpenBolt is a CLI only application, and most Puppet users run Foreman anyways, it made sense to integrate OpenBolt into Foreman, instead of writing another web UI.
|
|
31
|
+
OpenBolt is the Ansible counterpart and OpenBolt is OpenVox/Puppet native.
|
|
32
|
+
OpenBolt and OpenVox/Puppet integrate very well together and OpenBolt can reuse your existing code.
|
|
33
|
+
Since OpenBolt is a CLI only application, and most OpenVox and Puppet users run Foreman anyways, it made sense to integrate OpenBolt into Foreman, instead of writing another web UI.
|
|
34
34
|
|
|
35
35
|
## Installation
|
|
36
36
|
|
|
@@ -49,7 +49,9 @@ The [theforeman/foreman_proxy](https://github.com/theforeman/puppet-foreman_prox
|
|
|
49
49
|
The Foreman plugin provides UI elements to start Tasks on various nodes.
|
|
50
50
|
Foreman then talks to a Smartproxy to run OpenBolt.
|
|
51
51
|
The Smartproxy also establishes the connections to the various targets.
|
|
52
|
-
This is usually a
|
|
52
|
+
This is usually a SSH, WinRM, or Choria connection. The Choria transport
|
|
53
|
+
requires OpenBolt 5.5 or later and is not available in Puppet Bolt.
|
|
54
|
+
SSH and WinRM transports work with any version.
|
|
53
55
|
|
|
54
56
|
You need to have `bolt` in your `$PATH` on the Smartproxy.
|
|
55
57
|
OpenBolt packages are available at [yum.voxpupuli.org](https://yum.voxpupuli.org/) & [apt.voxpupuli.org](https://apt.voxpupuli.org/) in the openvox8 repo.
|
|
@@ -57,172 +59,18 @@ You can also use the legacy Bolt packages from Perforce from the `puppet-tools`
|
|
|
57
59
|
|
|
58
60
|
The integration is supported on Foreman 3.17 and all following versions, including development/nightly builds.
|
|
59
61
|
|
|
60
|
-
OpenBolt relies on Tasks & Plans. They are distributed as
|
|
62
|
+
OpenBolt relies on Tasks & Plans. They are distributed as modules.
|
|
61
63
|
The plugin assumes that you deployed your code.
|
|
62
64
|
We recommend to use [r10k](https://github.com/puppetlabs/r10k?tab=readme-ov-file#r10k) or [g10k](https://github.com/xorpaul/g10k?tab=readme-ov-file#g10k) to deploy code, as you do it on your compilers.
|
|
63
65
|
|
|
64
66
|
A handful of core/default Tasks & Plans are also included in the [OpenBolt rpm/deb packages](https://github.com/OpenVoxProject/openbolt/blob/main/Puppetfile).
|
|
65
67
|
|
|
66
|
-
##
|
|
68
|
+
## Documentation
|
|
67
69
|
|
|
68
|
-
(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-

|
|
73
|
-
|
|
74
|
-
The "Launch Task" option allows you to select any smartproxy with the `openbolt` feature (which is available when the OpenBolt Smartproxy plugin is installed).
|
|
75
|
-
Afterwards you can select N targets to run the task and select an available task from the selected Smartproxy.
|
|
76
|
-
On the right side you can configure OpenBolt connection settings.
|
|
77
|
-
|
|
78
|
-

|
|
79
|
-
|
|
80
|
-
After selecting a task, the task metadata is fetched and shown.
|
|
81
|
-
Additional input elements will appear, if the task support it.
|
|
82
|
-
|
|
83
|
-

|
|
84
|
-
|
|
85
|
-
The metadata can contains a description and datatypes for tasks.
|
|
86
|
-
Those information can be shown as well.
|
|
87
|
-
|
|
88
|
-

|
|
89
|
-
|
|
90
|
-
While the task is running, the UI polls the status from the smart proxy.
|
|
91
|
-
|
|
92
|
-

|
|
93
|
-
|
|
94
|
-
After the task finished, it will display a success for failure page.
|
|
95
|
-
|
|
96
|
-

|
|
97
|
-
|
|
98
|
-
You can also see the used parameters for a task.
|
|
99
|
-
|
|
100
|
-

|
|
101
|
-
|
|
102
|
-
We also display the used OpenBolt command line, in case you want to manually run it or debug it.
|
|
103
|
-
|
|
104
|
-

|
|
105
|
-
|
|
106
|
-
OpenBolt returns JSON for executed tasks.
|
|
107
|
-
That's visible in the UI.
|
|
108
|
-
For failed tasks but also for passed tasks.
|
|
109
|
-
|
|
110
|
-

|
|
111
|
-
|
|
112
|
-

|
|
113
|
-
|
|
114
|
-
## Development
|
|
115
|
-
|
|
116
|
-
### Linting
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
bundle exec rake lint # Run all linters (rubocop, erb_lint, eslint)
|
|
120
|
-
bundle exec rake lint:fix # Auto-fix where possible
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Ruby and ERB linters run directly. The JavaScript linter requires npm dependencies, so either install them locally (`npm install --legacy-peer-deps`) or run lint:js inside a container:
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
CONTAINER=1 bundle exec rake lint:js
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Unit Tests
|
|
130
|
-
|
|
131
|
-
Unit tests run inside Docker containers with a full Foreman installation. Requires Docker with compose support.
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
bundle exec rake test:unit:up # Build image, start containers, install deps
|
|
135
|
-
bundle exec rake test:unit:ruby # Run Ruby tests
|
|
136
|
-
bundle exec rake test:unit:js # Run JavaScript tests
|
|
137
|
-
bundle exec rake test:unit:all # Run all unit tests
|
|
138
|
-
bundle exec rake test:unit:down # Stop and remove containers
|
|
139
|
-
bundle exec rake test # Shortcut: up, test, down in one step
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Set `FOREMAN_VERSION` to test against a specific Foreman version (default: `3.18`):
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
FOREMAN_VERSION=3.17 bundle exec rake test:unit:up
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Acceptance Tests
|
|
149
|
-
|
|
150
|
-
Acceptance tests exercise the plugin through the browser using Capybara and Selenium. They build RPMs, start a multi-container environment (Foreman + OpenVox + SSH targets + Chromium), and run tests against the real UI.
|
|
151
|
-
|
|
152
|
-
**Prerequisites:**
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
bundle install --with acceptance
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
The [smart_proxy_openbolt](https://github.com/overlookinfra/smart_proxy_openbolt) and [foreman-packaging](https://github.com/theforeman/foreman-packaging) repos are cloned automatically when needed.
|
|
159
|
-
|
|
160
|
-
**Running:**
|
|
161
|
-
|
|
162
|
-
```bash
|
|
163
|
-
bundle exec rake acceptance # Full cycle: up, run tests, down
|
|
164
|
-
bundle exec rake acceptance:up # Build RPMs, start Foreman, configure everything
|
|
165
|
-
bundle exec rake acceptance:run # Run tests (requires up first)
|
|
166
|
-
bundle exec rake acceptance:down # Stop containers
|
|
167
|
-
bundle exec rake acceptance:clean # Full reset: stop containers, remove images and artifacts
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
The `acceptance:up` task is idempotent and can be re-run to pick up new RPM changes. It caches the Foreman Docker image per version so subsequent runs are faster.
|
|
171
|
-
|
|
172
|
-
**Watching tests in the browser:**
|
|
173
|
-
|
|
174
|
-
Set `HEADFUL=1` to disable headless mode, then open `http://localhost:7900` (password: `secret`) to watch the tests via noVNC:
|
|
175
|
-
|
|
176
|
-
```bash
|
|
177
|
-
HEADFUL=1 bundle exec rake acceptance:run
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
**Running a subset of tests:**
|
|
181
|
-
|
|
182
|
-
`acceptance:run` accepts `TEST=<path>` to limit which test files are loaded, and `TESTOPTS=<opts>` to forward options (e.g. `--name=/pattern/`) to the Test::Unit autorunner. Both can be combined.
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
# Run every test in one file
|
|
186
|
-
bundle exec rake acceptance:run TEST=test/acceptance/tests/settings_test.rb
|
|
187
|
-
|
|
188
|
-
# Run a single test by exact method name (any file)
|
|
189
|
-
bundle exec rake acceptance:run TESTOPTS='--name=test_echo_task_succeeds_on_all_targets'
|
|
190
|
-
|
|
191
|
-
# Run tests whose name matches a regex within one file
|
|
192
|
-
bundle exec rake acceptance:run \
|
|
193
|
-
TEST=test/acceptance/tests/settings_test.rb \
|
|
194
|
-
TESTOPTS='--name=/host_key/'
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
**Environment variables:**
|
|
198
|
-
|
|
199
|
-
| Variable | Default | Description |
|
|
200
|
-
|----------|---------|-------------|
|
|
201
|
-
| `CHROMEDRIVER_URL` | `http://localhost:4444` | Selenium WebDriver endpoint |
|
|
202
|
-
| `FOREMAN_BRANCH` | `<version>-stable` | Foreman git branch for unit test image (derived from `FOREMAN_VERSION`) |
|
|
203
|
-
| `FOREMAN_PACKAGING_REPO` | `https://github.com/theforeman/foreman-packaging.git` | Git URL for foreman-packaging (cloned automatically for RPM builds) |
|
|
204
|
-
| `FOREMAN_PASS` | `changeme` | Foreman login password |
|
|
205
|
-
| `FOREMAN_URL` | `https://foreman` | Foreman URL as seen by Chrome. Override to run tests against a live instance |
|
|
206
|
-
| `FOREMAN_USER` | `admin` | Foreman login username |
|
|
207
|
-
| `FOREMAN_VERSION` | `3.18` | Foreman version to test against |
|
|
208
|
-
| `HEADFUL` | unset | Set to `1` to show the browser in noVNC |
|
|
209
|
-
| `SELENIUM_IMAGE` | auto-detected (ARM/x86) | Selenium container image (auto-selects `seleniarm/standalone-chromium` or `selenium/standalone-chrome`) |
|
|
210
|
-
| `SMART_PROXY_OPENBOLT_REF` | `main` | Branch or tag to clone |
|
|
211
|
-
| `SMART_PROXY_OPENBOLT_REPO` | `https://github.com/overlookinfra/smart_proxy_openbolt.git` | Git URL for smart_proxy_openbolt (cloned automatically for RPM builds) |
|
|
212
|
-
|
|
213
|
-
### Building Packages
|
|
214
|
-
|
|
215
|
-
Build RPM or DEB packages locally using containers. The [foreman-packaging](https://github.com/theforeman/foreman-packaging) repo is cloned automatically:
|
|
216
|
-
|
|
217
|
-
```bash
|
|
218
|
-
bundle exec rake build:rpm # Build RPM
|
|
219
|
-
bundle exec rake build:deb # Build DEB
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## TODO
|
|
223
|
-
|
|
224
|
-
* Integrate plans into the web UI
|
|
225
|
-
* Provide a choria transport plugin
|
|
70
|
+
- [Usage](docs/usage.md): screenshots and walkthrough of the UI
|
|
71
|
+
- [Development](docs/development.md): linting, unit tests, acceptance tests, building packages
|
|
72
|
+
- [Releasing](docs/releasing.md): version locations, release steps, RPM/DEB packaging
|
|
73
|
+
- [Choria Testing](docs/choria-testing.md): setting up Choria on a Foreman install for transport testing
|
|
226
74
|
|
|
227
75
|
## Contributing & support
|
|
228
76
|
|
|
@@ -247,83 +95,3 @@ GNU General Public License for more details.
|
|
|
247
95
|
|
|
248
96
|
You should have received a copy of the GNU General Public License
|
|
249
97
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
250
|
-
|
|
251
|
-
## How to Release
|
|
252
|
-
|
|
253
|
-
### Version locations
|
|
254
|
-
|
|
255
|
-
The version is maintained in two files:
|
|
256
|
-
|
|
257
|
-
1. `lib/foreman_openbolt/version.rb` -- the gem version (authoritative source)
|
|
258
|
-
2. `package.json` -- the npm package version (must match)
|
|
259
|
-
|
|
260
|
-
If the minimum Foreman version changes, also update:
|
|
261
|
-
|
|
262
|
-
3. `lib/foreman_openbolt/engine.rb` -- `requires_foreman '>= X.Y.Z'`
|
|
263
|
-
4. `.github/workflows/build.yml` -- default `foreman_version` and `foreman_packaging_ref` inputs
|
|
264
|
-
|
|
265
|
-
### Release steps
|
|
266
|
-
|
|
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
|
|
274
|
-
- Builds the gem
|
|
275
|
-
- Creates a GitHub Release with auto-generated notes and the gem attached
|
|
276
|
-
- Publishes the gem to GitHub Packages
|
|
277
|
-
- Publishes the gem to RubyGems.org (requires the `release` environment)
|
|
278
|
-
- Verifies the gem is available on RubyGems.org
|
|
279
|
-
|
|
280
|
-
### RPM/DEB packaging
|
|
281
|
-
|
|
282
|
-
After the gem is published to RubyGems, both RPM and DEB packages need to be updated in [theforeman/foreman-packaging](https://github.com/theforeman/foreman-packaging).
|
|
283
|
-
|
|
284
|
-
A bot automatically creates PRs against the `rpm/develop` and `deb/develop` branches to pick up the new gem version. These PRs build packages for Foreman nightly.
|
|
285
|
-
|
|
286
|
-
For stable Foreman releases (currently 3.17 and 3.18), cherry-pick the packaging commits from the develop branches into the corresponding stable branches. For each stable version you want to support:
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
cd foreman-packaging
|
|
290
|
-
|
|
291
|
-
# RPM: cherry-pick from rpm/develop into a branch off the stable target
|
|
292
|
-
git checkout rpm/3.18
|
|
293
|
-
git checkout -b cherry-pick/rubygem-foreman_openbolt-rpm-3.18
|
|
294
|
-
git cherry-pick <commit-from-rpm/develop>
|
|
295
|
-
# Push to your fork and open a PR targeting rpm/3.18
|
|
296
|
-
|
|
297
|
-
# DEB: same approach for the deb side
|
|
298
|
-
git checkout deb/3.18
|
|
299
|
-
git checkout -b cherry-pick/rubygem-foreman-openbolt-deb-3.18
|
|
300
|
-
git cherry-pick <commit-from-deb/develop>
|
|
301
|
-
# Push to your fork and open a PR targeting deb/3.18
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
PRs against stable branches should be labeled "Stable branch".
|
|
305
|
-
|
|
306
|
-
**Alternative: manual version bump**
|
|
307
|
-
|
|
308
|
-
If the cherry-pick doesn't apply cleanly, you can bump the version manually on the stable branch instead.
|
|
309
|
-
|
|
310
|
-
*RPM:* Checkout the target branch and run `bump_rpm.sh`:
|
|
311
|
-
```bash
|
|
312
|
-
cd foreman-packaging
|
|
313
|
-
git checkout rpm/3.18
|
|
314
|
-
git checkout -b bump_rpm/rubygem-foreman_openbolt
|
|
315
|
-
./bump_rpm.sh packages/plugins/rubygem-foreman_openbolt
|
|
316
|
-
# Review changes, push to your fork, and open a PR targeting rpm/3.18
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
*DEB:* Checkout the target branch and update these files:
|
|
320
|
-
- `debian/gem.list` -- new gem filename
|
|
321
|
-
- `foreman_openbolt.rb` -- new version
|
|
322
|
-
- `debian/control` -- dependency versions (if changed)
|
|
323
|
-
- `debian/changelog` -- add a new entry
|
|
324
|
-
|
|
325
|
-
```bash
|
|
326
|
-
git checkout deb/3.18
|
|
327
|
-
git checkout -b bump_deb/ruby-foreman-openbolt
|
|
328
|
-
# Make the changes above, push to your fork, and open a PR targeting deb/3.18
|
|
329
|
-
```
|
data/Rakefile
CHANGED
|
@@ -20,4 +20,36 @@ end
|
|
|
20
20
|
DEFAULT_FOREMAN_VERSION = latest_foreman_version
|
|
21
21
|
FOREMAN_VERSION = ENV.fetch('FOREMAN_VERSION', DEFAULT_FOREMAN_VERSION)
|
|
22
22
|
|
|
23
|
+
def minimum_supported_foreman_version
|
|
24
|
+
engine_path = File.join(__dir__, 'lib', 'foreman_openbolt', 'engine.rb')
|
|
25
|
+
content = File.read(engine_path)
|
|
26
|
+
match = content.match(/requires_foreman\s+['"]>= (\d+\.\d+)/)
|
|
27
|
+
abort 'Could not parse minimum Foreman version from engine.rb'.red unless match
|
|
28
|
+
match[1]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def supported_foreman_releases
|
|
32
|
+
min = Gem::Version.new(minimum_supported_foreman_version)
|
|
33
|
+
max = Gem::Version.new(DEFAULT_FOREMAN_VERSION)
|
|
34
|
+
|
|
35
|
+
result = Shell.capture(
|
|
36
|
+
['git', 'ls-remote', '--tags', 'https://github.com/theforeman/foreman.git'],
|
|
37
|
+
print_command: false, allowed_exit_codes: [0, 1]
|
|
38
|
+
)
|
|
39
|
+
tags = result.output.scan(%r{refs/tags/([^\s]+)$}).flatten
|
|
40
|
+
versions = tags.filter_map do |tag|
|
|
41
|
+
Gem::Version.new(tag)
|
|
42
|
+
rescue ArgumentError
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
versions.reject(&:prerelease?)
|
|
47
|
+
.map { |v| "#{v.segments[0]}.#{v.segments[1]}" }
|
|
48
|
+
.uniq
|
|
49
|
+
.map { |v| Gem::Version.new(v) }
|
|
50
|
+
.grep(min..max)
|
|
51
|
+
.sort
|
|
52
|
+
.map(&:to_s)
|
|
53
|
+
end
|
|
54
|
+
|
|
23
55
|
task default: :lint
|
|
@@ -53,7 +53,7 @@ module ForemanOpenbolt
|
|
|
53
53
|
key = setting.name.sub(/^openbolt_/, '')
|
|
54
54
|
if setting.encrypted?
|
|
55
55
|
defaults[key] = ENCRYPTED_PLACEHOLDER unless setting.value.to_s.empty?
|
|
56
|
-
|
|
56
|
+
elsif !setting.value.to_s.empty?
|
|
57
57
|
defaults[key] = setting.value
|
|
58
58
|
end
|
|
59
59
|
end
|
|
@@ -136,6 +136,9 @@ module ForemanOpenbolt
|
|
|
136
136
|
rescue ActiveRecord::RecordInvalid => e
|
|
137
137
|
log_exception('launch_task', e)
|
|
138
138
|
render_error("Database error: #{e.message}", :internal_server_error)
|
|
139
|
+
rescue ProxyAPI::ProxyException => e
|
|
140
|
+
log_exception('launch_task', e)
|
|
141
|
+
render_error("Smart Proxy error: #{e.message}", :bad_gateway)
|
|
139
142
|
rescue StandardError => e
|
|
140
143
|
log_exception('launch_task', e)
|
|
141
144
|
render_error("Error launching task: #{e.message}", :internal_server_error)
|
|
@@ -35,6 +35,7 @@ module ForemanOpenbolt
|
|
|
35
35
|
TRANSPORTS = {
|
|
36
36
|
ssh: N_('SSH'),
|
|
37
37
|
winrm: N_('WinRM'),
|
|
38
|
+
choria: N_('Choria'),
|
|
38
39
|
}.freeze
|
|
39
40
|
LOG_LEVELS = {
|
|
40
41
|
error: N_('Error'),
|
|
@@ -43,28 +44,19 @@ module ForemanOpenbolt
|
|
|
43
44
|
debug: N_('Debug'),
|
|
44
45
|
trace: N_('Trace'),
|
|
45
46
|
}.freeze
|
|
47
|
+
CHORIA_TASK_AGENTS = {
|
|
48
|
+
bolt_tasks: N_('Bolt Tasks'),
|
|
49
|
+
shell: N_('Shell'),
|
|
50
|
+
}.freeze
|
|
46
51
|
# rubocop:enable Lint/ConstantDefinitionInBlock
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
type: :string,
|
|
50
|
-
default: 'ssh',
|
|
51
|
-
full_name: N_('Transport'),
|
|
52
|
-
description: N_('The transport method to use for connecting to target hosts'),
|
|
53
|
-
collection: proc { TRANSPORTS }
|
|
53
|
+
# General (all transports)
|
|
54
54
|
setting 'openbolt_log-level',
|
|
55
55
|
type: :string,
|
|
56
56
|
default: 'debug',
|
|
57
57
|
full_name: N_('Log Level'),
|
|
58
58
|
description: N_('Set the log level during OpenBolt execution'),
|
|
59
59
|
collection: proc { LOG_LEVELS }
|
|
60
|
-
setting 'openbolt_verbose',
|
|
61
|
-
type: :boolean,
|
|
62
|
-
default: false,
|
|
63
|
-
full_name: N_('Verbose'),
|
|
64
|
-
description: N_(
|
|
65
|
-
'Run the OpenBolt command with the --verbose flag. This prints additional information ' \
|
|
66
|
-
'during OpenBolt execution and will print any out::verbose plan statements.'
|
|
67
|
-
)
|
|
68
60
|
setting 'openbolt_noop',
|
|
69
61
|
type: :boolean,
|
|
70
62
|
default: false,
|
|
@@ -72,22 +64,132 @@ module ForemanOpenbolt
|
|
|
72
64
|
description: N_(
|
|
73
65
|
'Run the OpenBolt command with the --noop flag, which will make no changes to the target host'
|
|
74
66
|
)
|
|
67
|
+
setting 'openbolt_password',
|
|
68
|
+
type: :string,
|
|
69
|
+
default: nil,
|
|
70
|
+
full_name: N_('Password'),
|
|
71
|
+
description: N_('Password used for SSH or WinRM authentication'),
|
|
72
|
+
encrypted: true
|
|
75
73
|
setting 'openbolt_tmpdir',
|
|
76
74
|
type: :string,
|
|
77
|
-
default:
|
|
75
|
+
default: nil,
|
|
78
76
|
full_name: N_('Temporary Directory'),
|
|
79
77
|
description: N_('Directory to use for temporary files on target hosts during OpenBolt execution')
|
|
78
|
+
setting 'openbolt_transport',
|
|
79
|
+
type: :string,
|
|
80
|
+
default: 'ssh',
|
|
81
|
+
full_name: N_('Transport'),
|
|
82
|
+
description: N_('The transport method to use for connecting to target hosts'),
|
|
83
|
+
collection: proc { TRANSPORTS }
|
|
80
84
|
setting 'openbolt_user',
|
|
81
85
|
type: :string,
|
|
82
|
-
default:
|
|
86
|
+
default: nil,
|
|
83
87
|
full_name: N_('User'),
|
|
84
88
|
description: N_('Username used for SSH or WinRM authentication')
|
|
85
|
-
setting '
|
|
89
|
+
setting 'openbolt_verbose',
|
|
90
|
+
type: :boolean,
|
|
91
|
+
default: false,
|
|
92
|
+
full_name: N_('Verbose'),
|
|
93
|
+
description: N_(
|
|
94
|
+
'Run the OpenBolt command with the --verbose flag. This prints additional information ' \
|
|
95
|
+
'during OpenBolt execution and will print any out::verbose plan statements.'
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Choria
|
|
99
|
+
setting 'openbolt_choria-broker-timeout',
|
|
100
|
+
type: :integer,
|
|
101
|
+
default: nil,
|
|
102
|
+
full_name: N_('Choria Broker Timeout'),
|
|
103
|
+
description: N_('Time in seconds to wait when establishing a connection to a Choria broker.')
|
|
104
|
+
setting 'openbolt_choria-brokers',
|
|
86
105
|
type: :string,
|
|
87
|
-
default:
|
|
88
|
-
full_name: N_('
|
|
89
|
-
description: N_(
|
|
90
|
-
|
|
106
|
+
default: nil,
|
|
107
|
+
full_name: N_('Choria Brokers'),
|
|
108
|
+
description: N_(
|
|
109
|
+
'Comma-separated list of Choria broker addresses in host or host:port format ' \
|
|
110
|
+
'(e.g. broker1.example.com:4222,broker2.example.com:4222). Port defaults to 4222 if omitted. ' \
|
|
111
|
+
'When not set, the Choria client checks the config file, then SRV records, then falls back to puppet:4222.'
|
|
112
|
+
)
|
|
113
|
+
setting 'openbolt_choria-collective',
|
|
114
|
+
type: :string,
|
|
115
|
+
default: nil,
|
|
116
|
+
full_name: N_('Choria Collective'),
|
|
117
|
+
description: N_('Choria collective to route messages through.')
|
|
118
|
+
setting 'openbolt_choria-command-timeout',
|
|
119
|
+
type: :integer,
|
|
120
|
+
default: nil,
|
|
121
|
+
full_name: N_('Choria Command Timeout'),
|
|
122
|
+
description: N_('Time in seconds to wait for command completion on target nodes.')
|
|
123
|
+
setting 'openbolt_choria-config-file',
|
|
124
|
+
type: :string,
|
|
125
|
+
default: nil,
|
|
126
|
+
full_name: N_('Choria Config File'),
|
|
127
|
+
description: N_(
|
|
128
|
+
'Path on the smart proxy host to the Choria client configuration file. This file ' \
|
|
129
|
+
'must be readable by the foreman-proxy user. When not set, the proxy uses a built-in default.'
|
|
130
|
+
)
|
|
131
|
+
setting 'openbolt_choria-mcollective-certname',
|
|
132
|
+
type: :string,
|
|
133
|
+
default: nil,
|
|
134
|
+
full_name: N_('Choria MCollective Certname'),
|
|
135
|
+
description: N_(
|
|
136
|
+
'Override the MCollective certname for Choria client identity. When not set, the ' \
|
|
137
|
+
'proxy derives this automatically from its SSL certificate.'
|
|
138
|
+
)
|
|
139
|
+
setting 'openbolt_choria-puppet-environment',
|
|
140
|
+
type: :string,
|
|
141
|
+
default: nil,
|
|
142
|
+
full_name: N_('Choria Puppet Environment'),
|
|
143
|
+
description: N_(
|
|
144
|
+
'Puppet environment used by the Choria bolt_tasks agent to locate task files. ' \
|
|
145
|
+
"Only applies when the Choria Task Agent is 'bolt_tasks'. Defaults to " \
|
|
146
|
+
"'production' when not specified."
|
|
147
|
+
)
|
|
148
|
+
setting 'openbolt_choria-rpc-timeout',
|
|
149
|
+
type: :integer,
|
|
150
|
+
default: nil,
|
|
151
|
+
full_name: N_('Choria RPC Timeout'),
|
|
152
|
+
description: N_('Time in seconds to wait for RPC responses from target nodes.')
|
|
153
|
+
setting 'openbolt_choria-ssl-ca',
|
|
154
|
+
type: :string,
|
|
155
|
+
default: nil,
|
|
156
|
+
full_name: N_('Choria SSL CA'),
|
|
157
|
+
description: N_(
|
|
158
|
+
'Path on the smart proxy host to the Choria CA certificate. This file must be ' \
|
|
159
|
+
'readable by the foreman-proxy user.'
|
|
160
|
+
)
|
|
161
|
+
setting 'openbolt_choria-ssl-cert',
|
|
162
|
+
type: :string,
|
|
163
|
+
default: nil,
|
|
164
|
+
full_name: N_('Choria SSL Certificate'),
|
|
165
|
+
description: N_(
|
|
166
|
+
'Path on the smart proxy host to the Choria client SSL certificate. This file ' \
|
|
167
|
+
'must be readable by the foreman-proxy user.'
|
|
168
|
+
)
|
|
169
|
+
setting 'openbolt_choria-ssl-key',
|
|
170
|
+
type: :string,
|
|
171
|
+
default: nil,
|
|
172
|
+
full_name: N_('Choria SSL Key'),
|
|
173
|
+
description: N_(
|
|
174
|
+
'Path on the smart proxy host to the Choria client SSL private key. This file ' \
|
|
175
|
+
'must be readable by the foreman-proxy user.'
|
|
176
|
+
)
|
|
177
|
+
setting 'openbolt_choria-task-agent',
|
|
178
|
+
type: :string,
|
|
179
|
+
default: 'bolt_tasks',
|
|
180
|
+
full_name: N_('Choria Task Agent'),
|
|
181
|
+
description: N_(
|
|
182
|
+
'Choria agent used to execute tasks on target nodes. Use the bolt_tasks agent for ' \
|
|
183
|
+
'standard OpenBolt tasks, or the shell agent to run shell commands.'
|
|
184
|
+
),
|
|
185
|
+
collection: proc { CHORIA_TASK_AGENTS }
|
|
186
|
+
setting 'openbolt_choria-task-timeout',
|
|
187
|
+
type: :integer,
|
|
188
|
+
default: nil,
|
|
189
|
+
full_name: N_('Choria Task Timeout'),
|
|
190
|
+
description: N_('Time in seconds to wait for task completion on target nodes.')
|
|
191
|
+
|
|
192
|
+
# SSH
|
|
91
193
|
setting 'openbolt_host-key-check',
|
|
92
194
|
type: :boolean,
|
|
93
195
|
default: true,
|
|
@@ -95,7 +197,7 @@ module ForemanOpenbolt
|
|
|
95
197
|
description: N_('Whether to perform host key verification when connecting to targets over SSH')
|
|
96
198
|
setting 'openbolt_private-key',
|
|
97
199
|
type: :string,
|
|
98
|
-
default:
|
|
200
|
+
default: nil,
|
|
99
201
|
full_name: N_('SSH Private Key'),
|
|
100
202
|
description: N_(
|
|
101
203
|
'Path on the smart proxy host to the private key used for SSH authentication. This key must be ' \
|
|
@@ -103,7 +205,7 @@ module ForemanOpenbolt
|
|
|
103
205
|
)
|
|
104
206
|
setting 'openbolt_run-as',
|
|
105
207
|
type: :string,
|
|
106
|
-
default:
|
|
208
|
+
default: nil,
|
|
107
209
|
full_name: N_('SSH Run As User'),
|
|
108
210
|
description: N_(
|
|
109
211
|
'The user to run commands as on the target host. This requires that the user specified ' \
|
|
@@ -111,10 +213,12 @@ module ForemanOpenbolt
|
|
|
111
213
|
)
|
|
112
214
|
setting 'openbolt_sudo-password',
|
|
113
215
|
type: :string,
|
|
114
|
-
default:
|
|
216
|
+
default: nil,
|
|
115
217
|
full_name: N_('SSH Sudo Password'),
|
|
116
218
|
description: N_('Password used for privilege escalation when using SSH'),
|
|
117
219
|
encrypted: true
|
|
220
|
+
|
|
221
|
+
# WinRM
|
|
118
222
|
setting 'openbolt_ssl',
|
|
119
223
|
type: :boolean,
|
|
120
224
|
default: true,
|
data/lib/proxy_api/openbolt.rb
CHANGED
|
@@ -52,15 +52,25 @@ module ProxyAPI
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def parse_response(response, operation)
|
|
55
|
-
|
|
55
|
+
unless response
|
|
56
|
+
raise ProxyException.new(
|
|
57
|
+
@url, RuntimeError.new("No response from Smart Proxy during #{operation}"),
|
|
58
|
+
"No response from Smart Proxy during #{operation}"
|
|
59
|
+
)
|
|
60
|
+
end
|
|
56
61
|
|
|
57
62
|
body = response.body
|
|
58
|
-
|
|
63
|
+
if body.nil?
|
|
64
|
+
raise ProxyException.new(
|
|
65
|
+
@url, RuntimeError.new("Empty response body from Smart Proxy during #{operation}"),
|
|
66
|
+
"Empty response body from Smart Proxy during #{operation}"
|
|
67
|
+
)
|
|
68
|
+
end
|
|
59
69
|
|
|
60
70
|
JSON.parse(body)
|
|
61
71
|
rescue JSON::ParserError => e
|
|
62
72
|
raise ProxyException.new(
|
|
63
|
-
@url,
|
|
73
|
+
@url, e,
|
|
64
74
|
"Invalid JSON from Smart Proxy during #{operation}: #{e.message}. " \
|
|
65
75
|
"Response body (first 500 chars): #{body.to_s[0..500]}"
|
|
66
76
|
)
|
data/package.json
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Suppress a noisy deprecation warning emitted by Capybara 3.40.0 on Ruby 3.4+:
|
|
4
|
+
# URI::RFC3986_PARSER.make_regexp is obsolete. Use URI::RFC2396_PARSER.make_regexp explicitly.
|
|
5
|
+
# Fixed upstream in teamcapybara/capybara#2781 but not yet released. Remove this
|
|
6
|
+
# block once Capybara ships a release with that PR.
|
|
7
|
+
module SuppressCapybaraURIDeprecation
|
|
8
|
+
TARGET = 'URI::RFC3986_PARSER.make_regexp is obsolete'
|
|
9
|
+
def warn(message, **)
|
|
10
|
+
return if message.to_s.include?(TARGET)
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
Warning.singleton_class.prepend(SuppressCapybaraURIDeprecation)
|
|
15
|
+
|
|
3
16
|
require 'capybara'
|
|
4
17
|
require 'capybara/dsl'
|
|
5
18
|
require 'selenium-webdriver'
|
|
@@ -91,13 +91,13 @@ class TransportOptionsTest < AcceptanceTestCase
|
|
|
91
91
|
text: 'Select a task to see parameters', wait: 10
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
def
|
|
95
|
-
# The transport OpenBolt option has type ["ssh", "winrm"]
|
|
96
|
-
# ParameterField renders array-typed values as a <select>.
|
|
94
|
+
def test_transport_option_renders_as_select_with_ssh_winrm_and_choria
|
|
95
|
+
# The transport OpenBolt option has type ["ssh", "winrm", "choria"]
|
|
96
|
+
# and ParameterField renders array-typed values as a <select>.
|
|
97
97
|
field = find_by_id('param_transport', wait: 10)
|
|
98
98
|
assert_equal 'select', field.tag_name
|
|
99
99
|
option_values = all('#param_transport option').map(&:value)
|
|
100
|
-
assert_equal %w[ssh winrm], option_values
|
|
100
|
+
assert_equal %w[ssh winrm choria], option_values
|
|
101
101
|
assert_equal 'ssh', field.value
|
|
102
102
|
end
|
|
103
103
|
|
|
@@ -118,4 +118,44 @@ class TransportOptionsTest < AcceptanceTestCase
|
|
|
118
118
|
assert_selector '#param_host-key-check', wait: 10
|
|
119
119
|
assert_selector '#param_private-key', wait: 10
|
|
120
120
|
end
|
|
121
|
+
|
|
122
|
+
def test_switching_transport_to_choria_hides_ssh_only_options_and_shows_choria_options
|
|
123
|
+
# SSH-only options are present on load because the default transport is ssh.
|
|
124
|
+
assert_selector '#param_host-key-check', wait: 10
|
|
125
|
+
assert_selector '#param_private-key', wait: 10
|
|
126
|
+
assert_selector '#param_run-as', wait: 10
|
|
127
|
+
assert_selector '#param_sudo-password', wait: 10
|
|
128
|
+
|
|
129
|
+
# Switching transport to choria should hide ssh-only options and reveal
|
|
130
|
+
# choria-tagged options (OpenBoltOptionsSection filters by transport).
|
|
131
|
+
select 'choria', from: 'param_transport'
|
|
132
|
+
assert_no_selector '#param_host-key-check', wait: 10
|
|
133
|
+
assert_no_selector '#param_private-key', wait: 10
|
|
134
|
+
assert_no_selector '#param_run-as', wait: 10
|
|
135
|
+
assert_no_selector '#param_sudo-password', wait: 10
|
|
136
|
+
|
|
137
|
+
assert_selector '#param_choria-config-file', wait: 10
|
|
138
|
+
assert_selector '#param_choria-ssl-ca', wait: 10
|
|
139
|
+
assert_selector '#param_choria-brokers', wait: 10
|
|
140
|
+
|
|
141
|
+
# choria-task-agent must render as a <select> whose options match the
|
|
142
|
+
# proxy's enum exactly. The launch form gets the enum from the proxy's
|
|
143
|
+
# option metadata type array, so a drift in the proxy-side enum (or a
|
|
144
|
+
# mismatch with engine.rb's CHORIA_TASK_AGENTS collection shown on the
|
|
145
|
+
# Settings page) would only surface at task launch time otherwise.
|
|
146
|
+
task_agent = find_by_id('param_choria-task-agent', wait: 10)
|
|
147
|
+
assert_equal 'select', task_agent.tag_name
|
|
148
|
+
assert_equal %w[bolt_tasks shell], all('#param_choria-task-agent option').map(&:value)
|
|
149
|
+
assert_equal 'bolt_tasks', task_agent.value
|
|
150
|
+
|
|
151
|
+
# Switching back to ssh restores ssh-only options and hides choria-only ones.
|
|
152
|
+
# Includes the select-type field so a future regression that special-cases
|
|
153
|
+
# selects in the transport filter would be caught here.
|
|
154
|
+
select 'ssh', from: 'param_transport'
|
|
155
|
+
assert_selector '#param_host-key-check', wait: 10
|
|
156
|
+
assert_selector '#param_private-key', wait: 10
|
|
157
|
+
assert_no_selector '#param_choria-config-file', wait: 10
|
|
158
|
+
assert_no_selector '#param_choria-task-agent', wait: 10
|
|
159
|
+
assert_no_selector '#param_choria-brokers', wait: 10
|
|
160
|
+
end
|
|
121
161
|
end
|
|
@@ -141,6 +141,20 @@ class TaskControllerTest < ActionController::TestCase
|
|
|
141
141
|
assert_match(/Task execution failed/, JSON.parse(response.body)['error'])
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
+
test 'returns bad_gateway when proxy returns invalid JSON' do
|
|
145
|
+
stub_request(:post, "#{@proxy.url}/openbolt/launch/task")
|
|
146
|
+
.to_return(status: 200, body: 'not valid json',
|
|
147
|
+
headers: { 'Content-Type' => 'application/json' })
|
|
148
|
+
|
|
149
|
+
post :launch_task, params: {
|
|
150
|
+
proxy_id: @proxy.id,
|
|
151
|
+
task_name: 'mymod::install',
|
|
152
|
+
targets: 'host1',
|
|
153
|
+
}, session: @session
|
|
154
|
+
assert_response :bad_gateway
|
|
155
|
+
assert_match(/Smart Proxy error/, JSON.parse(response.body)['error'])
|
|
156
|
+
end
|
|
157
|
+
|
|
144
158
|
test 'returns error when proxy returns no job ID' do
|
|
145
159
|
stub_request(:post, "#{@proxy.url}/openbolt/launch/task")
|
|
146
160
|
.to_return(status: 200, body: { 'status' => 'ok' }.to_json,
|
|
@@ -214,7 +228,7 @@ class TaskControllerTest < ActionController::TestCase
|
|
|
214
228
|
ForemanTasks.stubs(:async_task)
|
|
215
229
|
end
|
|
216
230
|
|
|
217
|
-
test '
|
|
231
|
+
test 'sends real encrypted value to proxy and scrubs it in database' do
|
|
218
232
|
Setting['openbolt_password'] = 'real-secret-password'
|
|
219
233
|
|
|
220
234
|
post :launch_task, params: {
|
|
@@ -226,27 +240,11 @@ class TaskControllerTest < ActionController::TestCase
|
|
|
226
240
|
|
|
227
241
|
assert_response :success
|
|
228
242
|
|
|
229
|
-
#
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
.keys
|
|
234
|
-
.find { |sig| sig.uri.path == '/openbolt/launch/task' }
|
|
235
|
-
sent_body = JSON.parse(proxy_request.body)
|
|
236
|
-
assert_equal 'real-secret-password', sent_body['options']['password']
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
test 'scrubs encrypted options before storing in database' do
|
|
240
|
-
Setting['openbolt_password'] = 'real-secret-password'
|
|
241
|
-
|
|
242
|
-
post :launch_task, params: {
|
|
243
|
-
proxy_id: @proxy.id,
|
|
244
|
-
task_name: 'mymod::install',
|
|
245
|
-
targets: 'host1.example.com',
|
|
246
|
-
options: { 'password' => '[Use saved encrypted default]', 'transport' => 'ssh' },
|
|
247
|
-
}, session: @session
|
|
243
|
+
assert_requested(:post, "#{@proxy.url}/openbolt/launch/task") do |req|
|
|
244
|
+
sent_body = JSON.parse(req.body)
|
|
245
|
+
sent_body['options']['password'] == 'real-secret-password'
|
|
246
|
+
end
|
|
248
247
|
|
|
249
|
-
assert_response :success
|
|
250
248
|
job = ForemanOpenbolt::TaskJob.find_by(job_id: 'encrypted-job-1')
|
|
251
249
|
assert_equal '*****', job.openbolt_options['password']
|
|
252
250
|
assert_equal 'ssh', job.openbolt_options['transport']
|
|
@@ -318,6 +316,83 @@ class TaskControllerTest < ActionController::TestCase
|
|
|
318
316
|
end
|
|
319
317
|
end
|
|
320
318
|
|
|
319
|
+
context 'fetch_openbolt_options with Choria settings defaults' do
|
|
320
|
+
# Mirrors the real GET /openbolt/tasks/options response for Choria
|
|
321
|
+
# (see smart_proxy_openbolt/lib/smart_proxy_openbolt/main.rb OPENBOLT_OPTIONS).
|
|
322
|
+
def self.choria_proxy_options
|
|
323
|
+
{
|
|
324
|
+
'choria-task-agent' => { 'type' => %w[bolt_tasks shell], 'transport' => ['choria'], 'sensitive' => false },
|
|
325
|
+
'choria-config-file' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
326
|
+
'choria-mcollective-certname' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
327
|
+
'choria-ssl-ca' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
328
|
+
'choria-ssl-cert' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
329
|
+
'choria-ssl-key' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
330
|
+
'choria-collective' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
331
|
+
'choria-puppet-environment' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
332
|
+
'choria-rpc-timeout' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
333
|
+
'choria-task-timeout' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
334
|
+
'choria-command-timeout' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
335
|
+
'choria-brokers' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
336
|
+
'choria-broker-timeout' => { 'type' => 'string', 'transport' => ['choria'], 'sensitive' => false },
|
|
337
|
+
}
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
test 'merges all Choria setting values into their option defaults' do
|
|
341
|
+
Setting['openbolt_choria-task-agent'] = 'shell'
|
|
342
|
+
Setting['openbolt_choria-config-file'] = '/etc/choria/client.conf'
|
|
343
|
+
Setting['openbolt_choria-mcollective-certname'] = 'primary.example.com'
|
|
344
|
+
Setting['openbolt_choria-ssl-ca'] = '/etc/choria/ca.pem'
|
|
345
|
+
Setting['openbolt_choria-ssl-cert'] = '/etc/choria/client.pem'
|
|
346
|
+
Setting['openbolt_choria-ssl-key'] = '/etc/choria/client.key'
|
|
347
|
+
Setting['openbolt_choria-collective'] = 'mcollective'
|
|
348
|
+
Setting['openbolt_choria-puppet-environment'] = 'production'
|
|
349
|
+
Setting['openbolt_choria-rpc-timeout'] = 60
|
|
350
|
+
Setting['openbolt_choria-task-timeout'] = 300
|
|
351
|
+
Setting['openbolt_choria-command-timeout'] = 120
|
|
352
|
+
Setting['openbolt_choria-brokers'] = 'broker.example.com:4222'
|
|
353
|
+
Setting['openbolt_choria-broker-timeout'] = 10
|
|
354
|
+
|
|
355
|
+
stub_request(:get, "#{@proxy.url}/openbolt/tasks/options")
|
|
356
|
+
.to_return(status: 200, body: self.class.choria_proxy_options.to_json,
|
|
357
|
+
headers: { 'Content-Type' => 'application/json' })
|
|
358
|
+
|
|
359
|
+
get :fetch_openbolt_options, params: { proxy_id: @proxy.id }, session: @session
|
|
360
|
+
assert_response :success
|
|
361
|
+
|
|
362
|
+
body = JSON.parse(response.body)
|
|
363
|
+
assert_equal 'shell', body['choria-task-agent']['default']
|
|
364
|
+
assert_equal '/etc/choria/client.conf', body['choria-config-file']['default']
|
|
365
|
+
assert_equal 'primary.example.com', body['choria-mcollective-certname']['default']
|
|
366
|
+
assert_equal '/etc/choria/ca.pem', body['choria-ssl-ca']['default']
|
|
367
|
+
assert_equal '/etc/choria/client.pem', body['choria-ssl-cert']['default']
|
|
368
|
+
assert_equal '/etc/choria/client.key', body['choria-ssl-key']['default']
|
|
369
|
+
assert_equal 'mcollective', body['choria-collective']['default']
|
|
370
|
+
assert_equal 'production', body['choria-puppet-environment']['default']
|
|
371
|
+
assert_equal 60, body['choria-rpc-timeout']['default']
|
|
372
|
+
assert_equal 300, body['choria-task-timeout']['default']
|
|
373
|
+
assert_equal 120, body['choria-command-timeout']['default']
|
|
374
|
+
assert_equal 'broker.example.com:4222', body['choria-brokers']['default']
|
|
375
|
+
assert_equal 10, body['choria-broker-timeout']['default']
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
test 'omits nil-default settings and keeps real defaults when Choria settings are not configured' do
|
|
379
|
+
stub_request(:get, "#{@proxy.url}/openbolt/tasks/options")
|
|
380
|
+
.to_return(status: 200, body: self.class.choria_proxy_options.to_json,
|
|
381
|
+
headers: { 'Content-Type' => 'application/json' })
|
|
382
|
+
|
|
383
|
+
get :fetch_openbolt_options, params: { proxy_id: @proxy.id }, session: @session
|
|
384
|
+
assert_response :success
|
|
385
|
+
|
|
386
|
+
body = JSON.parse(response.body)
|
|
387
|
+
assert_equal 'bolt_tasks', body['choria-task-agent']['default']
|
|
388
|
+
assert_not body['choria-config-file'].key?('default')
|
|
389
|
+
assert_not body['choria-mcollective-certname'].key?('default')
|
|
390
|
+
assert_not body['choria-ssl-key'].key?('default')
|
|
391
|
+
assert_not body['choria-brokers'].key?('default')
|
|
392
|
+
assert_not body['choria-broker-timeout'].key?('default')
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
321
396
|
context 'fetch_task_history' do
|
|
322
397
|
test 'returns paginated task history' do
|
|
323
398
|
3.times { FactoryBot.create(:task_job, smart_proxy: @proxy) }
|
|
@@ -83,4 +83,49 @@ describe('ParameterField', () => {
|
|
|
83
83
|
fireEvent.change(input, { target: { value: 'new-value' } });
|
|
84
84
|
expect(handleChange).toHaveBeenCalledWith('username', 'new-value');
|
|
85
85
|
});
|
|
86
|
+
|
|
87
|
+
test('shows empty field for encrypted default instead of the placeholder value', () => {
|
|
88
|
+
const { container } = render(
|
|
89
|
+
<ParameterField
|
|
90
|
+
name="password"
|
|
91
|
+
metadata={{
|
|
92
|
+
type: 'String',
|
|
93
|
+
sensitive: true,
|
|
94
|
+
default: '[Use saved encrypted default]',
|
|
95
|
+
}}
|
|
96
|
+
onChange={jest.fn()}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
const input = container.querySelector('input[type="password"]');
|
|
100
|
+
expect(input.value).toBe('');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('shows encrypted placeholder when value matches placeholder', () => {
|
|
104
|
+
const { container } = render(
|
|
105
|
+
<ParameterField
|
|
106
|
+
name="password"
|
|
107
|
+
metadata={{
|
|
108
|
+
type: 'String',
|
|
109
|
+
sensitive: true,
|
|
110
|
+
default: '[Use saved encrypted default]',
|
|
111
|
+
}}
|
|
112
|
+
value="[Use saved encrypted default]"
|
|
113
|
+
onChange={jest.fn()}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
const input = container.querySelector('input[type="password"]');
|
|
117
|
+
expect(input.value).toBe('[Use saved encrypted default]');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('renders empty text input when value is undefined and no default', () => {
|
|
121
|
+
const { container } = render(
|
|
122
|
+
<ParameterField
|
|
123
|
+
name="tmpdir"
|
|
124
|
+
metadata={{ type: 'String' }}
|
|
125
|
+
onChange={jest.fn()}
|
|
126
|
+
/>
|
|
127
|
+
);
|
|
128
|
+
const input = container.querySelector('input[type="text"]');
|
|
129
|
+
expect(input.value).toBe('');
|
|
130
|
+
});
|
|
86
131
|
});
|
|
@@ -8,7 +8,7 @@ describe('LoadingIndicator', () => {
|
|
|
8
8
|
expect(screen.getByText(/running/i)).toBeInTheDocument();
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
test('shows
|
|
11
|
+
test('shows current status message for pending jobs', () => {
|
|
12
12
|
render(<LoadingIndicator jobStatus="pending" />);
|
|
13
13
|
expect(screen.getByText(/pending/i)).toBeInTheDocument();
|
|
14
14
|
});
|
|
@@ -10,7 +10,7 @@ afterEach(() => {
|
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
describe('useJobPolling', () => {
|
|
13
|
-
test('returns
|
|
13
|
+
test('returns initial pending state and does not poll when jobId is null', () => {
|
|
14
14
|
const { result } = renderHook(() => useJobPolling(null));
|
|
15
15
|
expect(result.current.status).toBe('pending');
|
|
16
16
|
expect(result.current.isPolling).toBe(false);
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_openbolt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Overlook InfraTech
|
|
@@ -18,7 +18,7 @@ dependencies:
|
|
|
18
18
|
version: 11.0.0
|
|
19
19
|
- - "<"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: '
|
|
21
|
+
version: '14'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -28,7 +28,7 @@ dependencies:
|
|
|
28
28
|
version: 11.0.0
|
|
29
29
|
- - "<"
|
|
30
30
|
- !ruby/object:Gem::Version
|
|
31
|
-
version: '
|
|
31
|
+
version: '14'
|
|
32
32
|
description: This plugin adds OpenBolt integration into Foreman, allowing users to
|
|
33
33
|
run tasks and plans present in their environment.
|
|
34
34
|
email:
|
|
@@ -180,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
180
180
|
- !ruby/object:Gem::Version
|
|
181
181
|
version: '0'
|
|
182
182
|
requirements: []
|
|
183
|
-
rubygems_version: 4.0.
|
|
183
|
+
rubygems_version: 4.0.10
|
|
184
184
|
specification_version: 4
|
|
185
185
|
summary: Foreman OpenBolt integration
|
|
186
186
|
test_files: []
|