Skip to content

Commit 5bb66f5

Browse files
committed
feat: add container engine configuration (docker/podman)
1 parent 4ccdfe0 commit 5bb66f5

10 files changed

Lines changed: 75 additions & 32 deletions

main.ps1

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ param(
1111
throw "The specified file path '$fullPath' does not exist."
1212
}
1313

14-
$config = Import-PowerShellDataFile -Path $fullPath
14+
try {
15+
$config = Import-PowerShellDataFile -Path $fullPath -ErrorAction Stop
16+
} catch {
17+
Write-Warning "Failed to import: $_ Please ensure the file is a valid PowerShell data file."
18+
}
1519

1620
# Prevent default password usage
1721
if ($config.piholePassword -eq "admin") {
@@ -99,6 +103,13 @@ param(
99103
}
100104
}
101105

106+
# Validate container engine setting
107+
if ($config.ContainsKey('containerEngine')) {
108+
if ($config.containerEngine -isnot [string] -or [string]::IsNullOrWhiteSpace($config.containerEngine)) {
109+
throw "The 'containerEngine' in the configuration file is invalid. It should be a non-empty string (e.g. 'docker' or 'podman')."
110+
}
111+
}
112+
102113
return $true
103114
})]
104115
[string]$ConfigPath,
@@ -134,6 +145,19 @@ Start-Transcript -Path $data['logFile'] -Append
134145

135146
Import-Module ./main.psm1
136147

148+
# Set container engine from PSD1 configuration (default to 'docker' if not set)
149+
$engine = 'docker'
150+
if ($data.ContainsKey('containerEngine') -and $data['containerEngine'] -ne '') {
151+
$engine = $data['containerEngine']
152+
}
153+
try {
154+
Set-ContainerEngine -Engine $engine
155+
Write-Host "Container engine set to: $(Get-ContainerEngine)"
156+
}
157+
catch {
158+
Write-Host "Failed to set container engine: $_" -ForegroundColor Yellow
159+
}
160+
137161
# Ensure Ansible is available locally
138162
Install-Ansible
139163

main.psd1.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
# Prefix for all container names in this stack. Only containers with this prefix will be managed.
77
stackName = "Pi-DNStack"
88

9+
# Select container engine
10+
# Officially supported: "docker". Other engines (e.g., "podman") may work if already installed, but "docker" is the only officially tested engine.
11+
containerEngine = "docker"
12+
913
# Container restart policy.
1014
# For more details, visit: https://docs.docker.com/engine/containers/start-containers-automatically/#use-a-restart-policy
1115
# Options:
@@ -144,7 +148,7 @@
144148
"https://raw.githubusercontent.com/mike-trewartha/Pi-hole-Talos-Threat-Blocklist/refs/heads/main/talos-threats.list",
145149
"https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt",
146150
"https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txt",
147-
"https://someonewhocares.org/hosts/hosts",
151+
"https://someonewhocares.org/hosts/hosts"
148152
)
149153
#endregion
150154

main.psm1

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -155,20 +155,23 @@ function ConfigDifferent {
155155
function Get-CurrentContainerConfig {
156156
param (
157157
[Parameter(Mandatory = $true)]
158-
[string]$ContainerName
158+
[string]$ContainerName,
159+
[Parameter(Mandatory = $false)]
160+
[string]$containerEngine = "docker"
159161
)
160162

161-
if ($null -eq (docker ps --filter "name=$name" --format "{{.Names}}")) {
163+
# Corrected: Use Invoke-Expression for the ps command to avoid parser errors with the variable
164+
if ($null -eq (Invoke-Expression "$containerEngine ps --filter 'name=$ContainerName' --format '{{.Names}}'")) {
162165
return $null
163166
}
164167

165-
[string]$image = docker inspect --format='{{.Config.Image}}' $ContainerName
168+
[string]$image = Invoke-Expression "$containerEngine inspect --format='{{.Config.Image}}' $ContainerName"
166169
# with help of https://chatgpt.com/share/6766e1f1-a8a0-8011-b306-59da137b7359
167-
[string]$ports = docker inspect --format '{{range $p, $conf := .NetworkSettings.Ports}}{{if $conf}}{{(index $conf 0).HostPort}}:{{(index $conf 0).HostPort}} {{end}}{{end}}' $ContainerName
168-
[string]$volumes = docker inspect --format '{{range .Mounts}}{{if .Source}}{{.Source}}:{{.Destination}} {{end}}{{end}}' $ContainerName
169-
[string]$environmentVariables = docker inspect --format='{{range .Config.Env}}{{.}}{{end}}' $ContainerName
170-
[string]$restartPolicy = docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' $ContainerName
171-
[string]$containerNetwork = docker inspect --format '{{.HostConfig.NetworkMode}}' $ContainerName
170+
[string]$ports = Invoke-Expression "$containerEngine inspect --format '{{range `$p, `$conf := .NetworkSettings.Ports}}{{if `$conf}}{{(index `$conf 0).HostPort}}:{{(index `$conf 0).HostPort}} {{end}}{{end}}' $ContainerName"
171+
[string]$volumes = Invoke-Expression "$containerEngine inspect --format '{{range .Mounts}}{{if .Source}}{{.Source}}:{{.Destination}} {{end}}{{end}}' $ContainerName"
172+
[string]$environmentVariables = Invoke-Expression "$containerEngine inspect --format='{{range .Config.Env}}{{.}}{{end}}' $ContainerName"
173+
[string]$restartPolicy = Invoke-Expression "$containerEngine inspect --format='{{.HostConfig.RestartPolicy.Name}}' $ContainerName"
174+
[string]$containerNetwork = Invoke-Expression "$containerEngine inspect --format '{{.HostConfig.NetworkMode}}' $ContainerName"
172175

173176
[hashtable]$currentConfig = @{
174177
Image = $image
@@ -212,12 +215,14 @@ function Deploy-Container {
212215
[Parameter(Mandatory = $false)]
213216
[string]$flags = "",
214217
[Parameter(Mandatory = $false)]
215-
[string]$extra = ""
218+
[string]$extra = "",
219+
[Parameter(Mandatory = $false)]
220+
[string]$containerEngine = "docker"
216221
)
217222

218223
try {
219224
# declarative checks
220-
$currentConfig = Get-CurrentContainerConfig -ContainerName $name
225+
$currentConfig = Get-CurrentContainerConfig -ContainerName $name -containerEngine $containerEngine
221226
$envs = @()
222227
if ($name -match "pihole") {
223228
$envs += "FTLCONF_webserver_api_password=$($data['piholePassword'])"
@@ -229,14 +234,14 @@ function Deploy-Container {
229234
# checks if there are configuration differences between the current and desired state
230235
elseif (ConfigDifferent -CurrentConfig $currentConfig -image $image -ports $ports -volumes $volumes -envs $envs -restartPolicy $restartPolicy -containerNetwork $network) {
231236
Write-Host "Container $name exists but configuration differs. Replacing container..."
232-
docker rm -f $name
237+
Invoke-Expression "$containerEngine rm -f $name"
233238
}
234239
else {
235240
Write-Host "Container $name is already deployed with the correct configuration."
236241
return
237242
}
238243

239-
[string]$command = "docker run -d --name $name"
244+
[string]$command = "$containerEngine run -d --name $name"
240245
if ($restartPolicy) {
241246
$command += " --restart $restartPolicy"
242247
}
@@ -292,7 +297,8 @@ function Deploy-Pihole {
292297
"$($data['piholeDnsPort']):53"
293298
) `
294299
-volumes $data['piholeVolumes'] `
295-
-flags "$($data['piholeFlags']) -e FTLCONF_webserver_api_password=$password"
300+
-flags "$($data['piholeFlags']) -e FTLCONF_webserver_api_password=$password" `
301+
-containerEngine $data['containerEngine']
296302
}
297303

298304
<#
@@ -318,7 +324,8 @@ function Deploy-Unbound {
318324
-restartPolicy $data['restartPolicy'] `
319325
-ports @("$($data['unboundPort']):53") `
320326
-volumes $data['unboundVolumes'] `
321-
-flags $data['unboundFlags']
327+
-flags $data['unboundFlags'] `
328+
-containerEngine $data['containerEngine']
322329
}
323330

324331
<#
@@ -348,7 +355,8 @@ function Deploy-Cloudflared {
348355
-ports @("$($data['cloudflaredPort']):5053") `
349356
-volumes $data['cloudflaredVolumes'] `
350357
-flags $data['cloudflaredFlags'] `
351-
-extra $extra
358+
-extra $extra `
359+
-containerEngine $data['containerEngine']
352360
}
353361

354362
<#
@@ -370,12 +378,12 @@ function Remove-OldContainers {
370378
if (-Not $data['unboundEnabled']) {
371379
Write-Host "Removing old unbound container if present..."
372380
# remove the container silently
373-
docker rm -f "$($data['stackName'])_unbound" 2>&1 >/dev/null
381+
Invoke-Expression "$($data['containerEngine']) rm -f $($data['stackName'])_unbound 2>&1 >/dev/null"
374382
}
375383

376384
if (-Not $data['cloudflaredEnabled']) {
377385
Write-Host "Removing old cloudflared container if present..."
378-
docker rm -f "$($data['stackName'])_cloudflared" 2>&1 >/dev/null
386+
Invoke-Expression "$($data['containerEngine']) rm -f $($data['stackName'])_cloudflared 2>&1 >/dev/null"
379387
}
380388
}
381389
#endregion
@@ -409,7 +417,7 @@ function Set-PiholeConfiguration {
409417
[string]$IP = Invoke-CommandWithCheck "hostname -I | awk '{print `$1}'"
410418
}
411419
else {
412-
[string]$IP = Invoke-CommandWithCheck "docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ""$($data['stackName'])_$container"""
420+
[string]$IP = Invoke-CommandWithCheck "$($data['containerEngine']) inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ""$($data['stackName'])_$container"""
413421
}
414422
if (-not $IP) {
415423
throw "Failed to get IP address for container $container"
@@ -442,7 +450,7 @@ function Set-PiholeConfiguration {
442450
echo 'PIHOLE_DNS_$nr=$dnsNetwork' >> /etc/pihole/setupVars.conf
443451
fi
444452
"@
445-
docker exec "$($data['stackName'])_pihole" /bin/bash -c $command
453+
Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole /bin/bash -c '$command'"
446454
}
447455

448456
Write-Host "Waiting for Pi-hole to be healthy..."
@@ -451,7 +459,7 @@ function Set-PiholeConfiguration {
451459
$healthy = $false
452460
while ($retryCount -lt $maxRetries -and -not $healthy) {
453461
Start-Sleep -Seconds 10
454-
$status = docker inspect --format='{{.State.Health.Status}}' "$($data['stackName'])_pihole"
462+
$status = Invoke-Expression "$($data['containerEngine']) inspect --format='{{.State.Health.Status}}' $($data['stackName'])_pihole"
455463
if ($status -eq "healthy") {
456464
$healthy = $true
457465
}
@@ -501,10 +509,10 @@ function Set-PiholeConfiguration {
501509
# with help of https://chatgpt.com/share/676050bc-c4bc-8011-aeec-5efcce256287
502510
do {
503511
[string]$command = "sed -i '/^PIHOLE_DNS_$nr=/d' /etc/pihole/setupVars.conf"
504-
[string]$output = docker exec "$($data['stackName'])_pihole" /bin/bash -c "grep '^PIHOLE_DNS_$nr=' /etc/pihole/setupVars.conf"
512+
[string]$output = Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole /bin/bash -c `"grep '^PIHOLE_DNS_$nr=' /etc/pihole/setupVars.conf`""
505513

506514
if ($output) {
507-
docker exec "$($data['stackName'])_pihole" /bin/bash -c $command
515+
Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole /bin/bash -c '$command'"
508516
$nr++
509517
}
510518
} while ($output)
@@ -521,19 +529,19 @@ else
521529
echo 'DNSSEC=$dnssecValue' >> /etc/pihole/setupVars.conf
522530
fi
523531
"@
524-
docker exec "$($data['stackName'])_pihole" /bin/bash -c $command
532+
Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole /bin/bash -c '$command'"
525533
#endregion
526534

527535
#region adlist
528536
# update gravity in case the db is not yet created
529-
docker exec "$($data['stackName'])_pihole" pihole updateGravity 2>&1 >/dev/null
537+
Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole pihole updateGravity 2>&1 >/dev/null"
530538
# remove deprecated adlists
531-
$existingAdlists = docker exec "$($data['stackName'])_pihole" pihole-FTL sqlite3 /etc/pihole/gravity.db "SELECT address FROM adlist;"
539+
$existingAdlists = Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole pihole-FTL sqlite3 /etc/pihole/gravity.db 'SELECT address FROM adlist;'"
532540
$existingAdlists = $existingAdlists -split "`n"
533541

534542
foreach ($adlist in $existingAdlists) {
535543
if ($adlist -notin $data['adlists']) {
536-
docker exec "$($data['stackName'])_pihole" pihole-FTL sqlite3 /etc/pihole/gravity.db "DELETE FROM adlist WHERE address='$adlist';"
544+
Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole pihole-FTL sqlite3 /etc/pihole/gravity.db 'DELETE FROM adlist WHERE address=`'$adlist`';'"
537545
}
538546
}
539547
# add new ones
@@ -542,10 +550,10 @@ fi
542550
INSERT OR IGNORE INTO adlist (address, enabled, date_added, date_modified, comment, date_updated, number, invalid_domains, status)
543551
VALUES ('$adlist', 1, cast(strftime('%s', 'now') as int), cast(strftime('%s', 'now') as int), 'Added via Pi-DNStack', NULL, 0, 0, 0);
544552
"@
545-
docker exec "$($data['stackName'])_pihole" pihole-FTL sqlite3 /etc/pihole/gravity.db "$command"
553+
Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole pihole-FTL sqlite3 /etc/pihole/gravity.db '$command'"
546554

547555
}
548-
docker exec "$($data['stackName'])_pihole" pihole updateGravity 2>&1 >/dev/null
556+
Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole pihole updateGravity 2>&1 >/dev/null"
549557
#endregion
550558

551559
#region set pihole interface
@@ -568,7 +576,7 @@ if [[ -n "$($data['listen'])" ]]; then
568576
fi
569577
"@
570578

571-
docker exec "$($data['stackName'])_pihole" /bin/bash -c $command
579+
Invoke-Expression "$($data['containerEngine']) exec $($data['stackName'])_pihole /bin/bash -c '$command'"
572580
#endregion
573581
}
574582

@@ -700,7 +708,7 @@ function Get-DnsIp {
700708
}
701709
else {
702710
# Use container ip, not recommended as it will not be accessible from outside
703-
return Invoke-CommandWithCheck "docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $($data['stackName'])_pihole"
711+
return Invoke-CommandWithCheck "$($data['containerEngine']) inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $($data['stackName'])_pihole"
704712
}
705713
}
706714

tests/configs/changes.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@{
22
restartPolicy = "always"
33
stackName = "Pi-DNStack"
4+
containerEngine = "docker"
45
containerNetwork = "bridge"
56
piholeImage = "pihole/pihole:latest"
67
piholeUiPort = "8081"

tests/configs/cloudflared_disabled.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@{
22
restartPolicy = "unless-stopped"
33
stackName = "Pi-DNStack"
4+
containerEngine = "docker"
45
containerNetwork = "bridge"
56
piholeImage = "pihole/pihole:latest"
67
piholeUiPort = "80"

tests/configs/default.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@{
22
restartPolicy = "unless-stopped"
33
stackName = "Pi-DNStack"
4+
containerEngine = "docker"
45
containerNetwork = "bridge"
56
piholeImage = "pihole/pihole:latest"
67
piholeUiPort = "80"

tests/configs/host_network.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@{
22
restartPolicy = "unless-stopped"
33
stackName = "Pi-DNStack"
4+
containerEngine = "docker"
45
containerNetwork = "host"
56
piholeImage = "pihole/pihole:latest"
67
piholeUiPort = ""

tests/configs/password_not_changed.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@{
22
restartPolicy = "unless-stopped"
33
stackName = "Pi-DNStack"
4+
containerEngine = "docker"
45
containerNetwork = "bridge"
56
piholeImage = "pihole/pihole:latest"
67
piholeUiPort = "80"

tests/configs/stackName_changed.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@{
22
restartPolicy = "unless-stopped"
33
stackName = "custom_stack"
4+
containerEngine = "docker"
45
containerNetwork = "bridge"
56
piholeImage = "pihole/pihole:latest"
67
piholeUiPort = "80"

tests/configs/unbound_disabled.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@{
22
restartPolicy = "unless-stopped"
33
stackName = "Pi-DNStack"
4+
containerEngine = "docker"
45
containerNetwork = "bridge"
56
piholeImage = "pihole/pihole:latest"
67
piholeUiPort = "80"

0 commit comments

Comments
 (0)