Skip to content

Commit 8fee027

Browse files
authored
Merge pull request #6 from eccenca/bugfix/moveConnectionTest
move connection test from init to execute
2 parents 545ecbc + 1186698 commit 8fee027

27 files changed

+456
-272
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1+
<!-- markdownlint-disable MD012 MD013 MD024 MD033 -->
12
# Change Log
23

34
All notable changes to this project will be documented in this file.
45

56
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](https://semver.org/)
67

8+
## [Unreleased]
9+
10+
### Fixed
11+
12+
- move connection test from init to execute phase (all tasks)
13+
- this avoids project loading errors
14+
- Fix typos and grammar in parameter descriptions
15+
16+
### Added
17+
18+
- testing infrastructure based in testcontainers
19+
20+
721
## [1.0.0] 2025-07-18
822

923
### Changed

cmem_plugin_ssh/download.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
PluginParameter(
8787
name="username",
8888
label="Username",
89-
description="The username of which a connection will be instantiated.",
89+
description="The username with which a connection will be instantiated.",
9090
),
9191
PluginParameter(
9292
name="authentication_method",
@@ -106,14 +106,18 @@
106106
name="password",
107107
label="Password",
108108
description="Depending on your authentication method this will either be used to"
109-
"connect via password to SSH or is used to decrypt the SSH private key",
109+
"connect via password to SSH, or to decrypt the SSH private key",
110110
param_type=PasswordParameterType(),
111111
default_value="",
112112
),
113113
PluginParameter(
114114
name="path",
115115
label="Path",
116-
description="The currently selected path withing your SSH instance.",
116+
description=(
117+
"The currently selected path within your SSH instance."
118+
" Auto-completion starts from user home folder, use '..' for parent directory"
119+
" or '/' for root directory."
120+
),
117121
default_value="",
118122
param_type=DirectoryParameterType("directories", "Folder"),
119123
),
@@ -158,6 +162,9 @@
158162
class DownloadFiles(WorkflowPlugin):
159163
"""SSH Workflow Plugin: File download"""
160164

165+
ssh_client: paramiko.SSHClient
166+
sftp: paramiko.SFTPClient
167+
161168
def __init__( # noqa: PLR0913
162169
self,
163170
hostname: str,
@@ -186,11 +193,8 @@ def __init__( # noqa: PLR0913
186193
self.input_ports = FixedNumberOfInputs([FixedSchemaPort(schema=generate_list_schema())])
187194
self.output_port = FixedSchemaPort(schema=FileEntitySchema())
188195
self.download_dir = tempfile.mkdtemp()
189-
self.ssh_client = paramiko.SSHClient()
190-
self.connect_ssh_client()
191-
self.sftp = self.ssh_client.open_sftp()
192196

193-
def connect_ssh_client(self) -> None:
197+
def establish_ssh_connection(self) -> None:
194198
"""Connect to the ssh client with the selected authentication method"""
195199
if self.authentication_method == "key":
196200
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
@@ -212,13 +216,19 @@ def connect_ssh_client(self) -> None:
212216
timeout=20,
213217
)
214218

215-
def close_connections(self) -> None:
219+
def cleanup_ssh_connections(self) -> None:
216220
"""Close connection from sftp and ssh"""
217221
self.sftp.close()
218222
self.ssh_client.close()
219223

224+
def _initialize_ssh_and_sftp_connections(self) -> None:
225+
self.ssh_client = paramiko.SSHClient()
226+
self.establish_ssh_connection()
227+
self.sftp = self.ssh_client.open_sftp()
228+
220229
def preview_results(self) -> str:
221230
"""Preview the results of an execution"""
231+
self._initialize_ssh_and_sftp_connections()
222232
return preview_results(
223233
ssh_client=self.ssh_client,
224234
no_subfolder=self.no_subfolder,
@@ -233,6 +243,8 @@ def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Enti
233243
_ = inputs
234244
schema = FileEntitySchema()
235245

246+
self._initialize_ssh_and_sftp_connections()
247+
236248
context.report.update(
237249
ExecutionReport(entity_count=0, operation="wait", operation_desc="files listed.")
238250
)
@@ -286,7 +298,7 @@ def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Enti
286298

287299
self.update_context(context, entities, files, schema)
288300

289-
self.close_connections()
301+
self.cleanup_ssh_connections()
290302

291303
return Entities(entities=iter(entities), schema=schema)
292304

cmem_plugin_ssh/execute_commands.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def setup_timeout(timeout: float) -> None | float:
9999
PluginParameter(
100100
name="username",
101101
label="Username",
102-
description="The username of which a connection will be instantiated.",
102+
description="The username with which a connection will be instantiated.",
103103
),
104104
PluginParameter(
105105
name="authentication_method",
@@ -119,21 +119,25 @@ def setup_timeout(timeout: float) -> None | float:
119119
name="password",
120120
label="Password",
121121
description="Depending on your authentication method this will either be used to"
122-
"connect via password to SSH or is used to decrypt the SSH private key",
122+
"connect via password to SSH, or to decrypt the SSH private key",
123123
param_type=PasswordParameterType(),
124124
default_value="",
125125
),
126126
PluginParameter(
127127
name="path",
128128
label="Path",
129-
description="The currently selected path withing your SSH instance.",
129+
description=(
130+
"The currently selected path within your SSH instance."
131+
" Auto-completion starts from user home folder, use '..' for parent directory"
132+
" or '/' for root directory."
133+
),
130134
default_value="",
131135
param_type=DirectoryParameterType("directories", "Folder"),
132136
),
133137
PluginParameter(
134138
name="input_method",
135139
label="Input method",
136-
description="Parameter to decide weather files will be used as stdin or no input is "
140+
description="Parameter to decide whether files will be used as stdin or no input is "
137141
"needed. If 'File input' is chosen, the input port will open for all entities with"
138142
"the FileEntitySchema.",
139143
param_type=ChoiceParameterType(COMMAND_INPUT_CHOICES),
@@ -142,7 +146,7 @@ def setup_timeout(timeout: float) -> None | float:
142146
name="output_method",
143147
label="Output method",
144148
description="Parameter to decide which type of output the user wants. This can be "
145-
"either no output, a structured process output with its own schema or "
149+
"either no output, a structured process output with its own schema, or "
146150
"a file based output",
147151
param_type=ChoiceParameterType(COMMAND_OUTPUT_CHOICES),
148152
),
@@ -164,6 +168,9 @@ def setup_timeout(timeout: float) -> None | float:
164168
class ExecuteCommands(WorkflowPlugin):
165169
"""Execute commands Plugin SSH"""
166170

171+
ssh_client: paramiko.SSHClient
172+
sftp: paramiko.SFTPClient
173+
167174
def __init__( # noqa: PLR0913
168175
self,
169176
hostname: str,
@@ -191,11 +198,8 @@ def __init__( # noqa: PLR0913
191198
self.timeout = setup_timeout(timeout)
192199
self.input_ports = self.setup_input_port()
193200
self.output_port = self.setup_output_port()
194-
self.ssh_client = paramiko.SSHClient()
195-
self.connect_ssh_client()
196-
self.sftp = self.ssh_client.open_sftp()
197201

198-
def connect_ssh_client(self) -> None:
202+
def establish_ssh_connection(self) -> None:
199203
"""Connect to the ssh client with the selected authentication method"""
200204
if self.authentication_method == "key":
201205
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
@@ -217,14 +221,21 @@ def connect_ssh_client(self) -> None:
217221
timeout=20,
218222
)
219223

220-
def close_connections(self) -> None:
224+
def cleanup_ssh_connections(self) -> None:
221225
"""Close connection from sftp and ssh"""
222226
self.sftp.close()
223227
self.ssh_client.close()
224228

229+
def _initialize_ssh_and_sftp_connections(self) -> None:
230+
self.ssh_client = paramiko.SSHClient()
231+
self.establish_ssh_connection()
232+
self.sftp = self.ssh_client.open_sftp()
233+
225234
def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Entities:
226235
"""Execute the workflow task"""
227236
entities: list = []
237+
238+
self._initialize_ssh_and_sftp_connections()
228239
context.report.update(
229240
ExecutionReport(
230241
entity_count=len(entities),
@@ -238,7 +249,7 @@ def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Enti
238249
if self.input_method == "no_input":
239250
self.no_input_execution(entities)
240251

241-
self.close_connections()
252+
self.cleanup_ssh_connections()
242253

243254
operation_desc = (
244255
f"times executed '{self.command}'"

cmem_plugin_ssh/list.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
PluginParameter(
6969
name="hostname",
7070
label="Hostname",
71-
description="Hostname to connect to.Usually in the form of an IP address",
71+
description="Hostname to connect to. Usually in the form of an IP address",
7272
),
7373
PluginParameter(
7474
name="port",
@@ -79,7 +79,7 @@
7979
PluginParameter(
8080
name="username",
8181
label="Username",
82-
description="The username of which a connection will be instantiated.",
82+
description="The username with which a connection will be instantiated.",
8383
),
8484
PluginParameter(
8585
name="authentication_method",
@@ -99,14 +99,18 @@
9999
name="password",
100100
label="Password",
101101
description="Depending on your authentication method this will either be used to"
102-
"connect via password to SSH or is used to decrypt the SSH private key",
102+
"connect via password to SSH, or to decrypt the SSH private key",
103103
param_type=PasswordParameterType(),
104104
default_value="",
105105
),
106106
PluginParameter(
107107
name="path",
108108
label="Path",
109-
description="The currently selected path withing your SSH instance.",
109+
description=(
110+
"The currently selected path within your SSH instance."
111+
" Auto-completion starts from user home folder, use '..' for parent directory"
112+
" or '/' for root directory."
113+
),
110114
default_value="",
111115
param_type=DirectoryParameterType("directories", "Folder"),
112116
),
@@ -151,6 +155,9 @@
151155
class ListFiles(WorkflowPlugin):
152156
"""List Plugin SSH"""
153157

158+
ssh_client: paramiko.SSHClient
159+
sftp: paramiko.SFTPClient
160+
154161
def __init__( # noqa: PLR0913
155162
self,
156163
hostname: str,
@@ -178,16 +185,13 @@ def __init__( # noqa: PLR0913
178185
self.max_workers = setup_max_workers(max_workers)
179186
self.input_ports = FixedNumberOfInputs([])
180187
self.output_port = FixedSchemaPort(schema=generate_list_schema())
181-
self.ssh_client = paramiko.SSHClient()
182-
self.connect_ssh_client()
183-
self.sftp = self.ssh_client.open_sftp()
184188

185-
def close_connections(self) -> None:
189+
def cleanup_ssh_connections(self) -> None:
186190
"""Close connection from sftp and ssh"""
187191
self.sftp.close()
188192
self.ssh_client.close()
189193

190-
def connect_ssh_client(self) -> None:
194+
def establish_ssh_connection(self) -> None:
191195
"""Connect to the ssh client with the selected authentication method"""
192196
if self.authentication_method == "key":
193197
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
@@ -211,6 +215,7 @@ def connect_ssh_client(self) -> None:
211215

212216
def preview_results(self) -> str:
213217
"""Preview the results of an execution"""
218+
self._initialize_ssh_and_sftp_connections()
214219
return preview_results(
215220
ssh_client=self.ssh_client,
216221
no_subfolder=self.no_subfolder,
@@ -220,6 +225,11 @@ def preview_results(self) -> str:
220225
max_workers=self.max_workers,
221226
)
222227

228+
def _initialize_ssh_and_sftp_connections(self) -> None:
229+
self.ssh_client = paramiko.SSHClient()
230+
self.establish_ssh_connection()
231+
self.sftp = self.ssh_client.open_sftp()
232+
223233
def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Entities:
224234
"""Execute the workflow task"""
225235
_ = inputs
@@ -228,6 +238,8 @@ def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Enti
228238
)
229239
entities = []
230240

241+
self._initialize_ssh_and_sftp_connections()
242+
231243
retrieval = SSHRetrieval(
232244
ssh_client=self.ssh_client,
233245
no_subfolder=self.no_subfolder,
@@ -316,7 +328,7 @@ def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Enti
316328
)
317329
)
318330

319-
self.close_connections()
331+
self.cleanup_ssh_connections()
320332

321333
return Entities(
322334
entities=iter(entities),

0 commit comments

Comments
 (0)