diff --git a/pkg/operator/ceph/object/admin.go b/pkg/operator/ceph/object/admin.go index 67ab074a3..f26f80a62 100644 --- a/pkg/operator/ceph/object/admin.go +++ b/pkg/operator/ceph/object/admin.go @@ -21,6 +21,7 @@ import ( "fmt" "net/http" "net/http/httputil" + "path/filepath" "regexp" "strings" @@ -230,6 +231,31 @@ func RunAdminCommandNoMultisite(c *Context, expectJSON bool, args ...string) (st // If Multus is enabled we proxy all the command to the mgr sidecar if c.clusterInfo.NetworkSpec.IsMultus() { + // check if remote command arguments contains file path: + srcFile, dstFile := "", "" + for i, arg := range args { + if strings.HasPrefix(arg, "--infile=") { + srcFile = strings.TrimPrefix(arg, "--infile=") + // place dest file to tmp dir and update cmd argument + dstFile = "/tmp/" + filepath.Base(srcFile) + args[i] = strings.ReplaceAll(arg, srcFile, dstFile) + } + } + if srcFile != "" { + // remote command contains file as argument + // copy file to remote container + err = c.Context.RemoteExecutor.CopyLocalFileToContainer(c.clusterInfo.Context, cephclient.ProxyAppLabel, cephclient.CommandProxyInitContainerName, c.clusterInfo.Namespace, srcFile, dstFile) + if err != nil { + return "", err + } + defer func() { + // cleanup copied file in remote container + _, stdErr, cleanupErr := c.Context.RemoteExecutor.ExecCommandInContainerWithFullOutput(c.clusterInfo.Context, cephclient.ProxyAppLabel, cephclient.CommandProxyInitContainerName, c.clusterInfo.Namespace, []string{"rm", dstFile}...) + if cleanupErr != nil { + logger.Errorf("failed to cleanup remote file %q: %s - %s", dstFile, cleanupErr, stdErr) + } + }() + } output, stderr, err = c.Context.RemoteExecutor.ExecCommandInContainerWithFullOutputWithTimeout(c.clusterInfo.Context, cephclient.ProxyAppLabel, cephclient.CommandProxyInitContainerName, c.clusterInfo.Namespace, append([]string{"radosgw-admin"}, args...)...) } else { command, args := cephclient.FinalizeCephCommandArgs("radosgw-admin", c.clusterInfo, args, c.Context.ConfigDir) diff --git a/pkg/util/exec/exec_pod.go b/pkg/util/exec/exec_pod.go index 3445472af..9ebad7f02 100644 --- a/pkg/util/exec/exec_pod.go +++ b/pkg/util/exec/exec_pod.go @@ -25,6 +25,7 @@ import ( "io" "net/http" "net/url" + "os" "strconv" "strings" @@ -133,3 +134,33 @@ func execute(ctx context.Context, method string, url *url.URL, config *rest.Conf func (e *RemotePodCommandExecutor) ExecCommandInContainerWithFullOutputWithTimeout(ctx context.Context, appLabel, containerName, namespace string, cmd ...string) (string, string, error) { return e.ExecCommandInContainerWithFullOutput(ctx, appLabel, containerName, namespace, append([]string{"timeout", strconv.Itoa(int(CephCommandsTimeout.Seconds()))}, cmd...)...) } + +func (e *RemotePodCommandExecutor) CopyLocalFileToContainer(ctx context.Context, appLabel, containerName, namespace string, srcPath, dstPath string) error { + options := metav1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", appLabel)} + pods, err := e.ClientSet.CoreV1().Pods(namespace).List(ctx, options) + if err != nil { + return err + } + if len(pods.Items) == 0 { + return errors.Errorf("no pods found with selector %q", appLabel) + } + file, err := os.Open(srcPath) + if err != nil { + return fmt.Errorf("failed to open local file: %w", err) + } + defer file.Close() + stdOut, stdErr, err := e.ExecWithOptions(ctx, ExecOptions{ + Command: []string{"sh", "-c", fmt.Sprintf("cat - > %s", dstPath)}, + Namespace: namespace, + PodName: pods.Items[0].Name, + ContainerName: containerName, + Stdin: file, + CaptureStdout: true, + CaptureStderr: true, + PreserveWhitespace: false, + }) + if err != nil { + return fmt.Errorf("%w: unable to copy file, stdOut=%q, stdErr=%q", err, stdOut, stdErr) + } + return nil +}