-
Notifications
You must be signed in to change notification settings - Fork 206
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1775 from sohankunkerkar/add-timezone-helper-fns
pkg/timezone: add helper function to configure timezone for pod/containers
- Loading branch information
Showing
1 changed file
with
104 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
//go:build linux | ||
// +build linux | ||
|
||
package timezone | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
|
||
securejoin "github.com/cyphar/filepath-securejoin" | ||
"github.com/sirupsen/logrus" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// ConfigureContainerTimeZone configure the time zone for a container. | ||
// It returns the path of the created /etc/localtime file if needed. | ||
func ConfigureContainerTimeZone(timezone, containerRunDir, mountPoint, etcPath, containerID string) (localTimePath string, err error) { | ||
if timezone == "" { | ||
return "", nil | ||
} | ||
|
||
timezonePath := filepath.Join("/usr/share/zoneinfo", timezone) | ||
// Allow using TZDIR per: | ||
// https://sourceware.org/git/?p=glibc.git;a=blob;f=time/tzfile.c;h=8a923d0cccc927a106dc3e3c641be310893bab4e;hb=HEAD#l149 | ||
tzdir := os.Getenv("TZDIR") | ||
if tzdir != "" { | ||
timezonePath = filepath.Join(tzdir, timezone) | ||
} | ||
if timezone == "local" { | ||
timezonePath, err = filepath.EvalSymlinks("/etc/localtime") | ||
if err != nil { | ||
return "", fmt.Errorf("finding local timezone for container %s: %w", containerID, err) | ||
} | ||
} | ||
|
||
etcFd, err := unix.Open(etcPath, unix.O_RDONLY|unix.O_PATH, 0) | ||
if err != nil { | ||
return "", fmt.Errorf("open /etc in the container: %w", err) | ||
} | ||
defer unix.Close(etcFd) | ||
|
||
// Make sure to remove any existing localtime file in the container to not create invalid links | ||
err = unix.Unlinkat(etcFd, "localtime", 0) | ||
if err != nil && !errors.Is(err, fs.ErrNotExist) { | ||
return "", fmt.Errorf("removing /etc/localtime: %w", err) | ||
} | ||
|
||
hostPath, err := securejoin.SecureJoin(mountPoint, timezonePath) | ||
if err != nil { | ||
return "", fmt.Errorf("resolve zoneinfo path in the container: %w", err) | ||
} | ||
|
||
var localtimePath string | ||
if _, err := os.Stat(hostPath); err != nil { | ||
// File does not exist, which means tzdata is not installed in the container. | ||
// Create /etc/localtime as a copy from the host. | ||
logrus.Debugf("Timezone %s does not exist in the container, create our own copy from the host", timezonePath) | ||
localtimePath, err = copyTimezoneFile(containerRunDir, timezonePath) | ||
if err != nil { | ||
return "", fmt.Errorf("setting timezone for container %s: %w", containerID, err) | ||
} | ||
} else { | ||
// File exists, let's create a symlink according to localtime(5) | ||
logrus.Debugf("Create localtime symlink for %s", timezonePath) | ||
err = unix.Symlinkat(hostPath, etcFd, "localtime") | ||
if err != nil { | ||
return "", fmt.Errorf("creating /etc/localtime symlink: %w", err) | ||
} | ||
} | ||
return localtimePath, nil | ||
} | ||
|
||
// copyTimezoneFile copies the timezone file from the host to the container. | ||
func copyTimezoneFile(containerRunDir, zonePath string) (string, error) { | ||
localtimeCopy := filepath.Join(containerRunDir, "localtime") | ||
file, err := os.Stat(zonePath) | ||
if err != nil { | ||
return "", err | ||
} | ||
if file.IsDir() { | ||
return "", errors.New("invalid timezone: is a directory") | ||
} | ||
src, err := os.Open(zonePath) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer src.Close() | ||
|
||
dest, err := os.Create(localtimeCopy) | ||
if err != nil { | ||
return "", err | ||
} | ||
defer dest.Close() | ||
|
||
_, err = io.Copy(dest, src) | ||
if err != nil { | ||
return "", err | ||
} | ||
return localtimeCopy, err | ||
} |