diff --git a/.github/actions/download-rootfs/action.yaml b/.github/actions/download-rootfs/action.yaml index d6ed8125..7cd5d69b 100644 --- a/.github/actions/download-rootfs/action.yaml +++ b/.github/actions/download-rootfs/action.yaml @@ -1,5 +1,5 @@ name: Download Ubuntu WSL Rootfs -description: Download the latest Rootfs for a particular release of Ubuntu WSL +description: Download the latest Rootfs for a particular release of Ubuntu WSL inputs: distros: @@ -93,6 +93,8 @@ runs: $file = ".\${name}.tar.gz" + Write-Output "Saving $($url[0..19] -join '')***$($url[-19..-1] -join '') to $file" + & ${{ github.action_path }}\download-rootfs.ps1 -Path "${file}" -URL "$url" if ( ! $? ) { $allSuccess = $false diff --git a/.github/actions/download-rootfs/download-rootfs.ps1 b/.github/actions/download-rootfs/download-rootfs.ps1 index d37e6e2e..b1f24e73 100644 --- a/.github/actions/download-rootfs/download-rootfs.ps1 +++ b/.github/actions/download-rootfs/download-rootfs.ps1 @@ -63,9 +63,14 @@ function Test-Checksums { # Parse checksum file $image = $URL.Segments[$Url.Segments.Length - 1] - $pattern = "(\w+) ${image}" + $pattern = "(\w+) \*${image}" + $matched = (Select-String -Pattern "${pattern}" -Path "${Checksums}").Matches + if ($null -eq $matched -or $matched.Count -eq 0) { + Write-Warning "Could not find $image in checksums file" + return $false + } - $newSHA256 = (Select-String -Pattern "${pattern}" -Path "${Checksums}").Matches[0].Groups[1] + $newSHA256 = $matched[0].Groups[1] if ($newSHA256 -eq "") { Write-Warning "Could not find $image in checksums file" return $false diff --git a/.github/workflows/build-pr.yaml b/.github/workflows/build-pr.yaml index 51099d3b..09e5455f 100644 --- a/.github/workflows/build-pr.yaml +++ b/.github/workflows/build-pr.yaml @@ -15,7 +15,7 @@ jobs: runs-on: windows-latest if: ${{ !github.event.pull_request.draft }} env: - rootfs64: 'http://cloud-images.ubuntu.com/wsl/jammy/current/ubuntu-jammy-wsl-amd64-wsl.rootfs.tar.gz' + rootfs64: 'http://cloud-images.ubuntu.com/wsl/jammy/current/ubuntu-jammy-wsl-amd64-ubuntu.rootfs.tar.gz' workDir: 'C:/Temp/builddir' steps: - name: Checkout WSL diff --git a/DistroLauncher/Ubuntu/InitTasks.cpp b/DistroLauncher/Ubuntu/InitTasks.cpp index a28296e5..05c96905 100644 --- a/DistroLauncher/Ubuntu/InitTasks.cpp +++ b/DistroLauncher/Ubuntu/InitTasks.cpp @@ -35,9 +35,14 @@ bool CheckInitTasks(WslApiLoader& api, bool checkDefaultUser) { namespace { void waitForInitTasks(WslApiLoader& api) { + // Wait for cloud-init to finish if systemd and its service is enabled. + static constexpr wchar_t script[] = LR"( +if status=$(LANG=C systemctl is-system-running 2>/dev/null) || [ "${status}" != "offline" ] && systemctl is-enabled --quiet cloud-init.service 2>/dev/null; then + cloud-init status --wait > /dev/null 2>&1 || true +fi +)"; DWORD exitCode = -1; - // Try running cloud-init unconditionally, but avoid printing to console. - auto hr = api.WslLaunchInteractive(L"cloud-init status --wait >/dev/null 2>&1", FALSE, &exitCode); + auto hr = api.WslLaunchInteractive(script, FALSE, &exitCode); if (FAILED(hr)) { Helpers::PrintErrorMessage(hr); return; diff --git a/e2e/go.work b/e2e/go.work index a6385f3f..729350fd 100644 --- a/e2e/go.work +++ b/e2e/go.work @@ -1,5 +1,5 @@ -go 1.22.0 +go 1.23.0 -toolchain go1.22.5 +toolchain go1.23.2 use ./launchertester diff --git a/e2e/launchertester/basic_setup_test.go b/e2e/launchertester/basic_setup_test.go index 61188d6e..819ff217 100644 --- a/e2e/launchertester/basic_setup_test.go +++ b/e2e/launchertester/basic_setup_test.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "os" + "os/exec" "path/filepath" "strconv" "strings" @@ -26,7 +27,7 @@ func TestBasicSetup(t *testing.T) { require.NoErrorf(t, err, "Unexpected error installing: %s\n%v", out, err) testCases := map[string]func(t *testing.T){ - "SystemdEnabled": testSystemdEnabled, + "SystemdEnabled": testSystemdIsEnabled, "SystemdUnits": testSystemdUnits, "CorrectUpgradePolicy": testCorrectUpgradePolicy, "UpgradePolicyIdempotent": testUpgradePolicyIdempotent, @@ -45,6 +46,7 @@ func TestSetupWithCloudInit(t *testing.T) { testCases := map[string]struct { install_root bool withRegistryUser string + withWSL1 bool wantUser string wantFile string }{ @@ -56,6 +58,8 @@ func TestSetupWithCloudInit(t *testing.T) { "With only remote users": {wantUser: "testmail"}, "With broken passwd file": {wantUser: "testmail"}, "Without checking user": {install_root: true, wantUser: "root", wantFile: "/home/testuser/with_default_user.done"}, + // TODO: Investigate why this causes the CI VM to crash and reenable it. + //"Do not block on WSL1": {install_root: true, withWSL1: true, wantUser: "root"}, } home, err := os.UserHomeDir() @@ -77,6 +81,14 @@ func TestSetupWithCloudInit(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { + if tc.withWSL1 { + require.NoError(t, exec.Command("wsl.exe", "--set-default-version", "1").Run(), "Setup: Cannot set WSL1 as default version") + require.NoError(t, exec.Command("wsl.exe", "--shutdown").Run(), "Setup: Cannot enforce WSL1 as default version y shutting down the VM") + t.Cleanup(func() { + t.Log("Cleaning up: Setting WSL2 back as default version") + require.NoError(t, exec.Command("wsl.exe", "--set-default-version", "2").Run(), "Setup: Cannot set WSL2 back as default version") + }) + } wslSetup(t) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) @@ -158,7 +170,7 @@ func TestSetupWithCloudInit(t *testing.T) { // launcher checks for the default user. Either way the user assertion in the end of this test case will work as exoected. require.NoError(t, <-registrySet, "Setup: Failed to set default user via GoWSL/registry") - testSystemdEnabled(t) + testSystemdEnabled(t, !tc.withWSL1) testInteropIsEnabled(t) if len(tc.wantFile) > 0 { testFileExists(t, tc.wantFile) diff --git a/e2e/launchertester/go.mod b/e2e/launchertester/go.mod index 298ae6ed..a7c50545 100644 --- a/e2e/launchertester/go.mod +++ b/e2e/launchertester/go.mod @@ -1,8 +1,6 @@ module github.com/ubuntu/wsl/e2e/launchertester -go 1.22.0 - -toolchain go1.22.3 +go 1.23.0 require ( github.com/stretchr/testify v1.9.0 diff --git a/e2e/launchertester/test_cases.go b/e2e/launchertester/test_cases.go index 829967dd..f65dd58d 100644 --- a/e2e/launchertester/test_cases.go +++ b/e2e/launchertester/test_cases.go @@ -26,8 +26,13 @@ func testDefaultUser(t *testing.T, expected string) { //nolint: thelper, this is require.Equal(t, expected, got, "Default user should be %s, got %s", expected, got) } -// testSystemdEnabled ensures systemd was enabled. -func testSystemdEnabled(t *testing.T) { //nolint: thelper, this is a test +// testSystemdIsEnabled ensures systemd was enabled. +func testSystemdIsEnabled(t *testing.T) { //nolint: thelper, this is a test + testSystemdEnabled(t, true) +} + +// testSystemdEnabled checks whether systemd was enabled or not as expected. +func testSystemdEnabled(t *testing.T, wantEnabled bool) { //nolint: thelper, this is a test ctx, cancel := context.WithTimeout(context.Background(), systemdBootTimeout) defer cancel() @@ -36,6 +41,11 @@ func testSystemdEnabled(t *testing.T) { //nolint: thelper, this is a test return // Success: Non-deterministic } + if !wantEnabled { + require.Contains(t, string(out), "offline", "systemd output should be offline") + return + } + // Only acceptable alternative is "degraded" var target *exec.ExitError require.ErrorAs(t, err, &target, "systemctl is-system-running: Only acceptable error is ExitError (caused by systemd being degraded)") diff --git a/e2e/launchertester/testdata/TestSetupWithCloudInit/do_not_block_on_wsl1 b/e2e/launchertester/testdata/TestSetupWithCloudInit/do_not_block_on_wsl1 new file mode 100644 index 00000000..fff82286 --- /dev/null +++ b/e2e/launchertester/testdata/TestSetupWithCloudInit/do_not_block_on_wsl1 @@ -0,0 +1,20 @@ +#cloud-config +users: + - name: nontestuser + groups: sudo + shell: /bin/bash + sudo: ALL=(ALL) NOPASSWD:ALL + - name: testuser + groups: sudo + shell: /bin/bash + sudo: ALL=(ALL) NOPASSWD:ALL + +write_files: + - path: /etc/wsl.conf + append: true + content: | + [user] + default=testuser + +runcmd: + - touch /home/testuser/with_default_user.done diff --git a/wsl-builder/common/releasesinfo.go b/wsl-builder/common/releasesinfo.go index 74c11860..50eeef2e 100644 --- a/wsl-builder/common/releasesinfo.go +++ b/wsl-builder/common/releasesinfo.go @@ -235,20 +235,19 @@ func (w *WslReleaseInfo) refreshedTerminalProfileID() error { // RootfsURL returns the URL to the rootfs tarball for the given architecture. // The base image name is in the format: -// ubuntu--wsl--.rootfs.tar.gz -// before 24.04, upgrade-flavor is always "wsl" +// ubuntu--wsl--.rootfs.tar.gz +// before 22.04, upgrade-type is always "wsl" // otherwise: -// - ubuntu -> wsl (upgade: lts) +// - ubuntu -> wsl (upgrade: lts) // - ubuntupreview -> preview (upgrade: always) -// - ubuntu24.04lts -> 24.04lts (upgrade: never) +// - ubuntu24.04lts -> ubuntu24.04lts (upgrade: never) func (w *WslReleaseInfo) RootfsURL(arch string) string { imageBaseName := fmt.Sprintf("ubuntu-%s-wsl-%s", w.CodeName, arch) suffix := "wsl" // We have multiple versions with different upgrade policy management. Pick the one based on the app name. - if strings.Compare(w.BuildVersion, "2404") >= 0 { - // The CPC publisher strip the "ubuntu" prefix - suffix = strings.TrimPrefix(strings.ToLower(w.AppID), "ubuntu") + if strings.Compare(w.BuildVersion, "2204") >= 0 { + suffix = strings.ToLower(w.AppID) if suffix == "" { suffix = "wsl" }