diff --git a/CHANGELOG.md b/CHANGELOG.md index d4992f64..68142981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Return non-zero exit code on container sub-commands #1414 - Fix excessive line spacing issue when listing nodes. #1241 - Return non-zero exit code on node sub-commands #1421 +- Fix panic when getting a long container list before building the container. #1391 ## v4.5.8, unreleased diff --git a/internal/app/wwctl/container/list/main.go b/internal/app/wwctl/container/list/main.go index 8e91e1f3..59a30c5a 100644 --- a/internal/app/wwctl/container/list/main.go +++ b/internal/app/wwctl/container/list/main.go @@ -27,6 +27,10 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err for i := 0; i < len(containerInfo); i++ { createTime := time.Unix(int64(containerInfo[i].CreateDate), 0) modTime := time.Unix(int64(containerInfo[i].ModDate), 0) + if vars.utc { + createTime = createTime.UTC() + modTime = modTime.UTC() + } sz := util.ByteToString(int64(containerInfo[i].ImgSize)) if vars.compressed { sz = util.ByteToString(int64(containerInfo[i].ImgSizeComp)) diff --git a/internal/app/wwctl/container/list/main_test.go b/internal/app/wwctl/container/list/main_test.go index b1f44e14..3816cb2e 100644 --- a/internal/app/wwctl/container/list/main_test.go +++ b/internal/app/wwctl/container/list/main_test.go @@ -4,68 +4,57 @@ import ( "bytes" "io" "os" + "os/exec" + "path" "strings" "testing" - "time" "github.com/stretchr/testify/assert" - "github.com/warewulf/warewulf/internal/pkg/api/routes/wwapiv1" - warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" - "github.com/warewulf/warewulf/internal/pkg/node" + "github.com/warewulf/warewulf/internal/pkg/testenv" "github.com/warewulf/warewulf/internal/pkg/warewulfd" "github.com/warewulf/warewulf/internal/pkg/wwlog" ) -func Test_List(t *testing.T) { +func Test_List_Args(t *testing.T) { tests := []struct { - name string - args []string - stdout string - inDb string - mockFunc func() + args []string + output string + fail bool }{ - { - name: "container list test", - args: []string{"-l"}, - stdout: `test 1 kernel`, - inDb: `WW_INTERNAL: 45 -nodeprofiles: - default: {} -nodes: - n01: - profiles: - - default + {args: []string{""}, + output: ` CONTAINER NAME + test `, - mockFunc: func() { - containerList = func() (containerInfo []*wwapiv1.ContainerInfo, err error) { - containerInfo = append(containerInfo, &wwapiv1.ContainerInfo{ - Name: "test", - NodeCount: 1, - KernelVersion: "kernel", - CreateDate: uint64(time.Unix(0, 0).Unix()), - ModDate: uint64(time.Unix(0, 0).Unix()), - Size: uint64(1), - }) - return - } - }, + fail: false, + }, + {args: []string{"-ul"}, + output: ` CONTAINER NAME NODES KERNEL VERSION CREATION TIME MODIFICATION TIME SIZE + test 0 02 Jan 00 03:04 UTC 01 Jan 70 00:00 UTC 0 B +`, + fail: false, + }, + {args: []string{"-c"}, + output: ` CONTAINER NAME NODES SIZE + test 0 37 B +`, + fail: false, }, } - - conf_yml := ` -WW_INTERNAL: 0 - ` - - conf := warewulfconf.Get() - err := conf.Parse([]byte(conf_yml)) + env := testenv.New(t) + env.WriteFile(t, path.Join(testenv.WWChrootdir, "test/rootfs/bin/sh"), `This is a fake shell, no pearls here.`) + // need to touch the files, so that the creation date of the container is constant, + // modification date of `../chroots/containername` is used as creation date. + // modification dates of directories change every time a file or subdir is added + // so we have to make it constant *after* its creation. + cmd := exec.Command("touch", "-d", "2000-01-02 03:04:05 UTC", + env.GetPath(path.Join(testenv.WWChrootdir, "test/rootfs")), + env.GetPath(path.Join(testenv.WWChrootdir, "test"))) + err := cmd.Run() assert.NoError(t, err) + defer env.RemoveAll(t) warewulfd.SetNoDaemon() for _, tt := range tests { - _, err = node.Parse([]byte(tt.inDb)) - assert.NoError(t, err) - t.Logf("Running test: %s\n", tt.name) - t.Run(tt.name, func(t *testing.T) { - tt.mockFunc() + t.Run(strings.Join(tt.args, "_"), func(t *testing.T) { baseCmd := GetCommand() baseCmd.SetArgs(tt.args) stdoutR, stdoutW, _ := os.Pipe() @@ -73,10 +62,11 @@ WW_INTERNAL: 0 wwlog.SetLogWriter(os.Stdout) baseCmd.SetOut(os.Stdout) baseCmd.SetErr(os.Stdout) - err = baseCmd.Execute() - if err != nil { - t.Errorf("Received error when running command, err: %v", err) - t.FailNow() + err := baseCmd.Execute() + if tt.fail { + assert.Error(t, err) + } else { + assert.NoError(t, err) } stdoutC := make(chan string) go func() { @@ -85,13 +75,12 @@ WW_INTERNAL: 0 stdoutC <- buf.String() }() stdoutW.Close() - stdout := <-stdoutC - assert.NotEmpty(t, stdout, "os.stdout should not be empty") - if !strings.Contains(stdout, tt.stdout) { - t.Errorf("Got wrong output, got:\n '%s'\n, but want:\n '%s'\n", stdout, tt.stdout) - t.FailNow() - } + assert.Equal(t, tt.output, stdout) + assert.Equal(t, + strings.ReplaceAll(strings.TrimSpace(tt.output), " ", ""), + strings.ReplaceAll(strings.TrimSpace(stdout), " ", "")) + }) } } diff --git a/internal/app/wwctl/container/list/root.go b/internal/app/wwctl/container/list/root.go index 76c6bc6b..09b9d82b 100644 --- a/internal/app/wwctl/container/list/root.go +++ b/internal/app/wwctl/container/list/root.go @@ -10,6 +10,7 @@ type variables struct { kernel bool chroot bool compressed bool + utc bool } // GetRootCommand returns the root cobra.Command for the application. @@ -28,6 +29,6 @@ func GetCommand() *cobra.Command { baseCmd.PersistentFlags().BoolVarP(&vars.size, "size", "s", false, "show size information") baseCmd.PersistentFlags().BoolVarP(&vars.chroot, "chroot", "c", false, "show size of chroot") baseCmd.PersistentFlags().BoolVar(&vars.compressed, "compressed", false, "show size of the compressed image") - + baseCmd.PersistentFlags().BoolVarP(&vars.utc, "utc", "u", false, "use UTC for time output") return baseCmd } diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go index 6e819766..325a0c5f 100644 --- a/internal/pkg/api/container/container.go +++ b/internal/pkg/api/container/container.go @@ -318,6 +318,7 @@ func ContainerList() (containerInfo []*wwapiv1.ContainerInfo, err error) { _, kernelVersion, _ := kernel.FindKernel(container.RootFsDir(source)) var creationTime uint64 sourceStat, err := os.Stat(container.SourceDir(source)) + wwlog.Debug("Checking creation time for: %s,%v", container.SourceDir(source), sourceStat.ModTime()) if err != nil { wwlog.Error("%s\n", err) } else { @@ -332,13 +333,13 @@ func ContainerList() (containerInfo []*wwapiv1.ContainerInfo, err error) { if err != nil { wwlog.Error("%s\n", err) } - imgF, err := os.Stat(container.ImageFile(source)) - if err != nil { - wwlog.Error("%s\n", err) + imgSize := 0 + if imgF, err := os.Stat(container.ImageFile(source)); err == nil { + imgSize = int(imgF.Size()) } - imgFC, err := os.Stat(container.ImageFile(source) + ".gz") - if err != nil { - wwlog.Error("%s\n", err) + imgCSize := 0 + if imgFC, err := os.Stat(container.ImageFile(source) + ".gz"); err == nil { + imgCSize = int(imgFC.Size()) } containerInfo = append(containerInfo, &wwapiv1.ContainerInfo{ Name: source, @@ -347,8 +348,8 @@ func ContainerList() (containerInfo []*wwapiv1.ContainerInfo, err error) { CreateDate: creationTime, ModDate: modTime, Size: uint64(size), - ImgSize: uint64(imgF.Size()), - ImgSizeComp: uint64(imgFC.Size()), + ImgSize: uint64(imgSize), + ImgSizeComp: uint64(imgCSize), }) } diff --git a/internal/pkg/testenv/testenv.go b/internal/pkg/testenv/testenv.go index 0b80bad3..fbc70e4b 100644 --- a/internal/pkg/testenv/testenv.go +++ b/internal/pkg/testenv/testenv.go @@ -10,6 +10,7 @@ import ( "path" "path/filepath" "testing" + "time" warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" @@ -131,6 +132,10 @@ func (env *TestEnv) WriteFile(t *testing.T, fileName string, content string) { defer f.Close() _, err = f.WriteString(content) assert.NoError(t, err) + err = os.Chtimes(env.GetPath(fileName), + time.Date(2006, time.February, 1, 3, 4, 5, 0, time.UTC), + time.Date(2006, time.February, 1, 3, 4, 5, 0, time.UTC)) + assert.NoError(t, err) } // ReadFile returns the content of fileName as converted to a