-
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.
pkg/timezone: add helper function to configure timezone for pods/cont…
…ainers This PR consolidates common functionality used by CRI-O and Podman in one central location. I aimed to keep this change more generic, considering that CRI-O and Podman have different ways for the file mounting and applying security labels. Signed-off-by: Sohan Kunkerkar <[email protected]>
- Loading branch information
1 parent
4d7586e
commit 5e15c0c
Showing
1 changed file
with
98 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,98 @@ | ||
//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, mountLabel, containerID string) (string, error) { | ||
if timezone == "" { | ||
return "", nil | ||
} | ||
var err error | ||
timezonePath := filepath.Join("/usr/share/zoneinfo", 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) | ||
} | ||
|
||
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, mountLabel, timezonePath) | ||
if err != nil { | ||
return "", fmt.Errorf("setting timezone for container %s: %w", containerID, err) | ||
} | ||
return localtimePath, nil | ||
} 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 "", nil | ||
} | ||
|
||
// copyTimezoneFile copies the timezone file from the host to the container. | ||
func copyTimezoneFile(containerRunDir, mountLabel, 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 | ||
} |