Skip to content

Commit 380679c

Browse files
committed
Add tests for timezone validation, split platforms
Signed-off-by: Dmitry Rubtsov <[email protected]>
1 parent 6473f5e commit 380679c

File tree

4 files changed

+144
-46
lines changed

4 files changed

+144
-46
lines changed

pkg/limayaml/defaults.go

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"strings"
2121
"sync"
2222
"text/template"
23-
"time"
2423

2524
"github.com/coreos/go-semver/semver"
2625
"github.com/docker/go-units"
@@ -136,27 +135,6 @@ func MACAddress(uniqueID string) string {
136135
return hw.String()
137136
}
138137

139-
func hostTimeZone() string {
140-
// WSL2 will automatically set the timezone
141-
if runtime.GOOS == "windows" {
142-
return ""
143-
}
144-
145-
if tzBytes, err := os.ReadFile("/etc/timezone"); err == nil {
146-
if tz := strings.TrimSpace(string(tzBytes)); isValidTimezone(tz) {
147-
return tz
148-
}
149-
}
150-
151-
if zoneinfoFile, err := filepath.EvalSymlinks("/etc/localtime"); err == nil {
152-
if tz := extractTimezoneFromPath(zoneinfoFile); isValidTimezone(tz) {
153-
return tz
154-
}
155-
}
156-
157-
return ""
158-
}
159-
160138
func defaultCPUs() int {
161139
const x = 4
162140
if hostCPUs := runtime.NumCPU(); hostCPUs < x {
@@ -1311,27 +1289,3 @@ func unique(s []string) []string {
13111289
}
13121290
return list
13131291
}
1314-
1315-
func isValidTimezone(tz string) bool {
1316-
if tz == "" {
1317-
return false
1318-
}
1319-
_, err := time.LoadLocation(tz)
1320-
if err != nil {
1321-
if len(tz) > 30 {
1322-
tz = tz[:30] + "..."
1323-
}
1324-
logrus.Warnf("invalid timezone %q", tz)
1325-
return false
1326-
}
1327-
return true
1328-
}
1329-
1330-
func extractTimezoneFromPath(zoneinfoFile string) string {
1331-
for baseDir := filepath.Dir(zoneinfoFile); baseDir != "/"; baseDir = filepath.Dir(baseDir) {
1332-
if _, err := os.Stat(filepath.Join(baseDir, "Etc/UTC")); err == nil {
1333-
return strings.TrimPrefix(zoneinfoFile, baseDir+string(os.PathSeparator))
1334-
}
1335-
}
1336-
return ""
1337-
}

pkg/limayaml/defaults_unix.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//go:build !windows
2+
3+
// SPDX-FileCopyrightText: Copyright The Lima Authors
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package limayaml
7+
8+
import (
9+
"errors"
10+
"fmt"
11+
"os"
12+
"path/filepath"
13+
"strings"
14+
"time"
15+
16+
"github.com/sirupsen/logrus"
17+
)
18+
19+
func hostTimeZone() string {
20+
if tzBytes, err := os.ReadFile("/etc/timezone"); err == nil {
21+
if tz := strings.TrimSpace(string(tzBytes)); tz != "" {
22+
if _, err := time.LoadLocation(tz); err != nil {
23+
logrus.Warnf("invalid timezone found in /etc/timezone: %v", err)
24+
} else {
25+
return tz
26+
}
27+
}
28+
}
29+
30+
if zoneinfoFile, err := filepath.EvalSymlinks("/etc/localtime"); err == nil {
31+
if tz, err := extractTZFromPath(zoneinfoFile); err != nil {
32+
logrus.Warnf("failed to extract timezone from %s: %v", zoneinfoFile, err)
33+
} else {
34+
return tz
35+
}
36+
}
37+
38+
logrus.Warn("unable to determine host timezone, falling back to default value")
39+
return ""
40+
}
41+
42+
func extractTZFromPath(zoneinfoFile string) (string, error) {
43+
if zoneinfoFile == "" {
44+
return "", errors.New("invalid zoneinfo file path")
45+
}
46+
47+
if _, err := os.Stat(zoneinfoFile); os.IsNotExist(err) {
48+
return "", fmt.Errorf("zoneinfo file does not exist: %s", zoneinfoFile)
49+
}
50+
51+
for dir := filepath.Dir(zoneinfoFile); dir != filepath.Dir(dir); dir = filepath.Dir(dir) {
52+
if _, err := os.Stat(filepath.Join(dir, "Etc", "UTC")); err == nil {
53+
return filepath.Rel(dir, zoneinfoFile)
54+
}
55+
}
56+
57+
return "", errors.New("timezone base directory not found")
58+
}

pkg/limayaml/defaults_unix_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//go:build !windows
2+
3+
// SPDX-FileCopyrightText: Copyright The Lima Authors
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package limayaml
7+
8+
import (
9+
"os"
10+
"path/filepath"
11+
"testing"
12+
13+
"gotest.tools/v3/assert"
14+
)
15+
16+
func TestExtractTimezoneFromPath(t *testing.T) {
17+
tmpDir := t.TempDir()
18+
19+
// Create test timezone directory structure
20+
assert.NilError(t, os.MkdirAll(filepath.Join(tmpDir, "Etc"), 0o755))
21+
assert.NilError(t, os.WriteFile(filepath.Join(tmpDir, "Etc", "UTC"), []byte{}, 0o644))
22+
assert.NilError(t, os.WriteFile(filepath.Join(tmpDir, "UTC"), []byte{}, 0o644))
23+
assert.NilError(t, os.MkdirAll(filepath.Join(tmpDir, "Antarctica"), 0o755))
24+
assert.NilError(t, os.WriteFile(filepath.Join(tmpDir, "Antarctica", "Troll"), []byte{}, 0o644))
25+
26+
tests := []struct {
27+
name string
28+
path string
29+
want string
30+
wantErr bool
31+
}{
32+
{
33+
"valid_timezone",
34+
filepath.Join(tmpDir, "Antarctica", "Troll"),
35+
"Antarctica/Troll",
36+
false,
37+
},
38+
{
39+
"root_level_zone",
40+
filepath.Join(tmpDir, "UTC"),
41+
"UTC",
42+
false,
43+
},
44+
{
45+
"outside_zoneinfo",
46+
"/tmp/somefile",
47+
"",
48+
true,
49+
},
50+
{
51+
"empty_path",
52+
"",
53+
"",
54+
true,
55+
},
56+
{
57+
"nonexistent_file",
58+
filepath.Join(tmpDir, "Invalid", "Zone"),
59+
"",
60+
true,
61+
},
62+
}
63+
64+
for _, tt := range tests {
65+
t.Run(tt.name, func(t *testing.T) {
66+
got, err := extractTZFromPath(tt.path)
67+
if tt.wantErr {
68+
assert.Assert(t, err != nil, "expected error but got none")
69+
} else {
70+
assert.NilError(t, err)
71+
}
72+
assert.Equal(t, tt.want, got)
73+
})
74+
}
75+
}

pkg/limayaml/defaults_windows.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//go:build windows
2+
3+
// SPDX-FileCopyrightText: Copyright The Lima Authors
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package limayaml
7+
8+
func hostTimeZone() string {
9+
// WSL2 will automatically set the timezone
10+
return ""
11+
}

0 commit comments

Comments
 (0)