【玩转client-go】使用client-go从POD拷贝文件出来

先讲思路,通过client-go RestClient模拟kubectl exec 的手法,结合容器镜像里的tar命令和golang原生tar包,作为管道的输入输出,实现文件的数据流拷贝,以下是具体做法。

首先是一段将文件内容输出到标准输出的代码

package main

import (
	"context"
	"github.com/octoboy233/client-go-usage/config"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/remotecommand"
	"os"
)

func main() {
	cli := config.InitRestClient()
	req := cli.CoreV1().RESTClient().Post().
		Namespace("default").
		Resource("pods").
		Name("nginx-pod").
		SubResource("exec").VersionedParams(
		&corev1.PodExecOptions{
			Container: "nginx-container",
			Command:   []string{"cat", "/usr/share/nginx/html/index.html"},
			Stdout:    true,
			Stderr:    true,
		}, scheme.ParameterCodec)
	exec, err := remotecommand.NewSPDYExecutor(config.InitRestCfg(), "POST", req.URL())
	if err != nil {
		panic(err)
	}
	if err := exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
		Stdout: os.Stdout,
		Stderr: os.Stderr,
	}); err != nil {
		panic(err)
	}
}

我们在此基础上修改实现文件拷贝。

修改Command为

Command: []string{
	"/bin/bash",
	"-c",
	"cd /usr/share/nginx/html/ && tar cf - index.html"},

tar cf - index.html这条命令的含义是创建一个tar归档文件,并将名为"index.html"的文件加入到归档中。

具体解释如下:

tar: tar命令用于创建、查看和提取tar归档文件。
cf: c表示创建新的归档文件,f表示指定归档文件的名称。
-: -(连字符)表示将归档文件的输出发送到stdout(标准输出),而不是保存到文件中。
index.html: 这是要添加到归档中的文件或目录的名称。在这种情况下,它是一个文件名。
因此,运行这条命令将创建一个tar归档文件,并将名为"index.html"的文件添加到归档中。归档文件的输出将被发送到stdout(你也可以使用重定向符号将其保存到文件中)

值得一提的是 tar cd - xxxx无法指定文件的绝对路径,因此需要先定位到目标文件夹。

新建一个管道,用于接收远程执行的结果
pipReader, pipWriter := io.Pipe()
defer pipWriter.Close()
执行远程命令,将exec命令的标准输出指定向刚创建的PipeWriter
go func() {
	if err := exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
		Stdout: pipWriter,
		Stderr: os.Stderr,
	}); err != nil {
		panic(err)
	}
}()

 值得一提的是,这里需要使用协程。否则会阻塞直到PipeWriter关闭。

从管道中读取tar文件
reader := tar.NewReader(pipReader)
for {
	header, err := reader.Next()
	if err != nil {
		if err == io.EOF {
			break //文件读取完毕
		}
	}
	fmt.Println("读取到文件:", header.FileInfo().Name())
	//外部创建新文件
	file, err := os.Create(header.FileInfo().Name())
	if err != nil {
		panic(err)
	}
	//如果是文件,复制到新建的文件中
	if header.Typeflag == tar.TypeReg {
		io.Copy(file, reader)
	}
}

循环读取,并把归档输出写入新创建的文件,当出现End of File(文件结束符)错误,代表输入已完成,没有可读取的输出。退出循环。

附上完整代码

package main

import (
	"archive/tar"
	"context"
	"fmt"
	"github.com/octoboy233/client-go-usage/config"
	"io"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/remotecommand"
	"os"
)

func main() {
	cli := config.InitRestClient()
	req := cli.CoreV1().RESTClient().Post().
		Namespace("default").
		Resource("pods").
		Name("nginx-pod").
		SubResource("exec").VersionedParams(
		&corev1.PodExecOptions{
			Container: "nginx-container",
			// tar文件输出到标准输出
			Command: []string{
				"/bin/bash",
				"-c",
				"cd /usr/share/nginx/html/ && tar cf - index.html"},
			//Command: []string{"tar", "cf", "-", "test.txt"},
			Stdout: true,
			Stderr: true,
		}, scheme.ParameterCodec)
	exec, err := remotecommand.NewSPDYExecutor(config.InitRestCfg(), "POST", req.URL())
	if err != nil {
		panic(err)
	}
	//新建一个管道,用于接收远程执行的结果
	pipReader, pipWriter := io.Pipe()
	defer pipWriter.Close()
	//执行远程命令
	go func() {
		if err := exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
			Stdout: pipWriter,
			Stderr: os.Stderr,
		}); err != nil {
			panic(err)
		}
	}()
	//从管道中读取tar文件
	reader := tar.NewReader(pipReader)
	for {
		header, err := reader.Next()
		if err != nil {
			if err == io.EOF {
				fmt.Println("文件读取完毕")
				break
			}
		}
		fmt.Println("读取到文件:", header.FileInfo().Name())
		//外部创建新文件
		file, err := os.Create(header.FileInfo().Name())
		if err != nil {
			panic(err)
		}
		//如果是文件,复制到新建的文件中
		if header.Typeflag == tar.TypeReg {
			io.Copy(file, reader)
		}
	}
}

你可能感兴趣的:(k8s,Kubernetes二次开发,golang学习之路,golang,开发语言,后端,kubernetes)