Skip to content

Commit

Permalink
render the variable using jinja2 template
Browse files Browse the repository at this point in the history
  • Loading branch information
abhijeetSaroha committed Nov 4, 2024
1 parent 93e4cfc commit 3f5627f
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 9 deletions.
73 changes: 68 additions & 5 deletions src/makim/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from copy import deepcopy
from itertools import product
from pathlib import Path
from typing import Any, Dict, List, Optional, Union, cast
from typing import Any, Dict, List, Optional, TypedDict, Union, cast

import dotenv
import paramiko
Expand Down Expand Up @@ -61,6 +61,19 @@
)


class HostConfig(TypedDict):
"""
Type definition for SSH host configuration containing.
Includes username, host, port, and optional password.
"""

username: str
host: str
port: int
password: Optional[str]


def strip_recursively(data: Any) -> Any:
"""Strip strings in list and dictionaries."""
if isinstance(data, str):
Expand Down Expand Up @@ -176,14 +189,23 @@ def _call_shell_app(self, cmd: str) -> None:

def _call_shell_remote(self, cmd: str, host_config: Any) -> None:
try:
# Render the host configuration values
env, variables = self._load_scoped_data('task')
rendered_config = self._render_host_config(host_config, env)

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# Use typed dict values to ensure correct types for paramiko
ssh.connect(
username=host_config['user'],
password=host_config.get('password'),
hostname=host_config['host'],
port=host_config.get('port', 22),
username=rendered_config[
'username'
], # Already str from _render_host_config
password=rendered_config.get(
'password'
), # Already Optional[str]
hostname=rendered_config['host'], # Already str
port=rendered_config['port'], # Already int
)

stdin, stdout, stderr = ssh.exec_command(
Expand Down Expand Up @@ -216,6 +238,47 @@ def _call_shell_remote(self, cmd: str, host_config: Any) -> None:
MakimError.SSH_EXECUTION_ERROR,
)

def _render_host_config(
self, host_config: dict[str, Any], env: dict[str, str]
) -> HostConfig:
"""Render the host configuration values using Jinja2 templates."""
rendered: Dict[str, Any] = {}

# Handle each field with appropriate type conversion
for key in ('username', 'host', 'password', 'port'):
value = host_config.get(key, '')

if value is None and key == 'password':
rendered[key] = None
continue

# Convert to string and render if it's a template
str_value = str(value) if value != '' else ''
if isinstance(value, str):
str_value = TEMPLATE.from_string(str_value).render(env=env)

# Handle each field according to its required type
if key == 'port':
rendered[key] = int(str_value) if str_value.isdigit() else 22
elif key == 'password':
rendered[key] = str_value if str_value else None
else: # username and host
if not str_value:
raise ValueError(f'{key} is required and cannot be empty')
rendered[key] = str_value

# Ensure all required fields are present
if 'port' not in rendered:
rendered['port'] = 22

# Cast to HostConfig to ensure type safety
return HostConfig(
username=rendered['username'],
host=rendered['host'],
port=rendered['port'],
password=rendered.get('password'),
)

def _check_makim_file(self, file_path: str = '') -> bool:
return Path(file_path or self.file).exists()

Expand Down
4 changes: 4 additions & 0 deletions tests/smoke/.env-ssh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SSH_HOST=localhost
SSH_PORT=2222
SSH_USER=testuser
SSH_PASSWORD=testpassword
9 changes: 5 additions & 4 deletions tests/smoke/.makim-ssh.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
env-file: .env-ssh
hosts:
test_container:
host: localhost
port: 2222
user: testuser
password: testpassword
host: ${{ env.SSH_HOST }}
port: ${{ env.SSH_PORT }}
user: ${{ env.SSH_USER }}
password: ${{ env.SSH_PASSWORD }}
backend: bash
groups:
docker:
Expand Down

0 comments on commit 3f5627f

Please sign in to comment.