Private/Init.ps1
|
# init: the identity values of a new machine, and their rewriting in a copy of a template. # Linux user name from the Windows one: lowercase, [a-z0-9_-] only function ConvertTo-LinuxUser([string]$Name) { $user = ($Name.ToLowerInvariant() -replace '[^a-z0-9_-]', '') if ($user -notmatch '^[a-z_]') { $user = "u$user" } return $user } # Is a TCP port listening on Windows (mirrored networking: shared with every distro)? function Test-WindowsPort([int]$Port) { [bool](Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue) } # Machines already described: their SSH ports and podman subnets function Get-UsedMachineValue { $ports = @(); $subnets = @() foreach ($name in Get-MachineName) { $o = Import-PowerShellDataFile -Path (Join-Path -Path (Get-MachineDir $name) -ChildPath 'machine.psd1') if ($o.Ssh.Port) { $ports += [int]$o.Ssh.Port } if ($o.PodmanSubnet) { $subnets += $o.PodmanSubnet } } @{ Ports = $ports; Subnets = $subnets } } function Get-FreeSshPort { $used = (Get-UsedMachineValue).Ports for ($p = 2222; $p -lt 2400; $p++) { if ($p -notin $used -and -not (Test-WindowsPort $p)) { return $p } } throw 'No free SSH port between 2222 and 2399: pass -Port' } function Get-FreePodmanSubnet { $used = (Get-UsedMachineValue).Subnets for ($n = 1; $n -lt 256; $n++) { $subnet = "10.89.$n.0/24" if ($subnet -notin $used) { return $subnet } } throw 'No free subnet in 10.89.0.0/16: pass -Subnet' } # Rewrites the identity values in a copy of a template ($Dir), where Get-Machine checks them. # $Values: Name, User, PublicKey (file name in ~\.ssh), Key (type and key), Port, Subnet, # Location. Returns the lines that still hold the template's user, to be reported. function Set-MachineIdentity([string]$Dir, [hashtable]$Values) { $psd1Path = Join-Path -Path $Dir -ChildPath 'machine.psd1' $userDataPath = Join-Path -Path $Dir -ChildPath 'user-data.yaml' $old = Import-PowerShellDataFile -Path $psd1Path $text = [IO.File]::ReadAllText($userDataPath) $oldUser = (Get-UserDataValue $text).User $u = $Values.User $esc = [regex]::Escape($oldUser) $text = $text -replace "(?m)^(\s*-\s*name:\s*)$esc(\s*)$", "`${1}$u`$2" $text = $text -replace '(?m)^(\s*default=)\S+', "`${1}$u" $text = $text -replace '(?m)^(\s*hostname=)\S+', "`${1}$($Values.Name)" $text = $text -replace "/home/$esc/", "/home/$u/" $text = $text -replace "(?m)^(\s*owner:\s*)${esc}:$esc(\s*)$", "`${1}${u}:$u`$2" if ($old.Ssh) { $text = $text -replace '(?m)^(\s*AllowUsers\s+)\S+', "`${1}$u" $text = $text -replace '(?m)^(\s*Port\s+)\d+(\s*)$', "`${1}$($Values.Port)`$2" $text = $text -replace '(?m)^(\s*-\s*)(ssh-[a-z0-9-]+|ecdsa-sha2-\S+)\s+\S+.*$', "`${1}$($Values.Key)" } if ($old.PodmanSubnet) { $text = $text -replace '(?m)^(\s*default_subnet\s*=\s*")[^"]*(")', "`${1}$($Values.Subnet)`$2" } [IO.File]::WriteAllText($userDataPath, $text) $psd1 = [IO.File]::ReadAllText($psd1Path) $psd1 = $psd1 -replace '(?m)^\s*Description\s*=.*\r?\n', '' $psd1 = $psd1 -replace "(?m)^(\s*Location\s*=\s*)'[^']*'", "`${1}'$($Values.Location)'" if ($old.Ssh) { $psd1 = $psd1 -replace '(Ssh\s*=\s*@\{[^}]*Port\s*=\s*)\d+', "`${1}$($Values.Port)" $psd1 = $psd1 -replace "(Ssh\s*=\s*@\{[^}]*PublicKey\s*=\s*)'[^']*'", "`${1}'$($Values.PublicKey)'" } if ($old.PodmanSubnet) { $psd1 = $psd1 -replace "(?m)^(\s*PodmanSubnet\s*=\s*)'[^']*'", "`${1}'$($Values.Subnet)'" } [IO.File]::WriteAllText($psd1Path, $psd1) if ($oldUser -eq $u) { return } foreach ($file in Get-ChildItem -Path $Dir -Recurse -File) { $n = 0 foreach ($line in [IO.File]::ReadAllLines($file.FullName)) { $n++ if ($line -match "\b$esc\b") { '{0}:{1}: {2}' -f [IO.Path]::GetRelativePath($Dir, $file.FullName), $n, $line.Trim() } } } } # Adds the provenance keys before the closing brace of machine.psd1, then the hash of the files function Add-MachineProvenance([string]$Dir, $Template) { $psd1Path = Join-Path -Path $Dir -ChildPath 'machine.psd1' $lines = @( " Template = '$($Template.Name)'" " TemplateSource = '$($Template.Source)'" ) if ($Template.Version) { $lines += " TemplateVersion = '$($Template.Version)'" } $lines += " TemplateHash = '__HASH__'" $psd1 = [IO.File]::ReadAllText($psd1Path) -replace "`r", '' $close = $psd1.LastIndexOf('}') $psd1 = $psd1.Substring(0, $close).TrimEnd() + "`n`n # copied by ws-creator init: list compares the files with TemplateHash`n" + ($lines -join "`n") + "`n}`n" [IO.File]::WriteAllText($psd1Path, $psd1) $psd1 = $psd1.Replace('__HASH__', (Get-MachineHash $Dir)) [IO.File]::WriteAllText($psd1Path, $psd1) } |