Skip to content

Commit 70100e4

Browse files
Improve filemode and tar header
1 parent 41f8ac3 commit 70100e4

5 files changed

Lines changed: 81 additions & 34 deletions

File tree

.DS_Store

6 KB
Binary file not shown.

cmd/container-structure-test/app/cmd/test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ func run(out io.Writer) error {
102102
Runtime: opts.Runtime,
103103
Platform: opts.Platform,
104104
PodTemplate: opts.PodTemplate,
105-
AllowReuse: opts.AllowReuse,
106105
}
107106

108107
var err error
@@ -250,7 +249,6 @@ func AddTestFlags(cmd *cobra.Command) {
250249
cmd.Flags().BoolVar(&opts.NoColor, "no-color", false, "no color in the output")
251250
cmd.Flags().StringVar(&opts.JunitSuiteName, "junit-suite-name", "", fmt.Sprintf("name to use for the junit test suite (defaults to '%s')", output.DefaultJunitSuiteName))
252251
cmd.Flags().StringVar(&opts.PodTemplate, "pod-template", "", "pod template to instantiate pods from with kubernetes driver")
253-
cmd.Flags().BoolVar(&opts.AllowReuse, "allow-reuse", false, "if set, reuse the running pod for multiple tests, this is a lot faster but test can interfere with each other. Use with kubernetes driver")
254252
cmd.Flags().StringArrayVarP(&opts.ConfigFiles, "config", "c", []string{}, "test config files")
255253
cmd.MarkFlagRequired("config")
256254
cmd.Flags().StringVar(&opts.TestReport, "test-report", "", "generate test report and write it to specified file (supported format: json, junit; default: json)")

pkg/drivers/docker_driver.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ import (
1919
"bufio"
2020
"bytes"
2121
"fmt"
22-
"github.com/joho/godotenv"
2322
"io"
2423
"os"
2524
"path"
2625
"path/filepath"
2726
"strings"
2827

28+
"github.com/joho/godotenv"
29+
2930
"github.com/pkg/errors"
3031
"github.com/sirupsen/logrus"
3132

pkg/drivers/driver.go

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,13 @@ const (
3030
)
3131

3232
type DriverConfig struct {
33-
Image string // used by Docker/Tar drivers
34-
Save bool // used by Docker/Tar drivers
35-
Metadata string // used by Host driver
36-
Runtime string // used by Docker driver
37-
Platform string // used by Docker driver
38-
RunOpts unversioned.ContainerRunOptions // used by Docker driver
39-
Namespace string
40-
PodnamePrefix string
41-
AllowReuse bool
42-
PodTemplate string
33+
Image string // used by Docker/Tar drivers
34+
Save bool // used by Docker/Tar drivers
35+
Metadata string // used by Host driver
36+
Runtime string // used by Docker driver
37+
Platform string // used by Docker driver
38+
RunOpts unversioned.ContainerRunOptions // used by Docker driver
39+
PodTemplate string
4340
}
4441

4542
type Driver interface {

pkg/drivers/k8s_driver.go

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,43 @@ import (
4040
kexec "k8s.io/client-go/util/exec"
4141
)
4242

43+
func findFile(file string) (string, error) {
44+
stat, err := os.Stat(file)
45+
if err == nil && !stat.IsDir() {
46+
return file, nil
47+
}
48+
49+
// fallback to path relative to executable
50+
if exePath, err := os.Executable(); err == nil {
51+
exeDir := filepath.Dir(exePath)
52+
fallbackPath := filepath.Join(exeDir, file)
53+
if stat, err := os.Stat(fallbackPath); err == nil && !stat.IsDir() {
54+
return fallbackPath, nil
55+
}
56+
}
57+
58+
// fallback to user home
59+
if homeDir, err := os.UserHomeDir(); err == nil {
60+
fallbackPath := filepath.Join(homeDir, file)
61+
if stat, err := os.Stat(fallbackPath); err == nil && !stat.IsDir() {
62+
return fallbackPath, nil
63+
}
64+
}
65+
66+
return "", os.ErrNotExist
67+
}
68+
4369
// PodFromFile will read a given file with pod definition and returns a corev1.Pod
4470
// accordingly.
4571
func PodFromFile(file string) (*corev1.Pod, error) {
4672
decode := scheme.Codecs.UniversalDeserializer().Decode
47-
stream, err := os.ReadFile(file)
73+
74+
resolvedPodTemplateFile, err := findFile(file)
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
stream, err := os.ReadFile(resolvedPodTemplateFile)
4880
if err != nil {
4981
return nil, err
5082
}
@@ -65,7 +97,6 @@ type K8sDriver struct {
6597
KubeConfig *rest.Config
6698
PodTemplate *corev1.Pod
6799
podName string
68-
AllowReuse bool
69100
env []unversioned.EnvVar
70101
}
71102

@@ -107,21 +138,15 @@ func NewK8sDriver(args DriverConfig) (Driver, error) {
107138
runOpts: args.RunOpts,
108139
PodTemplate: template,
109140
KubeConfig: k8sConfig,
110-
AllowReuse: args.AllowReuse,
111141
podName: "",
112142
}, nil
113143
}
114144

115145
func (d *K8sDriver) Setup(envVars []unversioned.EnvVar, fullCommands [][]string) error {
116-
// Pod creation will be handled in ProcessCommand
117-
logrus.Info("k8s driver setup, no pod created yet")
118146
return nil
119147
}
120148

121149
func (d *K8sDriver) Teardown(fullCommands [][]string) error {
122-
if d.AllowReuse && d.podName != "" {
123-
d.Destroy()
124-
}
125150
return nil
126151
}
127152

@@ -131,37 +156,35 @@ func (d *K8sDriver) SetEnv(envVars []unversioned.EnvVar) error {
131156
}
132157

133158
func (d *K8sDriver) ProcessCommand(envVars []unversioned.EnvVar, fullCommand []string) (string, string, int, error) {
134-
if !d.AllowReuse || d.podName == "" {
159+
if d.podName == "" {
135160
logrus.Infof("k8s driver creating pod in namespace %s", d.PodTemplate.ObjectMeta.Namespace)
136161
allEnvs := append(d.env, envVars...)
137162
// create a pod that waits
138163
pod, err := d.createPod(allEnvs, []string{"sleep", "99999"})
139164
if err != nil {
140165
return "", "", 0, err
141166
}
142-
d.podName = pod.Name
143-
144-
if !d.AllowReuse {
145-
defer d.Destroy()
146-
}
147167

148168
// Wait for the pod to be running
149169
err = d.waitForPod()
150170
if err != nil {
151171
return "", "", 0, err
152172
}
173+
174+
d.podName = pod.Name
175+
logrus.Infof("k8s driver created pod %s in namespace %s", d.podName, d.PodTemplate.ObjectMeta.Namespace)
153176
}
154177

155178
return d.execInPod(fullCommand)
156179
}
157180

158181
func (d *K8sDriver) StatFile(path string) (os.FileInfo, error) {
159-
// A better approach would be to have a long-running pod and exec into it.
160182
command := []string{"stat", "-c", "%n,%s,%F,%a,%u,%g", path}
161183
stdout, _, _, err := d.ProcessCommand(nil, command)
162184
if err != nil {
163185
return nil, err
164186
}
187+
165188
parts := strings.Split(strings.TrimSpace(stdout), ",")
166189
if len(parts) != 6 {
167190
return nil, fmt.Errorf("unexpected output from stat: %s", stdout)
@@ -183,18 +206,29 @@ func (d *K8sDriver) StatFile(path string) (os.FileInfo, error) {
183206
}
184207

185208
// Use bitSize 32 because os.FileMode is a uint32
186-
fileMode, err := strconv.ParseUint(parts[3], 8, 32)
209+
fileMode64, err := strconv.ParseUint(parts[3], 8, 32)
187210
if err != nil {
188211
return nil, err
189212
}
190213

214+
fileMode := os.FileMode(fileMode64)
215+
216+
// Manually map the Linux special bits to Go constants
217+
if fileMode64&01000 != 0 { // Linux Sticky Bit
218+
fileMode |= os.ModeSticky
219+
}
220+
221+
if isDir {
222+
fileMode |= os.ModeDir
223+
}
224+
191225
return &fileInfo{
192226
name: parts[0],
193227
size: size,
194228
isDir: isDir,
195229
uid: uid,
196230
gid: gid,
197-
fileMode: os.FileMode(fileMode),
231+
fileMode: fileMode,
198232
}, nil
199233
}
200234

@@ -224,10 +258,20 @@ func (fi *fileInfo) IsDir() bool {
224258
return fi.isDir
225259
}
226260
func (fi *fileInfo) Sys() interface{} {
227-
return &tar.Header{
228-
Uid: int(fi.uid),
229-
Gid: int(fi.gid),
261+
hdr := &tar.Header{
262+
Name: fi.name,
263+
Size: fi.size,
264+
Mode: int64(fi.fileMode),
265+
Uid: int(fi.uid),
266+
Gid: int(fi.gid),
267+
ModTime: fi.ModTime(),
268+
}
269+
if fi.isDir {
270+
hdr.Typeflag = tar.TypeDir
271+
} else {
272+
hdr.Typeflag = tar.TypeReg
230273
}
274+
return hdr
231275
}
232276

233277
func (d *K8sDriver) ReadFile(path string) ([]byte, error) {
@@ -305,14 +349,20 @@ func (d *K8sDriver) GetConfig() (unversioned.Config, error) {
305349
}
306350

307351
func (d *K8sDriver) Destroy() {
352+
d.stopPod()
353+
}
354+
355+
func (d *K8sDriver) stopPod() {
308356
if d.podName == "" {
357+
logrus.Info("k8s driver no pod defined, nothing to clean")
309358
return
310359
}
311360
err := d.Client.CoreV1().Pods(d.PodTemplate.Namespace).Delete(context.Background(), d.podName, metav1.DeleteOptions{})
312361
if err != nil {
313362
logrus.Warnf("Error when removing pod %s: %s", d.podName, err.Error())
314363
}
315364
d.podName = ""
365+
logrus.Infof("Stopped pod %s", d.podName)
316366
}
317367

318368
func (d *K8sDriver) createPod(envVars []unversioned.EnvVar, command []string) (*corev1.Pod, error) {
@@ -368,7 +418,6 @@ func (d *K8sDriver) waitForPod() error {
368418
}
369419

370420
func (d *K8sDriver) execInPod(command []string) (string, string, int, error) {
371-
372421
// TTY must be false to keep stdout and stderr separate
373422
req := d.Client.CoreV1().RESTClient().Post().
374423
Resource("pods").
@@ -405,5 +454,7 @@ func (d *K8sDriver) execInPod(command []string) (string, string, int, error) {
405454
}
406455
}
407456

457+
logrus.Infof("Executed command %v in %s", command, d.podName)
458+
408459
return stdout.String(), stderr.String(), exitCode, nil
409460
}

0 commit comments

Comments
 (0)