您现在的位置是:首页 >其他 >Cobra in 云原生网站首页其他
Cobra in 云原生
简介Cobra in 云原生
官方教程 https://cobra.dev/
cobra-cli 命令行使用
mkdir mycli && cd mycli
go mod init mycli
cobra-cli init [--author "lu-hongcheng" --license mit]
cobra-cli add say
cobra add hello -p sayCmd
这种方式文件结构比较固定,一般我还是采用手撸的方式
以云原生项目 karpor 为例
1. 项目启动
cmd/karpor/main.go:31
package main
import (
"os"
"github.com/KusionStack/karpor/cmd/karpor/app"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/component-base/cli"
)
func main() {
ctx := genericapiserver.SetupSignalContext()
cmd := app.NewServerCommand(ctx)
syncCmd := app.NewSyncerCommand(ctx)
cmd.AddCommand(syncCmd)
code := cli.Run(cmd)
os.Exit(code)
}
SetupSignalContext 返回 context,接受信号 SIGTERM 和 SIGINT 时被取消
var onlyOneSignalHandler = make(chan struct{})
func SetupSignalContext() context.Context {
close(onlyOneSignalHandler) // panics when called twice
shutdownHandler = make(chan os.Signal, 2)
ctx, cancel := context.WithCancel(context.Background())
signal.Notify(shutdownHandler, shutdownSignals...)
go func() {
<-shutdownHandler
cancel()
<-shutdownHandler
os.Exit(1) // second signal. Exit directly.
}()
return ctx
}
这其实和很多后端项目的起手式一样
func (a *App) Run(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
for _, srv := range a.servers {
go func(srv server.Server) {
err := srv.Start(ctx)
if err != nil {
log.Printf("Server start err: %v", err)
}
}(srv)
}
select {
case <-signals:
// Received termination signal
log.Println("Received termination signal")
case <-ctx.Done():
// Context canceled
log.Println("Context canceled")
}
// Gracefully stop the servers
for _, srv := range a.servers {
err := srv.Stop(ctx)
if err != nil {
log.Printf("Server stop err: %v", err)
}
}
return nil
}
2. 创建命令
ok 获得了 ctx,调用 app.NewServerCommand(ctx) 创建一个新的服务器命令
cmd/karpor/app/server.go:97
func NewServerCommand(ctx context.Context) *cobra.Command {
o, err := NewOptions(os.Stdout, os.Stderr)
if err != nil {
klog.Background().Error(err, "Unable to initialize command options")
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
}
expvar.Publish("CoreOptions", expvar.Func(func() interface{} {
return o.CoreOptions
}))
expvar.Publish("StorageOptions", expvar.Func(func() interface{} {
return o.SearchStorageOptions
}))
expvar.Publish("AIOptions", expvar.Func(func() interface{} {
displayOpts := *o.AIOptions
displayOpts.AIAuthToken = "[hidden]"
return &displayOpts
}))
expvar.Publish("Version", expvar.Func(func() interface{} {
return version.GetVersion()
}))
cmd := &cobra.Command{
Use: "karpor",
Short: "Launch an API server",
Long: "Launch an API server",
RunE: func(c *cobra.Command, args []string) error {
if o.CoreOptions.Version {
fmt.Println(version.GetVersion())
return nil
}
if err := o.Complete(); err != nil {
return err
}
if err := o.Validate(args); err != nil {
return err
}
if err := o.RunServer(ctx.Done()); err != nil {
return err
}
return nil
},
}
o.AddFlags(cmd.Flags())
return cmd
}
创建 option -> 补全 option -> 校验 option -> 用 option 启动 server (和 k8s-apiserver 代码格式一模一样)
AIOptions 配置
这里看一下怎么配置 AIOption 的,我对这方面比较感兴趣
cmd/karpor/app/options/ai.go:43 创建 AIOptions
type AIOptions struct {
AIBackend string
AIAuthToken string
AIBaseURL string
AIModel string
AITemperature float32
AITopP float32
// proxy options
AIProxyEnabled bool
AIHTTPProxy string
AIHTTPSProxy string
AINoProxy string
}
func NewAIOptions() *AIOptions {
return &AIOptions{}
}
参数和标志
参数 : go run main.go add 3 5 args 数组将会包含以下内容 args := []string{"3", "5"},这个很少用,一般使用命令标志。
cmd/karpor/app/options/ai.go:67
o.AIOptions.AddFlags(fs)
// AddFlags adds flags for a specific Option to the specified FlagSet
func (o *AIOptions) AddFlags(fs *pflag.FlagSet) {
if o == nil {
return
}
fs.StringVar(&o.AIBackend, "ai-backend", defaultBackend, "The ai backend")
fs.StringVar(&o.AIAuthToken, "ai-auth-token", "", "The ai auth token")
fs.StringVar(&o.AIBaseURL, "ai-base-url", "", "The ai base url")
fs.StringVar(&o.AIModel, "ai-model", defaultModel, "The ai model")
fs.Float32Var(&o.AITemperature, "ai-temperature", defaultTemperature, "The ai temperature")
fs.Float32Var(&o.AITopP, "ai-top-p", defaultTopP, "The ai top-p")
fs.BoolVar(&o.AIProxyEnabled, "ai-proxy-enabled", false, "The ai proxy enable")
fs.StringVar(&o.AIHTTPProxy, "ai-http-proxy", "", "The ai http proxy")
fs.StringVar(&o.AIHTTPSProxy, "ai-https-proxy", "", "The ai https proxy")
fs.StringVar(&o.AINoProxy, "ai-no-proxy", "", "The ai no-proxy")
}
3. 运行命令
然后创建子命令 syncCmd 添加到 rootCmd 中 cmd.AddCommand(syncCmd)
code := cli.Run(cmd)
os.Exit(code)
这个写法也很典型,它确保日志记录已正确设置。在日志记录设置之前,它将错误作为纯文本打印到标准错误输出。这涵盖了命令行标志解析错误和未知命令。之后,它会使用 klog 记录这些错误,这涵盖了运行时错误。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。





U8W/U8W-Mini使用与常见问题解决
QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结