Kubernetes 控制平面 - 调度器

调度器 kube-scheduler 负责分配调度 Pod 到集群内的节点上,它监听 kube-apiserver,查询还未分配 Node 的 Pod,然后根据调度策略为这些 Pod 分配节点(更新 Pod 的 NodeName 字段)。 调度器需要充分考虑诸多的因素: 公平调度; 资源高效利用; QoS; affinity 和 anti-affinity; 数据本地化(data locality) ; 内部负载干扰(inter-workload interference) ; deadlines。 kube-scheduler 调度分为两个阶段, predicate 和 priority: predicate: 过滤不符合条件的节点; priority:优先级排序,选择优先级最高的节点。. predicate 策略 PodFitsHostPorts:检查是否有 Host Ports 冲突。 PodFitsPorts:同P odFitsHostPorts。 PodFitsResources:检查 Node 的资源是否充足,包括允许的Pod数量、CPU、内存、GPU个数以及其他的OpaqueIntResources。 HostName:检查 pod.Spec.NodeName 是否与候选节点一致。 MatchNodeSelector:检查候选节点的 pod.Spec.NodeSelector 是否匹配 NoVolumeZoneConflict:检查 volume zone 是否冲突。 MatchInterPodAffinity:检查是否匹配 Pod 的亲和性要求。 NoDiskConflict:检查是否存在 Volume 冲突,仅限于 GCE PD、AWS EBS、Ceph RBD以及 iSCSI。 PodToleratesNodeTaints:检查 Pod 是否容忍 Node Taints。 CheckNodeMemoryPressure:检查 Pod 是否可以调度到 MemoryPressure 的节点上。 CheckNodeDiskPressure:检查 Pod 是否可以调度到 DiskPressure 的节点上。 NoVolumeNodeConflict:检查节点是否满足 Pod 所引用的 Volume 的条件。 priority 策略 SelectorSpreadPriority:优先减少节点上属于同一个 Service 或 Replication Controller 的 Pod 数量。 尽量将同一个 rc 下的多个副本分散到不同节点,增加可用性 InterPodAffinityPriority:优先将Pod调度到相同的拓扑上(如同一个节点、Rack、Zone等)。 LeastRequestedPriority:优先调度到请求资源少的节点上。 BalancedResourceAllocation:优先平衡各节点的资源使用。 NodePreferAvoidPodsPriority:alpha.kubernetes.io/preferAvoidPods字段判断,权重为10000,避免其他优先级策略的影响 NodeAffinityPriority:优先调度到匹配NodeAffinity的节点上。 TaintTolerationPriority:优先调度到匹配TaintToleration的节点上。 ServiceSpreadingPriority:尽量将同一个service的Pod分布到不同节点上,已经被SelectorSpreadPriority替代( 默认未使用)。 EqualPriority:将所有节点的优先级设置为1 (默认未使用) ImageLocalityPriority:尽量将使用大镜像的容器调度到已经下拉了该镜像的节点上(默认未使用) MostRequestedPriority:尽量调度到已经使用过的Node.上,特别适用于cluster-autoscaler (默认未使用) 资源需求 CPU ...

九月 28, 2023 · 4 分钟 · 774 字 · 熊鑫伟,我

深入了解 Kubernetes kube-apiserver 组件架构

深入理解Kube-APIServer kube-apiserver是Kubernetes最重要的核心组件之一,主要提供以下的功能 提供集群管理的REST API接口,包括认证授权、数据校验以及集群状态变更等 提供其他模块之间的数据交互和通信的枢纽(其他模块通过API Server查询或修改数据,只有API Server才直接操作etcd) apiserver 主要功能: 认证:使用集群判断身份。 鉴权:使用操作 CRUD,需要权限。 准入: 对于Kubernetes来说,需要一些额外的 actions,例如写入的值不规范,需要对其进行修改,修改后需要 校验。最后需要 限流,以防止恶意或者漏洞导致拥堵 Mutating Validating Admission 限流 APIServer对象的实现 访问控制 API Server 是所有组件交互的 中间枢纽。 Kubernetes API的每个请求都会经过多阶段的访问控制之后才会被接受,这包括认证、授权以及准入控制(Admission Control)等。 前面的是 Mutating Webhook,可以改一个对象的值,而 Validating Webhook 是不可以修改对象的值,不生效的。 更加详细的请求处理流程: 📜 对上面的解释: 如何处理API请求:API源码存在于kubernetes/pkg/api路径中,会处理集群内以及集群外客户端的请求。 那么,当HTTP请求到达Kubernetes API时,具体会发现什么呢?从上层看,会发现以下交互: HTTP请求由一串过滤器(filters)进行处理,这些过滤器注册在DefaultBuildHandlerChain()(参阅源码:https://github.com/kubernetes/apiserver 中的config.go)中,并执行相应的处理。过滤器要么会将信息传递并附加到ctx.RequestInfo上(例如通过了身份认证的用户),要么返回适当的HTTP响应代码。 第二步,复用器(multiplexer,参阅源码:https://github.com/kubernetes/apiserver 中的container.go)会根据HTTP路径,将HTTP请求路由到相应的处理程序(handler)。 第三步,routes(在routes/*中定义)会将处理程序(handler)与HTTP路径进行连接。 第四步,按照API Group进行注册的处理程序(参阅源码:https://github.com/kubernetes/apiserver 中的groupversion.go和installer.go),会处理HTTP请求和上下文(context,如user、rights等),并将请求的对象从存储中传送出来。 注意,为了简洁,在上图中我们省略了HTTP路径中的$NAMESPACE字段。 现在我们进一步深入的对前文中提到的DefaultBuildHandlerChain()中建立的过滤器(filters)进行介绍: **WithRequestInfo():**在requestinfo.go中定义,将RequestInfo附加到上下文中。 **WithMaxInFlightLimit():**在maxinflight.go中定义,对当前的请求数量进行限制。 **WithTimeoutForNonLongRunningRequests():**在timeout.go中定义,超时暂停非长时间运行请求(如大多数GET,PUT,POST,DELETE请求),这种请求与长时间运行请求(如watch和proxy请求)正好相反。 **WithPanicRecovery():**在wrap.go中定义,包装一个http Handler来恢复和记录报错。 **WithCORS():**在cors.go中定义,提供了一个CORS实现;CORS代表跨原始资源共享(Cross-Origin Resource Sharing),是一种允许嵌入在HTML页面中的JavaScript生成XMLHttpRequests请求到一个域(domain)的机制,这个域不同于JavaScript的初始起源。 **WithAuthentication():**在authentication.go中定义,尝试以用户身份对给定的请求进行验证,并将用户信息存储在提供的上下文中。成功后,授权HTTP header将从请求中删除。 **WithAudit():**在audit.go中定义,使用所有传入请求的审计日志信息来充实handler。审计日志的条目包含很多信息,例如请求的源IP、调用操作的用户信息以及请求的命名空间等。 **WithImpersonation():**在impersonation.go中定义,通过检查试图对用户进行修改的请求(类似sudo),来对假用户进行处理; ...

九月 28, 2023 · 7 分钟 · 1404 字 · 熊鑫伟,我

深入了解Kubernetes等组件之ETCD

开始前 ETCD 是 Kubernetes 中所有组件中最难的,因为 ETCD 是有状态的,而不是无状态的。 我在之前做 k3s runtime 设计的时候,了解了一些关于 ETCD 和 Raft 算法相关的概念,作为前奏知识,请分别前往 ETCD 以及 Raft算法 进行前奏学习。 这一篇来深入并且贯穿的讲解 ETCD 和 Raft。并且站在 Kubernetes 的角度来深入 剖析 ETCD。 ETCD 介绍 Etcd是CoreOS基于Raft开发的分布式key-value存储,可用于服务发现、共享配置以及一致性保障(如数据库选主、分布式锁等)。 包含的功能以及特性 前奏 都有学过,看下 Kubernetes 最关心的 存储: 主要功能 基本的key-value存储 监听机制 key的过期及续约机制,用于监控和服务发现 原子Compare And Swap和Compare And Delete,用于分布式锁和leader选举 使用场景 可以用于键值对存储,应用程序可以读取和写入 etcd 中的数据 etcd 比较多的应用场景是用于服务注册与发现 基于监听机制的分布式异步系统 etcd 是一个键值存储的组件,其他的应用都是基于其键值存储的功能展开。 采用kv型数据存储,一般情况下比关系型数据库快。 支持动态存储(内存)以及静态存储(磁盘)。 分布式存储,可集成为多节点集群。 存储方式,采用类似目录结构。(B+tree) 只有叶子节点才能真正存储数据,相当于文件。 叶子节点的父节点一定是目录,目录不能存储数据。 服务注册与发现: 强一致性、高可用的服务存储目录。 基于 Raft 算法的 etcd 天生就是这样一个强一致性、高可用的服务存储目录。 一种注册服务和服务健康状况的机制。 用户可以在 etcd 中注册服务,并且对注册的服务配置 key TTL,定时保持服务的心跳以达到监控健康状态的效果。 消息发布订阅: ...

九月 26, 2023 · 6 分钟 · 1264 字 · 熊鑫伟,我
在开源社区提问的艺术

在开源社区中学会如何提问

author(Github) 在提问之前 在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前,请先做到以下事情: 尝试在你准备提问的论坛的旧文章中搜索答案。 尝试上网搜索以找到答案。 尝试阅读手册以找到答案。 尝试阅读常见问题文件(FAQ)以找到答案。 尝试自己检查或试验以找到答案。 向你身边的强者朋友打听以找到答案。 如果你是程序开发者,请尝试阅读源代码以找到答案。 当你提出问题的时候,请先表明你已经做了上述的努力;这将有助于树立你并不是一个不劳而获且浪费别人的时间的提问者。如果你能一并表达在做了上述努力的过程中所学到的东西会更好,因为我们更乐于回答那些表现出能从答案中学习的人的问题。 运用某些策略,比如先用 Google 搜索你所遇到的各种错误信息(搜索 Google 论坛 和网页),这样很可能直接就找到了能解决问题的文件或邮件列表线索。即使没有结果,在邮件列表或新闻组寻求帮助时加上一句 我在 Google 中搜过下列句子但没有找到什么有用的东西 也是件好事,即使它只是表明了搜索引擎不能提供哪些帮助。这么做(加上搜索过的字串)也让遇到相似问题的其他人能被搜索引擎引导到你的提问来。 别着急,不要指望几秒钟的 Google 搜索就能解决一个复杂的问题。在向专家求助之前,再阅读一下常见问题文件(FAQ)、放轻松、坐得舒服一些,再花点时间思考一下这个问题。相信我们,他们能从你的提问看出你做了多少阅读与思考,如果你是有备而来,将更有可能得到解答。不要将所有问题一股脑拋出,只因你的第一次搜索没有找到答案(或者找到太多答案)。 准备好你的问题,再将问题仔细的思考过一遍,因为草率的发问只能得到草率的回答,或者根本得不到任何答案。越是能表现出在寻求帮助前你为解决问题所付出的努力,你越有可能得到实质性的帮助。 小心别问错了问题。如果你的问题基于错误的假设,某个普通黑客(J. Random Hacker)多半会一边在心里想着蠢问题…,一边用无意义的字面解释来答复你,希望着你会从问题的回答(而非你想得到的答案)中汲取教训。 绝不要自以为够格得到答案,你没有;你并没有。毕竟你没有为这种服务支付任何报酬。你将会是自己去挣到一个答案,靠提出有内涵的、有趣的、有思维激励作用的问题 —— 一个有潜力能贡献社区经验的问题,而不仅仅是被动的从他人处索取知识。 另一方面,表明你愿意在找答案的过程中做点什么是一个非常好的开端。谁能给点提示?、我的这个例子里缺了什么?以及我应该检查什么地方比请把我需要的确切的过程贴出来更容易得到答复。因为你表现出只要有人能指个正确方向,你就有完成它的能力和决心。 当你提问时 慎选提问的论坛 小心选择你要提问的场合。如果你做了下述的事情,你很可能被忽略掉或者被看作失败者: 在与主题不合的论坛上贴出你的问题。 在探讨进阶技术问题的论坛张贴非常初级的问题;反之亦然。 在太多的不同新闻群组上重复转贴同样的问题(cross-post)。 向既非熟人也没有义务解决你问题的人发送私人电邮。 黑客会剔除掉那些搞错场合的问题,以保护他们沟通的渠道不被无关的东西淹没。你不会想让这种事发生在自己身上的。 因此,第一步是找到对的论坛。再说一次,Google 和其它搜索引擎还是你的朋友,用它们来找到与你遭遇到困难的软硬件问题最相关的网站。通常那儿都有常见问题(FAQ)、邮件列表及相关说明文件的链接。如果你的努力(包括阅读 FAQ)都没有结果,网站上也许还有报告 Bug(Bug-reporting)的流程或链接,如果是这样,链过去看看。 向陌生的人或论坛发送邮件最可能是风险最大的事情。举例来说,别假设一个提供丰富内容的网页的作者会想充当你的免费顾问。不要对你的问题是否会受到欢迎做太乐观的估计 —— 如果你不确定,那就向别处发送,或者压根别发。 在选择论坛、新闻群组或邮件列表时,别太相信它的名字,先看看 FAQ 或者许可书以弄清楚你的问题是否切题。发文前先翻翻已有的话题,这样可以让你感受一下那里的文化。事实上,事先在新闻组或邮件列表的历史记录中搜索与你问题相关的关键词是个极好的主意,也许这样就找到答案了。即使没有,也能帮助你归纳出更好的问题。 别像机关枪似的一次“扫射”所有的帮助渠道,这就像大喊大叫一样会使人不快。要一个一个地来。 搞清楚你的主题!最典型的错误之一是在某种致力于跨平台可移植的语言、套件或工具的论坛中提关于 Unix 或 Windows 操作系统程序界面的问题。如果你不明白为什么这是大错,最好在搞清楚这之间差异之前什么也别问。 一般来说,在仔细挑选的公共论坛中提问,会比在私有论坛中提同样的问题更容易得到有用的回答。有几个理由可以支持这点,一是看潜在的回复者有多少,二是看观众有多少。黑客较愿意回答那些能帮助到许多人的问题。 可以理解的是,老练的黑客和一些热门软件的作者正在接受过多的错发信息。就像那根最后压垮骆驼背的稻草一样,你的加入也有可能使情况走向极端 —— 已经好几次了,一些热门软件的作者由于涌入其私人邮箱的大量不堪忍受的无用邮件而不再提供支持。 Stack Overflow 搜索,然后在 Stack Exchange 问。 近年来,Stack Exchange 社区已经成为回答技术及其他问题的主要渠道,尤其是那些开放源码的项目。 ...

九月 17, 2023 · 4 分钟 · 841 字 · 熊鑫伟,我

自动化的高级追求: Prow 是什么?Kubernetes 为什么需要它

why? 故事是从这个 proposal 开始idea~ 🤖 OpenIM cicd robot machine proposal Prow是基于Kubernetes的CI/CD系统。作业可以由各种类型的事件触发,并将其状态报告给许多不同的服务。除了作业执行,Prow还以策略执行、通过 /foo 风格命令的聊天操作和自动PR合并的形式提供GitHub自动化。 有关 Golang 文档,请参阅 GoDoc 。请注意,这些库仅供prow使用,我们不会尝试保留向后兼容性。 Kubernetes 专门为 Prow 提供了网页命令查询: https://prow.k8s.io/command-help 关于Prow如何运行作业的简要概述,请参阅 Prow作业的生命周期 。 要查看Prow的常用用法和交互流,请参见拉取请求交互序列图。 hello world 最简单的一个上手案例莫过于 pull request 。 提出一个拉取请求(以下简称PR)。在PR正文中,可以随意添加区域标签(如果合适),例如 /area <AREA> 。标签列表在这里 。也可以随意推荐一位评论者 /assign @theirname 。 一旦你的审阅者满意,他们会说 /lgtm ,这将应用 lgtm 标签,如果他们是OWNER,将应用 approved 标签。 approved 标签也将自动应用于所有者打开的PR。如果你和你的审阅者都不是OWNER,请 /assign 某个所有者。如果你的PR有 lgtm 和 approved 标签,没有任何 do-not-merge/* 标签,并且所有测试均通过,则PR将自动合并。 查看测试结果 Kubernetes TestGrid 显示历史测试结果 在 testgrid/config.yaml 配置自己的 testgrid 仪表盘 Gubernator 格式化每次运行的输出 PR Dashboard 查找需要注意的 PR Prow 安排测试并更新问题 Prow 响应 GitHub 事件、定时器和在 GitHub 评论中给出的手动命令 。 prow dashboard 显示当前正在测试什么 在 config/jobs 配置 prow 运行新测试 Triage Dashboard 汇总故障 将故障集群在一起 搜索跨作业的测试失败 在特定的测试和/或作业的 regex 中过滤失败 Velodrome 指标跟踪作业和测试健康状况。 Kettle 进行收集,metrics 进行报告,velodrome 是前端。 功能和特性 prow 的功能很强大,甚至是比 actions 更加出众。可以测试、批处理、工件发布的作业执行。 ...

九月 16, 2023 · 5 分钟 · 858 字 · 熊鑫伟,我

一份完整的开源贡献指南(提供给第一次踏入开源伙伴秘籍)

任务分配 time:Within a week 完成 first contribute,目的:了解开源项目的贡献流程 完成 sealos 开发环境构建 了解 kuberentes 基本使用,核心概念,核心组件的作用 基本使用: 创建 一个 pod 并理解什么是 pod 创建一个 deployment 理解 deployment 与 pod 的关系 创建一个 configmap, 理解挂载配置文件给 pod 创建一个 service,通过 service 在集群内访问 pod 核心概念,核心组件的作用: kubectl apiserver controller-manager scheduler kubelet kube-proxy etcd 这些组件分别是做什么的 可以用一个 kubectl apply 一个 deployment 这些组件分别做了哪些事来梳理整个流程 🚸 next time:会分配一个具体的任务以及介绍 sealos 源码架构。 资源🗓️ 参考资料: 贡献文档:https://github.com/labring/sealos/blob/main/CONTRIBUTING.md 开发环境搭建文档:https://github.com/labring/sealos/blob/main/DEVELOPGUIDE.md 使用 sealos 快速构建 kubernetes 学习环境文档:https://github.com/labring/sealos#quickstart 搭建单机环境即可。 ...

九月 16, 2023 · 6 分钟 · 1108 字 · 熊鑫伟,我

我的第一个博客

你好,我是熊鑫伟 在光阴的长河中,我如此荣幸地踏入大四的这个重要时刻。不仅仅是一位学生,我热衷于编写那些跳动的代码,于是,我成为了开源的狂热追随者。欢迎你探索我的数字天地——我的GitHub 。这里,你将看到我对技术的热爱,我的第三代博客,我期待它能成为我与这个世界对话的终极纽带。 在无数社区中,OpenIM 如同闪耀的北极星,引领我前行,灌溉我的技术之心,让我真实地行动起来。 然而,我的生命并非仅停留在屏幕前。从此,世界不再是书本,而是展开在我脚下的广阔天地。 户外的召唤使我欣喜,无论是我独自的冒险还是与朋友的欢聚,每一个步伐都是对自然的敬意和生命的探索。 我寻找开源的深意,并在阅读中不断地得到启示: 《开源的成功之路》:它让我明白,每一个成功都不是偶然,而是走过的那些布满荆棘的道路的回报。 《大教堂与集市》:开源并不仅仅是代码的分享,它更是心与心的交流,是一种对自由与创新的追求。 《黑客与画家》:这部作品是我至今的最爱,它提醒我,真正的价值不在于你拥有了什么,而在于你创造了什么。 > “你的价值取决于你创造了什么,而不是你拥有什么。” 这句话是我每日的座右铭,激励我持续前行,创造属于自己的美好。 开源,于我,是一个旅程,充满了未知与挑战。但我渴望在这条路上遇到更多的同路人,与你共同分享,共同创造。 我真心邀请你,与我一起,探索这个充满可能的世界。

九月 14, 2023 · 1 分钟 · 16 字 · Xinwei Xiong, Me

关于我的 Hugo 博客 (教程)

重新搭建我的博客(静态) 冤大头回来了 … 太难了这次,准备简历的时候,发现我的博客没了,我最亲爱的,陪伴了一年的,备受好评的博客 cubxxw.com 牺牲了 呜呜呜呜 别急别急,吸取教训,第一件事是什么,坚决不用动态博客了,从大一的第一代博客使用 workpress, 那时候用的服务器管理工具是 著名顶顶的 宝塔 , 虽然现在我还在用,哈哈哈,不过以后绝对绝对不会再用了 。第二代博客还记得吗 ,第二代博客就是我刚刚牺牲掉的博客,使用 docker 搭建,存活了两年(大二到大三),服务器中途都换了一次,不过得益于 Docker 优雅的移植性haha,所以我的博客得以存活。 那这次为啥挂了??? 挂的时间是 2023 年 9 月 1 日。原因是 Java 出现故障,发现 swtich 空间不足,然后,准备移植的,修复的,实在是觉得无力维护,我希望我的博客可以长久生存几年,十几年,甚至是几十年上百年。 所以,从头开始 !!! 选择合适的博客模板 之前用过 vuepress 做笔记,vuepress 相对来说不是特别合适我现在做的,因为已经有视觉疲惫了哈哈哈,看着很不舒服,所以用我很喜欢的一个开源项目,大家很多人也耳熟能详的顶级开源项目:hugo , GitHub 地址是:https://github.com/gohugoio/hugo 接下来就是选择合适的主题了,我参考了几个热门的 theme 选择了 https://github.com/adityatelange/hugo-PaperMod 安装 Hugo 我热衷于源码,可以随时改代码,提 PR ,于是用源码构建: ❯ git clone https://github.com/cubxxw/hugo.git ❯ cd hugo ❯ go build ❯ mv hugo /usr/bin 部署主题 选择我们使用的主题: ❯ git clone https://github.com/adityatelange/hugo-PaperMod themes/PaperMod --depth=1 # 如果希望后面更新主题 ❯ cd themes/PaperMod ❯ git pull 使用 git submodule with ...

九月 12, 2023 · 9 分钟 · 1874 字 · 熊鑫伟,我

如何安装和使用自主人工智能工具Auto-GPT

前言 🔮 在我的 Slack 工作区中,集成了多个 AI,分别有 ChatGPT 4、ChatGPT 3.5、Claude …… 我们可以通过 Slack 免费并且无限制的和 AI 交互,欢迎大家加入到 Slack,这里是 链接: https://join.slack.com/t/kubecub/shared_invite/zt-1se0k2bae-lkYzz0_T~BYh3rjkvlcUqQ 介绍 很早之前就了解到了 Auto-GPT,作为 GitHub 上近期增长速度最快的项目(没有之一),Auto-GPT 在开源社区可谓是人尽皆知,甚至 star 已经很快就超过 Kubernetes,目前有 125k star。 得益于 Auto-GPT 的出色技术,可以高精度和高效率地自动执行许多任务。 它利用了 GPT-4 强大的自然语言处理功能。 我们甚至可以通过它来实现更多的自动化的工作,比如说前一节 在 Sealos 上开发一款 AI 自动云原生化项目自动上线工具 什么是 AutoGPT 它的 GitHub 地址: GitHub 从本质上讲,Auto-GPT 利用 OpenAI 最新人工智能模型的多功能性与软件和服务进行在线互动,使其能够 “自主 “执行X和Y等任务。但正如我们在大型语言模型方面的学习,这种能力似乎像海洋一样宽广,但却像水坑一样深。 AutoGPT 是一个由人工智能驱动的应用程序,利用 GPT-4 等 LLM 的强大功能自主创建和处理各种工作。通过使用 Auto GPT,组织和个人可以简化报告创作、内容创建和数据分析等流程,以节省时间并减少错误。 AutoGPT 改变了任务自动化的游戏规则,使组织和个人能够专注于其他关键任务,同时将重复和琐碎的工作留给程序。 随着 LLM 的不断发展,我们可以期待看到像 Auto GPT 这样功能越来越强大的软件能够执行越来越复杂的任务。 ...

七月 16, 2023 · 2 分钟 · 414 字 · 熊鑫伟,我
Go 调试和 dlv 工具学习

Go 调试测试以及调试工具 dlv 学习

调试Go工程 ::: tip prepare: vscode golang 1.92 ::: demo go mod init test In main.go file package main import ( "fmt" ) // Swap functions func swap(x, y *string) (string, string) { //XOR exchange *x, *y = *y, *x } func main() { fmt.Println("Hello, world!") //Swap functions for i := 0; i < 10; i++ { a := "a" b := "b" swap(&a, &b) fmt.Println(a, b) } } vscode一键生成测试 >gotest for package/function ::: tip 分别是为包生成测试单元,为函数生成测试单元。 ::: 生成如下: package main import ( "testing" ) func Test_main(t *testing.T) { tests := []struct { name string }{ // TODO: Add test cases. } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { main() }) } } func Test_swap(t *testing.T) { type args struct { x *string y *string } tests := []struct { name string args args want string want1 string }{ // TODO: Add test cases. } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, got1 := swap(tt.args.x, tt.args.y) if got != tt.want { t.Errorf("swap() got = %v, want %v", got, tt.want) } if got1 != tt.want1 { t.Errorf("swap() got1 = %v, want %v", got1, tt.want1) } }) } } 我们在 TODO: Add test cases. 那边给出测试就好了: ...

六月 19, 2023 · 10 分钟 · 1939 字 · 熊鑫伟,我