Repository: aksiksi/compose2nix
Pull request #10 (2024-02-29 02:57:13)

Match Docker/Podman default container stop timeout


Patch:
diff --git a/compose.go b/compose.go
index a1b6c3b..3282e0d 100644
--- a/compose.go
+++ b/compose.go
@@ -10,6 +10,7 @@ import (
 	"regexp"
 	"slices"
 	"strings"
+	"time"
 
 	"github.com/compose-spec/compose-go/loader"
 	"github.com/compose-spec/compose-go/types"
@@ -66,6 +67,7 @@ type Generator struct {
 	RemoveVolumes          bool
 	NoCreateRootTarget     bool
 	WriteHeader            bool
+	DefaultStopTimeout     time.Duration
 
 	serviceToContainerName map[string]string
 }
@@ -230,15 +232,6 @@ func healthCheckCommandToString(cmd []string) (string, error) {
 func (g *Generator) buildNixContainer(service types.ServiceConfig) (*NixContainer, error) {
 	name := g.serviceToContainerName[service.Name]
 
-	systemdConfig := NewNixContainerSystemdConfig()
-	if err := systemdConfig.ParseRestartPolicy(&service); err != nil {
-		return nil, err
-	}
-	// Restart configs provided via labels will always override Compose restart settings.
-	if err := systemdConfig.ParseSystemdLabels(&service); err != nil {
-		return nil, err
-	}
-
 	c := &NixContainer{
 		Runtime:       g.Runtime,
 		Name:          name,
@@ -247,7 +240,7 @@ func (g *Generator) buildNixContainer(service types.ServiceConfig) (*NixContaine
 		Ports:         portConfigsToPortStrings(service.Ports),
 		User:          service.User,
 		Volumes:       make(map[string]string),
-		SystemdConfig: systemdConfig,
+		SystemdConfig: NewNixContainerSystemdConfig(),
 		LogDriver:     "journald", // This is the NixOS default
 	}
 
@@ -481,6 +474,21 @@ func (g *Generator) buildNixContainer(service types.ServiceConfig) (*NixContaine
 		}
 	}
 
+	// Restart policy.
+	if err := c.SystemdConfig.ParseRestartPolicy(&service); err != nil {
+		return nil, err
+	}
+
+	// Override systemd stop timeout to match Docker/Podman default of 10 seconds.
+	// https://docs.podman.io/en/latest/markdown/podman-stop.1.html
+	//
+	// Users can always override this by setting per-service Compose labels, or by passing in a CLI
+	// flag.
+	if g.DefaultStopTimeout == 0 {
+		g.DefaultStopTimeout = defaultSystemdStopTimeout
+	}
+	c.SystemdConfig.Service.Set("TimeoutStopSec", int(g.DefaultStopTimeout.Seconds()))
+
 	// Sort slices now that we're done processing the container.
 	slices.Sort(c.DependsOn)
 	slices.Sort(c.ExtraOptions)
@@ -510,6 +518,11 @@ func (g *Generator) buildNixContainer(service types.ServiceConfig) (*NixContaine
 		c.SystemdConfig.Unit.UpheldBy = append(c.SystemdConfig.Unit.UpheldBy, fmt.Sprintf("%s-%s.service", g.Runtime, containerName))
 	}
 
+	// systemd configs provided via labels always override everything else.
+	if err := c.SystemdConfig.ParseSystemdLabels(&service); err != nil {
+		return nil, err
+	}
+
 	return c, nil
 }
 
diff --git a/main.go b/main.go
index 97eb3fe..7183c11 100644
--- a/main.go
+++ b/main.go
@@ -34,6 +34,7 @@ var generateUnusedResources = flag.Bool("generate_unused_resources", false, "if
 var checkSystemdMounts = flag.Bool("check_systemd_mounts", false, "if set, volume paths will be checked against systemd mount paths on the current machine and marked as container dependencies.")
 var removeVolumes = flag.Bool("remove_volumes", false, "if set, volumes will be removed on systemd service stop.")
 var createRootTarget = flag.Bool("create_root_target", true, "if set, a root systemd target will be created, which when stopped tears down all resources.")
+var defaultStopTimeout = flag.Duration("default_stop_timeout", defaultSystemdStopTimeout, "default stop timeout for generated container services.")
 var version = flag.Bool("version", false, "display version and exit")
 
 func main() {
@@ -89,6 +90,7 @@ func main() {
 		RemoveVolumes:          *removeVolumes,
 		NoCreateRootTarget:     !*createRootTarget,
 		WriteHeader:            true,
+		DefaultStopTimeout:     *defaultStopTimeout,
 	}
 	containerConfig, err := g.Run(ctx)
 	if err != nil {
diff --git a/systemd.go b/systemd.go
index f85e481..45b9f75 100644
--- a/systemd.go
+++ b/systemd.go
@@ -15,7 +15,8 @@ import (
 )
 
 const (
-	composeLabelPrefix = "compose2nix"
+	composeLabelPrefix        = "compose2nix"
+	defaultSystemdStopTimeout = 10 * time.Second
 )
 
 var (


Test patch:
diff --git a/nixos-test/docker-compose.nix b/nixos-test/docker-compose.nix
index 44fe33b..70d68ac 100644
--- a/nixos-test/docker-compose.nix
+++ b/nixos-test/docker-compose.nix
@@ -1,4 +1,4 @@
-# Auto-generated using compose2nix v0.1.6.
+# Auto-generated using compose2nix v0.1.7.
 { pkgs, lib, ... }:
 
 {
@@ -29,6 +29,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 360;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -69,6 +70,7 @@
   systemd.services."docker-radarr" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     after = [
       "docker-network-myproject-default.service"
diff --git a/nixos-test/podman-compose.nix b/nixos-test/podman-compose.nix
index febe084..56fe3ff 100644
--- a/nixos-test/podman-compose.nix
+++ b/nixos-test/podman-compose.nix
@@ -1,4 +1,4 @@
-# Auto-generated using compose2nix v0.1.6.
+# Auto-generated using compose2nix v0.1.7.
 { pkgs, lib, ... }:
 
 {
@@ -34,6 +34,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 360;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -72,6 +73,7 @@
   systemd.services."podman-radarr" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     after = [
       "podman-network-myproject-default.service"
diff --git a/testdata/TestDocker_EnvFilesOnly_out.nix b/testdata/TestDocker_EnvFilesOnly_out.nix
index 3524fee..c99516d 100644
--- a/testdata/TestDocker_EnvFilesOnly_out.nix
+++ b/testdata/TestDocker_EnvFilesOnly_out.nix
@@ -43,6 +43,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
       RestartSec = lib.mkOverride 500 "5s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 120;
@@ -93,6 +94,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 10;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -140,6 +142,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RestartSec = lib.mkOverride 500 "3m0s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 10;
     startLimitIntervalSec = 86400;
@@ -201,6 +204,7 @@
   systemd.services."docker-torrent-client" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 86400;
@@ -260,6 +264,7 @@
   systemd.services."docker-traefik" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "none";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       AllowIsolate = lib.mkOverride 500 true;
diff --git a/testdata/TestDocker_RemoveVolumes_out.nix b/testdata/TestDocker_RemoveVolumes_out.nix
index f88caaf..e2dbc3f 100644
--- a/testdata/TestDocker_RemoveVolumes_out.nix
+++ b/testdata/TestDocker_RemoveVolumes_out.nix
@@ -45,6 +45,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
       RestartSec = lib.mkOverride 500 "5s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 120;
@@ -101,6 +102,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 10;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -153,6 +155,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RestartSec = lib.mkOverride 500 "3m0s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 10;
     startLimitIntervalSec = 86400;
@@ -226,6 +229,7 @@
   systemd.services."docker-torrent-client" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 86400;
@@ -286,6 +290,7 @@
   systemd.services."docker-traefik" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "none";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       AllowIsolate = lib.mkOverride 500 true;
diff --git a/testdata/TestDocker_SystemdMount_out.nix b/testdata/TestDocker_SystemdMount_out.nix
index b43371b..000bc88 100644
--- a/testdata/TestDocker_SystemdMount_out.nix
+++ b/testdata/TestDocker_SystemdMount_out.nix
@@ -45,6 +45,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
       RestartSec = lib.mkOverride 500 "5s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 120;
@@ -103,6 +104,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 10;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -157,6 +159,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RestartSec = lib.mkOverride 500 "3m0s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 10;
     startLimitIntervalSec = 86400;
@@ -232,6 +235,7 @@
   systemd.services."docker-torrent-client" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 86400;
@@ -294,6 +298,7 @@
   systemd.services."docker-traefik" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "none";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       AllowIsolate = lib.mkOverride 500 true;
diff --git a/testdata/TestDocker_WithProject_out.nix b/testdata/TestDocker_WithProject_out.nix
index 9eb1ace..b301823 100644
--- a/testdata/TestDocker_WithProject_out.nix
+++ b/testdata/TestDocker_WithProject_out.nix
@@ -45,6 +45,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
       RestartSec = lib.mkOverride 500 "5s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 120;
@@ -101,6 +102,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 10;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -153,6 +155,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RestartSec = lib.mkOverride 500 "3m0s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 10;
     startLimitIntervalSec = 86400;
@@ -226,6 +229,7 @@
   systemd.services."docker-torrent-client" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 86400;
@@ -286,6 +290,7 @@
   systemd.services."docker-traefik" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "none";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       AllowIsolate = lib.mkOverride 500 true;
diff --git a/testdata/TestDocker_out.nix b/testdata/TestDocker_out.nix
index a9d7f26..4f822f5 100644
--- a/testdata/TestDocker_out.nix
+++ b/testdata/TestDocker_out.nix
@@ -44,6 +44,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
       RestartSec = lib.mkOverride 500 "5s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 120;
@@ -99,6 +100,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 10;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -150,6 +152,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RestartSec = lib.mkOverride 500 "3m0s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 10;
     startLimitIntervalSec = 86400;
@@ -222,6 +225,7 @@
   systemd.services."docker-torrent-client" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 86400;
@@ -281,6 +285,7 @@
   systemd.services."docker-traefik" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "none";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       AllowIsolate = lib.mkOverride 500 true;
diff --git a/testdata/TestPodman_WithProject_out.nix b/testdata/TestPodman_WithProject_out.nix
index 055ee15..95c62f4 100644
--- a/testdata/TestPodman_WithProject_out.nix
+++ b/testdata/TestPodman_WithProject_out.nix
@@ -50,6 +50,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
       RestartSec = lib.mkOverride 500 "5s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 120;
@@ -100,6 +101,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 10;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -151,6 +153,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RestartSec = lib.mkOverride 500 "3m0s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 10;
     startLimitIntervalSec = 86400;
@@ -217,6 +220,7 @@
   systemd.services."podman-torrent-client" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 86400;
@@ -275,6 +279,7 @@
   systemd.services."podman-traefik" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "none";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       AllowIsolate = lib.mkOverride 500 true;
diff --git a/testdata/TestPodman_out.nix b/testdata/TestPodman_out.nix
index e285309..10d06fb 100644
--- a/testdata/TestPodman_out.nix
+++ b/testdata/TestPodman_out.nix
@@ -49,6 +49,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
       RestartSec = lib.mkOverride 500 "5s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 120;
@@ -98,6 +99,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RuntimeMaxSec = lib.mkOverride 500 10;
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       Description = lib.mkOverride 500 "This is the sabnzbd container!";
@@ -148,6 +150,7 @@
     serviceConfig = {
       Restart = lib.mkOverride 500 "always";
       RestartSec = lib.mkOverride 500 "3m0s";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 10;
     startLimitIntervalSec = 86400;
@@ -213,6 +216,7 @@
   systemd.services."podman-torrent-client" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "on-failure";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     startLimitBurst = 3;
     startLimitIntervalSec = 86400;
@@ -270,6 +274,7 @@
   systemd.services."podman-traefik" = {
     serviceConfig = {
       Restart = lib.mkOverride 500 "none";
+      TimeoutStopSec = lib.mkOverride 500 10;
     };
     unitConfig = {
       AllowIsolate = lib.mkOverride 500 true;