我想使用https://github.com/kubernetes/client-go将文件从我的文件系统复制到容器,反之亦然。
kubectl cp <file-spec-src> <file-spec-dest> -c <specific-container>
go 客户端中是否有包装调用的函数?或者我可以使用类似RESTClient的东西吗?
我想使用https://github.com/kubernetes/client-go将文件从我的文件系统复制到容器,反之亦然。
kubectl cp <file-spec-src> <file-spec-dest> -c <specific-container>
go 客户端中是否有包装调用的函数?或者我可以使用类似RESTClient的东西吗?
有使用client-go的代码实现将文件复制到容器,也可以从容器中复制文件。
https://github.com/ica10888/client-go-helper/blob/master/pkg/kubectl/cp.go
├─kubectl
│ client.go
│ cp.go
└─ stub.s
客户
package kubectl
import (
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
func InitRestClient() (*rest.Config, error, *corev1client.CoreV1Client) {
// Instantiate loader for kubeconfig file.
kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(),
&clientcmd.ConfigOverrides{},
)
// Get a rest.Config from the kubeconfig file. This will be passed into all
// the client objects we create.
restconfig, err := kubeconfig.ClientConfig()
if err != nil {
panic(err)
}
// Create a Kubernetes core/v1 client.
coreclient, err := corev1client.NewForConfig(restconfig)
if err != nil {
panic(err)
}
return restconfig, err, coreclient
}
copyToPod 和 copyFromPod
package kubectl
import (
"archive/tar"
"fmt"
"io"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/remotecommand"
_ "k8s.io/kubectl/pkg/cmd/cp"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"log"
"os"
"path"
"path/filepath"
"strings"
_ "unsafe"
)
func (i *pod) copyToPod(srcPath string, destPath string) error {
restconfig, err, coreclient := InitRestClient()
reader, writer := io.Pipe()
if destPath != "/" && strings.HasSuffix(string(destPath[len(destPath)-1]), "/") {
destPath = destPath[:len(destPath)-1]
}
if err := checkDestinationIsDir(i, destPath); err == nil {
destPath = destPath + "/" + path.Base(srcPath)
}
go func() {
defer writer.Close()
err := cpMakeTar(srcPath, destPath, writer)
cmdutil.CheckErr(err)
}()
var cmdArr []string
cmdArr = []string{"tar", "-xf", "-"}
destDir := path.Dir(destPath)
if len(destDir) > 0 {
cmdArr = append(cmdArr, "-C", destDir)
}
//remote shell.
req := coreclient.RESTClient().
Post().
Namespace(i.Namespace).
Resource("pods").
Name(i.Name).
SubResource("exec").
VersionedParams(&corev1.PodExecOptions{
Container: i.ContainerName,
Command: cmdArr,
Stdin: true,
Stdout: true,
Stderr: true,
TTY: false,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(restconfig, "POST", req.URL())
if err != nil {
log.Fatalf("error %s\n", err)
return err
}
err = exec.Stream(remotecommand.StreamOptions{
Stdin: reader,
Stdout: os.Stdout,
Stderr: os.Stderr,
Tty: false,
})
if err != nil {
log.Fatalf("error %s\n", err)
return err
}
return nil
}
func checkDestinationIsDir(i *pod, destPath string) error {
return i.Exec([]string{"test", "-d", destPath})
}
//go:linkname cpMakeTar k8s.io/kubectl/pkg/cmd/cp.makeTar
func cpMakeTar(srcPath, destPath string, writer io.Writer) error
func (i *pod) copyFromPod(srcPath string, destPath string) error {
restconfig, err, coreclient := InitRestClient()
reader, outStream := io.Pipe()
//todo some containers failed : tar: Refusing to write archive contents to terminal (missing -f option?) when execute `tar cf -` in container
cmdArr := []string{"tar", "cf", "-", srcPath}
req := coreclient.RESTClient().
Get().
Namespace(i.Namespace).
Resource("pods").
Name(i.Name).
SubResource("exec").
VersionedParams(&corev1.PodExecOptions{
Container: i.ContainerName,
Command: cmdArr,
Stdin: true,
Stdout: true,
Stderr: true,
TTY: false,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(restconfig, "POST", req.URL())
if err != nil {
log.Fatalf("error %s\n", err)
return err
}
go func() {
defer outStream.Close()
err = exec.Stream(remotecommand.StreamOptions{
Stdin: os.Stdin,
Stdout: outStream,
Stderr: os.Stderr,
Tty: false,
})
cmdutil.CheckErr(err)
}()
prefix := getPrefix(srcPath)
prefix = path.Clean(prefix)
prefix = cpStripPathShortcuts(prefix)
destPath = path.Join(destPath, path.Base(prefix))
err = untarAll(reader, destPath, prefix)
return err
}
func untarAll(reader io.Reader, destDir, prefix string) error {
tarReader := tar.NewReader(reader)
for {
header, err := tarReader.Next()
if err != nil {
if err != io.EOF {
return err
}
break
}
if !strings.HasPrefix(header.Name, prefix) {
return fmt.Errorf("tar contents corrupted")
}
mode := header.FileInfo().Mode()
destFileName := filepath.Join(destDir, header.Name[len(prefix):])
baseName := filepath.Dir(destFileName)
if err := os.MkdirAll(baseName, 0755); err != nil {
return err
}
if header.FileInfo().IsDir() {
if err := os.MkdirAll(destFileName, 0755); err != nil {
return err
}
continue
}
evaledPath, err := filepath.EvalSymlinks(baseName)
if err != nil {
return err
}
if mode&os.ModeSymlink != 0 {
linkname := header.Linkname
if !filepath.IsAbs(linkname) {
_ = filepath.Join(evaledPath, linkname)
}
if err := os.Symlink(linkname, destFileName); err != nil {
return err
}
} else {
outFile, err := os.Create(destFileName)
if err != nil {
return err
}
defer outFile.Close()
if _, err := io.Copy(outFile, tarReader); err != nil {
return err
}
if err := outFile.Close(); err != nil {
return err
}
}
}
return nil
}
func getPrefix(file string) string {
return strings.TrimLeft(file, "/")
}
//go:linkname cpStripPathShortcuts k8s.io/kubectl/pkg/cmd/cp.stripPathShortcuts
func cpStripPathShortcuts(p string) string
触摸存根
由于这个问题的答案很老,所以我是这样做的:
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
}
然后你可以像 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.ExecCmd([]string{"ls", "-l", "/tmp"}, "namespace", "podname", "containername")
if err != nil {
fmt.Printf("%v\n", err)
}
fmt.Println("out:")
fmt.Printf("%s", out.String())
具有讽刺意味的是,今天有人支持我对 Java 的这个问题的回答。我还没有打开repo 看看,但如果它比 Java 库更client-go
暴露命令,我会非常非常惊讶。cp