foreman_remote_execution 16.3.1 → 16.4.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.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/job_invocations_controller.rb +3 -5
  3. data/app/lib/proxy_api/remote_execution_ssh.rb +9 -0
  4. data/app/models/concerns/foreman_remote_execution/host_extensions.rb +12 -4
  5. data/app/models/concerns/foreman_remote_execution/smart_proxy_extensions.rb +14 -0
  6. data/app/views/api/v2/smart_proxies/ca_pubkey.json.rabl +1 -0
  7. data/db/migrate/20250606125543_add_ca_pub_key_to_smart_proxy.rb +5 -0
  8. data/lib/foreman_remote_execution/plugin.rb +1 -0
  9. data/lib/foreman_remote_execution/version.rb +1 -1
  10. data/test/unit/concerns/host_extensions_test.rb +43 -0
  11. data/webpack/JobInvocationDetail/JobInvocationDetail.scss +4 -0
  12. data/webpack/JobInvocationDetail/JobInvocationHostTable.js +216 -129
  13. data/webpack/JobInvocationDetail/TemplateInvocation.js +19 -15
  14. data/webpack/JobInvocationDetail/TemplateInvocationComponents/OutputToggleGroup.js +43 -25
  15. data/webpack/JobInvocationDetail/TemplateInvocationPage.js +16 -1
  16. data/webpack/JobInvocationDetail/__tests__/TemplateInvocation.test.js +114 -72
  17. data/webpack/JobInvocationDetail/index.js +4 -5
  18. data/webpack/JobWizard/JobWizard.js +11 -10
  19. data/webpack/JobWizard/autofill.js +10 -2
  20. data/webpack/JobWizard/steps/AdvancedFields/__tests__/__snapshots__/AdvancedFields.test.js.snap +1 -1
  21. data/webpack/JobWizard/steps/CategoryAndTemplate/index.js +25 -13
  22. data/webpack/JobWizard/steps/HostsAndInputs/SelectAPI.js +16 -11
  23. data/webpack/JobWizard/steps/form/ResourceSelect.js +18 -16
  24. data/webpack/JobWizard/steps/form/SearchSelect.js +6 -7
  25. data/webpack/JobWizard/validation.js +1 -3
  26. data/webpack/react_app/components/TargetingHosts/index.js +49 -32
  27. metadata +5 -3
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState, useEffect, useRef } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import {
4
4
  Select,
@@ -25,14 +25,13 @@ export const SearchSelect = ({
25
25
  const [onSearch, response, isLoading] = useNameSearch(apiKey, url);
26
26
  const [isOpen, setIsOpen] = useState(false);
27
27
  const [typingTimeout, setTypingTimeout] = useState(null);
28
+ const initializedRef = useRef(false);
28
29
  useEffect(() => {
29
- onSearch(selected.name || '');
30
- if (typingTimeout) {
31
- return () => clearTimeout(typingTimeout);
30
+ if (!initializedRef.current) {
31
+ onSearch(selected.name || '');
32
+ initializedRef.current = true;
32
33
  }
33
- return undefined;
34
- // eslint-disable-next-line react-hooks/exhaustive-deps
35
- }, []);
34
+ }, [onSearch, selected.name]);
36
35
  let selectOptions = [];
37
36
  if (response.subtotal > maxResults) {
38
37
  selectOptions = [
@@ -46,8 +46,6 @@ export const useValidation = ({ advancedValues, templateValues }) => {
46
46
  setValid(currValid => ({ ...currValid, advanced: false }));
47
47
  }
48
48
  });
49
-
50
- // eslint-disable-next-line react-hooks/exhaustive-deps
51
- }, [advancedValues, templateValues]);
49
+ }, [advancedValues, templateValues, templateInputs, advancedTemplateInputs]);
52
50
  return [valid, setValid];
53
51
  };
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useState, useCallback, useRef } from 'react';
2
2
  import { useSelector, useDispatch } from 'react-redux';
3
3
 
4
4
  import { get } from 'foremanReact/redux/API';
@@ -50,38 +50,55 @@ const WrappedTargetingHosts = () => {
50
50
  const [apiUrl, setApiUrl] = useState(getApiUrl(searchQuery, pagination));
51
51
  const intervalExists = useSelector(selectIntervalExists);
52
52
 
53
- const handleSearch = (query, status) => {
54
- const defaultPagination = { page: 1, per_page: pagination.per_page };
55
- stopApiInterval();
56
-
57
- setApiUrl(getApiUrl(buildSearchQuery(query, status), defaultPagination));
58
- setSearchQuery(query);
59
- setPagination(defaultPagination);
60
- };
61
-
62
- const handlePagination = args => {
63
- stopApiInterval();
64
- setPagination(args);
65
- setApiUrl(getApiUrl(buildSearchQuery(searchQuery, statusFilter), args));
66
- };
67
-
68
- const stopApiInterval = () => {
53
+ const stopApiInterval = useCallback(() => {
69
54
  if (intervalExists) {
70
55
  dispatch(stopInterval(TARGETING_HOSTS));
71
56
  }
72
- };
73
-
74
- const getData = url =>
75
- withInterval(
76
- get({
77
- key: TARGETING_HOSTS,
78
- url,
79
- handleError: () => {
80
- dispatch(stopInterval(TARGETING_HOSTS));
81
- },
82
- }),
83
- 1000
84
- );
57
+ }, [dispatch, intervalExists]);
58
+
59
+ // Use ref to avoid infinite loop from handleSearch depending on pagination.per_page
60
+ const perPageRef = useRef(pagination.per_page);
61
+
62
+ const handleSearch = useCallback(
63
+ (query, status) => {
64
+ const defaultPagination = { page: 1, per_page: perPageRef.current };
65
+ stopApiInterval();
66
+
67
+ setApiUrl(getApiUrl(buildSearchQuery(query, status), defaultPagination));
68
+ setSearchQuery(query);
69
+ setPagination(defaultPagination);
70
+ },
71
+ [stopApiInterval]
72
+ );
73
+
74
+ // Keep ref in sync with pagination
75
+ useEffect(() => {
76
+ perPageRef.current = pagination.per_page;
77
+ }, [pagination.per_page]);
78
+
79
+ const handlePagination = useCallback(
80
+ args => {
81
+ stopApiInterval();
82
+ setPagination(args);
83
+ setApiUrl(getApiUrl(buildSearchQuery(searchQuery, statusFilter), args));
84
+ },
85
+ [searchQuery, statusFilter, stopApiInterval]
86
+ );
87
+
88
+ const getData = useCallback(
89
+ url =>
90
+ withInterval(
91
+ get({
92
+ key: TARGETING_HOSTS,
93
+ url,
94
+ handleError: () => {
95
+ dispatch(stopInterval(TARGETING_HOSTS));
96
+ },
97
+ }),
98
+ 1000
99
+ ),
100
+ [dispatch]
101
+ );
85
102
 
86
103
  useEffect(() => {
87
104
  dispatch(getData(apiUrl));
@@ -93,11 +110,11 @@ const WrappedTargetingHosts = () => {
93
110
  return () => {
94
111
  dispatch(stopInterval(TARGETING_HOSTS));
95
112
  };
96
- }, [dispatch, apiUrl, autoRefresh]);
113
+ }, [dispatch, apiUrl, autoRefresh, getData]);
97
114
 
98
115
  useEffect(() => {
99
116
  handleSearch(searchQuery, statusFilter);
100
- }, [statusFilter, searchQuery]);
117
+ }, [statusFilter, searchQuery, handleSearch]);
101
118
 
102
119
  return (
103
120
  <TargetingHostsPage
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_remote_execution
3
3
  version: !ruby/object:Gem::Version
4
- version: 16.3.1
4
+ version: 16.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Remote Execution team
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-11-11 00:00:00.000000000 Z
10
+ date: 2026-01-06 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: deface
@@ -223,6 +223,7 @@ files:
223
223
  - app/views/api/v2/remote_execution_features/index.json.rabl
224
224
  - app/views/api/v2/remote_execution_features/main.json.rabl
225
225
  - app/views/api/v2/remote_execution_features/show.json.rabl
226
+ - app/views/api/v2/smart_proxies/ca_pubkey.json.rabl
226
227
  - app/views/api/v2/smart_proxies/pubkey.json.rabl
227
228
  - app/views/api/v2/subnets/remote_execution_proxies.json.rabl
228
229
  - app/views/api/v2/template_invocations/base.json.rabl
@@ -340,6 +341,7 @@ files:
340
341
  - db/migrate/20240522093412_add_smart_proxy_id_to_template_invocation.rb
341
342
  - db/migrate/20240522093413_migrate_smart_proxy_ids_to_template_invocations.rb
342
343
  - db/migrate/20241126150849_remove_remote_execution_workers_pool_size.rb
344
+ - db/migrate/20250606125543_add_ca_pub_key_to_smart_proxy.rb
343
345
  - db/seeds.d/100-assign_features_with_templates.rb
344
346
  - db/seeds.d/20-permissions.rb
345
347
  - db/seeds.d/50-notification_blueprints.rb
@@ -606,7 +608,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
606
608
  - !ruby/object:Gem::Version
607
609
  version: '0'
608
610
  requirements: []
609
- rubygems_version: 3.6.9
611
+ rubygems_version: 4.0.3
610
612
  specification_version: 4
611
613
  summary: A plugin bringing remote execution to the Foreman, completing the config
612
614
  management functionality with remote management functionality.