From 3735e3b2f17b4c8cf83894dcb507b7632ea48ef9 Mon Sep 17 00:00:00 2001 From: charvenetis Date: Wed, 4 Mar 2026 17:12:54 +0200 Subject: [PATCH] feat: support multiple source files in mgrctl cp (Fixes #657) Allow passing multiple source file paths to `mgrctl cp`. The last argument is treated as the destination. All sources must be consistently either local paths or server: prefixed paths; mixing is not allowed. Update the Use string and Long description to reflect the new multi-source behavior. Add validation to reject mixed local and server source paths. --- mgrctl/cmd/cp/cp.go | 31 ++++++++++++++++--- uyuni-tools.changes.yatesmold.multi-source-cp | 1 + 2 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 uyuni-tools.changes.yatesmold.multi-source-cp diff --git a/mgrctl/cmd/cp/cp.go b/mgrctl/cmd/cp/cp.go index a7a6d76a70e..4a0d68d2564 100644 --- a/mgrctl/cmd/cp/cp.go +++ b/mgrctl/cmd/cp/cp.go @@ -1,10 +1,14 @@ // SPDX-FileCopyrightText: 2024 SUSE LLC +// SPDX-FileCopyrightText: 2026 YatesMold // // SPDX-License-Identifier: Apache-2.0 package cp import ( + "fmt" + "strings" + "github.com/spf13/cobra" "github.com/uyuni-project/uyuni-tools/shared" "github.com/uyuni-project/uyuni-tools/shared/kubernetes" @@ -25,11 +29,12 @@ func NewCommand(globalFlags *types.GlobalFlags) *cobra.Command { flags := &flagpole{} cpCmd := &cobra.Command{ - Use: "cp [path/to/source.file] [path/to/destination.file]", + Use: "cp [path/to/source1.file ...] [path/to/destination]", Short: L("Copy files to and from the containers"), - Long: L(`Takes a source and destination parameters. - One of them can be prefixed with 'server:' to indicate the path is within the server pod.`), - Args: cobra.ExactArgs(2), + Long: L(`Takes at least one source and destination parameters. + Several source files can be passed, the last parameter will be the destination. + Either the source parameters or the destination can be prefixed with 'server:' to indicate the path is within the server pod.`), + Args: cobra.MinimumNArgs(2), RunE: func(cmd *cobra.Command, args []string) error { viper, err := utils.ReadConfig(cmd, utils.GlobalConfigFilename, globalFlags.ConfigPath) if err != nil { @@ -51,5 +56,21 @@ func NewCommand(globalFlags *types.GlobalFlags) *cobra.Command { func run(flags *flagpole, _ *cobra.Command, args []string) error { cnx := shared.NewConnection(flags.Backend, podman.ServerContainerName, kubernetes.ServerFilter) - return cnx.Copy(args[0], args[1], flags.User, flags.Group) + dst := args[len(args)-1] + srcs := args[:len(args)-1] + + // Validate that source paths don't mix local and server paths. + hasServerSrc := strings.HasPrefix(srcs[0], "server:") + for _, src := range srcs[1:] { + if strings.HasPrefix(src, "server:") != hasServerSrc { + return fmt.Errorf(L("cannot mix local and server sources")) + } + } + + for _, src := range srcs { + if err := cnx.Copy(src, dst, flags.User, flags.Group); err != nil { + return err + } + } + return nil } diff --git a/uyuni-tools.changes.yatesmold.multi-source-cp b/uyuni-tools.changes.yatesmold.multi-source-cp new file mode 100644 index 00000000000..7064b58d0be --- /dev/null +++ b/uyuni-tools.changes.yatesmold.multi-source-cp @@ -0,0 +1 @@ +- Support multiple source files in mgrctl cp (gh#657)