META - Attach Enhancements
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 25/100
- issue の種類
- 機能追加
- 明瞭さ
- 説明が足りない
- 活発さ
- 停滞
- 技術スタック
- powershell, python, vscode
調査の方向性
このメタ Issue は複数の個別の機能強化を調整するものなので、まずリンクされている Issue 2242-2245 と 2247 から始めてください。次に、参照されているエントリーポイント ansible-pwsh.ps1 と ansible-debug.py を確認し、提案されているデバッグワークフローを理解してください。完了とするには、この調整 Issue 全体ではなく、リンクされている特定の機能強化を実装して検証する必要があります。
索引モデルが issue の本文から書いたものです。
説明
Prerequisites
- I have written a descriptive issue title.
- I have searched all issues to ensure it has not already been requested.
Summary
This is a meta issue I'm opening to hopefully explain some of the new feature requests I've opened and why.
I'm currently working on trying to add support for debugging Ansible modules written in PowerShell through a client like VSCode. My current approach is the following:
- I have this launch configuration
{
"version": "0.2.0",
"configurations": [
{
"name": "Ansible PowerShell Debugger",
"type": "PowerShell",
"request": "launch",
"script": "/home/jborean/dev/ansible/bin/ansible-pwsh.ps1",
"args": [],
"cwd": "/home/jborean/dev/ansible",
"serverReadyAction": {
"action": "startDebugging",
"pattern": "Listening for debug request on port \\d+",
"name": "Ansible Launch Debugger Environment"
}
},
{
"name": "Ansible Launch Debugger Environment",
"type": "debugpy",
"request": "launch",
"program": "/home/jborean/dev/ansible/bin/ansible-debug.py",
"args": [
"win-hostname"
],
"console": "integratedTerminal",
"cwd": "/home/jborean/dev/ansible-tester",
},
],
}
This launch configuration starts a PowerShell script ansible-pwsh.ps1 which sets up a local socket and exposes it through a named pipe locally. It will listen for requests on the socket and setup an attach configuration to start the debug session.
ansible-pwsh.ps1
#!/usr/bin/env pwsh
using namespace System.IO
using namespace System.IO.Pipes
using namespace System.Management.Automation.Language
using namespace System.Net.Sockets
using namespace System.Text
using namespace System.Threading
using namespace System.Threading.Tasks
#Requires -Version 7.4
[CmdletBinding()]
param ()
$ErrorActionPreference = 'Stop'
$configPath = '~/.ansible/test/debugging'
if (-not (Test-Path -LiteralPath $configPath)) {
New-Item -ItemType Directory -Path $configPath | Out-Null
}
$configFile = Join-Path -Path $configPath -ChildPath 'pwsh_debugger.json'
$sock = $null
try {
# Start listening to a socket locally that we will forward to the remote
# host via SSH.
$sock = [TcpListener]::new(
[IPAddress]::IPv6Loopback,
0)
$sock.Server.DualMode = $true
$sock.Start()
$configInfo = @{
pid = $PID
port = $sock.LocalEndpoint.Port
} | ConvertTo-Json -Compress
Set-Content -LiteralPath $configFile -Value $configInfo
Write-Host "Listening for debug request on port $($sock.LocalEndpoint.Port)"
while ($true) {
$task = $sock.AcceptTcpClientAsync()
while (-not $task.AsyncWaitHandle.WaitOne(300)) { }
$client = $task.GetAwaiter().GetResult()
Write-Host "Client connected from $($client.Client.RemoteEndPoint)"
$pipe = $stream = $null
try {
$stream = $client.GetStream()
$reader = [StreamReader]::new($stream, [Encoding]::UTF8, $true, $client.ReceiveBufferSize, $true)
$task = $reader.ReadLineAsync()
while (-not $task.AsyncWaitHandle.WaitOne(300)) { }
$debugInfoRaw = $task.GetAwaiter().GetResult()
$reader.Dispose()
Write-Host "Received debug request`n$debugInfoRaw"
$debugInfo = ConvertFrom-Json -InputObject $debugInfoRaw
$pipeName = "MyPipe-$(New-Guid)"
$pipe = [NamedPipeServerStream]::new(
$pipeName,
[PipeDirection]::InOut,
1,
[PipeTransmissionMode]::Byte,
[PipeOptions]::Asynchronous)
$task = $pipe.WaitForConnectionAsync()
Write-Host "Starting VSCode attach to Pipe $pipeName"
$attachConfig = @{
name = $debugInfo.ModuleName
type = "PowerShell"
request = "attach"
customPipeName = $pipeName
runspaceId = $debugInfo.RunspaceId
pathMappings = @($debugInfo.PathMappings)
}
$attachTask = Start-NewDebugSession -Request Attach -Configuration $attachConfig
Write-Host "Waiting for VSCode to attach to Pipe"
while (-not $task.AsyncWaitHandle.WaitOne(300)) {}
$null = $task.GetAwaiter().GetResult()
Write-Host "VSCode attached to pipe. Sending back confirmation byte to debug session."
$task = $stream.WriteAsync([byte[]]@(0), 0, 1)
while (-not $task.AsyncWaitHandle.WaitOne(300)) {}
$null = $task.GetAwaiter().GetResult()
# FIXME: SSH socket forwarding won't break the copy so need to find out how to detect that.
Write-Host "Starting socket <-> pipe streaming"
$writeTask = $pipe.CopyToAsync($stream)
$readTask = $stream.CopyToAsync($pipe)
Write-Host "Waiting for startDebugging attach response to arrive"
while (-not $attachTask.AsyncWaitHandle.WaitOne(300)) { }
$null = $attachTask.GetAwaiter().GetResult()
$task = [Task]::WhenAny($writeTask, $readTask)
while (-not $task.AsyncWaitHandle.WaitOne(300)) {}
$finishedTask = $task.GetAwaiter().GetResult()
if ($finishedTask -eq $writeTask) {
Write-Host "VSCode disconnected from Pipe $pipeName and RunspaceId $runspaceId"
}
elseif ($finishedTask -eq $readTask) {
Write-Host "Socket disconnected from Pipe $pipeName and RunspaceId $runspaceId"
}
else {
Write-Host "Unknown task finished for Pipe $pipeName and RunspaceId $runspaceId"
}
}
finally {
${pipe}?.Dispose()
${stream}?.Dispose()
$client.Dispose()
}
}
}
finally {
if (Test-Path -LiteralPath $configFile) {
Remove-Item -LiteralPath $configFile -Force
}
${sock}?.Dispose()
}
The ansible-debug.py script is a simple script that will forward that local socket to the Windows host through SSH and setup some env var that Ansible uses to read the PowerShell debug information.
ansible-debug.py
#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
from __future__ import annotations
import argparse
import contextlib
import json
import os
import pathlib
import re
import subprocess
import sys
import typing as t
HAS_ARGCOMPLETE = False
try:
import argcomplete
HAS_ARGCOMPLETE = True
except ImportError:
pass
_PORT_ALLOC_PATTERN = re.compile(r'Allocated port (\d+) for remote forward to .*')
def _parse_args(args: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Ansible Module Debugging CLI")
parser.add_argument(
'hostname',
nargs=1,
type=str,
help="The SSH hostname to forward the debug ports to. This should include username and/or port if necessary.",
)
if HAS_ARGCOMPLETE:
argcomplete.autocomplete(parser)
return parser.parse_args(args)
@contextlib.contextmanager
def forward_ports(
hostname: str,
local_port: int,
) -> t.Generator[int]:
ssh_cmd = [
'ssh',
'-v', # Verbose output to capture port allocation
'-NT', # Don't execute remote command, disable PTY allocation
'-o', 'ExitOnForwardFailure=yes',
'-R', f'localhost:0:localhost:{local_port}',
hostname
]
# Start SSH process with stderr captured for debug output
process = subprocess.Popen(
ssh_cmd,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
text=True,
)
try:
assert process.stdout is not None
proc_out = []
port = 0
for line in process.stdout:
if match := _PORT_ALLOC_PATTERN.match(line):
port = int(match.group(1))
break
proc_out.append(line)
if port == 0:
raise Exception(f"SSH failed to forward ports. Output:\n{'\n'.join(proc_out)}")
yield port
finally:
process.terminate()
def main() -> None:
args = _parse_args(sys.argv[1:])
config_path = pathlib.Path.home() / '.ansible' / 'test' / 'debugging' / 'pwsh_debugger.json'
if not config_path.exists():
raise Exception(f"PowerShell debugger configuration file not found. Please ensure it exists at {config_path.absolute()}")
debug_info = json.loads(config_path.read_text())
local_port = debug_info.get('port', 0)
if not local_port:
raise Exception("No local port specified in the PowerShell debugger configuration file.")
with forward_ports(args.hostname[0], local_port=local_port) as remote_port:
debug_options = {
'wait': False,
'host': 'localhost',
'port': remote_port,
}
new_env = os.environ.copy() | {
'_ANSIBLE_ANSIBALLZ_PWSH_DEBUGGER_CONFIG': json.dumps(debug_options),
}
res = subprocess.run('/bin/bash', env=new_env, check=False)
sys.exit(res.returncode)
if __name__ == '__main__':
main()
From there when Ansible starts the PowerShell process on the Windows host, it'll connect to the socket, send the runspace/debug information over that socket, redirect any socket data to the local named pipe for that process and wait for VSCode to "attach".
From a workflow perspective this works fine but it has required a few changes to PSES to get working locally. These changes are mentioned in the following issues:
- https://github.com/PowerShell/PowerShellEditorServices/issues/2242 - Local and Remote paths need to be translated
- https://github.com/PowerShell/PowerShellEditorServices/issues/2243 - Ansible doesn't store the scripts as files but just sets the file metadata so this is needed for 5.1 to set breakpoints correctly
- https://github.com/PowerShell/PowerShellEditorServices/issues/2244 - Allows
ansible-pwsh.ps1to start an attach request on demand with the dynamic pipe/runspace/path mapping information - https://github.com/PowerShell/PowerShellEditorServices/issues/2245 - I haven't implemented this but would allow breakpoints to be set (at least on 7+) before running the user's code
- https://github.com/PowerShell/PowerShellEditorServices/issues/2247 - A nice to have but after the temp attach session has disconnected/ended, the temp console is no longer needed and has to be manually closed or the user needs to manually move back to the original terminal
Proposed Design
See the linked issues.
- 主要言語
- C#
- スター
- 767
- フォーク
- 266
- 平均マージ
- 3日 16時間
- マージ済み PR(30日)
- 1
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
PowerShell/PowerShellEditorServices のほかの issue
-
[Bug] None of the string overloads of `EditorExtensionServiceProvider.GetService()` work on .NET. オープン
PowerShell/PowerShellEditorServices#2331 · リアクション 1 件 · 担当者 2 名 ·
-
Area-Language Server Issue-Enhancement
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
PowerShell/PowerShellEditorServices#2315 · コメント 1 件 ·
-
Area-Debugging Issue-Enhancement
PowerShell/PowerShellEditorServices#2294 · リアクション 1 件 · 担当者 2 名 ·
-
Issue-Bug Needs: Triage
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
PowerShell/PowerShellEditorServices#2293 · コメント 1 件 ·
-
PowerShell/PowerShellEditorServices#2282 · コメント 1 件 · リアクション 1 件 · 担当者 2 名 ·
PowerShell/PowerShellEditorServices の issue をすべて見る
似ている issue
-
Documentation
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
cake-build/cake#5024 ·
-
Frontend status/draft TechnicalDebt
難易度 2/5 1〜2日 初心者へのやさしさ 75/100
Altinn/altinn-auth#4143 ·
-
.NET Flaky Test Testing Tests
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
getsentry/sentry-dotnet#5617 · コメント 1 件 ·
-
:watch: Not Triaged dotnet-fsharp/svc
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
-
Client customer-reported needs-team-attention question Service Attention WebPubSub
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
Azure/azure-sdk-for-net#63292 · コメント 3 件 · リアクション 1 件 ·