wwctl image completions and arguments

Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
Jonathon Anderson
2025-02-08 14:52:48 -07:00
parent 1d34477425
commit 5494424c09
14 changed files with 59 additions and 55 deletions

View File

@@ -48,9 +48,15 @@ func ProfileKernelVersion(cmd *cobra.Command, args []string, toComplete string)
return kernelVersions, cobra.ShellCompDirectiveNoFileComp return kernelVersions, cobra.ShellCompDirectiveNoFileComp
} }
func Images(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func Images(num int) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
sources, _ := image.ListSources() return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return sources, cobra.ShellCompDirectiveNoFileComp if num <= 0 || len(args) < num {
sources, _ := image.ListSources()
return sources, cobra.ShellCompDirectiveNoFileComp
} else {
return nil, cobra.ShellCompDirectiveNoFileComp
}
}
} }
func Profiles(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func Profiles(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

View File

@@ -10,11 +10,6 @@ import (
) )
func CobraRunE(cmd *cobra.Command, args []string) (err error) { func CobraRunE(cmd *cobra.Command, args []string) (err error) {
if len(args) > 2 {
wwlog.Warn("copy only requires 2 arguments but you provided %d arguments. Hence, they will be ignored.", len(args))
}
cdp := &wwapiv1.ImageCopyParameter{ cdp := &wwapiv1.ImageCopyParameter{
ImageSource: args[0], ImageSource: args[0],
ImageDestination: args[1], ImageDestination: args[1],

View File

@@ -13,7 +13,7 @@ var (
Short: "Copy an existing image", Short: "Copy an existing image",
Long: "This command will duplicate an imported image.", Long: "This command will duplicate an imported image.",
RunE: CobraRunE, RunE: CobraRunE,
Args: cobra.MinimumNArgs(2), Args: cobra.ExactArgs(2),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp

View File

@@ -22,7 +22,7 @@ are:
Imported images are used to create bootable images.`, Imported images are used to create bootable images.`,
Example: "wwctl image import docker://ghcr.io/warewulf/warewulf-rockylinux:8 rockylinux-8", Example: "wwctl image import docker://ghcr.io/warewulf/warewulf-rockylinux:8 rockylinux-8",
RunE: CobraRunE, RunE: CobraRunE,
Args: cobra.MinimumNArgs(1), Args: cobra.RangeArgs(1, 2),
PreRun: func(cmd *cobra.Command, args []string) { PreRun: func(cmd *cobra.Command, args []string) {
if SetForce && SetUpdate { if SetForce && SetUpdate {
wwlog.Warn("Both --force and --update flags are set, will ignore --update flag") wwlog.Warn("Both --force and --update flags are set, will ignore --update flag")

View File

@@ -27,12 +27,12 @@ Image Kernel Version Default Nodes
}, },
"list": { "list": {
files: map[string][]string{ files: map[string][]string{
"image1": []string{ "image1": {
"/boot/vmlinuz-5.14.0-427.18.1.el9_4.x86_64", "/boot/vmlinuz-5.14.0-427.18.1.el9_4.x86_64",
"/boot/vmlinuz-5.14.0-427.24.1.el9_4.x86_64", "/boot/vmlinuz-5.14.0-427.24.1.el9_4.x86_64",
"/boot/vmlinuz-4.14.0-427.18.1.el8_4.x86_64", "/boot/vmlinuz-4.14.0-427.18.1.el8_4.x86_64",
}, },
"image2": []string{ "image2": {
"/boot/vmlinuz-0-rescue-eb46964329b146e39518c625feab3ea0", "/boot/vmlinuz-0-rescue-eb46964329b146e39518c625feab3ea0",
"/boot/vmlinuz-5.14.0-362.24.1.el9_3.aarch64", "/boot/vmlinuz-5.14.0-362.24.1.el9_3.aarch64",
"/boot/vmlinuz-5.14.0-427.31.1.el9_4.aarch64+debug", "/boot/vmlinuz-5.14.0-427.31.1.el9_4.aarch64+debug",
@@ -56,12 +56,12 @@ image2 /boot/vmlinuz-5.14.0-427.31.1.el9_4.aarch64+debug 5.14.0-427.31.1
}, },
"single image": { "single image": {
files: map[string][]string{ files: map[string][]string{
"image1": []string{ "image1": {
"/boot/vmlinuz-5.14.0-427.18.1.el9_4.x86_64", "/boot/vmlinuz-5.14.0-427.18.1.el9_4.x86_64",
"/boot/vmlinuz-5.14.0-427.24.1.el9_4.x86_64", "/boot/vmlinuz-5.14.0-427.24.1.el9_4.x86_64",
"/boot/vmlinuz-4.14.0-427.18.1.el8_4.x86_64", "/boot/vmlinuz-4.14.0-427.18.1.el8_4.x86_64",
}, },
"image2": []string{ "image2": {
"/boot/vmlinuz-0-rescue-eb46964329b146e39518c625feab3ea0", "/boot/vmlinuz-0-rescue-eb46964329b146e39518c625feab3ea0",
"/boot/vmlinuz-5.14.0-362.24.1.el9_3.aarch64", "/boot/vmlinuz-5.14.0-362.24.1.el9_3.aarch64",
"/boot/vmlinuz-5.14.0-427.31.1.el9_4.aarch64+debug", "/boot/vmlinuz-5.14.0-427.31.1.el9_4.aarch64+debug",

View File

@@ -14,7 +14,7 @@ var (
Long: "This command lists the kernels that are available in the imported images.", Long: "This command lists the kernels that are available in the imported images.",
RunE: CobraRunE, RunE: CobraRunE,
Aliases: []string{"kernel"}, Aliases: []string{"kernel"},
ValidArgsFunction: completions.Images, ValidArgsFunction: completions.Images(0), // no limit
} }
) )

View File

@@ -24,9 +24,13 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
if err != nil { if err != nil {
return err return err
} }
if vars.full { if vars.full {
t.AddHeader("IMAGE NAME", "NODES", "KERNEL VERSION", "CREATION TIME", "MODIFICATION TIME", "SIZE") t.AddHeader("IMAGE NAME", "NODES", "KERNEL VERSION", "CREATION TIME", "MODIFICATION TIME", "SIZE")
for i := 0; i < len(imageInfo); i++ { for i := 0; i < len(imageInfo); i++ {
if len(args) > 0 && !util.InSlice(args, imageInfo[i].Name) {
continue
}
createTime := time.Unix(int64(imageInfo[i].CreateDate), 0) createTime := time.Unix(int64(imageInfo[i].CreateDate), 0)
modTime := time.Unix(int64(imageInfo[i].ModDate), 0) modTime := time.Unix(int64(imageInfo[i].ModDate), 0)
sz := util.ByteToString(int64(imageInfo[i].ImgSize)) sz := util.ByteToString(int64(imageInfo[i].ImgSize))
@@ -53,6 +57,9 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
} else if vars.kernel { } else if vars.kernel {
t.AddHeader("IMAGE NAME", "NODES", "KERNEL VERSION") t.AddHeader("IMAGE NAME", "NODES", "KERNEL VERSION")
for i := 0; i < len(imageInfo); i++ { for i := 0; i < len(imageInfo); i++ {
if len(args) > 0 && !util.InSlice(args, imageInfo[i].Name) {
continue
}
t.AddLine( t.AddLine(
imageInfo[i].Name, imageInfo[i].Name,
strconv.FormatUint(uint64(imageInfo[i].NodeCount), 10), strconv.FormatUint(uint64(imageInfo[i].NodeCount), 10),
@@ -62,6 +69,9 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
} else if showSize { } else if showSize {
t.AddHeader("IMAGE NAME", "NODES", "SIZE") t.AddHeader("IMAGE NAME", "NODES", "SIZE")
for i := 0; i < len(imageInfo); i++ { for i := 0; i < len(imageInfo); i++ {
if len(args) > 0 && !util.InSlice(args, imageInfo[i].Name) {
continue
}
sz := util.ByteToString(int64(imageInfo[i].ImgSize)) sz := util.ByteToString(int64(imageInfo[i].ImgSize))
if vars.compressed { if vars.compressed {
sz = util.ByteToString(int64(imageInfo[i].ImgSizeComp)) sz = util.ByteToString(int64(imageInfo[i].ImgSizeComp))
@@ -84,9 +94,15 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
} }
} else { } else {
t.AddHeader("IMAGE NAME") t.AddHeader("IMAGE NAME")
list, _ := image.ListSources() list, err := image.ListSources()
for _, cont := range list { if err != nil {
t.AddLine(cont) return err
}
for _, name := range list {
if len(args) > 0 && !util.InSlice(args, name) {
continue
}
t.AddLine(name)
} }
} }
t.Print() t.Print()

View File

@@ -2,6 +2,7 @@ package list
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/warewulf/warewulf/internal/app/wwctl/completions"
) )
type variables struct { type variables struct {
@@ -22,6 +23,7 @@ func GetCommand() *cobra.Command {
Long: "This command will show you the images that are imported into Warewulf.", Long: "This command will show you the images that are imported into Warewulf.",
RunE: CobraRunE(&vars), RunE: CobraRunE(&vars),
Aliases: []string{"ls"}, Aliases: []string{"ls"},
ValidArgsFunction: completions.Images(0), // no limit
} }
baseCmd.PersistentFlags().BoolVarP(&vars.full, "long", "l", false, "show all") baseCmd.PersistentFlags().BoolVarP(&vars.full, "long", "l", false, "show all")
baseCmd.PersistentFlags().BoolVarP(&vars.kernel, "kernel", "k", false, "show kernel version") baseCmd.PersistentFlags().BoolVarP(&vars.kernel, "kernel", "k", false, "show kernel version")

View File

@@ -11,10 +11,6 @@ import (
) )
func CobraRunE(cmd *cobra.Command, args []string) (err error) { func CobraRunE(cmd *cobra.Command, args []string) (err error) {
if len(args) != 2 {
return fmt.Errorf("rename requires 2 arguments: %d provided", len(args))
}
crp := &wwapiv1.ImageRenameParameter{ crp := &wwapiv1.ImageRenameParameter{
ImageName: args[0], ImageName: args[0],
TargetName: args[1], TargetName: args[1],

View File

@@ -2,7 +2,7 @@ package rename
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/warewulf/warewulf/internal/pkg/image" "github.com/warewulf/warewulf/internal/app/wwctl/completions"
) )
var baseCmd = &cobra.Command{ var baseCmd = &cobra.Command{
@@ -12,14 +12,8 @@ var baseCmd = &cobra.Command{
Short: "Rename an existing image", Short: "Rename an existing image",
Long: "This command will rename an existing image.", Long: "This command will rename an existing image.",
RunE: CobraRunE, RunE: CobraRunE,
Args: cobra.MinimumNArgs(2), Args: cobra.ExactArgs(2),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: completions.Images(1),
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := image.ListSources()
return list, cobra.ShellCompDirectiveNoFileComp
},
} }
var SetBuild bool var SetBuild bool

View File

@@ -11,36 +11,39 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
cntexec "github.com/warewulf/warewulf/internal/app/wwctl/image/exec" cntexec "github.com/warewulf/warewulf/internal/app/wwctl/image/exec"
"github.com/warewulf/warewulf/internal/pkg/image" "github.com/warewulf/warewulf/internal/pkg/image"
"github.com/warewulf/warewulf/internal/pkg/util"
"github.com/warewulf/warewulf/internal/pkg/wwlog" "github.com/warewulf/warewulf/internal/pkg/wwlog"
) )
func CobraRunE(cmd *cobra.Command, args []string) error { func CobraRunE(cmd *cobra.Command, args []string) error {
imageName := args[0] imageName := args[0]
var allargs []string
if !image.ValidSource(imageName) { if !image.ValidSource(imageName) {
return fmt.Errorf("unknown Warewulf image: %s", imageName) return fmt.Errorf("unknown Warewulf image: %s", imageName)
} }
shellName := os.Getenv("SHELL")
if !image.ValidSource(imageName) { if !image.ValidSource(imageName) {
return fmt.Errorf("unknown Warewulf image: %s", imageName) return fmt.Errorf("unknown Warewulf image: %s", imageName)
} }
var shells []string var shells []string
if shellName == "" { if os.Getenv("SHELL") != "" {
shells = append(shells, "/bin/bash") shells = append(shells, os.Getenv("SHELL"))
} else {
shells = append(shells, shellName, "/bin/bash")
} }
shells = append(shells, "/bin/bash", "/bin/sh")
shellName := ""
for _, s := range shells { for _, s := range shells {
if _, err := os.Stat(path.Join(image.RootFsDir(imageName), s)); err == nil { if util.IsFile(path.Join(image.RootFsDir(imageName), s)) {
shellName = s shellName = s
break break
} }
} }
if shellName == "" {
return fmt.Errorf("no shell found in image: %s", imageName)
}
args = append(args, shellName) args = append(args, shellName)
allargs = append(allargs, args...) wwlog.Debug("%s: exec with args: %v", imageName, args)
wwlog.Debug("Calling exec with args: %s", allargs)
cntexec.SetBinds(binds) cntexec.SetBinds(binds)
cntexec.SetNode(nodeName) cntexec.SetNode(nodeName)
cntexec.SyncUser = syncUser cntexec.SyncUser = syncUser
@@ -48,5 +51,5 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
if cntexec.Build { if cntexec.Build {
wwlog.Info("Image build will be skipped if the shell ends with a non-zero exit code.") wwlog.Info("Image build will be skipped if the shell ends with a non-zero exit code.")
} }
return cntexec.CobraRunE(cmd, allargs) return cntexec.CobraRunE(cmd, args)
} }

View File

@@ -13,7 +13,7 @@ var (
Long: "Run a interactive shell inside of a warewulf IMAGE.\n", Long: "Run a interactive shell inside of a warewulf IMAGE.\n",
Aliases: []string{"chroot"}, Aliases: []string{"chroot"},
RunE: CobraRunE, RunE: CobraRunE,
Args: cobra.MinimumNArgs(1), Args: cobra.ExactArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp

View File

@@ -10,7 +10,6 @@ import (
) )
func CobraRunE(cmd *cobra.Command, args []string) (err error) { func CobraRunE(cmd *cobra.Command, args []string) (err error) {
csp := &wwapiv1.ImageShowParameter{ csp := &wwapiv1.ImageShowParameter{
ImageName: args[0], ImageName: args[0],
} }

View File

@@ -2,7 +2,7 @@ package show
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/warewulf/warewulf/internal/pkg/image" "github.com/warewulf/warewulf/internal/app/wwctl/completions"
) )
var ( var (
@@ -12,16 +12,9 @@ var (
Short: "Show root fs dir for image", Short: "Show root fs dir for image",
Long: `Shows the base directory for the chroot of the given image. Long: `Shows the base directory for the chroot of the given image.
More information about the image can be shown with the '-a' option.`, More information about the image can be shown with the '-a' option.`,
RunE: CobraRunE, RunE: CobraRunE,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: completions.Images(0), // no limit
if len(args) != 0 { Args: cobra.ExactArgs(1),
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := image.ListSources()
return list, cobra.ShellCompDirectiveNoFileComp
},
Args: cobra.MinimumNArgs(1),
} }
ShowAll bool ShowAll bool
) )