@@ -262,11 +262,114 @@ func (r *Controller) generateBootstrapDataForWorker(ctx context.Context, log log
262262 files = append (files , resolveCertsForIngress ... )
263263 }
264264
265+ commandsMap := make (map [provisioner.VarName ]string )
266+ var commands []string
267+
268+ if scope .Config .Spec .IsWindows {
269+ var winFiles []provisioner.File
270+ commands , winFiles = getWindowsCommands (scope )
271+ files = append (files , winFiles ... )
272+ } else {
273+ commands , commandsMap , err = getLinuxCommands (scope )
274+ if err != nil {
275+ return nil , fmt .Errorf ("error generating linux commands: %w" , err )
276+ }
277+ }
278+
279+ var (
280+ customUserData string
281+ vars map [provisioner.VarName ]string
282+ )
283+ if scope .Config .Spec .CustomUserDataRef != nil {
284+ customUserData , err = resolveContentFromFile (ctx , r .Client , scope .Cluster , scope .Config .Spec .CustomUserDataRef )
285+ if err != nil {
286+ return nil , fmt .Errorf ("error extracting the contents of the provided custom worker user data: %w" , err )
287+ }
288+ vars = commandsMap
289+ }
290+
291+ return scope .provisioner .ToProvisionData (& provisioner.InputProvisionData {
292+ Files : files ,
293+ Commands : commands ,
294+ CustomUserData : customUserData ,
295+ Vars : vars ,
296+ })
297+ }
298+
299+ func getWindowsCommands (scope * Scope ) ([]string , []provisioner.File ) {
300+ //if scope.Config.Spec.K0sInstallDir == "/usr/local/bin" {
301+ // scope.Config.Spec.K0sInstallDir = "C:\\bootstrap"
302+ //}
303+ k0sPath := filepath .Join (scope .Config .Spec .K0sInstallDir , "k0s.exe" )
304+
305+ var files []provisioner.File
306+ files = append (files , provisioner.File {
307+ Path : `C:\bootstrap\k0s_install.ps1` ,
308+ Permissions : "0644" ,
309+ Content : fmt .Sprintf (`Write-Host "=== Checking Windows Containers feature using DISM ==="
310+
311+ function Is-FeatureEnabled {
312+ param([string]$Name)
313+ $output = dism.exe /online /Get-Features
314+ return $output -match "$Name\s+:\s+Enabled"
315+ }
316+
317+ $featureName = "Containers"
318+
319+ if (Is-FeatureEnabled $featureName) {
320+ Write-Host "$featureName is already enabled. Nothing to do."
321+ } else {
322+ Write-Host "$featureName is not enabled. Installing..."
323+ $result = dism.exe /online /Enable-Feature /FeatureName:$featureName /All /NoRestart
324+ $exitCode = $LASTEXITCODE
325+ Write-Host "DISM exit code: $exitCode"
326+
327+ if ($exitCode -eq 0) {
328+ Write-Host "Feature installed successfully, no reboot required."
329+ } elseif ($exitCode -eq 3010) {
330+ Write-Host "Feature installed successfully, reboot required."
331+ Restart-Computer -Force
332+ } else {
333+ throw "DISM failed with exit code $exitCode"
334+ }
335+ }
336+
337+ # --- Download and run k0s ---
338+ Write-Host "=== Downloading k0s binary ==="
339+
340+ $k0sUrl = "https://get.k0sproject.io/%s/k0s-%s-amd64.exe" # PoC supporting only amd64
341+ $dest = "%s"
342+
343+ Invoke-WebRequest -Uri $k0sUrl -OutFile $dest -UseBasicParsing
344+
345+ Write-Host "=== Executing k0s to check version ==="
346+ & $dest --version` , scope .Config .Spec .Version , scope .Config .Spec .Version , k0sPath ),
347+ })
348+
349+ commands := scope .Config .Spec .PreStartCommands
350+ // Download and enable containers and k0s bootstrap script
351+ commands = append (commands , `powershell.exe -NoProfile -NonInteractive -File "C:\bootstrap\k0s_install.ps1"` )
352+ // TODO: implement ingress support for Windows
353+ //commands = append(commands, ingressCommands...)
354+
355+ installCmdParts := []string {
356+ fmt .Sprintf (`powershell.exe -NoProfile -NonInteractive -Command "& { %s install worker --token-file %s }"` , k0sPath , scope .Config .GetJoinTokenPath ()),
357+ }
358+ installCmdParts = append (installCmdParts , mergeExtraArgs (scope .Config .Spec .Args , scope .ConfigOwner , true , scope .Config .Spec .UseSystemHostname )... )
359+
360+ commands = append (commands , fmt .Sprintf (`powershell.exe -NoProfile -NonInteractive -Command "& { %s }` , strings .Join (installCmdParts , " " )))
361+ commands = append (commands , fmt .Sprintf (`powershell.exe -NoProfile -NonInteractive -Command "& { & %s start }` , k0sPath ))
362+ commands = append (commands , scope .Config .Spec .PostStartCommands ... )
363+
364+ return commands , files
365+ }
366+
367+ func getLinuxCommands (scope * Scope ) ([]string , map [provisioner.VarName ]string , error ) {
265368 commandsMap := make (map [provisioner.VarName ]string )
266369
267370 downloadCommands , err := util .DownloadCommands (scope .Config .Spec .PreInstalledK0s , scope .Config .Spec .DownloadURL , scope .Config .Spec .Version , scope .Config .Spec .K0sInstallDir )
268371 if err != nil {
269- return nil , fmt .Errorf ("error generating download commands: %w" , err )
372+ return nil , nil , fmt .Errorf ("error generating download commands: %w" , err )
270373 }
271374 installCmd := createInstallCmd (scope )
272375
@@ -288,24 +391,7 @@ func (r *Controller) generateBootstrapDataForWorker(ctx context.Context, log log
288391 // https://cluster-api.sigs.k8s.io/developer/providers/contracts/bootstrap-config#sentinel-file
289392 commands = append (commands , "mkdir -p /run/cluster-api && touch /run/cluster-api/bootstrap-success.complete" )
290393
291- var (
292- customUserData string
293- vars map [provisioner.VarName ]string
294- )
295- if scope .Config .Spec .CustomUserDataRef != nil {
296- customUserData , err = resolveContentFromFile (ctx , r .Client , scope .Cluster , scope .Config .Spec .CustomUserDataRef )
297- if err != nil {
298- return nil , fmt .Errorf ("error extracting the contents of the provided custom worker user data: %w" , err )
299- }
300- vars = commandsMap
301- }
302-
303- return scope .provisioner .ToProvisionData (& provisioner.InputProvisionData {
304- Files : files ,
305- Commands : commands ,
306- CustomUserData : customUserData ,
307- Vars : vars ,
308- })
394+ return commands , commandsMap , nil
309395}
310396
311397func (r * Controller ) getK0sToken (ctx context.Context , scope * Scope ) (string , error ) {
0 commit comments