如何使用 kubernetes client-go 将文件复制到容器?

Kyr*_*roy 2 go kubernetes client-go

我想使用https://github.com/kubernetes/client-go将文件从我的文件系统复制到容器,反之亦然。

kubectl cp <file-spec-src> <file-spec-dest> -c <specific-container>
Run Code Online (Sandbox Code Playgroud)

go 客户端中是否有封装调用的函数?或者我可以使用类似RESTClient的东西吗?

小智 9

有代码使用client-go实现将文件复制到容器,也可以从容器复制文件。

\n

https://github.com/ica10888/client-go-helper/blob/master/pkg/kubectl/cp.go

\n
\xe2\x94\x9c\xe2\x94\x80kubectl\n   \xe2\x94\x82  client.go\n   \xe2\x94\x82  cp.go\n   \xe2\x94\x94\xe2\x94\x80 stub.s\n\n
Run Code Online (Sandbox Code Playgroud)\n

客户

\n
package kubectl \n\nimport (\n    corev1client "k8s.io/client-go/kubernetes/typed/core/v1"\n    "k8s.io/client-go/rest"\n    "k8s.io/client-go/tools/clientcmd"\n)\n\n\nfunc InitRestClient() (*rest.Config, error, *corev1client.CoreV1Client) {\n    // Instantiate loader for kubeconfig file.\n    kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(\n        clientcmd.NewDefaultClientConfigLoadingRules(),\n        &clientcmd.ConfigOverrides{},\n    )\n    // Get a rest.Config from the kubeconfig file.  This will be passed into all\n    // the client objects we create.\n    restconfig, err := kubeconfig.ClientConfig()\n    if err != nil {\n        panic(err)\n    }\n    // Create a Kubernetes core/v1 client.\n    coreclient, err := corev1client.NewForConfig(restconfig)\n    if err != nil {\n        panic(err)\n    }\n    return restconfig, err, coreclient\n}\n
Run Code Online (Sandbox Code Playgroud)\n

copyToPod 和 copyFromPod

\n
package kubectl\n\nimport (\n    "archive/tar"\n    "fmt"\n    "io"\n    corev1 "k8s.io/api/core/v1"\n    "k8s.io/client-go/kubernetes/scheme"\n    "k8s.io/client-go/tools/remotecommand"\n    _ "k8s.io/kubectl/pkg/cmd/cp"\n    cmdutil "k8s.io/kubectl/pkg/cmd/util"\n    "log"\n    "os"\n    "path"\n    "path/filepath"\n    "strings"\n    _ "unsafe"\n)\n\nfunc (i *pod) copyToPod(srcPath string, destPath string) error {\n    restconfig, err, coreclient := InitRestClient()\n\n    reader, writer := io.Pipe()\n    if destPath != "/" && strings.HasSuffix(string(destPath[len(destPath)-1]), "/") {\n        destPath = destPath[:len(destPath)-1]\n    }\n    if err := checkDestinationIsDir(i, destPath); err == nil {\n        destPath = destPath + "/" + path.Base(srcPath)\n    }\n    go func() {\n        defer writer.Close()\n        err := cpMakeTar(srcPath, destPath, writer)\n        cmdutil.CheckErr(err)\n    }()\n    var cmdArr []string\n\n    cmdArr = []string{"tar", "-xf", "-"}\n    destDir := path.Dir(destPath)\n    if len(destDir) > 0 {\n        cmdArr = append(cmdArr, "-C", destDir)\n    }\n    //remote shell.\n    req := coreclient.RESTClient().\n        Post().\n        Namespace(i.Namespace).\n        Resource("pods").\n        Name(i.Name).\n        SubResource("exec").\n        VersionedParams(&corev1.PodExecOptions{\n            Container: i.ContainerName,\n            Command:   cmdArr,\n            Stdin:     true,\n            Stdout:    true,\n            Stderr:    true,\n            TTY:       false,\n        }, scheme.ParameterCodec)\n\n    exec, err := remotecommand.NewSPDYExecutor(restconfig, "POST", req.URL())\n    if err != nil {\n        log.Fatalf("error %s\\n", err)\n        return err\n    }\n    err = exec.Stream(remotecommand.StreamOptions{\n        Stdin:  reader,\n        Stdout: os.Stdout,\n        Stderr: os.Stderr,\n        Tty:    false,\n    })\n    if err != nil {\n        log.Fatalf("error %s\\n", err)\n        return err\n    }\n    return nil\n}\n\nfunc checkDestinationIsDir(i *pod, destPath string) error {\n    return i.Exec([]string{"test", "-d", destPath})\n}\n\n//go:linkname cpMakeTar k8s.io/kubectl/pkg/cmd/cp.makeTar\nfunc cpMakeTar(srcPath, destPath string, writer io.Writer) error\n\nfunc (i *pod) copyFromPod(srcPath string, destPath string) error {\n    restconfig, err, coreclient := InitRestClient()\n    reader, outStream := io.Pipe()\n    //todo some containers failed : tar: Refusing to write archive contents to terminal (missing -f option?) when execute `tar cf -` in container\n    cmdArr := []string{"tar", "cf", "-", srcPath}\n    req := coreclient.RESTClient().\n        Get().\n        Namespace(i.Namespace).\n        Resource("pods").\n        Name(i.Name).\n        SubResource("exec").\n        VersionedParams(&corev1.PodExecOptions{\n            Container: i.ContainerName,\n            Command:   cmdArr,\n            Stdin:     true,\n            Stdout:    true,\n            Stderr:    true,\n            TTY:       false,\n        }, scheme.ParameterCodec)\n\n    exec, err := remotecommand.NewSPDYExecutor(restconfig, "POST", req.URL())\n    if err != nil {\n        log.Fatalf("error %s\\n", err)\n        return err\n    }\n    go func() {\n        defer outStream.Close()\n        err = exec.Stream(remotecommand.StreamOptions{\n            Stdin:  os.Stdin,\n            Stdout: outStream,\n            Stderr: os.Stderr,\n            Tty:    false,\n        })\n        cmdutil.CheckErr(err)\n    }()\n    prefix := getPrefix(srcPath)\n    prefix = path.Clean(prefix)\n    prefix = cpStripPathShortcuts(prefix)\n    destPath = path.Join(destPath, path.Base(prefix))\n    err = untarAll(reader, destPath, prefix)\n    return err\n}\n\nfunc untarAll(reader io.Reader, destDir, prefix string) error {\n    tarReader := tar.NewReader(reader)\n    for {\n        header, err := tarReader.Next()\n        if err != nil {\n            if err != io.EOF {\n                return err\n            }\n            break\n        }\n\n        if !strings.HasPrefix(header.Name, prefix) {\n            return fmt.Errorf("tar contents corrupted")\n        }\n\n        mode := header.FileInfo().Mode()\n        destFileName := filepath.Join(destDir, header.Name[len(prefix):])\n\n        baseName := filepath.Dir(destFileName)\n        if err := os.MkdirAll(baseName, 0755); err != nil {\n            return err\n        }\n        if header.FileInfo().IsDir() {\n            if err := os.MkdirAll(destFileName, 0755); err != nil {\n                return err\n            }\n            continue\n        }\n\n        evaledPath, err := filepath.EvalSymlinks(baseName)\n        if err != nil {\n            return err\n        }\n\n        if mode&os.ModeSymlink != 0 {\n            linkname := header.Linkname\n\n            if !filepath.IsAbs(linkname) {\n                _ = filepath.Join(evaledPath, linkname)\n            }\n\n            if err := os.Symlink(linkname, destFileName); err != nil {\n                return err\n            }\n        } else {\n            outFile, err := os.Create(destFileName)\n            if err != nil {\n                return err\n            }\n            defer outFile.Close()\n            if _, err := io.Copy(outFile, tarReader); err != nil {\n                return err\n            }\n            if err := outFile.Close(); err != nil {\n                return err\n            }\n        }\n    }\n\n    return nil\n}\n\nfunc getPrefix(file string) string {\n    return strings.TrimLeft(file, "/")\n}\n\n//go:linkname cpStripPathShortcuts k8s.io/kubectl/pkg/cmd/cp.stripPathShortcuts \nfunc cpStripPathShortcuts(p string) string\n\n\n
Run Code Online (Sandbox Code Playgroud)\n

触摸存根

\n
\n
Run Code Online (Sandbox Code Playgroud)\n


Nic*_*ico 8

由于这个问题的答案已经很老了,所以我是这样做的:

package main

import (
  "bytes"
  "fmt"
  "io"
  "k8s.io/apimachinery/pkg/runtime/schema"
  "k8s.io/apimachinery/pkg/runtime/serializer"
  "k8s.io/cli-runtime/pkg/genericclioptions"
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/kubernetes/scheme"
  "k8s.io/client-go/rest"
  "k8s.io/kubectl/pkg/cmd/cp"
  "k8s.io/kubectl/pkg/cmd/exec"
  "log"
  "os"
)

type PodExec struct {
  RestConfig *rest.Config
  *kubernetes.Clientset
}
func NewPodExec(config rest.Config, clientset *kubernetes.Clientset) *PodExec {
  config.APIPath = "/api" // Make sure we target /api and not just /
  config.GroupVersion = &schema.GroupVersion{Version: "v1"} // this targets the core api groups so the url path will be /api/v1
  config.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs}
  return &PodExec{
    RestConfig: &config,
    Clientset:  clientset,
  }  
}

func (p *PodExec) PodCopyFile(src string, dst string, containername string) (*bytes.Buffer, *bytes.Buffer, *bytes.Buffer, error) {
  ioStreams, in, out, errOut := genericclioptions.NewTestIOStreams()
  copyOptions := cp.NewCopyOptions(ioStreams)
  copyOptions.Clientset = p.Clientset
  copyOptions.ClientConfig = p.RestConfig
  copyOptions.Container = containername
  err := copyOptions.Run([]string{src, dst})
  if err != nil {
    return nil, nil, nil, fmt.Errorf("Could not run copy operation: %v", err)
  }
  return in, out, errOut, nil
}
Run Code Online (Sandbox Code Playgroud)

然后您可以像 kubectl cp 一样使用 PodCopyFile

podExec := podexec.NewPodExec(*restconfig, clientset) // Here, you need to get your restconfig and clientset from either ~/.kube/config or built-in pod config.
_, out, _, err := podExec.PodCopyFile("/srcfile", "/dstfile", "containername")
if err != nil {
    fmt.Printf("%v\n", err)
}
fmt.Println("out:")
fmt.Printf("%s", out.String())
Run Code Online (Sandbox Code Playgroud)