从k8s当中学习go cli脚手架开发利器-cobra

1.前言

大部分的项目都会引入cobra来作为项目的命令行解析工具,k8s当中大量使用cobra,学习借鉴一下k8s当中是如何使用cobra,在此记录一下。

2.cobra简介

cobra是一个提供简单接口来创建强大的现代CLI界面的库类似git  & git tools,cobra也是一个应用程序,它会生成你的应用程序的脚手架来快速开发基于cobra的应用程序 cobra提供:

  • 简单的基于子命令的命令行:app server、app fetch 等等

  • 完全符合POSIX的标志(包含短版本和长版本)

  • 嵌套子命令

  • 全局、本地和级联的标志

  • 使用 cobra init appname和cobra add cmdname 可以很容易生成应用程序和命令

  • 智能提示(app srver... did you mean app server?)

  • 自动生成命令和标志

  • 自动识别 -h --help 等等为help标志

  • 为应用程序自动shell补全(bash、zsh、fish、powershell)

  • 为应用程序自动生成手册

  • 命令别名

  • 灵活定义帮助、用法等等

  • 可选的与viper的紧密集成

3.分析

kubernetes当中的组件都是大量使用cobra,这里挑选kubeadm的cora实现来模仿分析。

从入口开始

// cmd/kubeadm/kubeadm.go
package mainimport ("k8s.io/kubernetes/cmd/kubeadm/app"kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
)func main() {kubeadmutil.CheckErr(app.Run())
}

此处直接调用了 app.Run() ,对于golang的工程而言,在cmd的第一层启动目录往往是越薄越好【1】,所以此处包装了将真正的启动逻辑封装到到**app.Run()**当中。

app.Run() 的调用位置在cmd/kubeadm/app/kubeadm.go

package appimport ("flag""os""github.com/spf13/pflag"cliflag "k8s.io/component-base/cli/flag""k8s.io/klog/v2""k8s.io/kubernetes/cmd/kubeadm/app/cmd"
)// Run creates and executes new kubeadm command
func Run() error {klog.InitFlags(nil)pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)pflag.CommandLine.AddGoFlagSet(flag.CommandLine)pflag.Set("logtostderr", "true")// We do not want these flags to show up in --help// These MarkHidden calls must be after the lines abovepflag.CommandLine.MarkHidden("version")pflag.CommandLine.MarkHidden("log-flush-frequency")pflag.CommandLine.MarkHidden("alsologtostderr")pflag.CommandLine.MarkHidden("log-backtrace-at")pflag.CommandLine.MarkHidden("log-dir")pflag.CommandLine.MarkHidden("logtostderr")pflag.CommandLine.MarkHidden("stderrthreshold")pflag.CommandLine.MarkHidden("vmodule")cmd := cmd.NewKubeadmCommand(os.Stdin, os.Stdout, os.Stderr)return cmd.Execute()
}

在Run()在设定了一系列的参数信息后,创建了cmd对象,并执行cmd对象的Execute(),这里的cmd对象就是一个cobra命令对象,而Execute是cobra提供执行命令的方法,cobra内部使用pflag库,通过设置 pflag 属性,可以对 cobra 的运行产生作用。pflag 也兼容 golang flag 库,此处通过 AddGoFlagSet(flag.CommandLine) 实现了对 golang flag 的兼容。

cobra对象如何生成的,是我们需要关心的,**NewKubeadmCommand(os.Stdin, os.Stdout, os.Stderr)**的实现在cmd/kubeadm/app/cmd/cmd.go

// NewKubeadmCommand returns cobra.Command to run kubeadm command
func NewKubeadmCommand(in io.Reader, out, err io.Writer) *cobra.Command {var rootfsPath stringcmds := &cobra.Command{Use:   "kubeadm",Short: "kubeadm: easily bootstrap a secure Kubernetes cluster",Long: dedent.Dedent(`┌──────────────────────────────────────────────────────────┐│ KUBEADM                                                  ││ Easily bootstrap a secure Kubernetes cluster             ││                                                          ││ Please give us feedback at:                              ││ https://github.com/kubernetes/kubeadm/issues             │└──────────────────────────────────────────────────────────┘Example usage:Create a two-machine cluster with one control-plane node(which controls the cluster), and one worker node(where your workloads, like Pods and Deployments run).┌──────────────────────────────────────────────────────────┐│ On the first machine:                                    │├──────────────────────────────────────────────────────────┤│ control-plane# kubeadm init                              │└──────────────────────────────────────────────────────────┘┌──────────────────────────────────────────────────────────┐│ On the second machine:                                   │├──────────────────────────────────────────────────────────┤│ worker# kubeadm join <arguments-returned-from-init>      │└──────────────────────────────────────────────────────────┘You can then repeat the second step on as many other machines as you like.`),SilenceErrors: true,SilenceUsage:  true,PersistentPreRunE: func(cmd *cobra.Command, args []stringerror {if rootfsPath != "" {if err := kubeadmutil.Chroot(rootfsPath); err != nil {return err}}return nil},}cmds.ResetFlags()cmds.AddCommand(newCmdCertsUtility(out))cmds.AddCommand(newCmdCompletion(out, ""))cmds.AddCommand(newCmdConfig(out))cmds.AddCommand(newCmdInit(out, nil))cmds.AddCommand(newCmdJoin(out, nil))cmds.AddCommand(newCmdReset(in, out, nil))
 cmds.AddCommand(newCmdVersion(out))cmds.AddCommand(newCmdToken(out, err))cmds.AddCommand(upgrade.NewCmdUpgrade(out))cmds.AddCommand(alpha.NewCmdAlpha())options.AddKubeadmOtherFlags(cmds.PersistentFlags(), &rootfsPath)cmds.AddCommand(newCmdKubeConfigUtility(out))return cmds
}

NewKubeadmCommand() 首先构造了 kubeadm的根命令对象cmds(也就是 kubeadm 命令),然后依次将kubeadm的子命令(例如init、join、version等命令)通过cmds.AddCommand()方法添加到 cmds 对象,cmd/kubeadm/app/kubeadm.go 中末尾执行的 cmd.Execute() 正是执行的这个 cmds 的 Execute() 方法

子命令当中NewCmdVersion()较为简单,源码位置cmd/kubeadm/app/cmd/version.go

// newCmdVersion provides the version information of kubeadm.
func newCmdVersion(out io.Writer) *cobra.Command {cmd := &cobra.Command{Use:   "version",Short: "Print the version of kubeadm",RunE: func(cmd *cobra.Command, args []stringerror {return RunVersion(out, cmd)},Args: cobra.NoArgs,}cmd.Flags().StringP("output", "o", "", "Output format; available options are 'yaml', 'json' and 'short'")return cmd
}

3.依样画葫芦

3.1目录结构

➜  cobra_project tree -CL 5
.
├── cmd
│   ├── app
│   │   ├── cloud.go
│   │   └── cmd
│   │       ├── cmd.go
│   │       ├── util
│   │       │   └── chroot_unix.go
│   │       └── version.go
│   └── cloud.go
├── go.mod
└── go.sum4 directories, 7 files

3.2效果展示

➜  cobra_project go run cmd/cloud.go version
cloud version: "1.5.0"
➜  cobra_project go run cmd/cloud.go version -h
Print the version of cloudUsage:cloud version [flags]Flags:-h, --help            help for version-o, --output string   Output format; available options are 'yaml', 'json' and 'short'
➜  cobra_project go run cmd/cloud.go version -o json
{"clientVersion": "1.5.0"
}
➜  cobra_project go run cmd/cloud.go┌──────────────────────────────────────────────────────────┐
│ This is cloud tools description                          │
│                                                          │
└──────────────────────────────────────────────────────────┘Usage:cloud [command]Available Commands:completion  Generate the autocompletion script for the specified shellhelp        Help about any commandversion     Print the version of cloudFlags:-h, --help   help for cloudUse "cloud [command] --help" for more information about a command

3.3实战

mkdir cobra_project

/cmd/cloud.go文件

package mainimport ("cobra_project/cmd/app""fmt""os"
)func main() {if err := app.Run(); err != nil {fmt.Fprintf(os.Stderr, "error: %v\n", err)os.Exit(1)}os.Exit(0)
}

/cmd/app/cloud.go文件

package appimport ("cobra_project/cmd/app/cmd""os"
)func Run() error {cmd := cmd.NewCloudCommand(os.Stdin, os.Stdout, os.Stderr)return cmd.Execute()
}

/cmd/app/cmd/cmd.go文件

package cmdimport (cloudutil "cobra_project/cmd/app/cmd/util""github.com/spf13/cobra""io""regexp""strings"
)// NewCloudCommand returns cobra.Command to run kubeadm command
func NewCloudCommand(in io.Reader, out, err io.Writer) *cobra.Command {var rootfsPath stringcmds := &cobra.Command{Use:   "cloud",Short: "cloud is powerful cloud native tool",Long: Dedent(`┌──────────────────────────────────────────────────────────┐│ This is cloud tools description                          ││                                                          │└──────────────────────────────────────────────────────────┘`),SilenceErrors: true,SilenceUsage:  true,PersistentPreRunE: func(cmd *cobra.Command, args []stringerror {if rootfsPath != "" {if err := cloudutil.Chroot(rootfsPath); err != nil {return err}}return nil},}cmds.AddCommand(newCmdVersion(out))return cmds
}var (whitespaceOnly    = regexp.MustCompile("(?m)^[ \t]+$")leadingWhitespace = regexp.MustCompile("(?m)(^[ \t]*)(?:[^ \t\n])")
)func Dedent(text stringstring {var margin stringtext = whitespaceOnly.ReplaceAllString(text, "")indents := leadingWhitespace.FindAllStringSubmatch(text, -1)// Look for the longest leading string of spaces and tabs common to all// lines.for i, indent := range indents {if i == 0 {margin = indent[1]} else if strings.HasPrefix(indent[1], margin) {// Current line more deeply indented than previous winner:// no change (previous winner is still on top).continueelse if strings.HasPrefix(margin, indent[1]) {// Current line consistent with and no deeper than previous winner:// it's the new winner.margin = indent[1]} else {// Current line and previous winner have no common whitespace:// there is no margin.margin = ""break}}if margin != "" {text = regexp.MustCompile("(?m)^"+margin).ReplaceAllString(text, "")}return text
}

/cmd/app/cmd/version文件

package cmdimport ("encoding/json""fmt""github.com/pkg/errors""github.com/spf13/cobra""gopkg.in/yaml.v2""io"
)// Version provides the version information of cloud
type Version struct {ClientVersion string `json:"clientVersion"`
}func newCmdVersion(out io.Writer) *cobra.Command {cmd := &cobra.Command{Use:   "version",Short: "Print the version of cloud",RunE: func(cmd *cobra.Command, args []stringerror {return RunVersion(out, cmd)},Args: cobra.NoArgs,}cmd.Flags().StringP("output", "o", "", "Output format; available options are 'yaml', 'json' and 'short'")return cmd
}// RunVersion provides the version information of kubeadm in format depending on arguments
// specified in cobra.Command.
func RunVersion(out io.Writer, cmd *cobra.Command) error {v := Version{ClientVersion: "1.5.0",}const flag = "output"of, err := cmd.Flags().GetString(flag)if err != nil {return errors.Wrapf(err, "error accessing flag %s for command %s", flag, cmd.Name())}switch of {case "":fmt.Fprintf(out, "cloud version: %#v\n", v.ClientVersion)case "short":fmt.Fprintf(out, "%s\n", v.ClientVersion)case "yaml":y, err := yaml.Marshal(&v)if err != nil {return err}fmt.Fprintln(out, string(y))case "json":y, err := json.MarshalIndent(&v, "", "  ")if err != nil {return err}fmt.Fprintln(out, string(y))default:return errors.Errorf("invalid output format: %s", of)}return nil
}

/cmd/app/cmd/util/chroot_unix.go文件

package utilimport ("os""path/filepath""syscall""github.com/pkg/errors"
)// Chroot chroot()s to the new path.
// NB: All file paths after this call are effectively relative to
// `rootfs`
func Chroot(rootfs stringerror {if err := syscall.Chroot(rootfs); err != nil {return errors.Wrapf(err, "unable to chroot to %s", rootfs)}root := filepath.FromSlash("/")if err := os.Chdir(root); err != nil {return errors.Wrapf(err, "unable to chdir to %s", root)}return nil
}

4.总结

对于云开发者而言,开发的时候可以多借鉴cncf项目当中的一些优秀的用法,笔者在这块相对比较薄弱,最近也在恶补这块的习惯,共勉。

【1】cmd目录下的第一层逻辑通常建议比较薄,可以参考k8当中的所有组件下的cmd目录,以及golang工程标准的项目结构建议https://github.com/golang-standards/project-layout

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/642260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计组与原理:系统总线

大家好啊&#xff0c;这里来到计组第二部分内容&#xff1a;系统总线 跳转上一篇&#xff1a;计组原理&#xff1a;系统概论与基本组成 系统总线 1.总线的基本概念单总线结构框图面向 CPU 的双总线结构框图以存储器为中心的双总线结构框图 2.总线的分类片内总线系统总线通信总线…

Linux之权限(内容详细,细节满满)

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 Linux 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力 目录 一.前言 二.权限修改的两种方法 …

Python学习02—Python开发环境的配置

1.Python语言的简单概述 Python的诞生离不开一个人&#xff0c;他就是Guido van Rossum&#xff0c;他在1989年的圣诞节期间&#xff0c;在荷兰的阿姆斯特丹度假&#xff0c;为了打发假期时间&#xff0c;就想着设计一种编程语言&#xff0c;后期便诞生了Python Python语言拥…

掌握技术脉搏:程序员如何在技术变革中保持领先,避免被裁员

笔者刚入行IT业的时候&#xff0c;有个前辈老猿工就语重心长的说&#xff0c;写代码要与时俱进&#xff0c;永远不能停止学习&#xff0c;如果不能时刻走在前面&#xff0c;就会被淘汰。哪天感觉到力不从心&#xff0c;就要考虑努力往管理层走啦。随着计算机技术和互联网技术的…

MySQL-B-tree和B+tree区别

B-tree&#xff08;平衡树&#xff09;和Btree&#xff08;平衡树的一种变种&#xff09;是两种常见的树状数据结构&#xff0c;用于构建索引以提高数据库的查询性能。它们在一些方面有相似之处&#xff0c;但也有一些关键的区别。以下是B-tree和Btree的主要区别&#xff1a; …

Java Server-Sent Events通信

Server-Sent Events特点与优势 后端可以向前端发送信息&#xff0c;类似于websocket&#xff0c;但是websocket是双向通信&#xff0c;但是sse为单向通信&#xff0c;服务器只能向客户端发送文本信息&#xff0c;效率比websocket高。 单向通信&#xff1a;SSE只支持服务器到客…

openssl3.2/test/certs - 003 - genroot “Root CA“ root-key2 root-cert2

文章目录 openssl3.2/test/certs - 003 - genroot "Root CA" root-key2 root-cert2概述笔记END openssl3.2/test/certs - 003 - genroot “Root CA” root-key2 root-cert2 概述 索引贴 > openssl3.2 - 官方demo学习 - test - certs 笔记 // openssl3.2/test/…

Leetcode—22.括号生成【中等】

2023每日刷题&#xff08;七十九&#xff09; Leetcode—22.括号生成 算法思想 实现代码 class Solution { public:vector<string> generateParenthesis(int n) {vector<string> ans;int m n * 2;string path(m, 0);function<void(int, int)> dfs [&…

小红书多模态团队建立新「扩散模型」:解码脑电波,高清还原人眼所见

近些年&#xff0c;研究人员们对探索大脑如何解读视觉信息&#xff0c;并试图还原出原始图像一直孜孜不倦。去年一篇被 CVPR 录用的论文&#xff0c;通过扩散模型重建视觉影像&#xff0c;给出了非常炸裂的效果—— AI 不光通过脑电波知道你看到了什么&#xff0c;并且帮你画了…

Acwing4993. FEB

题目 有一个长度为 N 的字符串 S&#xff0c;其中的每个字符要么是 B&#xff0c;要么是 E。 我们规定 S 的价值等于其中包含的子串 BB 以及子串 EE 的数量之和。 例如&#xff0c;BBBEEE 中包含 22 个 BB 以及 22 个 EE&#xff0c;所以 BBBEEE 的价值等于 44。 我们想要计…

技术浪潮中的职业变革:程序员面临的裁员挑战与应对策略

目录 前言 冲破时空壁垒&#xff1a;探索技术变革的酷时代&#xff01; 逆境成长&#xff1a;一个互联网人战胜失业困境的故事 裁员的双面影响&#xff1a;挑战与机遇 应对裁员潮危机&#xff1a;程序员的超级英雄之路 前言 惊人的新闻传来&#xff0c;一对来自中国的工程师…

基于神经网络的电力系统的负荷预测

一、背景介绍&#xff1a; 电力系统负荷预测是生产部门的重要工作之一&#xff0c;通过准确的负荷预测&#xff0c;可以经济合理地安排机组的启停、减少旋转备用容量、合理安排检修计划、降低发电成本和提高经济效益。负荷预测按预测的时间可以分为长期、中期和短期负荷预测。…

MySQL慢查询日志

慢查询日志 MySQL的慢查询日志是用来记录MySQL响应时间超过阈值的语句&#xff0c;即为超过long_query_time值得sql语句 默认没有开启慢查询(如果开启慢查询会导致性能降低) 慢日志配置 查看是否开启慢查询 show variables like %slow_query_log%; 开启慢查询 #只对当前数据库生…

​​快速排序(四)——挖坑法,前后指针法与非递归

目录 ​一.前言 二.挖坑法 三.前后指针法 四.递归优化 五.非递归 六.结语 一.前言 本文我们接着上篇文章的重点快排&#xff0c;现在继续讲解对快排优化的挖坑法&#xff0c;前后指针法以及非递归方法&#xff0c;下面是上篇文章快排链接&#xff1a;https://mp.csdn.net…

C#,字符串匹配(模式搜索)有限自动机(Finite Automata)算法的源代码

一、有限状态自动机 图中两个圆圈&#xff0c;也叫节点&#xff0c;用于表示状态&#xff0c;从图中可以看成&#xff0c;它有两个状态&#xff0c;分别叫0和1。从每个节点出发&#xff0c;都会有若干条边。当处于某个状态时&#xff0c;如果输入的字符跟该节点出发的某条边的内…

题记(25)--继续畅通工程

目录 一、题目内容 二、输入描述 三、输出描述 四、输入输出示例 五、完整C语言代码 一、题目内容 省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通&#xff08;但不一定有直接的公路相连&#xff0c;只要能间接通过公路可达即可&#xff09;。现得到城…

grpc java 编译小记

有关grpc的前置知识&#xff1a;gRPC 相关依赖 io.grpc:grpc-netty-shaded:1.61.0 - 这是gRPC框架的一个库&#xff0c;提供Netty实现以支持gRPC的网络通信。 io.grpc:grpc-protobuf:1.61.0 - 这是gRPC框架的一个库&#xff0c;提供对Protocol Buffers的支持&#xff0c;用于定…

网络安全的介绍

1.什么是网络安全 网络安全是一门关注保护计算机系统、网络基础设施和数据免受未经授权访问、破坏或窃取的学科。随着数字化时代的发展&#xff0c;网络安全变得尤为重要&#xff0c;因为大量的个人信息、商业机密和政府数据都储存在电子设备和云端系统中。以下是网络安全的概…

用Axure RP 9制作滑块

制作流程 1.打开界面 放置一个水平线 修改长为400 线段为5 2.放入圆 如图 3.修改圆的长和宽 如图 4.将圆变成动态面板 5.设置交互事件 如图 6.增加交互事件 7.增加 8.修改成跟随水平

基于springboot+vue的网上点餐系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 背景和意…