mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
fix(container): propagate host timezone to all containers
Replace TZ env var with /etc/localtime bind-mount in docker-compose and inject timezone spec opts into containerd bot containers.
This commit is contained in:
@@ -7,9 +7,9 @@ services:
|
||||
POSTGRES_DB: memoh
|
||||
POSTGRES_USER: memoh
|
||||
POSTGRES_PASSWORD: memoh123
|
||||
TZ: ${TZ:-UTC}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- "5432:5432"
|
||||
healthcheck:
|
||||
|
||||
+3
-7
@@ -7,9 +7,9 @@ services:
|
||||
POSTGRES_DB: memoh
|
||||
POSTGRES_USER: memoh
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-memoh123}
|
||||
TZ: ${TZ:-UTC}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
expose:
|
||||
- "5432"
|
||||
healthcheck:
|
||||
@@ -47,8 +47,6 @@ services:
|
||||
- COMMIT_HASH=${MEMOH_COMMIT:-unknown}
|
||||
- BUILD_TIME=${MEMOH_BUILD_TIME:-unknown}
|
||||
container_name: memoh-migrate
|
||||
environment:
|
||||
TZ: ${TZ:-UTC}
|
||||
entrypoint: ["/app/memoh-server", "migrate", "up"]
|
||||
volumes:
|
||||
- ${MEMOH_CONFIG:-./conf/app.docker.toml}:/app/config.toml:ro
|
||||
@@ -70,13 +68,12 @@ services:
|
||||
container_name: memoh-server
|
||||
privileged: true
|
||||
pid: host
|
||||
environment:
|
||||
TZ: ${TZ:-UTC}
|
||||
volumes:
|
||||
- ${MEMOH_CONFIG:-./conf/app.docker.toml}:/app/config.toml:ro
|
||||
- containerd_data:/var/lib/containerd
|
||||
- server_cni_state:/var/lib/cni
|
||||
- memoh_data:/opt/memoh/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
@@ -93,10 +90,9 @@ services:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.agent
|
||||
container_name: memoh-agent
|
||||
environment:
|
||||
TZ: ${TZ:-UTC}
|
||||
volumes:
|
||||
- ${MEMOH_CONFIG:-./conf/app.docker.toml}:/config.toml:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- "8081:8081"
|
||||
depends_on:
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/containerd/containerd/v2/pkg/oci"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// TimezoneSpecOpts returns OCI spec options that propagate the host timezone
|
||||
// into the container via /etc/localtime bind-mount and TZ environment variable.
|
||||
func TimezoneSpecOpts() []oci.SpecOpts {
|
||||
var opts []oci.SpecOpts
|
||||
if _, err := os.Stat("/etc/localtime"); err == nil {
|
||||
opts = append(opts, oci.WithMounts([]specs.Mount{{
|
||||
Destination: "/etc/localtime",
|
||||
Type: "bind",
|
||||
Source: "/etc/localtime",
|
||||
Options: []string{"rbind", "ro"},
|
||||
}}))
|
||||
}
|
||||
if tz := os.Getenv("TZ"); tz != "" {
|
||||
opts = append(opts, oci.WithEnv([]string{"TZ=" + tz}))
|
||||
}
|
||||
return opts
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTimezoneSpecOpts_WithTZ(t *testing.T) {
|
||||
t.Setenv("TZ", "Asia/Shanghai")
|
||||
opts := TimezoneSpecOpts()
|
||||
if _, err := os.Stat("/etc/localtime"); err == nil {
|
||||
if len(opts) < 1 {
|
||||
t.Fatal("expected at least mount opt when /etc/localtime exists")
|
||||
}
|
||||
}
|
||||
found := false
|
||||
for range opts {
|
||||
found = true
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("expected at least one spec opt when TZ is set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimezoneSpecOpts_WithoutTZ(t *testing.T) {
|
||||
t.Setenv("TZ", "")
|
||||
opts := TimezoneSpecOpts()
|
||||
for _, opt := range opts {
|
||||
if opt == nil {
|
||||
t.Fatal("unexpected nil spec opt")
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat("/etc/localtime"); err != nil && len(opts) != 0 {
|
||||
t.Fatalf("expected no opts when /etc/localtime absent and TZ empty, got %d", len(opts))
|
||||
}
|
||||
}
|
||||
@@ -210,6 +210,7 @@ func (h *ContainerdHandler) CreateContainer(c echo.Context) error {
|
||||
}),
|
||||
oci.WithProcessArgs("/bin/sh", "-lc", fmt.Sprintf("bootstrap(){ [ -e /app/mcp ] || { mkdir -p /app; [ -f /opt/mcp ] && cp -a /opt/mcp /app/mcp 2>/dev/null || true; }; if [ -d /opt/mcp-template ]; then mkdir -p %q; for f in /opt/mcp-template/*; do name=$(basename \"$f\"); [ -e %q/\"$name\" ] || cp -a \"$f\" %q/\"$name\" 2>/dev/null || true; done; fi; }; bootstrap; exec /app/mcp", dataMount, dataMount, dataMount)),
|
||||
}
|
||||
specOpts = append(specOpts, ctr.TimezoneSpecOpts()...)
|
||||
|
||||
_, err = h.service.CreateContainer(ctx, ctr.CreateContainerRequest{
|
||||
ID: containerID,
|
||||
@@ -878,6 +879,7 @@ func (h *ContainerdHandler) SetupBotContainer(ctx context.Context, botID string)
|
||||
}),
|
||||
oci.WithProcessArgs("/bin/sh", "-lc", fmt.Sprintf("bootstrap(){ [ -e /app/mcp ] || { mkdir -p /app; [ -f /opt/mcp ] && cp -a /opt/mcp /app/mcp 2>/dev/null || true; }; if [ -d /opt/mcp-template ]; then mkdir -p %q; for f in /opt/mcp-template/*; do name=$(basename \"$f\"); [ -e %q/\"$name\" ] || cp -a \"$f\" %q/\"$name\" 2>/dev/null || true; done; fi; }; bootstrap; exec /app/mcp", dataMount, dataMount, dataMount)),
|
||||
}
|
||||
specOpts = append(specOpts, ctr.TimezoneSpecOpts()...)
|
||||
|
||||
_, err = h.service.CreateContainer(ctx, ctr.CreateContainerRequest{
|
||||
ID: containerID,
|
||||
|
||||
@@ -134,6 +134,7 @@ func (m *Manager) EnsureBot(ctx context.Context, botID string) error {
|
||||
},
|
||||
}),
|
||||
}
|
||||
specOpts = append(specOpts, ctr.TimezoneSpecOpts()...)
|
||||
|
||||
_, err = m.service.CreateContainer(ctx, ctr.CreateContainerRequest{
|
||||
ID: m.containerID(botID),
|
||||
|
||||
@@ -177,6 +177,7 @@ func (m *Manager) CreateVersion(ctx context.Context, botID string) (*VersionInfo
|
||||
},
|
||||
}),
|
||||
}
|
||||
specOpts = append(specOpts, ctr.TimezoneSpecOpts()...)
|
||||
|
||||
_, err = m.service.CreateContainerFromSnapshot(ctx, ctr.CreateContainerRequest{
|
||||
ID: containerID,
|
||||
@@ -318,6 +319,7 @@ func (m *Manager) RollbackVersion(ctx context.Context, botID string, version int
|
||||
},
|
||||
}),
|
||||
}
|
||||
specOpts = append(specOpts, ctr.TimezoneSpecOpts()...)
|
||||
|
||||
_, err = m.service.CreateContainerFromSnapshot(ctx, ctr.CreateContainerRequest{
|
||||
ID: containerID,
|
||||
|
||||
Reference in New Issue
Block a user