-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Added samples to the API #40930
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
craigloewen-msft
wants to merge
1
commit into
master
Choose a base branch
from
user/crloewen/wslc-api-samples
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,099
−0
Open
Added samples to the API #40930
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Build outputs and restored packages for the WSL Container API samples. | ||
| packages/ | ||
| bin/ | ||
| obj/ | ||
| x64/ | ||
| x86/ | ||
| ARM64/ | ||
| Win32/ | ||
| Debug/ | ||
| Release/ | ||
| *.exe | ||
| *.pdb | ||
| *.ilk | ||
| *.obj | ||
| *.log | ||
| *.tlog | ||
| *.user | ||
|
|
||
| # Runtime data created when the samples are run. | ||
| WslcNextcloudStorage/ | ||
| WslcNextcloudData/ | ||
| WslcStorage/ | ||
| WslcQrStorage/ | ||
| *.tar | ||
| *_out.txt | ||
| *_err.txt | ||
| out*.txt | ||
| err*.txt | ||
| run_nextcloud_test.ps1 | ||
|
|
||
| # The repository-root .gitignore excludes project files (*.sln, *.csproj, | ||
| # *.vcxproj, *.filters) because they are normally generated by CMake. These | ||
| # samples are hand-authored standalone projects, so re-include their sources. | ||
| !*.sln | ||
| !*.csproj | ||
| !*.vcxproj | ||
| !*.filters |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # WSL Container API samples | ||
|
|
||
| Standalone, self-contained samples that demonstrate the | ||
| [WSL Container API](https://aka.ms/wslc) (`Microsoft.WSL.Containers`). | ||
|
|
||
| | Sample | Language | Description | | ||
| | --- | --- | --- | | ||
| | [WSLC-HelloWorld](WSLC-HelloWorld) | C | Minimal sample that runs `echo` in an `alpine` container and prints its output. | | ||
| | [WSLC-Neofetch](WSLC-Neofetch) | C++/WinRT | Runs the Linux `neofetch` command from a native Windows `.exe`. | | ||
| | [WSLC-NextCloud](WSLC-NextCloud) | C# | Runs a Nextcloud server in a container, exposed on `http://localhost:8080`. | | ||
| | [WSLC-CustomContainer](WSLC-CustomContainer) | C# CLI | Generates a scannable QR code in your terminal with a Python tool in a **custom Containerfile that is auto-built at F5**. | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Custom container image for the WSLC-CustomContainer sample. | ||
| # | ||
| # A tiny Python tool that turns text (e.g. a URL) into a scannable QR code drawn | ||
| # with Unicode block characters, printed straight to the terminal. | ||
| # | ||
| # This image is built automatically when the C# project is built, via the | ||
| # <WslcImage> item in WSLCCustomContainer.csproj (no manual docker/wslc steps). | ||
|
|
||
| FROM python:3.11-slim | ||
|
|
||
| # qrcode renders ASCII/Unicode QR codes with no extra native dependencies. | ||
| RUN pip install --no-cache-dir qrcode | ||
|
|
||
| COPY qr.py /app/qr.py | ||
|
|
||
| # No ENTRYPOINT/CMD: the host keeps the container alive with its own init | ||
| # process (sleep) and then execs `python /app/qr.py <text>` to render the code. | ||
| # (Run it directly with: wslc run customcontainer python /app/qr.py "your text".) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| #!/usr/bin/env python3 | ||
| """Turn text (e.g. a URL) into a scannable QR code printed to the terminal. | ||
|
|
||
| Used by the WSLC-CustomContainer sample. The Windows host passes the text to | ||
| encode as command-line arguments; the QR is drawn with Unicode block characters | ||
| so it scans straight from the terminal. | ||
| """ | ||
|
|
||
| import sys | ||
|
|
||
| import qrcode | ||
|
|
||
|
|
||
| def main() -> int: | ||
| text = " ".join(sys.argv[1:]).strip() | ||
| if not text: | ||
| print("usage: qr.py <text-or-url>", file=sys.stderr) | ||
| return 2 | ||
|
|
||
| qr = qrcode.QRCode(border=2) | ||
| qr.add_data(text) | ||
| qr.make(fit=True) | ||
|
|
||
| print(f"QR code for: {text}\n") | ||
| # invert=True renders dark modules as spaces on a light background, which | ||
| # scans reliably in terminals with a dark color scheme. | ||
| qr.print_ascii(invert=True) | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| // WSLC-CustomContainer | ||
| // | ||
| // A barebones Windows console application that turns text (e.g. a URL) into a | ||
| // scannable QR code, rendered by a tiny Python tool running inside a *custom* | ||
| // Linux container. | ||
| // | ||
| // What makes this sample different from the others: it ships its own | ||
| // Containerfile that is built automatically as part of the normal build (F5) | ||
| // via the <WslcImage> item in WSLCCustomContainer.csproj. The built image is | ||
| // saved to customcontainer.tar next to the executable, and this app loads that | ||
| // local tar (no registry pull) before running the tool. | ||
| // | ||
| // dotnet run -- "https://aka.ms/wslc" | ||
|
|
||
| using Microsoft.WSL.Containers; | ||
|
|
||
| const string imageName = "customcontainer:latest"; | ||
|
|
||
| // The text to encode comes from the command line; fall back to a sample URL. | ||
| string text = args.Length > 0 ? string.Join(' ', args) : "https://aka.ms/wslc"; | ||
|
|
||
| // Everything lives beside the executable — no hard-coded absolute paths. The | ||
| // container image tar is produced next to the exe by the build. | ||
| string baseDir = AppContext.BaseDirectory; | ||
| string sessionPath = Path.Combine(baseDir, "WslcQrStorage"); | ||
| string imageTarPath = Path.Combine(baseDir, "customcontainer.tar"); | ||
|
|
||
| if (!File.Exists(imageTarPath)) | ||
| { | ||
| Console.Error.WriteLine($"[wslc] Image tar not found: {imageTarPath}"); | ||
| Console.Error.WriteLine("[wslc] Build the project first so the custom image is auto-built."); | ||
| return 1; | ||
| } | ||
|
|
||
| Session? session = null; | ||
| Container? container = null; | ||
| Process? process = null; | ||
|
|
||
| int exitCode = 1; | ||
| using var stopEvent = new ManualResetEventSlim(false); | ||
| var consoleLock = new object(); | ||
| using Stream stdout = Console.OpenStandardOutput(); | ||
| using Stream stderr = Console.OpenStandardError(); | ||
|
|
||
| void Write(Stream target, byte[] data) | ||
| { | ||
| lock (consoleLock) | ||
| { | ||
| target.Write(data, 0, data.Length); | ||
| target.Flush(); | ||
| } | ||
| } | ||
|
|
||
| try | ||
| { | ||
| // ---- Session ---- | ||
| Console.Error.WriteLine("[wslc] Creating session..."); | ||
| var sessionSettings = new SessionSettings("WSLCCustomContainer", sessionPath) | ||
| { | ||
| CpuCount = 2, | ||
| MemorySizeInMB = 2048, | ||
| VhdRequirements = new VhdOptions(string.Empty, 4UL * 1024 * 1024 * 1024, VhdType.Dynamic), | ||
| }; | ||
|
|
||
| session = new Session(sessionSettings); | ||
| session.Start(); | ||
|
|
||
| // ---- Load the locally built image from the tar (no registry pull) ---- | ||
| Console.Error.WriteLine($"[wslc] Loading image from {Path.GetFileName(imageTarPath)}..."); | ||
| session.LoadImage(imageTarPath); | ||
|
|
||
| // ---- Create & start container ---- | ||
| Console.Error.WriteLine("[wslc] Starting container..."); | ||
|
|
||
| // The init process keeps the container alive while we exec our tool. | ||
| var initProcess = new ProcessSettings | ||
| { | ||
| CommandLine = new List<string> { "/bin/sleep", "infinity" }, | ||
| }; | ||
|
|
||
| var containerSettings = new ContainerSettings(imageName) | ||
| { | ||
| InitProcess = initProcess, | ||
| EnableAutoRemove = true, | ||
| }; | ||
|
|
||
| container = session.CreateContainer(containerSettings); | ||
| container.Start(); | ||
|
|
||
| // ---- Exec the QR tool, passing the text to encode ---- | ||
| Console.Error.WriteLine($"[wslc] Generating QR code for: {text}"); | ||
| Console.Error.WriteLine(); | ||
|
|
||
| var processSettings = new ProcessSettings | ||
| { | ||
| CommandLine = new List<string> { "python", "/app/qr.py", text }, | ||
| OutputMode = ProcessOutputMode.Event, | ||
| }; | ||
|
|
||
| process = container.CreateProcess(processSettings); | ||
| process.OutputReceived += data => Write(stdout, data); | ||
| process.ErrorReceived += data => Write(stderr, data); | ||
| process.Exited += code => | ||
| { | ||
| exitCode = code; | ||
| stopEvent.Set(); | ||
| }; | ||
|
|
||
| process.Start(); | ||
| stopEvent.Wait(); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| Console.Error.WriteLine($"[wslc] Error: {ex.Message}"); | ||
| } | ||
|
Comment on lines
+112
to
+115
|
||
| finally | ||
| { | ||
| // ---- Cleanup (single path for both success and failure) ---- | ||
| Console.Error.WriteLine("[wslc] Shutting down..."); | ||
| process?.Dispose(); | ||
| if (container is not null) | ||
| { | ||
| try { container.Stop(Signal.SIGTERM, TimeSpan.FromSeconds(10)); } catch { /* best effort */ } | ||
|
|
||
| container.Dispose(); | ||
| } | ||
| if (session is not null) | ||
| { | ||
| try { session.Terminate(); } catch { /* best effort */ } | ||
|
|
||
| session.Dispose(); | ||
| } | ||
| } | ||
|
|
||
| return exitCode; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # WSLC-CustomContainer | ||
|
|
||
| A barebones C# console sample that turns text (e.g. a URL) into a **scannable QR | ||
| code** printed to your terminal — rendered by a tiny Python tool running inside | ||
| a **custom Linux container**, using the `Microsoft.WSL.Containers` SDK. | ||
|
|
||
| Unlike the other samples (which pull public images), this one ships its own | ||
| `Containerfile` that is **built automatically as part of the normal build (F5)** | ||
| via the SDK's `<WslcImage>` MSBuild item — no manual `docker`/`wslc` steps. The | ||
| app then loads that locally built image from a tar (no registry pull) and runs | ||
| `qr.py` inside the container. | ||
|
|
||
| ## How the auto-build works | ||
|
|
||
| `WSLCCustomContainer.csproj` declares: | ||
|
|
||
| ```xml | ||
| <WslcImage Include="customcontainer" | ||
| Image="customcontainer:latest" | ||
| Dockerfile="Container\Containerfile" | ||
| Context="Container" | ||
| Sources="Container" | ||
| TarLocation="$(OutDir)customcontainer.tar" /> | ||
| ``` | ||
|
|
||
| After `Build`, the package runs `wslc image build` + `wslc image save`, | ||
| producing `customcontainer.tar` next to the executable. The step is incremental: | ||
| it only re-runs when files under `Container\` change. (Requires the `wslc` CLI, | ||
| installed by WSL — `wsl --install --no-distribution`.) | ||
|
|
||
| ## Build | ||
|
|
||
| Requires the .NET 8 SDK. From this folder: | ||
|
|
||
| ``` | ||
| dotnet build -c Debug | ||
| ``` | ||
|
|
||
| ## Run | ||
|
|
||
| ``` | ||
| dotnet run -- "https://aka.ms/wslc" | ||
| ``` | ||
|
|
||
| Pass any text or URL; with no argument it encodes a sample URL. The QR is drawn | ||
| with Unicode blocks so you can scan it straight from the terminal. | ||
|
|
||
| ## Storage | ||
|
|
||
| Everything lives next to the executable (no absolute paths): `WslcQrStorage\` | ||
| holds the ephemeral session VHD, and `customcontainer.tar` is the auto-built | ||
| image. |
37 changes: 37 additions & 0 deletions
37
doc/samples/WSLC-CustomContainer/WSLCCustomContainer.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <RootNamespace>WSLCCustomContainer</RootNamespace> | ||
| <AssemblyName>qr</AssemblyName> | ||
| <!-- The native wslcsdk.dll shipped by the SDK package is architecture | ||
| specific, and the package picks which copy to deploy from PlatformTarget. | ||
| Default to x64 so a plain `dotnet build` / `dotnet run` works out of the | ||
| box. On an ARM64 device build with `dotnet build -p:PlatformTarget=ARM64`. --> | ||
| <PlatformTarget Condition="'$(PlatformTarget)' == ''">x64</PlatformTarget> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.WSL.Containers" Version="2.9.3" /> | ||
| </ItemGroup> | ||
|
|
||
| <!-- | ||
| Auto-build the custom container image as part of the normal build (F5). | ||
| The Microsoft.WSL.Containers package runs `wslc image build` + `wslc image | ||
| save` after Build, producing customcontainer.tar next to the executable. | ||
| The app loads that local tar at run time (no registry pull). The image step | ||
| is incremental: it only re-runs when files under Container\ change. | ||
| --> | ||
| <ItemGroup> | ||
| <WslcImage Include="customcontainer" | ||
| Image="customcontainer:latest" | ||
| Dockerfile="Container\Containerfile" | ||
| Context="Container" | ||
| Sources="Container" | ||
| TarLocation="$(OutDir)customcontainer.tar" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # WSLC-HelloWorld | ||
|
|
||
| The simplest [WSL Container API](https://aka.ms/wslc) sample, written in **C** | ||
| using the flat C API (`wslcsdk.h`). Running `helloworld.exe` starts a lightweight | ||
| WSL container from a small Linux image (`alpine:latest`), runs `echo` inside it, | ||
| and prints the output to your terminal. | ||
|
|
||
| ## Build | ||
|
|
||
| Open `WSLCHelloWorld.sln` in Visual Studio and build (x64), or from a developer | ||
| command prompt: | ||
|
|
||
| ``` | ||
| nuget restore WSLCHelloWorld.sln | ||
| msbuild WSLCHelloWorld.sln /p:Configuration=Debug /p:Platform=x64 | ||
| ``` | ||
|
|
||
| ## Run | ||
|
|
||
| ``` | ||
| x64\Debug\helloworld.exe | ||
| ``` | ||
|
|
||
| You should see `Hello, World from a WSL container!` printed to stdout. Progress | ||
| messages (`[wslc] ...`) go to stderr. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio Version 17 | ||
| VisualStudioVersion = 17.14.37027.9 d17.14 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WSLCHelloWorld", "WSLCHelloWorld.vcxproj", "{2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|ARM64 = Debug|ARM64 | ||
| Debug|x64 = Debug|x64 | ||
| Release|ARM64 = Release|ARM64 | ||
| Release|x64 = Release|x64 | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}.Debug|ARM64.ActiveCfg = Debug|ARM64 | ||
| {2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}.Debug|ARM64.Build.0 = Debug|ARM64 | ||
| {2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}.Debug|x64.ActiveCfg = Debug|x64 | ||
| {2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}.Debug|x64.Build.0 = Debug|x64 | ||
| {2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}.Release|ARM64.ActiveCfg = Release|ARM64 | ||
| {2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}.Release|ARM64.Build.0 = Release|ARM64 | ||
| {2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}.Release|x64.ActiveCfg = Release|x64 | ||
| {2B5C2E11-2C0A-4F1B-9F3D-7E8A1C2D3E4F}.Release|x64.Build.0 = Release|x64 | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| EndGlobal |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
sessionis IDisposable so we can useusinghereThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Same for containers, processes, and such)