Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/MIDebugEngine/Microsoft.MIDebugEngine.pkgdef
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
"1"="{A2BBC114-47E4-473F-A49C-69EE89711243}"
; WSL Port supplier
"2"="{267B1341-AC92-44DC-94DF-2EE4205DD17E}"
; Podman Port Supplier
"3"="{D4F2F3A5-6B7C-4E8D-9F0A-1B2C3D4E5F6A}"

; Registration to use lldb with the port suppliers
[$RootKey$\AD7Metrics\Engine\{5D630903-189D-4837-9785-699B05BEC2A9}]
Expand Down Expand Up @@ -83,6 +85,8 @@
"0"="{3FDDF14E-E758-4695-BE0C-7509920432C9}"
; WSL Port supplier
"1"="{267B1341-AC92-44DC-94DF-2EE4205DD17E}"
; Podman Port Supplier
"2"="{D4F2F3A5-6B7C-4E8D-9F0A-1B2C3D4E5F6A}"

[$RootKey$\AD7Metrics\Engine\{5D630903-189D-4837-9785-699B05BEC2A9}\IncompatibleList]
"MI Debug Engine - gdb"="{91744D97-430F-42C1-9779-A5813EBD6AB2}"
Expand Down
41 changes: 41 additions & 0 deletions src/SSHDebugPS/ConnectionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using liblinux;
using liblinux.Persistence;
using Microsoft.SSHDebugPS.Docker;
using Microsoft.SSHDebugPS.Podman;
using Microsoft.SSHDebugPS.SSH;
using Microsoft.SSHDebugPS.UI;
using Microsoft.SSHDebugPS.Utilities;
Expand Down Expand Up @@ -65,6 +66,46 @@ public static DockerConnection GetDockerConnection(string name, bool supportSSHC
}
}

public static PodmanConnection GetPodmanConnection(string name, bool supportSSHConnections)
{
if (string.IsNullOrWhiteSpace(name))
return null;

PodmanContainerTransportSettings settings;
Connection remoteConnection;

ThreadHelper.ThrowIfNotOnUIThread();
if (!PodmanConnection.TryConvertConnectionStringToSettings(name, out settings, out remoteConnection) || settings == null)
{
string connectionString;

bool success = ShowContainerPickerWindow(IntPtr.Zero, supportSSHConnections, ContainerRuntimeType.Podman, out connectionString);
if (success)
{
success = PodmanConnection.TryConvertConnectionStringToSettings(connectionString, out settings, out remoteConnection);
}

if (!success || settings == null)
{
VSMessageBoxHelper.PostErrorMessage(StringResources.Error_ContainerConnectionStringInvalidTitle, StringResources.Error_ContainerConnectionStringInvalidMessage);
return null;
}
}

string displayName = PodmanConnection.CreateConnectionString(settings.ContainerName, remoteConnection?.Name, settings.HostName);
if (PodmanHelper.IsContainerRunning(settings.HostName, settings.ContainerName, remoteConnection))
{
return new PodmanConnection(settings, remoteConnection, displayName);
}
else
{
VSMessageBoxHelper.PostErrorMessage(
StringResources.Error_ContainerUnavailableTitle,
StringResources.Error_ContainerUnavailableMessage.FormatCurrentCultureWithArgs(settings.ContainerName));
return null;
}
}

public static SSHConnection GetSSHConnection(string name)
{
ThreadHelper.ThrowIfNotOnUIThread();
Expand Down
4 changes: 2 additions & 2 deletions src/SSHDebugPS/ContainerRuntimeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Microsoft.SSHDebugPS
/// </summary>
public enum ContainerRuntimeType
{
Unknown,
Docker
Docker,
Podman
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@

namespace Microsoft.SSHDebugPS.Docker
{
public class DockerContainerInstance : ContainerInstance
public class ContainerInstance : IContainerInstance
{
/// <summary>
/// Create a DockerContainerInstance from the results of docker ps in JSON format
/// Create a ContainerInstance from the results of docker ps in JSON format
/// </summary>
public static bool TryCreate(string json, out DockerContainerInstance instance)
public static bool TryCreate(string json, out ContainerInstance instance)
{
instance = null;
try
{
JObject obj = JObject.Parse(json);
instance = obj.ToObject<DockerContainerInstance>();
instance = obj.ToObject<ContainerInstance>();
}
catch (Exception e)
{
Expand All @@ -37,15 +37,15 @@ public static bool TryCreate(string json, out DockerContainerInstance instance)
return instance != null;
}

protected DockerContainerInstance() { }
protected ContainerInstance() { }

#region JsonProperties

[JsonProperty("ID")]
public override string Id { get; set; }
public virtual string Id { get; set; }

[JsonProperty("Names")]
public override string Name { get; set; }
public virtual string Name { get; set; }

[JsonProperty(nameof(Image))]
public virtual string Image { get; protected set; }
Expand All @@ -67,24 +67,71 @@ protected DockerContainerInstance() { }

#endregion

// Docker container names: only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed. It is also case sensitive
protected override bool EqualsInternal(ContainerInstance instance)
#region IEquatable

public static bool operator ==(ContainerInstance left, ContainerInstance right)
{
if (instance is DockerContainerInstance other)
if (left is null || right is null)
{
// the id can be a partial on a container
return String.Equals(Id, other.Id, StringComparison.Ordinal) ||
Id.StartsWith(other.Id, StringComparison.Ordinal) ||
other.Id.StartsWith(Id, StringComparison.Ordinal);
return ReferenceEquals(left, right);
}

return left.Equals(right);
}

public static bool operator !=(ContainerInstance left, ContainerInstance right)
{
return !(left == right);
}

public bool Equals(IContainerInstance instance)
{
if (instance is ContainerInstance container)
{
return this.EqualsInternal(container);
}

return false;
}

public override bool Equals(object obj)
{
if (obj is IContainerInstance instance)
{
return this.Equals(instance);
}
return false;
}

protected override int GetHashCodeInternal()
public override int GetHashCode()
{
return GetHashCodeInternal();
}

#endregion

#region Helper Methods

// Container names: only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed. It is also case sensitive
protected virtual bool EqualsInternal(ContainerInstance instance)
{
if (GetType() != instance.GetType())
{
return false;
}

// the id can be a partial on a container
return String.Equals(Id, instance.Id, StringComparison.Ordinal) ||
Id.StartsWith(instance.Id, StringComparison.Ordinal) ||
instance.Id.StartsWith(Id, StringComparison.Ordinal);
}

protected virtual int GetHashCodeInternal()
{
// Since IDs can be partial, we don't have a good way to get a good hash code.
return string.IsNullOrWhiteSpace(Id) ? 0 : Id.Substring(0,1).GetHashCode();
}

#endregion
}
}
12 changes: 6 additions & 6 deletions src/SSHDebugPS/Docker/DockerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ internal class DockerConnection : PipeConnection

internal const string SshPrefixRegex = @"^[Ss]{2}[Hh]\s*=\s*";
internal const string SshPrefix = "ssh=";
internal const string DockerHostPrefixRegex = @"^host\s*=\s*";
internal const string DockerHostPrefix = "host=";
internal const string HostPrefixRegex = @"^host\s*=\s*";
internal const string HostPrefix = "host=";
internal const char Separator = ';';

internal static string CreateConnectionString(string containerName, string remoteConnectionName, string hostName)
Expand All @@ -34,7 +34,7 @@ internal static string CreateConnectionString(string containerName, string remot

if (!string.IsNullOrWhiteSpace(hostName))
{
connectionString += Separator + DockerHostPrefix + hostName;
connectionString += Separator + HostPrefix + hostName;
}

return connectionString;
Expand All @@ -56,7 +56,7 @@ internal static bool TryConvertConnectionStringToSettings(string connectionStrin
if (connectionStrings.Length <= 3 && connectionStrings.Length > 0)
{
Regex SshRegex = new Regex(SshPrefixRegex);
Regex dockerHostRegex = new Regex(DockerHostPrefixRegex);
Regex hostRegex = new Regex(HostPrefixRegex);

foreach (var item in connectionStrings)
{
Expand All @@ -66,9 +66,9 @@ internal static bool TryConvertConnectionStringToSettings(string connectionStrin
Match match = SshRegex.Match(segment);
remoteConnection = ConnectionManager.GetSSHConnection(segment.Substring(match.Length));
}
else if (dockerHostRegex.IsMatch(segment))
else if (hostRegex.IsMatch(segment))
{
Match match = dockerHostRegex.Match(segment);
Match match = hostRegex.Match(segment);
hostName = segment.Substring(match.Length);
}
else if (segment.Contains("="))
Expand Down
12 changes: 6 additions & 6 deletions src/SSHDebugPS/Docker/DockerDiscoveryStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ internal sealed class DockerDiscoveryStrategy : IContainerDiscoveryStrategy
public string ConnectionToolTip => UIResources.ConnectionToolTip;
public string HostnameAutomationName => UIResources.HostnameAutomationName;

public IEnumerable<DockerContainerInstance> GetLocalContainers(string hostname, out int totalContainers)
public IEnumerable<ContainerInstance> GetLocalContainers(string hostname, out int totalContainers)
{
return DockerHelper.GetLocalDockerContainers(hostname, out totalContainers);
}

public IEnumerable<DockerContainerInstance> GetRemoteContainers(IConnection connection, string hostname, out int totalContainers)
public IEnumerable<ContainerInstance> GetRemoteContainers(IConnection connection, string hostname, out int totalContainers)
{
return DockerHelper.GetRemoteDockerContainers(connection, hostname, out totalContainers);
}

public void AssignPlatforms(IEnumerable<DockerContainerInstance> containers, string hostname)
public void AssignPlatforms(IEnumerable<ContainerInstance> containers, string hostname)
{
if (!containers.Any())
return;
Expand All @@ -44,7 +44,7 @@ public void AssignPlatforms(IEnumerable<DockerContainerInstance> containers, str

if (lcow && serverOS.IndexOf("windows", StringComparison.OrdinalIgnoreCase) >= 0)
{
foreach (DockerContainerInstance container in containers)
foreach (ContainerInstance container in containers)
{
string containerPlatform = string.Empty;
if (DockerHelper.TryGetContainerPlatform(hostname, container.Name, out containerPlatform))
Expand All @@ -60,15 +60,15 @@ public void AssignPlatforms(IEnumerable<DockerContainerInstance> containers, str
else
{
string platform = textInfo.ToTitleCase(serverOS);
foreach (DockerContainerInstance container in containers)
foreach (ContainerInstance container in containers)
{
container.Platform = platform;
}
}
}
else
{
foreach (DockerContainerInstance container in containers)
foreach (ContainerInstance container in containers)
{
container.Platform = unknownOS;
}
Expand Down
14 changes: 7 additions & 7 deletions src/SSHDebugPS/Docker/DockerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,11 @@ internal static bool TryGetContainerPlatform(string hostname, string containerNa
return true;
}

internal static IEnumerable<DockerContainerInstance> GetLocalDockerContainers(string hostname, out int totalContainers)
internal static IEnumerable<ContainerInstance> GetLocalDockerContainers(string hostname, out int totalContainers)
{
totalContainers = 0;
int containerCount = 0;
List<DockerContainerInstance> containers = new List<DockerContainerInstance>();
List<ContainerInstance> containers = new List<ContainerInstance>();

DockerCommandSettings settings = new DockerCommandSettings(hostname, false);
settings.SetCommand(dockerPSCommand, dockerPSArgs);
Expand All @@ -187,7 +187,7 @@ internal static IEnumerable<DockerContainerInstance> GetLocalDockerContainers(st
{
if (args.Trim()[0] == '{')
{
if (DockerContainerInstance.TryCreate(args, out DockerContainerInstance containerInstance))
if (ContainerInstance.TryCreate(args, out ContainerInstance containerInstance))
{
containers.Add(containerInstance);
}
Expand All @@ -205,7 +205,7 @@ internal static IEnumerable<DockerContainerInstance> GetLocalDockerContainers(st
// Another fallback option would be to: docker inspect <containerName> --format {{.State.Status}} which should return "running"
internal static bool IsContainerRunning(string hostName, string containerName, Connection remoteConnection)
{
IEnumerable<DockerContainerInstance> containers;
IEnumerable<ContainerInstance> containers;
if (remoteConnection != null)
{
containers = GetRemoteDockerContainers(remoteConnection, hostName, out _);
Expand All @@ -228,7 +228,7 @@ internal static bool IsContainerRunning(string hostName, string containerName, C
return false;
}

internal static IEnumerable<DockerContainerInstance> GetRemoteDockerContainers(IConnection connection, string hostname, out int totalContainers)
internal static IEnumerable<ContainerInstance> GetRemoteDockerContainers(IConnection connection, string hostname, out int totalContainers)
{
totalContainers = 0;
SSHConnection sshConnection = connection as SSHConnection;
Expand All @@ -239,7 +239,7 @@ internal static IEnumerable<DockerContainerInstance> GetRemoteDockerContainers(I
return null;
}

List<DockerContainerInstance> containers = new List<DockerContainerInstance>();
List<ContainerInstance> containers = new List<ContainerInstance>();

DockerCommandSettings settings = new DockerCommandSettings(hostname, true);
settings.SetCommand(dockerPSCommand, dockerPSArgs);
Expand Down Expand Up @@ -300,7 +300,7 @@ internal static IEnumerable<DockerContainerInstance> GetRemoteDockerContainers(I

foreach (var item in outputLines)
{
if (DockerContainerInstance.TryCreate(item, out DockerContainerInstance containerInstance))
if (ContainerInstance.TryCreate(item, out ContainerInstance containerInstance))
{
containers.Add(containerInstance);
}
Expand Down
6 changes: 3 additions & 3 deletions src/SSHDebugPS/IContainerDiscoveryStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ internal interface IContainerDiscoveryStrategy
string ConnectionToolTip { get; }
string HostnameAutomationName { get; }

IEnumerable<DockerContainerInstance> GetLocalContainers(string hostname, out int totalContainers);
IEnumerable<DockerContainerInstance> GetRemoteContainers(IConnection connection, string hostname, out int totalContainers);
void AssignPlatforms(IEnumerable<DockerContainerInstance> containers, string hostname);
IEnumerable<ContainerInstance> GetLocalContainers(string hostname, out int totalContainers);
IEnumerable<ContainerInstance> GetRemoteContainers(IConnection connection, string hostname, out int totalContainers);
void AssignPlatforms(IEnumerable<ContainerInstance> containers, string hostname);
}
}
17 changes: 17 additions & 0 deletions src/SSHDebugPS/Microsoft.SSHDebugPS.pkgdef
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
"PortPickerCLSID"="{91BDF293-E6A0-49C4-B033-6F36CFC4FF98}"
"Name"="Docker (Linux Container)"

[$RootKey$\AD7Metrics\PortSupplier\{D4F2F3A5-6B7C-4E8D-9F0A-1B2C3D4E5F6A}]
"CLSID"="{C9E1E1E4-3E5A-4F2B-8D1A-5C6F7A8B9D0E}"
"PortPickerCLSID"="{E2A3B4C5-6D7E-4F8A-9B0C-1D2E3F4A5B6C}"
"Name"="Podman (Linux Container)"

[$RootKey$\AD7Metrics\PortSupplier\{267B1341-AC92-44DC-94DF-2EE4205DD17E}]
"CLSID"="{B8587A49-00BD-4DEE-94B9-6EBF49003E04}"
"Name"="Windows Subsystem for Linux (WSL)"
Expand Down Expand Up @@ -47,6 +52,18 @@
"InprocServer32"="$WinDir$\SYSTEM32\MSCOREE.DLL"
"CodeBase"="$PackageFolder$\Microsoft.SSHDebugPS.dll"

[$RootKey$\CLSID\{C9E1E1E4-3E5A-4F2B-8D1A-5C6F7A8B9D0E}]
"Assembly"="Microsoft.SSHDebugPS"
"Class"="Microsoft.SSHDebugPS.Podman.PodmanPortSupplier"
"InprocServer32"="$WinDir$\SYSTEM32\MSCOREE.DLL"
"CodeBase"="$PackageFolder$\Microsoft.SSHDebugPS.dll"

[$RootKey$\CLSID\{E2A3B4C5-6D7E-4F8A-9B0C-1D2E3F4A5B6C}]
"Assembly"="Microsoft.SSHDebugPS"
"Class"="Microsoft.SSHDebugPS.Podman.PodmanLinuxPortPicker"
"InprocServer32"="$WinDir$\SYSTEM32\MSCOREE.DLL"
"CodeBase"="$PackageFolder$\Microsoft.SSHDebugPS.dll"

[$RootKey$\RuntimeConfiguration\dependentAssembly\codeBase\{7E3052B2-FB42-4E38-B22C-1FD281BD4413}]
"name"="Microsoft.SSHDebugPS"
; With local development workflow and release workflow, there are two publicKeyTokens but no way to specify both.
Expand Down
Loading