Wsl-Image/Wsl-Image.Builtins.ps1
# Copyright 2022 Antoine Martin # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. [Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage()] $WslImageSources = @{ [WslImageSource]::Incus = "https://raw.githubusercontent.com/antoinemartin/PowerShell-Wsl-Manager/refs/heads/rootfs/incus.rootfs.json" [WslImageSource]::Builtins = "https://raw.githubusercontent.com/antoinemartin/PowerShell-Wsl-Manager/refs/heads/rootfs/builtins.rootfs.json" } [Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage()] $WslImageCacheFileCache = @{} function Get-WslBuiltinImage { <# .SYNOPSIS Gets the list of builtin WSL root filesystems from the remote repository. .DESCRIPTION The Get-WslBuiltinImage cmdlet fetches the list of available builtin WSL root filesystems from the official PowerShell-Wsl-Manager repository. This provides an up-to-date list of supported images that can be used to create WSL instances. The cmdlet downloads a JSON file from the remote repository and converts it into WslImage objects that can be used with other Wsl-Manager commands. The cmdlet implements intelligent caching with ETag support to reduce network requests and improve performance. Cached data is valid for 24 hours unless the -Sync parameter is used to force a refresh. .PARAMETER Source Specifies the source type for fetching root filesystems. Must be of type WslImageSource. Defaults to [WslImageSource]::Builtins which points to the official repository of builtin images. .PARAMETER Sync Forces a synchronization with the remote repository, bypassing the local cache. When specified, the cmdlet will always fetch the latest data from the remote repository regardless of cache validity period and ETag headers. .EXAMPLE Get-WslBuiltinImage Gets all available builtin root filesystems from the default repository source. .EXAMPLE Get-WslBuiltinImage -Source Builtins Explicitly gets builtin root filesystems from the builtins source. .EXAMPLE Get-WslBuiltinImage -Sync Forces a fresh download of all builtin root filesystems, ignoring local cache and ETag headers. .INPUTS None. You cannot pipe objects to Get-WslBuiltinImage. .OUTPUTS WslImage[] Returns an array of WslImage objects representing the available builtin images. .NOTES - This cmdlet requires an internet connection to fetch data from the remote repository - The source URL is determined by the WslImageSources hashtable using the Source parameter - Returns null if the request fails or if no images are found - The Progress function is used to display download status during network operations - Uses HTTP ETag headers for efficient caching and conditional requests (304 responses) - Cache is stored in the WslImage base path with filename from the URI - Cache validity period is 24 hours (86400 seconds) - In-memory cache (WslImageCacheFileCache) is used alongside file-based cache - ETag support allows for efficient cache validation without re-downloading unchanged data .LINK https://github.com/antoinemartin/PowerShell-Wsl-Manager .COMPONENT Wsl-Manager .FUNCTIONALITY WSL Distribution Management #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [WslImageSource]$Source = [WslImageSource]::Builtins, [switch]$Sync ) $Uri = [System.Uri]$WslImageSources[$Source] $CacheFilename = $Uri.Segments[-1] $cacheFile = Join-Path -Path ([WslImage]::BasePath) -ChildPath $CacheFilename $currentTime = [int][double]::Parse((Get-Date -UFormat %s)) $cacheValidDuration = 86400 # 24 hours in seconds $hasCacheFile = $WslImageCacheFileCache.ContainsKey($Source) -or (Test-Path $cacheFile) # Populate cache if not already done if ($hasCacheFile -and -not $WslImageCacheFileCache.ContainsKey($Source)) { Write-Verbose "Loading cache from file $cacheFile" $cache = Get-Content -Path $cacheFile | ConvertFrom-Json $WslImageCacheFileCache[$Source] = $cache $cache.builtins = $cache.builtins | ForEach-Object { [WslImage]::new($_) } } if (-not $Sync -and $hasCacheFile) { $cache = $WslImageCacheFileCache[$Source] Write-Verbose "Cache lastUpdate: $($cache.lastUpdate) Current time $($currentTime), diff $($currentTime - $cache.lastUpdate)" if (($currentTime - $cache.lastUpdate) -lt $cacheValidDuration -and $null -ne $cache.builtins) { return $cache.builtins | Foreach-Object { $_.RefreshState() } } } try { $headers = @{} if ($hasCacheFile) { $cache = $WslImageCacheFileCache[$Source] if ($cache.etag) { $headers = @{ "If-None-Match" = $cache.etag[0] } } } Progress "Fetching $($Source) images from: $Uri" $prevProgressPreference = $global:ProgressPreference $global:ProgressPreference = 'SilentlyContinue' $response = try { Invoke-WebRequest -Uri $Uri -Headers $headers -UseBasicParsing } catch { $_.Exception.Response } finally { $global:ProgressPreference = $prevProgressPreference } if ($response.StatusCode -eq 304) { Write-Verbose "No updates found. Extending cache validity." $cache.lastUpdate = $currentTime $result = $cache.builtins | Foreach-Object { $_.RefreshState() } $cache | ConvertTo-Json -Depth 10 | Set-Content -Path $cacheFile -Force return $result } if (-not $response.Content) { throw [WslManagerException]::new("The response content from $Uri is null. Please check the URL or network connection.") } $etag = $response.Headers["ETag"] $imagesObjects = $response.Content | ConvertFrom-Json $images = $imagesObjects | ForEach-Object { [WslImage]::new($_) } $cacheData = @{ URl = $Uri lastUpdate = $currentTime etag = $etag builtins = $images } $WslImageCacheFileCache[$Source] = $cacheData $cacheData | ConvertTo-Json -Depth 10 | Set-Content -Path $cacheFile -Force return $images } catch { if ($_.Exception -is [WslManagerException]) { throw $_.Exception } Write-Error "Failed to retrieve builtin root filesystems: $($_.Exception.Message)" return $null } } |