kubectl源码分析之patch

Elasticsearch 报错 error At least one unigram is required but all tokens were ngrams 【代码】Elasticsearch 报错 error At least one unigram is required but all tokens were ngrams。 阅读详情

 欢迎关注我的公众号:

 目前刚开始写一个月,一共写了18篇原创文章,文章目录如下:

istio多集群探秘,部署了50次多集群后我得出的结论

istio多集群链路追踪,附实操视频

istio防故障利器,你知道几个,istio新手不要读,太难!

istio业务权限控制,原来可以这么玩

istio实现非侵入压缩,微服务之间如何实现压缩

不懂envoyfilter也敢说精通istio系列-http-rbac-不要只会用AuthorizationPolicy配置权限

不懂envoyfilter也敢说精通istio系列-02-http-corsFilter-不要只会vs

不懂envoyfilter也敢说精通istio系列-03-http-csrf filter-再也不用再代码里写csrf逻辑了

不懂envoyfilter也敢说精通istio系列http-jwt_authn-不要只会RequestAuthorization

不懂envoyfilter也敢说精通istio系列-05-fault-filter-故障注入不止是vs

不懂envoyfilter也敢说精通istio系列-06-http-match-配置路由不只是vs

不懂envoyfilter也敢说精通istio系列-07-负载均衡配置不止是dr

不懂envoyfilter也敢说精通istio系列-08-连接池和断路器

不懂envoyfilter也敢说精通istio系列-09-http-route filter

不懂envoyfilter也敢说精通istio系列-network filter-redis proxy

不懂envoyfilter也敢说精通istio系列-network filter-HttpConnectionManager

不懂envoyfilter也敢说精通istio系列-ratelimit-istio ratelimit完全手册

 

————————————————

type PatchOptions struct {//patch结构体
	resource.FilenameOptions

	RecordFlags *genericclioptions.RecordFlags
	PrintFlags  *genericclioptions.PrintFlags
	ToPrinter   func(string) (printers.ResourcePrinter, error)
	Recorder    genericclioptions.Recorder

	Local     bool
	PatchType string
	Patch     string

	namespace                    string
	enforceNamespace             bool
	dryRun                       bool
	outputFormat                 string
	args                         []string
	builder                      *resource.Builder
	unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)

	genericclioptions.IOStreams
}
func NewPatchOptions(ioStreams genericclioptions.IOStreams) *PatchOptions {
	return &PatchOptions{//初始化patch结构体
		RecordFlags: genericclioptions.NewRecordFlags(),
		Recorder:    genericclioptions.NoopRecorder{},
		PrintFlags:  genericclioptions.NewPrintFlags("patched").WithTypeSetter(scheme.Scheme),
		IOStreams:   ioStreams,
	}
}
//创建patch命令
func NewCmdPatch(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewPatchOptions(ioStreams)//初始化结构体

	cmd := &cobra.Command{//创建cobra命令
		Use:                   "patch (-f FILENAME | TYPE NAME) -p PATCH",
		DisableFlagsInUseLine: true,
		Short:                 i18n.T("Update field(s) of a resource using strategic merge patch"),
		Long:                  patchLong,
		Example:               patchExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd, args))//准备
			cmdutil.CheckErr(o.Validate())//校验
			cmdutil.CheckErr(o.RunPatch())//运行
		},
	}

	o.RecordFlags.AddFlags(cmd)//record选项
	o.PrintFlags.AddFlags(cmd)//打印选项

	cmd.Flags().StringVarP(&o.Patch, "patch", "p", "", "The patch to be applied to the resource JSON file.")//patch选项
	cmd.MarkFlagRequired("patch")//必须
	cmd.Flags().StringVar(&o.PatchType, "type", "strategic", fmt.Sprintf("The type of patch being provided; one of %v", sets.StringKeySet(patchTypes).List()))// type选项
	cmdutil.AddDryRunFlag(cmd)//干跑选项
	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to update")//文件选项
	cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, patch will operate on the content of the file, not the server-side resource.")//local选项

	return cmd
}
//准备
func (o *PatchOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	var err error
	o.RecordFlags.Complete(cmd)//record准备
	o.Recorder, err = o.RecordFlags.ToRecorder()//record flag转recorder
	if err != nil {
		return err
	}

	o.outputFormat = cmdutil.GetFlagString(cmd, "output")//设置输出选项
	o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run")//设置干跑

	o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {//print flag转printer函数
		o.PrintFlags.NamePrintFlags.Operation = operation
		if o.dryRun {
			o.PrintFlags.Complete("%s (dry run)")
		}

		return o.PrintFlags.ToPrinter()
	}

	o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()//设置namespace和enforceNamespace
	if err != nil {
		return err
	}
	o.args = args//设置参数
	o.builder = f.NewBuilder()//设置builder
	o.unstructuredClientForMapping = f.UnstructuredClientForMapping//设置unstructuredClientForMapping 

	return nil
}
//校验
func (o *PatchOptions) Validate() error {
	if o.Local && len(o.args) != 0 {// 如果指定了local则参数必须为空
		return fmt.Errorf("cannot specify --local and server resources")
	}
	if len(o.Patch) == 0 {//patch选项不能为空
		return fmt.Errorf("must specify -p to patch")
	}
	if len(o.PatchType) != 0 {//patch类型必须正确
		if _, ok := patchTypes[strings.ToLower(o.PatchType)]; !ok {
			return fmt.Errorf("--type must be one of %v, not %q", sets.StringKeySet(patchTypes).List(), o.PatchType)
		}
	}

	return nil
}
///运行
func (o *PatchOptions) RunPatch() error {
	patchType := types.StrategicMergePatchType//设置patch类型
	if len(o.PatchType) != 0 {//如果指定了patch类型,则重新设置
		patchType = patchTypes[strings.ToLower(o.PatchType)]
	}

	patchBytes, err := yaml.ToJSON([]byte(o.Patch))//获取patch字节数组
	if err != nil {
		return fmt.Errorf("unable to parse %q: %v", o.Patch, err)
	}

	r := o.builder.
		Unstructured().
		ContinueOnError().
		LocalParam(o.Local).
		NamespaceParam(o.namespace).DefaultNamespace().
		FilenameParam(o.enforceNamespace, &o.FilenameOptions).
		ResourceTypeOrNameArgs(false, o.args...).
		Flatten().
		Do()//用build构造result对象
	err = r.Err()
	if err != nil {
		return err
	}

	count := 0
	err = r.Visit(func(info *resource.Info, err error) error {//visit result
		if err != nil {
			return err
		}
		count++
		name, namespace := info.Name, info.Namespace//获取name和namespace

		if !o.Local && !o.dryRun {//如果非local且非干跑
			mapping := info.ResourceMapping()//获取mapping
			client, err := o.unstructuredClientForMapping(mapping)//通过mapping获取client
			if err != nil {
				return err
			}

			helper := resource.NewHelper(client, mapping)//构造helper
			patchedObj, err := helper.Patch(namespace, name, patchType, patchBytes, nil)//应用patch到服务端
			if err != nil {
				return err
			}

			didPatch := !reflect.DeepEqual(info.Object, patchedObj)//判断是否做了patch

			// if the recorder makes a change, compute and create another patch
			if mergePatch, err := o.Recorder.MakeRecordMergePatch(patchedObj); err != nil {//判断是否创建change-cause注解
				klog.V(4).Infof("error recording current command: %v", err)
			} else if len(mergePatch) > 0 {
				if recordedObj, err := helper.Patch(info.Namespace, info.Name, types.MergePatchType, mergePatch, nil); err != nil {//创建changecause注解
					klog.V(4).Infof("error recording reason: %v", err)
				} else {
					patchedObj = recordedObj
				}
			}

			printer, err := o.ToPrinter(patchOperation(didPatch))//print flag转peinter
			if err != nil {
				return err
			}
			return printer.PrintObj(patchedObj, o.Out)//打印对象
		}

		originalObjJS, err := runtime.Encode(unstructured.UnstructuredJSONScheme, info.Object)//原始的json
		if err != nil {
			return err
		}

		originalPatchedObjJS, err := getPatchedJSON(patchType, originalObjJS, patchBytes, info.Object.GetObjectKind().GroupVersionKind(), scheme.Scheme)//patch后的json
		if err != nil {
			return err
		}

		targetObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalPatchedObjJS)//patch后的对象
		if err != nil {
			return err
		}

		didPatch := !reflect.DeepEqual(info.Object, targetObj)//判断是否做了patch
		printer, err := o.ToPrinter(patchOperation(didPatch))// print flag转printer
		if err != nil {
			return err
		}
		return printer.PrintObj(targetObj, o.Out)//打印对象
	})
	if err != nil {
		return err
	}
	if count == 0 {
		return fmt.Errorf("no objects passed to patch")
	}
	return nil
}
//获取patch后的json
func getPatchedJSON(patchType types.PatchType, originalJS, patchJS []byte, gvk schema.GroupVersionKind, creater runtime.ObjectCreater) ([]byte, error) {
	switch patchType {//switchpatch类型
	case types.JSONPatchType://如果时候json
		patchObj, err := jsonpatch.DecodePatch(patchJS)//解码patch json
		if err != nil {
			return nil, err
		}
		bytes, err := patchObj.Apply(originalJS)//应用patch json
		// TODO: This is pretty hacky, we need a better structured error from the json-patch
		if err != nil && strings.Contains(err.Error(), "doc is missing key") {
			msg := err.Error()
			ix := strings.Index(msg, "key:")
			key := msg[ix+5:]
			return bytes, fmt.Errorf("Object to be patched is missing field (%s)", key)
		}
		return bytes, err//返回patch后的json

	case types.MergePatchType://如果是merge
		return jsonpatch.MergePatch(originalJS, patchJS)//合并patch,返回json

	case types.StrategicMergePatchType://如果是strategic
		// get a typed object for this GVK if we need to apply a strategic merge patch
		obj, err := creater.New(gvk)//创建对象
		if err != nil {
			return nil, fmt.Errorf("cannot apply strategic merge patch for %s locally, try --type merge", gvk.String())
		}
		return strategicpatch.StrategicMergePatch(originalJS, patchJS, obj)//合并patch,返回json

	default:
		// only here as a safety net - go-restful filters content-type
		return nil, fmt.Errorf("unknown Content-Type header for patch: %v", patchType)
	}
}

k8s use-context是什么 kubectl的命令用于在 Kubernetes 集群中切换上下文(context),从而方便地在多个集群或命名空间之间进行操作。一个上下文定义了kubectl使用的和的组合。 阅读详情

相关推荐

Kubernetes任务调用Job与CronJob及源码分析

这份面试题几乎包含了他在一年内遇到的所有面试题以及答案,甚至包括面试中的细节对话以及语录,可谓是细节到极致,甚至简历优化和怎么投简历更容易得到面试机会也包括在内!也包括教你怎么去获得一些大厂,比如阿里,腾讯的内推名额!某位名人说过成功是靠99%的汗水和1%的机遇得到的,而你想获得那1%的机遇你首先就得付出99%的汗水!你只有朝着你的目标一步一步坚持不懈的走下去你才能有机会获得成功!成功只会留给那些有准备的人!这个方法的逻辑十分的清晰,我们下面撸一撸~

2401_85376163的博客 1280

k8s实战之kubectl

本课程将详细介绍k8s所有命令,以及命令的go源码分析,学习知其然,知其所以然

Gemini 3.5 Flash长程Agentic能力深度实测与工程化落地指南

Agentic AI并非简单多步调用,而是指大模型在复杂任务中持续维护状态、自主规划工具链、闭环验证结果的系统性能力。其核心挑战在于上下文一致性、运行时语义理解及MCP等外部工具协同——这直接决定AI能否替代工程师完成端到端工程任务。当前主流模型如Gemini 3.5 Flash已在17步以上长程推理中展现稳定性,但仍在自我调试、符号与运行时语义对齐、跨协议状态感知等关键维度存在瓶颈。本文基于VS Code实战场景,解析Agentic工作流构建方法、GEMINI.md上下文建模技巧、MCP服务器选型策略及真

weixin_33725239的博客 410

helm源码分析之pull

发布一个k8s部署视频:https://edu.youkuaiyun.com/course/detail/26967 课程内容:各种k8s部署方式。包括minikube部署,kubeadm部署,kubeasz部署,rancher部署,k3s部署。包括开发测试环境部署k8s,和生产环境部署k8s。 腾讯课堂连接地址https://ke.qq.com/course/478827?taid=4373109931462251&tuin=ba64518 第二个视频发布https://edu.youkuaiyun.com/c..

mark's technic world 1370

.NET技术博客的深度实践:从Span<T>到程序员成长路径

Span<T>是.NET中实现零分配内存操作的核心类型,其本质是栈上管理的内存视图,依赖JIT内联与内存布局优化实现高性能;理解其原理需深入CLR行为、IL指令及GC交互机制。这类底层技术价值不仅在于性能提升,更支撑高并发、低延迟场景下的系统稳定性,如实时物流调度、金融交易中间件等。而真正掌握Span<T>,离不开对真实故障归因、跨版本兼容性验证和团队协作认知的同步演进——这正是面向一线.NET从业者的深度技术博客所承载的技术成长路径。

diedangxiang4092的博客 430

Kubernetes Kubectl top 命令

kubectl top命令可显⽰节点和Pod对象的资源使⽤信息,它依赖于集群中的资源指标API来收集各项指标数据。它包含有node和pod两个⼦命令,可分别⽤于显⽰Node对象和Pod对象的相关资源占⽤率。,例如下⾯显⽰所有节点的资源占⽤状况的结果中显⽰了各节点累计CPU资源占⽤时长及百分⽐,以及内容空间占⽤量及占⽤⽐例。⽽名称空间级别的Pod对象资源占⽤率的使⽤⽅式会略有不同,使⽤时,⼀般应该限定名称空间及使⽤标签选择器过滤出⽬标Pod对象。列出Node资源占⽤率命令的语法格式为。

专注基础架构领域 2551

kubectl源码分析之apply

发布一个k8s部署视频:https://edu.youkuaiyun.com/course/detail/26967 课程内容:各种k8s部署方式。包括minikube部署,kubeadm部署,kubeasz部署,rancher部署,k3s部署。包括开发测试环境部署k8s,和生产环境部署k8s。 腾讯课堂连接地址https://ke.qq.com/course/478827?taid=4373109931462251&tuin=ba64518 第二个视频发布https://edu.youkuaiyun.com/c..

mark's technic world 1809

车道线检测(传统基于Opencv的方法)

传统车道线检测方法主要是使用颜色选择、高斯平滑、边缘检测、ROI区域选择、霍夫变换、标定车道边界等Opnecv图像处理操作去检测道路上的车道线,我自己对很多用这类处理对很多道路场景车道线进行过实验,这类算法往往需要人工根据不同街道场景手动去反复调整各算法参数,从而才能使检测效果最佳。

Yangxiaoaijiejie的博客 2073

kubectl源码分析之set image

发布一个k8s部署视频:https://edu.youkuaiyun.com/course/detail/26967 课程内容:各种k8s部署方式。包括minikube部署,kubeadm部署,kubeasz部署,rancher部署,k3s部署。包括开发测试环境部署k8s,和生产环境部署k8s。 腾讯课堂连接地址https://ke.qq.com/course/478827?taid=4373109931462251&tuin=ba64518 第二个视频发布https://edu.youkuaiyun.com/c..

mark's technic world 1372

IAR9.X半主模式异常解析:为何硬件复位后仍陷调试状态?

本文深入解析了IAR 9.X半主模式异常问题,探讨了硬件复位后MCU仍陷调试状态的原因及解决方案。通过分析半主模式工作机制,提供了禁用半主功能、重写低级IO函数等具体方法,并给出了版本兼容性处理和调试与量产配置分离的优化建议,帮助开发者有效解决复位异常问题。

oo7890的博客 605

ArcGIS 10.2.2自带Python 2.7.5环境配置全攻略:从pip安装到gdal、arcpy调用

本文详细指导如何在ArcGIS 10.2.2自带的Python 2.7.5环境中进行高效配置。核心内容包括安装pip包管理器、部署gdal等关键地理数据处理库,并确保arcpy模块的无缝调用。通过解决路径配置与PyCharm集成问题,帮助用户在此经典环境中构建稳定、兼容的地理信息自动化工作流,有效连接ArcGIS桌面功能与Python脚本编程。

moon9的博客 769

openkruise 缩容_从一个 OpenKruise 用户疑问开始理解K8s

云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!背景OpenKruise 是阿里云开源的大规模应用自动化管理引擎,在功能上对标了 Kubernetes 原生的 Deployment / StatefulSet 等控制器,但 OpenKruise 提供了更多的增强功能如:优雅原地升级、发布优先级/打散策略、多可用区workload抽象管理、统一 si...

weixin_34923852的博客 134

给大一新生的蓝桥杯EDA备赛规划:从电路分析到立创EDA,如何用一年时间高效备赛?

本文为大一新生提供了一份系统的蓝桥杯EDA备赛规划,从电路分析基础到立创EDA实战操作,分阶段指导如何高效备赛。内容涵盖理论学习、软件操作、PCB设计专项提升及冲刺优化策略,帮助学生在一年内全面提升电子设计能力,为竞赛做好充分准备。

weixin_31476015的博客 291

突破AWX Operator部署困境:从日志分析到数据库修复的全栈排障指南

作为Kubernetes环境中Ansible自动化的核心组件,AWX Operator的部署故障常常让管理员陷入困境: - operator日志中充斥着模糊的错误信息 - PostgreSQL数据库初始化失败却找不到具体原因 - Pod状态显示Running但AWX界面始终无法访问 - 调试配置修改后问题依旧反复出现 本文将系统梳理AWX Operator故障排查方法论,通过12个实战场

gitblog_00817的博客 436

VisualStudio2022启动报错

把Users\...\AppData\Local\Microsoft\VisualStudio\11.0\ComponentModelCache中的Microsoft.VisualStudio.Default.cache这个文件移除,再重启vs,各项功能都能正常使用了。第一次使用方法1有效果,不知道什么原因后面又报错再删除缓存文件无效了,最后使用方法2启动VS成功。vs2022启动时,报错弹框,在ActivityLog查看具体报错信息,报错原因未知。网上搜索到两种解决方法。方法1:删除缓存文件。

面朝大海,春暖花开 3956

手把手教你逆向抖店滑块验证码:从抓包到生成captchaBody的完整流程(附Python复现代码)

本文详细解析了抖店滑块验证码的逆向过程,从抓包分析到参数生成,最终用Python完整复现验证流程。涵盖关键接口分析、指纹参数生成、轨迹加密逻辑及核心加密算法实现,帮助开发者突破抖店的多重安全机制,实现高效的人机验证绕过。

weixin_30681121的博客 429

【kubernetes v1.21】(kube-apiserver 3)kube-apiserver Admission Control 与 CRD 超深度分析

Part 3: kube-apiserver Admission Control 与 CRD 超深度分析

zhonglinzhang 73

二十八.国民技术MCU开发之UID获取与应用

在国民NT MCU中均有一个96位和128位的唯一设备标识符,其中UCID128 位, UID 为 96 位。手册中描述的作用是:通常用来做为序列号或作为密码,在编写闪存时,将此唯一标识与软件加解密算法相结合,进一步提高代码在闪存存储器内的安全性,也可用于激活带安全功能的自举程序(SecureBootloader)。UCID 和 UID,遵守国民技术芯片序列号定义,它包含芯片生产及版本相关信息。

AIoT深度探索者 | 嵌入式系统架构师 深耕物联网(IoT)与人工智能(AI)融合技术,聚焦​​边缘计算、图像开发、低功耗通信协议​​,致力于智能硬件,关注我获取最新技术文章信息与海量资源~ 1680

随机森林工业级实战:参数原理、抗干扰设计与可审计落地

随机森林作为最常用的集成学习模型,其核心价值不在于‘堆砌多棵树’,而在于通过Bootstrap抽样、特征子集随机化和去相关性聚合三层统计机制,实现高鲁棒性与可解释性的平衡。理解n_estimators与方差衰减率的关系、max_features对树间相关性的影响、OOB误差的免验证集评估原理,是避免调参玄学的关键。在电商风控、设备预测、医疗诊断等工业场景中,它能有效应对数据漂移、小样本脆弱性和强共线性等现实挑战。本文聚焦参数背后的统计契约与工程权衡,结合OOB监控、Permutation重要性、概念漂移检测

weixin_34218579的博客 372

时间序列预测 | Python实现LSTM时间序列数据预测

目录LSTM神经网络LSTM原理LSTM数据下载LSTM程序设计导入Python包时序数据处理LSTM模块定义模型训练模型评估参数设定运行结果参考资料致谢 LSTM神经网络 长短期记忆网络——通常简称为“LSTM”——是一种特殊的 RNN,能够学习长期依赖关系。它们由Hochreiter & Schmidhuber (1997)引入,并在后续工作中被许多人提炼和推广。1它们在处理各种各样的问题时效果非常好,现在被广泛使用。LSTM 被明确设计为避免长期依赖问题。长时间记住信息实际上是他们的默认行为

前程算法屋的博客 3912

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

juju reactive charm (by quqi99)

sudo snap install charm --classic mkdir -p /bak/work/charms/layers export JUJU_REPOSITORY=/bak/work/charms export LAYER_PATH=$JUJU_REPOSITORY/layers cd $LAYER_PATH charm create simple cd simple sudo ...

技术并艺术着 8742

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

3.本地单机架构minikube

搭建minikube

weixin_47252600的博客 1148

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

通过 kubectl 查看 K8s 内节点、Pod 资源使用情况

kubectl describe node -n ${namespace} 例如:我要查看生产环境下所有节点的资源分配情况(生产环境的 namespace 为 product) kubectl describe node -n product

cab5的博客 8万+

【Kubernetes】kubectl top pod 异常?

kubectl top pod 异常?本文手把手解决,跟着小涛一块试试吧~

http://DBAtao.cn/ 1万+

centos 安装swoole问题

centos安装swoole

weixin_43946021的博客 722

分享kubectl的奇技淫巧

Kubectl 是 Kubernetes 最重要的命令行工具。在 Flant,我们会在 Wiki 和 Slack 上相互分享 Kubectl 的妙用(其实我们还有个搜索引擎,不过那就是另外一回事了)。多年以来,我们在 kubectl 方面积累了很多技巧,现在想要将其中的部分分享给社区。我相信很多读者对这些命令都非常熟悉;然而我还是希望读者能够从本文中有所获益,进而提高生产力。本文没什么结论,但是可以提供一个小列表,其中包含本文相关的有用链接。原文来自微信公众号《CNCF》

Vic的博客 1336
上一篇: kubectl源码分析之apply edit-last-applied
下一篇: kubectl源码分析之replace
hxpjava1
博客等级 码龄19年 1365粉丝 511原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hxpjava1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值