当前位置: 首页 > article >正文

【玩转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)
		}
	}
}


http://www.kler.cn/a/146946.html

相关文章:

  • 支付域——新零售支付
  • 《基于 PySpark 的电影推荐系统分析及问题解决》
  • Mac——基本操作使用整理
  • 深度学习transformer
  • 【设计模式】行为型模式(五):解释器模式、访问者模式、依赖注入
  • 李秀贤主演警匪片《蓝色霹雳火》
  • Android 13.0 开机过滤部分通知声音(莫名其妙的通知声音)
  • 蓝桥杯官网算法赛(蓝桥小课堂)
  • 做直播服务器要什么样的配置呢?
  • C语言做一个恶作剧关机程序
  • 大数据Doris(三十):删除数据(Delete)
  • RK356x U-Boot研究所(命令篇)3.11 mmc命令的用法
  • 论文阅读:“Model-based teeth reconstruction”
  • Python BDD 框架比较之 pytest-bdd vs behave
  • 14. 最长公共前缀
  • vue 中 asstes 和 static 有什么联系与区别
  • python-opencv 人脸68点特征点检测
  • git日历坐标系? 手动实现github活跃/贡献图
  • Android UnsatisfiedLinkError问题定位
  • AIGC系列之:DDPM原理解读(简单易懂版)
  • C语言——打印出所有的“水仙花数”
  • Day49:647. 回文子串、516.最长回文子序列
  • WPF实战项目十七(客户端):数据等待加载弹框动画
  • 「Linux」git的安装与使用
  • Android 12 打开网络ADB并禁用USB连接ADB
  • Ubuntu新手使用教程