foreman_remote_execution 16.0.1 → 16.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a41cb26fee902fc19cc5a8fe4adcbacdef4944095478f1d55a6220c718c16ea
4
- data.tar.gz: a0332ea0045f6c1365ff40ae80824f4f7cae6689faa5de33c06684f602ad980d
3
+ metadata.gz: 40f170b56effb3a3f7f415ac040d45242641bc3210266bf723d957a4337e8aa2
4
+ data.tar.gz: c6a8e5a792629049aa8e40fdc2a3b6b20cc182af3e0e3d1578e5e3614525e7d0
5
5
  SHA512:
6
- metadata.gz: b0c82d765d09a001fde64799b48d76c70f7801f438311c77ed0e4d6a1f8baa1f19f1e2a7d3a88175676c3d426c0a623fce59a8fbd1a4cd6c6a05e60b12eeb3b0
7
- data.tar.gz: 565ae8340a1238de653aec41ac1f210f47105b14977bd51d923ec7bd75631222962a9bb5e6df63d757ddd67a114109edb05626e30fc050fa6b84b2e900c0a529
6
+ metadata.gz: 7b47defe2e23b8ce3ac1ebc08164749d7c2a03a133f78dfcb702c76cd4f434786b0dbeceb72c2d7ca47cedbd5d1f688175b9e0680245eac4e1064f9739d2b68c
7
+ data.tar.gz: c0d069ef4dde5b1a07cb38b60f0dcc7218487cc58a5a2961039f2603517a2fdc5060b603b4a13b5ff4c659a13b9b25eb836b59e5e3d6efbe1e69202364cc0f87
@@ -38,9 +38,17 @@ class TemplateInvocationsController < ApplicationController
38
38
  }
39
39
  end
40
40
 
41
+ smart_proxy = @template_invocation.smart_proxy
42
+ if smart_proxy
43
+ proxy = {
44
+ name: smart_proxy.name,
45
+ href: smart_proxy_path(smart_proxy),
46
+ }
47
+ end
48
+
41
49
  auto_refresh = @job_invocation.task.try(:pending?)
42
50
  finished = @job_invocation.status_label == 'failed' || @job_invocation.status_label == 'succeeded' || @job_invocation.status_label == 'cancelled'
43
- render :json => { :output => lines, :preview => template_invocation_preview(@template_invocation, @host), :input_values => transformed_input_values, :job_invocation_description => @job_invocation.description, :task_id => @template_invocation_task.id, :task_cancellable => @template_invocation_task.cancellable?, :host_name => @host.name, :permissions => {
51
+ render :json => { :output => lines, :preview => template_invocation_preview(@template_invocation, @host), :proxy => proxy, :input_values => transformed_input_values, :job_invocation_description => @job_invocation.description, :task_id => @template_invocation_task.id, :task_cancellable => @template_invocation_task.cancellable?, :host_name => @host.name, :permissions => {
44
52
  :view_foreman_tasks => User.current.allowed_to?(:view_foreman_tasks),
45
53
  :cancel_job_invocations => User.current.allowed_to?(:cancel_job_invocations),
46
54
  :execute_jobs => User.current.allowed_to?(:create_job_invocations) && (!@host.infrastructure_host? || User.current.can?(:execute_jobs_on_infrastructure_hosts)),
@@ -105,7 +105,11 @@ handle_zypp_res_codes () {
105
105
 
106
106
  # Action
107
107
  <% if package_manager == 'yum' -%>
108
- yum -y <%= input("options") %> <%= action %> <%= input("package") %>
108
+ <% if @host.respond_to?(:yum_or_yum_transient) -%>
109
+ <%= @host.yum_or_yum_transient %> -y <%= input("options") %> <%= action %> <%= input("package") %>
110
+ <% else -%>
111
+ yum -y <%= input("options") %> <%= action %> <%= input("package") %>
112
+ <% end -%>
109
113
  <% elsif package_manager == 'apt' -%>
110
114
  <%-
111
115
  action = 'install' if action == 'group install'
@@ -1,3 +1,3 @@
1
1
  module ForemanRemoteExecution
2
- VERSION = '16.0.1'.freeze
2
+ VERSION = '16.0.3'.freeze
3
3
  end
@@ -169,7 +169,7 @@ const Inputs = ({ data }) => {
169
169
  const inputs =
170
170
  data?.pattern_template_invocations?.[0]?.template_invocation_input_values;
171
171
 
172
- if (!inputs) return null;
172
+ if (!inputs || inputs.length === 0) return null;
173
173
  return (
174
174
  <ExpandableSection
175
175
  toggleText={__('User Inputs')}
@@ -53,6 +53,7 @@ export const TemplateInvocation = ({
53
53
  jobID,
54
54
  isInTableView,
55
55
  hostName,
56
+ hostProxy,
56
57
  }) => {
57
58
  const templateURL = showTemplateInvocationUrl(hostID, jobID);
58
59
  const hostDetailsPageUrl = useForemanHostDetailsPageUrl();
@@ -150,10 +151,18 @@ export const TemplateInvocation = ({
150
151
  permissions={permissions}
151
152
  />
152
153
  {!isInTableView && (
153
- <div>
154
- {__('Target:')}{' '}
155
- <a href={`${hostDetailsPageUrl}${hostName}`}>{hostName}</a>
156
- </div>
154
+ <>
155
+ <div>
156
+ {__('Target:')}{' '}
157
+ <a href={`${hostDetailsPageUrl}${hostName}`}>{hostName}</a>{' '}
158
+ {!!hostProxy && (
159
+ <>
160
+ {__('using Smart Proxy')}{' '}
161
+ <a href={hostProxy.href}>{hostProxy.name}</a>
162
+ </>
163
+ )}
164
+ </div>
165
+ </>
157
166
  )}
158
167
  {showTemplatePreview && <PreviewTemplate inputValues={inputValues} />}
159
168
  {showCommand && (
@@ -185,6 +194,7 @@ export const TemplateInvocation = ({
185
194
  TemplateInvocation.propTypes = {
186
195
  hostID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
187
196
  hostName: PropTypes.string, // only used when isInTableView is false
197
+ hostProxy: PropTypes.object, // only used when isInTableView is false
188
198
  jobID: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
189
199
  isInTableView: PropTypes.bool,
190
200
  };
@@ -192,6 +202,7 @@ TemplateInvocation.propTypes = {
192
202
  TemplateInvocation.defaultProps = {
193
203
  isInTableView: true,
194
204
  hostName: '',
205
+ hostProxy: {},
195
206
  };
196
207
 
197
208
  CopyToClipboard.propTypes = {
@@ -1,4 +1,9 @@
1
- import React from 'react';
1
+ import React, {
2
+ useEffect,
3
+ useLayoutEffect,
4
+ useState,
5
+ useCallback,
6
+ } from 'react';
2
7
  import PropTypes from 'prop-types';
3
8
  import { Button } from '@patternfly/react-core';
4
9
  import { translate as __ } from 'foremanReact/common/I18n';
@@ -55,6 +60,48 @@ export const OutputCodeBlock = ({ code, showOutputType, scrollElement }) => {
55
60
  const filteredCode = code.filter(
56
61
  ({ output_type: outputType }) => showOutputType[outputType]
57
62
  );
63
+
64
+ const scrollElementSelected = useCallback(
65
+ () => document.querySelector(scrollElement),
66
+ [scrollElement]
67
+ );
68
+ const onClickScrollToTop = useCallback(() => {
69
+ if (scrollElementSelected()) scrollElementSelected().scrollTo(0, 0);
70
+ }, [scrollElementSelected]);
71
+
72
+ const onClickScrollToBottom = useCallback(() => {
73
+ if (scrollElementSelected())
74
+ scrollElementSelected().scrollTo(0, scrollElementSelected().scrollHeight);
75
+ }, [scrollElementSelected]);
76
+
77
+ const [isScrolledBottom, setIsScrolledBottom] = useState(true);
78
+ useEffect(() => {
79
+ const element = scrollElementSelected();
80
+ const onScroll = () => {
81
+ if (
82
+ Math.abs(
83
+ element.scrollHeight - element.scrollTop - element.clientHeight
84
+ ) < 1
85
+ )
86
+ setIsScrolledBottom(true);
87
+ else setIsScrolledBottom(false);
88
+ };
89
+ if (element) {
90
+ element.addEventListener('scroll', onScroll);
91
+ }
92
+ return () => {
93
+ if (element) {
94
+ element.removeEventListener('scroll', onScroll);
95
+ }
96
+ };
97
+ }, [scrollElementSelected]);
98
+
99
+ useLayoutEffect(() => {
100
+ if (isScrolledBottom) {
101
+ onClickScrollToBottom();
102
+ }
103
+ }, [code.length, isScrolledBottom, onClickScrollToBottom]);
104
+
58
105
  if (!filteredCode.length) {
59
106
  return <div>{__('No output for the selected filters')}</div>;
60
107
  }
@@ -81,13 +128,7 @@ export const OutputCodeBlock = ({ code, showOutputType, scrollElement }) => {
81
128
  );
82
129
  });
83
130
  });
84
- const scrollElementSeleceted = () => document.querySelector(scrollElement);
85
- const onClickScrollToTop = () => {
86
- scrollElementSeleceted().scrollTo(0, 0);
87
- };
88
- const onClickScrollToBottom = () => {
89
- scrollElementSeleceted().scrollTo(0, scrollElementSeleceted().scrollHeight);
90
- };
131
+
91
132
  return (
92
133
  <div className="invocation-output">
93
134
  <Button
@@ -102,12 +102,10 @@ export const RowActions = ({ hostID, jobID }) => {
102
102
  .map(({ text, href, onClick, permission, isDisabled }) =>
103
103
  permission
104
104
  ? {
105
- title: text,
106
- component: 'a',
107
- className: 'jobs-table-action-item',
108
- href,
105
+ title: <a href={href}>{text}</a>,
109
106
  onClick,
110
107
  isDisabled,
108
+ className: 'jobs-table-action-item',
111
109
  }
112
110
  : null
113
111
  )
@@ -1,3 +1,9 @@
1
- .jobs-table-action-item:hover {
2
- color: var(--pf-v5-c-dropdown__menu-item--Color);
1
+ .jobs-table-action-item a {
2
+
3
+ color: inherit;
4
+ text-decoration: none;
5
+
6
+ :hover {
7
+ color: var(--pf-v5-c-dropdown__menu-item--Color);
8
+ }
3
9
  }
@@ -15,6 +15,7 @@ const TemplateInvocationPage = ({
15
15
  const {
16
16
  job_invocation_description: jobDescription,
17
17
  host_name: hostName,
18
+ proxy: hostProxy,
18
19
  } = useSelector(selectTemplateInvocation);
19
20
  const description = sprintf(__('Template Invocation for %s'), hostName);
20
21
  const breadcrumbOptions = {
@@ -36,6 +37,7 @@ const TemplateInvocationPage = ({
36
37
  jobID={jobID}
37
38
  isInTableView={false}
38
39
  hostName={hostName}
40
+ hostProxy={hostProxy}
39
41
  />
40
42
  </PageLayout>
41
43
  );
@@ -32,12 +32,16 @@ describe('TemplateInvocation', () => {
32
32
  jobID="1"
33
33
  isInTableView={false}
34
34
  hostName="example-host"
35
+ hostProxy={{ name: 'example-proxy', href: '#' }}
35
36
  />
36
37
  </Provider>
37
38
  );
38
39
 
39
- expect(screen.getByText('Target:')).toBeInTheDocument();
40
40
  expect(screen.getByText('example-host')).toBeInTheDocument();
41
+ expect(screen.getByText('example-proxy')).toBeInTheDocument();
42
+
43
+ expect(screen.getByText(/using Smart Proxy/)).toBeInTheDocument();
44
+ expect(screen.getByText(/Target:/)).toBeInTheDocument();
41
45
 
42
46
  expect(screen.getByText('This is red text')).toBeInTheDocument();
43
47
  expect(screen.getByText('This is default text')).toBeInTheDocument();
@@ -50,6 +54,7 @@ describe('TemplateInvocation', () => {
50
54
  jobID="1"
51
55
  isInTableView={false}
52
56
  hostName="example-host"
57
+ hostProxy={{ name: 'example-proxy', href: '#' }}
53
58
  />
54
59
  </Provider>
55
60
  );
@@ -101,6 +106,7 @@ describe('TemplateInvocation', () => {
101
106
  jobID="1"
102
107
  isInTableView={false}
103
108
  hostName="example-host"
109
+ hostProxy={{ name: 'example-proxy', href: '#' }}
104
110
  />
105
111
  );
106
112
 
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.0.1
4
+ version: 16.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Foreman Remote Execution team
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-26 00:00:00.000000000 Z
10
+ date: 2025-05-13 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: deface
@@ -92,8 +92,8 @@ email:
92
92
  executables: []
93
93
  extensions: []
94
94
  extra_rdoc_files:
95
- - README.md
96
95
  - LICENSE
96
+ - README.md
97
97
  files:
98
98
  - LICENSE
99
99
  - README.md
@@ -605,7 +605,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
605
605
  - !ruby/object:Gem::Version
606
606
  version: '0'
607
607
  requirements: []
608
- rubygems_version: 3.6.2
608
+ rubygems_version: 3.6.7
609
609
  specification_version: 4
610
610
  summary: A plugin bringing remote execution to the Foreman, completing the config
611
611
  management functionality with remote management functionality.