Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
45 changes: 39 additions & 6 deletions localization/strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -2134,10 +2134,6 @@ Usage:
<value>Container '{}' is running.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcContainerIpAddressNotSupported" xml:space="preserve">
<value>ContainerIpAddress is not yet supported.</value>
<comment>{Locked="ContainerIpAddress"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcContainerModeNoPorts" xml:space="preserve">
<value>Port mappings are not supported with container network mode; ports are owned by the target container.</value>
</data>
Expand Down Expand Up @@ -2387,21 +2383,44 @@ For privacy information about this product please visit https://aka.ms/privacy.<
<value>Additional networks are not allowed when the primary network mode is 'host' or 'none'.</value>
<comment>{Locked="host"}{Locked="none"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcNetworkModeNoAdditionalNetworks" xml:space="preserve">
<value>The primary network mode '{}' does not support connecting or disconnecting additional networks.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcDuplicateNetwork" xml:space="preserve">
<value>Duplicate network: '{}'</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcEndpointSettingsNotSupported" xml:space="preserve">
<value>Endpoint settings are not yet supported (network '{}').</value>
<data name="MessageWslcDriverOptDuplicate" xml:space="preserve">
<value>Duplicate driver option '{}'.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcDriverOptInvalid" xml:space="preserve">
<value>Invalid driver option '{}'; expected 'key=value'.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
Comment thread
Copilot marked this conversation as resolved.
</data>
<data name="MessageWslcEndpointSettingUnknown" xml:space="preserve">
<value>Unknown endpoint setting '{}' for network '{}'.</value>
<comment>{FixedPlaceholder="{}"}{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
Comment thread
Copilot marked this conversation as resolved.
Outdated
</data>
<data name="MessageWslcIpAddressSingleValue" xml:space="preserve">
<value>Only one IP address may be specified for network '{}'.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcLinkEmpty" xml:space="preserve">
<value>Network link cannot be empty.</value>
</data>
<data name="MessageWslcAliasEmpty" xml:space="preserve">
<value>Network alias cannot be empty.</value>
</data>
<data name="MessageWslcAliasRequiresUserDefinedNetwork" xml:space="preserve">
<value>Network aliases require a user-defined network. Use --network to specify one.</value>
<comment>{Locked="--network "}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcEndpointSettingsRequireNetwork" xml:space="preserve">
<value>Endpoint settings are not supported for network mode '{}'.</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
</data>
<data name="MessageWslcAliasAmbiguousWithMultipleNetworks" xml:space="preserve">
<value>Network aliases cannot be specified when multiple networks are requested. Use a single --network argument.</value>
<comment>{Locked="--network "}Command line arguments, file names and string inserts should not be translated</comment>
Expand Down Expand Up @@ -2992,6 +3011,20 @@ On first run, creates the file with all settings commented out at their defaults
<data name="WSLCCLI_NetworkAliasArgDescription" xml:space="preserve">
<value>Add a network-scoped alias for the container</value>
</data>
<data name="WSLCCLI_DriverOptArgDescription" xml:space="preserve">
<value>Set endpoint driver options (--driver-opt key=value)</value>
<comment>{Locked="--driver-opt "}Command line arguments, file names and string inserts should not be translated</comment>
Comment thread
Copilot marked this conversation as resolved.
</data>
<data name="WSLCCLI_IpAddressArgDescription" xml:space="preserve">
<value>Assign the container an IPv4 address on the network</value>
</data>
<data name="WSLCCLI_LinkArgDescription" xml:space="preserve">
<value>Add link to another container (--link name:alias)</value>
<comment>{Locked="--link "}Command line arguments, file names and string inserts should not be translated</comment>
Comment thread
Copilot marked this conversation as resolved.
</data>
<data name="WSLCCLI_LinkLocalIpArgDescription" xml:space="preserve">
<value>Add a link-local IPv4 address for the container</value>
</data>
<data name="WSLCCLI_NetworkAliasEmptyError" xml:space="preserve">
<value>Invalid {} value: network alias cannot be empty or whitespace</value>
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
Expand Down
95 changes: 78 additions & 17 deletions src/windows/inc/docker_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,78 @@ struct Network
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Network, Id, Name, Driver, Scope, Internal, IPAM, Options, Labels);
};

struct EndpointIPAMConfig
{
std::string IPv4Address;
std::optional<std::vector<std::string>> LinkLocalIPs;
};

inline void to_json(nlohmann::json& j, const EndpointIPAMConfig& v)
{
j = nlohmann::json::object();
if (!v.IPv4Address.empty())
{
j["IPv4Address"] = v.IPv4Address;
}
if (v.LinkLocalIPs.has_value() && !v.LinkLocalIPs->empty())
{
j["LinkLocalIPs"] = *v.LinkLocalIPs;
}
}

struct EndpointConfig
{
std::optional<std::vector<std::string>> Aliases;
std::optional<EndpointIPAMConfig> IPAMConfig;
std::optional<std::vector<std::string>> Links;
std::optional<std::map<std::string, std::string>> DriverOpts;
};

inline void to_json(nlohmann::json& j, const EndpointConfig& v)
{
j = nlohmann::json::object();
if (v.Aliases.has_value() && !v.Aliases->empty())
{
j["Aliases"] = *v.Aliases;
}
if (v.IPAMConfig.has_value())
{
auto ipam = nlohmann::json(*v.IPAMConfig);
if (!ipam.empty())
{
j["IPAMConfig"] = std::move(ipam);
}
}
if (v.Links.has_value() && !v.Links->empty())
{
j["Links"] = *v.Links;
}
if (v.DriverOpts.has_value() && !v.DriverOpts->empty())
{
j["DriverOpts"] = *v.DriverOpts;
}
}

struct ContainerNetworkRequest
{
using TResponse = void;
std::string Container;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(ContainerNetworkRequest, Container);
std::optional<EndpointConfig> EndpointConfig;
};

inline void to_json(nlohmann::json& j, const ContainerNetworkRequest& v)
{
j = nlohmann::json{{"Container", v.Container}};
if (v.EndpointConfig.has_value())
{
auto endpoint = nlohmann::json(*v.EndpointConfig);
if (!endpoint.empty())
{
j["EndpointConfig"] = std::move(endpoint);
}
}
}

struct Mount
{
std::string Name;
Expand Down Expand Up @@ -245,31 +309,28 @@ struct HostConfig
HostConfig, Mounts, PortBindings, NetworkMode, Init, Dns, DnsSearch, DnsOptions, Binds, Tmpfs, Devices, DeviceRequests, ShmSize, Memory, NanoCpus, Ulimits);
};

struct InspectEndpointIPAMConfig
{
std::string IPv4Address;
std::optional<std::vector<std::string>> LinkLocalIPs;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InspectEndpointIPAMConfig, IPv4Address, LinkLocalIPs);
};

struct EndpointSettings
{
std::string IPAddress;
std::string Gateway;
std::string MacAddress;
int IPPrefixLen{};
std::optional<std::vector<std::string>> Aliases;
std::optional<std::vector<std::string>> Links;
std::optional<std::map<std::string, std::string>> DriverOpts;
std::optional<InspectEndpointIPAMConfig> IPAMConfig;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(EndpointSettings, IPAddress, Gateway, MacAddress, IPPrefixLen, Aliases);
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(EndpointSettings, IPAddress, Gateway, MacAddress, IPPrefixLen, Aliases, Links, DriverOpts, IPAMConfig);
};

struct EndpointConfig
{
std::optional<std::vector<std::string>> Aliases;
};

inline void to_json(nlohmann::json& j, const EndpointConfig& v)
{
j = nlohmann::json::object();
if (v.Aliases.has_value() && !v.Aliases->empty())
{
j["Aliases"] = *v.Aliases;
}
}

struct NetworkingConfig
{
std::map<std::string, EndpointConfig> EndpointsConfig;
Expand Down
13 changes: 12 additions & 1 deletion src/windows/inc/wslc_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,26 @@ struct ContainerConfig
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ContainerConfig, Env, Cmd, Entrypoint, User, WorkingDir, StopTimeout, Healthcheck);
};

struct InspectEndpointIPAMConfig
{
std::string IPv4Address;
std::vector<std::string> LinkLocalIPs;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InspectEndpointIPAMConfig, IPv4Address, LinkLocalIPs);
};

struct InspectEndpointSettings
{
std::string IPAddress;
std::string Gateway;
std::string MacAddress;
int IPPrefixLen{};
std::vector<std::string> Aliases;
std::vector<std::string> Links;
std::map<std::string, std::string> DriverOpts;
std::optional<InspectEndpointIPAMConfig> IPAMConfig;

NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InspectEndpointSettings, IPAddress, Gateway, MacAddress, IPPrefixLen, Aliases);
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(InspectEndpointSettings, IPAddress, Gateway, MacAddress, IPPrefixLen, Aliases, Links, DriverOpts, IPAMConfig);
};

struct InspectNetworkSettings
Expand Down
5 changes: 4 additions & 1 deletion src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,10 @@ typedef struct _WSLCNetworkConnection
typedef struct _WSLCNetworkConnectionOptions
{
[unique] LPCSTR NetworkName;
[unique] LPCSTR ContainerIpAddress; // Reserved for future --ip support; must be NULL today.
// Endpoint settings for the network connection (Aliases, IPAddress, Links, LinkLocalIPs, DriverOpts).
// KVP-encoded; duplicate keys are allowed (e.g., multiple "Aliases" entries).
[unique, size_is(SettingsCount)] const KeyValuePair* Settings;
ULONG SettingsCount;
} WSLCNetworkConnectionOptions;

typedef struct _WSLCContainerNetwork
Expand Down
4 changes: 4 additions & 0 deletions src/windows/wslc/arguments/ArgumentDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ _(DNSOption, "dns-option", NO_ALIAS, Kind::Value, L
_(DNSSearch, "dns-search", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DNSSearchArgDescription()) \
_(Domainname, "domainname", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DomainnameArgDescription()) \
_(Driver, "driver", L"d", Kind::Value, Localization::WSLCCLI_DriverOptionDescription()) \
_(DriverOpt, "driver-opt", NO_ALIAS, Kind::Value, Localization::WSLCCLI_DriverOptArgDescription()) \
Comment thread
beena352 marked this conversation as resolved.
_(Entrypoint, "entrypoint", NO_ALIAS, Kind::Value, Localization::WSLCCLI_EntrypointArgDescription()) \
_(Env, "env", L"e", Kind::Value, Localization::WSLCCLI_EnvArgDescription()) \
_(EnvFile, "env-file", NO_ALIAS, Kind::Value, Localization::WSLCCLI_EnvFileArgDescription()) \
Expand Down Expand Up @@ -78,9 +79,12 @@ _(ImportFile, "file", NO_ALIAS, Kind::Positional, L
_(Input, "input", L"i", Kind::Value, Localization::WSLCCLI_InputArgDescription()) \
_(Interactive, "interactive", L"i", Kind::Flag, Localization::WSLCCLI_InteractiveArgDescription()) \
_(Internal, "internal", NO_ALIAS, Kind::Flag, Localization::WSLCCLI_NetworkInternalArgDescription()) \
_(IpAddress, "ip", NO_ALIAS, Kind::Value, Localization::WSLCCLI_IpAddressArgDescription()) \
_(Label, "label", L"l", Kind::Value, Localization::WSLCCLI_LabelArgDescription()) \
_(Last, "last", L"n", Kind::Value, Localization::WSLCCLI_LastArgDescription()) \
_(Latest, "latest", L"l", Kind::Flag, Localization::WSLCCLI_LatestArgDescription()) \
_(Link, "link", NO_ALIAS, Kind::Value, Localization::WSLCCLI_LinkArgDescription()) \
_(LinkLocalIp, "link-local-ip", NO_ALIAS, Kind::Value, Localization::WSLCCLI_LinkLocalIpArgDescription()) \
_(Memory, "memory", L"m", Kind::Value, Localization::WSLCCLI_MemoryArgDescription()) \
_(Name, "name", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NameArgDescription()) \
_(Network, "network", NO_ALIAS, Kind::Value, Localization::WSLCCLI_NetworkArgDescription()) \
Expand Down
5 changes: 5 additions & 0 deletions src/windows/wslc/commands/NetworkConnectCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ std::vector<Argument> NetworkConnectCommand::GetArguments() const
return {
Argument::Create(ArgType::NetworkName, true),
Argument::Create(ArgType::ContainerId, true),
Argument::Create(ArgType::NetworkAlias, false, NO_LIMIT),
Comment thread
beena352 marked this conversation as resolved.
Outdated
Argument::Create(ArgType::IpAddress, false),
Argument::Create(ArgType::Link, false, NO_LIMIT),
Argument::Create(ArgType::LinkLocalIp, false, NO_LIMIT),
Argument::Create(ArgType::DriverOpt, false, NO_LIMIT),
};
}

Expand Down
11 changes: 11 additions & 0 deletions src/windows/wslc/services/NetworkModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ struct CreateNetworkOptions
std::optional<std::string> Gateway;
};

struct ConnectNetworkOptions
{
std::string NetworkName;
std::string ContainerId;
std::vector<std::string> Aliases;
std::optional<std::string> IpAddress;
std::vector<std::string> Links;
std::vector<std::string> LinkLocalIps;
std::vector<std::string> DriverOpts;
};

struct PruneNetworksResult
{
std::vector<std::string> PrunedNetworks;
Expand Down
39 changes: 36 additions & 3 deletions src/windows/wslc/services/NetworkService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,46 @@ models::PruneNetworksResult NetworkService::Prune(models::Session& session, cons
return result;
}

void NetworkService::Connect(models::Session& session, const std::string& networkName, const std::string& containerId)
void NetworkService::Connect(models::Session& session, const models::ConnectNetworkOptions& connectOptions)
{
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(containerId.c_str(), &container));
THROW_IF_FAILED(session.Get()->OpenContainer(connectOptions.ContainerId.c_str(), &container));

// Build KVPs so the pointers remain valid for the duration of the ConnectToNetwork call.
std::vector<KeyValuePair> settings;
settings.reserve(
connectOptions.Aliases.size() + connectOptions.Links.size() + connectOptions.LinkLocalIps.size() +
connectOptions.DriverOpts.size() + (connectOptions.IpAddress.has_value() ? 1 : 0));

for (const auto& alias : connectOptions.Aliases)
{
settings.push_back({.Key = "Aliases", .Value = alias.c_str()});
}

if (connectOptions.IpAddress.has_value())
{
settings.push_back({.Key = "IPAddress", .Value = connectOptions.IpAddress->c_str()});
}

for (const auto& link : connectOptions.Links)
{
settings.push_back({.Key = "Links", .Value = link.c_str()});
}

for (const auto& linkLocalIp : connectOptions.LinkLocalIps)
{
settings.push_back({.Key = "LinkLocalIPs", .Value = linkLocalIp.c_str()});
}

for (const auto& entry : connectOptions.DriverOpts)
{
settings.push_back({.Key = "DriverOpts", .Value = entry.c_str()});
}

WSLCNetworkConnectionOptions options{};
options.NetworkName = networkName.c_str();
options.NetworkName = connectOptions.NetworkName.c_str();
options.Settings = settings.empty() ? nullptr : settings.data();
options.SettingsCount = static_cast<ULONG>(settings.size());
THROW_IF_FAILED(container->ConnectToNetwork(&options));
}

Expand Down
2 changes: 1 addition & 1 deletion src/windows/wslc/services/NetworkService.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct NetworkService
static std::vector<WSLCNetworkInformation> List(models::Session& session);
static wsl::windows::common::wslc_schema::Network Inspect(models::Session& session, const std::string& name);
static models::PruneNetworksResult Prune(models::Session& session, const std::vector<std::pair<std::string, std::string>>& filters = {});
static void Connect(models::Session& session, const std::string& networkName, const std::string& containerId);
static void Connect(models::Session& session, const models::ConnectNetworkOptions& connectOptions);
static void Disconnect(models::Session& session, const std::string& networkName, const std::string& containerId);
};
} // namespace wsl::windows::wslc::services
33 changes: 30 additions & 3 deletions src/windows/wslc/tasks/NetworkTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,36 @@ void ConnectNetwork(CLIExecutionContext& context)
WI_ASSERT(context.Args.Contains(ArgType::NetworkName));
WI_ASSERT(context.Args.Contains(ArgType::ContainerId));

const auto networkName = WideToMultiByte(context.Args.Get<ArgType::NetworkName>());
const auto containerId = WideToMultiByte(context.Args.Get<ArgType::ContainerId>());
NetworkService::Connect(context.Data.Get<Data::Session>(), networkName, containerId);
models::ConnectNetworkOptions options{};
Comment thread
beena352 marked this conversation as resolved.
options.NetworkName = WideToMultiByte(context.Args.Get<ArgType::NetworkName>());
options.ContainerId = WideToMultiByte(context.Args.Get<ArgType::ContainerId>());

for (const auto& alias : context.Args.GetAll<ArgType::NetworkAlias>())
{
options.Aliases.emplace_back(WideToMultiByte(alias));
}

if (context.Args.Contains(ArgType::IpAddress))
{
options.IpAddress = WideToMultiByte(context.Args.Get<ArgType::IpAddress>());
}

for (const auto& link : context.Args.GetAll<ArgType::Link>())
{
options.Links.emplace_back(WideToMultiByte(link));
}

for (const auto& linkLocalIp : context.Args.GetAll<ArgType::LinkLocalIp>())
{
options.LinkLocalIps.emplace_back(WideToMultiByte(linkLocalIp));
}

for (const auto& driverOpt : context.Args.GetAll<ArgType::DriverOpt>())
{
options.DriverOpts.emplace_back(WideToMultiByte(driverOpt));
}

NetworkService::Connect(context.Data.Get<Data::Session>(), options);
}

void DisconnectNetwork(CLIExecutionContext& context)
Expand Down
Loading
Loading