Compartilhamento de tecnologia

aprendizado da biblioteca de comando cobra k8s

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Introdução

Quando abri o código k8s, descobri que basicamente os serviços principais usam a biblioteca cobra como capacidade de processamento de linha de comando.Portanto, para ter uma compreensão mais profunda do aprendizado de código por trás do código, vamos primeiro escrever uma demonstração baseada nesta biblioteca para aprofundar nossa compreensão dela.

2.Introdução básica à biblioteca cobra

Github:GitHub - spf13/cobra: Um comandante para interações modernas de CLI do Go Aqui está, Cobra é uma ferramenta de linha de comando implementada na linguagem Go. E agora está sendo usado por muitos projetos, como Kubernetes, Hugo e Github CLI, etc.Ao usar o Cobra, podemos criar rapidamente ferramentas de linha de comando, especialmente adequadas para escrever测试脚本, vários serviços Admin CLIespere.

Por causa dos parâmetros da linha de comando, o próprio golang vem com a biblioteca "flag". Como usar a biblioteca de flags?

  1. flag.Parse()
  2. args := flag.Args()

Após a análise, cada comando precisa ser julgado, o que é muito problemático.

Mas quando a usamos, precisamos fazer várias análises e julgamentos na linha de comando, mas com esta biblioteca podemos reduzir esse código redundante.

3 Demonstração de uso do Cobra

Sem mais delongas, vamos explicar diretamente como usar este projeto. Meu ambiente está no macos.

3.1 Primeiro baixe as bibliotecas dependentes

Execução de linha de comando

go get -u github.com/spf13/cobra@latest
3.2 Ferramentas CLI de linha de comando
$ go install github.com/spf13/cobra-cli@latest

Porque eu já construí o diretório bin no caminho do meu código golang, então depois de executar este comando, é o seguinte

Vimos o arquivo executável do cobra-cli.

Aqui podemos usar isso no comando para inicializá-lo.As etapas coletivas são as seguintes

  1. xxx@MBP src % mkdir greet
  2. xxx@MBP src % cd greet
  3. xxx@MBP test % ls
  4. xxx@MBP greet % cobra-cli init
  5. Error: Please run `go mod init <MODNAME>` before `cobra-cli init`
  6. xxx@MBP greet % go mod init greet
  7. go: creating new go.mod: module greet
  8. xxx@MBP greet % ls
  9. go.mod
  10. xxx@MBP greet % cobra-cli init
  11. Your Cobra application is ready at
  12. /Users/XXX/workspace/golang/src/greet
  13. xxx@MBP greet % ls
  14. LICENSE cmd go.mod go.sum main.go
  15. xxx@MBP greet %

A primeira etapa é criar um diretório de teste, executar go mod init test e, em seguida, executar a linha de comando cobra-cli init e o arquivo de andaime será gerado.

Os seguintes arquivos serão gerados no diretório:

  1. main.go cmd LICENSE go.mod go.sum
  2. ├── LICENSE
  3. ├── cmd
  4. │   └── root.go
  5. └── main.go
  6. go.mod
  7.   go.sum

3.3 Escreva arquivos de código

O arquivo que acabamos de gerar pode não ser muito útil porque não há opções de comando específicas, então continuamos a executar e adicionar uma palavra de comando

cobra-cli add [command]

Desta forma, ele irá gerar outro arquivo.

Por exemplo, execute

cobra-cli adicionar saudação

  1. // Package cmd /*
  2. package cmd
  3. import (
  4. "fmt"
  5. "github.com/spf13/cobra"
  6. )
  7. // greetCmd represents the greet command
  8. var greetCmd = &cobra.Command{
  9. Use:   "greet",
  10. Short: "A brief description of your command",
  11. Long: `A longer description that spans multiple lines and likely contains examples
  12. and usage of using your command. For example:
  13. Cobra is a CLI library for Go that empowers applications.
  14. This application is a tool to generate the needed files
  15. to quickly create a Cobra application.`,
  16. Run: func(cmd *cobra.Command, args []string) {
  17.  // 这段代码是我加的。
  18. if len(args) < 1 {
  19. cmd.Help()
  20. return
  21. }
  22. name := args[0]
  23. fmt.Println("greet called:", name)
  24. },
  25. }
  26. func init() {
  27. rootCmd.AddCommand(greetCmd)
  28. // Here you will define your flags and configuration settings.
  29. // Cobra supports Persistent Flags which will work for this command
  30. // and all subcommands, e.g.:
  31. // greetCmd.PersistentFlags().String("foo", "", "A help for foo")
  32. // Cobra supports local flags which will only run when this command
  33. // is called directly, e.g.:
  34. greetCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
  35. }

Portanto, podemos ver que o comando greet foi adicionado.

Em seguida, compilamos e geramos o arquivo de destino, e então executamos o seguinte

  1. ./greet
  2. A longer description that spans multiple lines and likely contains
  3. examples and usage of using your application. For example:
  4. Cobra is a CLI library for Go that empowers applications.
  5. This application is a tool to generate the needed files
  6. to quickly create a Cobra application.
  7. Usage:
  8. greet [command]
  9. Available Commands:
  10. completion Generate the autocompletion script for the specified shell
  11. greet       A brief description of your command
  12. help       Help about any command
  13. mockMsg     A brief description of your command
  14. Flags:
  15.  -h, --help     help for greet
  16.  -t, --toggle   Help message for toggle
  17. Use "greet [command] --help" for more information about a command.
 

A partir do conteúdo retornado acima, podemos ver que há um subcomando gree adicional.

E você pode adicionar sinalizadores, como -h

A execução acima pode conter a lógica de negócios que queremos processar.

Se quisermos adicionar alguns valores de flag, só precisamos adicionar o seguinte código em run

  1. g, _ := cmd.Flags().GetInt32("goroutine")
  2. p, _ := cmd.Flags().GetInt32("packet")
  3. fmt.Println("mockmsg called,flags:g=", g, ",p=", p, ",args:", args)

Por exemplo, implementamos o seguinte:

  1. Run: func(cmd *cobra.Command, args []string) {
  2. if len(args) < 1 {
  3. cmd.Help()
  4. return
  5. }
  6. name := args[0]
  7. fmt.Println("greet called:", name)
  8. g, _ := cmd.Flags().GetInt32("goroutine")
  9. p, _ := cmd.Flags().GetInt32("packet")
  10. fmt.Println("mockmsg called,flags:g=", g, ",p=", p, ",args:", args)
  11. },

Desta forma, pode ser usado assim

  1. ./greet greet xx -p 10 -g 100
  2. greet called: xx
  3. greetCmd called,flags:g= 100 ,p= 10 ,args: [xx]

4 Como usá-lo em K8s

Examinei principalmente o código do módulo kubctl e descobri que esse módulo possui um encapsulamento cmd mais rico para suportar cenários mais complexos.

Entre eles, partimos do principal do kubectl

um método de entrada

  1. // NewDefaultKubectlCommand creates the `kubectl` command with default arguments
  2. func NewDefaultKubectlCommand() *cobra.Command {
  3. ioStreams := genericiooptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
  4. return NewDefaultKubectlCommandWithArgs(KubectlOptions{
  5. PluginHandler: NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes),
  6. Arguments:     os.Args,
  7. ConfigFlags:   defaultConfigFlags().WithWarningPrinter(ioStreams),
  8. IOStreams:     ioStreams,
  9. })
  10. }

Como o kubectl pode processar arquivos e sinalizadores, vários parâmetros precisam ser definidos para esta situação. Descobrimos que cada campo na estrutura do cmd foi totalmente utilizado.

Esses dois métodos são os métodos principais, que são basicamente inicializados e atribuídos à estrutura de comando.

  1. // NewDefaultKubectlCommandWithArgs creates the `kubectl` command with arguments
  2. func NewDefaultKubectlCommandWithArgs(o KubectlOptions) *cobra.Command {
  3. cmd := NewKubectlCommand(o)

Entre eles, NewKubectlCommand é usado para novas instâncias de cmd.

  1. func NewKubectlCommand(o KubectlOptions) *cobra.Command {
  2. warningHandler := rest.NewWarningWriter(o.IOStreams.ErrOut, rest.WarningWriterOptions{Deduplicate: true, Color: term.AllowsColorOutput(o.IOStreams.ErrOut)})
  3. warningsAsErrors := false
  4. // Parent command to which all subcommands are added.
  5. cmds := &cobra.Command{
  6. Use:   "kubectl",

É aqui que vemos o uso dos comandos kubectl. Como este artigo fala principalmente sobre o uso do comando, não entraremos em detalhes sobre outros comandos get do k8s. Falarei sobre isso no próximo artigo.

Abaixo está uma explicação detalhada dos campos relacionados específicos do Comando.

以下是对 `Command` 结构体中每个字段的详细解释:
​
  1. 1. `Use`:这是一个字符串,用于描述命令的基本使用方式。它规定了命令所需的参数和可选参数的格式,为用户提供了简洁明了的使用指导。
  2.    - 例如:`"add [-F file | -D dir]... [-f format] profile"` 表示 `add` 命令可以有可选的 `-F` 和 `-D` 参数,其中 `-F` 后跟一个文件,`-D` 后跟一个目录,这两个参数是互斥的,并且可以多次出现,还有一个可选的 `-f` 参数后跟格式信息,最后需要一个 `profile` 参数。
  3. 2. `Aliases`:一个字符串切片,存储了该命令的别名。这使得用户可以通过不同的名称来调用同一个命令,增加了命令使用的灵活性。
  4.    - 比如:`["add_item", "insert"]` 是 `add` 命令的别名。
  5. 3. `SuggestFor`:也是一个字符串切片,其中包含了此命令可能被建议替代的其他命令名称。这有助于在用户输入类似但不完全准确的命令时提供相关的建议。
  6. 4. `Short`:一个简短的字符串,用于在帮助输出中提供命令的简短描述,让用户快速了解命令的主要功能。
  7.    - 例如:"添加新的项目"
  8. 5. `GroupID`:指定该子命令在其父命令的“帮助”输出中所属的组标识,便于对命令进行分组展示和管理。
  9. 6. `Long`:详细的长字符串,在“帮助 <此命令>”输出中提供更全面和深入的命令描述,包括更多的功能细节、使用示例、注意事项等。
  10. 7. `Example`:包含命令使用的示例字符串,通过实际的例子帮助用户更好地理解如何正确使用该命令。
  11. 8. `ValidArgs`:一个字符串切片,列出了在外壳自动补全中所有有效的非标志参数。
  12. 9. `ValidArgsFunction`:一个函数,动态地提供有效的非标志参数用于外壳自动补全,是一种更灵活的参数提供方式。
  13. 10. `Args`:定义了预期的参数的相关规则和处理方式。
  14. 11. `ArgAliases`:一个字符串切片,列出了有效参数的别名,这些别名不会在外壳自动补全中被提示,但手动输入时会被接受。
  15. 12. `BashCompletionFunction`:用于传统 Bash 自动补全生成器的自定义 Bash 函数,为特定的 Bash 环境提供定制的自动补全功能。
  16. 13. `Deprecated`:如果命令已被弃用,存储了使用该命令时将显示的提示信息,告知用户该命令不应再被使用。
  17. 14. `Annotations`:一个键值对映射,允许应用程序为命令添加自定义的标识、分组或特殊选项等元数据。
  18. 15. `Version`:存储命令的版本信息,用于版本控制和显示。
  19. 16. 各种 `Run` 函数:
  20.    - `PersistentPreRun` 和 `PersistentPreRunE`:在命令执行前被调用,且子命令会继承并执行,用于进行一些持久的预处理操作。
  21.    - `PreRun` 和 `PreRunE`:在命令执行前被调用,但子命令不会继承,用于特定于当前命令的预处理。
  22.    - `Run` 和 `RunE`:实际的工作函数,实现命令的主要逻辑。
  23.    - `PostRun` 和 `PostRunE`:在 `Run` 函数执行后被调用,用于进行后续的处理操作。
  24. 17. `commandgroups`:一个指针切片,指向子命令所属的组对象。
  25. 18. `args`:实际从标志解析得到的参数切片。
  26. 19. `flagErrorBuf`:一个字节缓冲区,用于存储来自 `pflag` 的错误消息。
  27. 20. `flags`:一个 `flag.FlagSet` 对象,包含了所有的标志。
  28. 21. `pflags`:存储持久的标志。
  29. 22. `lflags`:本地标志的缓存,用于优化 `LocalFlags` 函数调用。
  30. 23. `iflags`:继承的标志的缓存,用于优化相关函数调用。
  31. 24. `parentsPflags`:父命令的所有持久标志。
  32. 25. `globNormFunc`:一个全局的标准化函数,用于处理标志名称的标准化。
  33. 26. `usageFunc`:用户定义的使用函数,用于自定义命令的使用说明。
  34. 27. `usageTemplate`:用户定义的使用模板。
  35. 28. `flagErrorFunc`:用户定义的标志错误处理函数。
  36. 29. `helpTemplate`:用户定义的帮助模板。
  37. 30. `helpFunc`:用户定义的帮助函数。
  38. 31. `helpCommand`:具有“帮助”用途的命令,如果用户未定义,则使用默认的帮助命令。
  39. 32. `helpCommandGroupID`:帮助命令所属的组标识。
  40. 33. `completionCommandGroupID`:自动完成命令所属的组标识。
  41. 34. `versionTemplate`:用户定义的版本模板。
  42. 35. `errPrefix`:用户定义的错误消息前缀。
  43. 36. `inReader`:用户定义的输入读取器,替代标准输入。
  44. 37. `outWriter`:用户定义的输出写入器,替代标准输出。
  45. 38. `errWriter`:用户定义的错误输出写入器,替代标准错误输出。
  46. 39. `FParseErrWhitelist`:要忽略的标志解析错误的列表。
  47. 40. `CompletionOptions`:用于控制外壳自动完成的选项。
  48. 41. `commandsAreSorted`:一个布尔值,指示命令切片是否已排序。
  49. 42. `commandCalledAs`:一个结构体,记录命令被调用时的名称和是否被调用的状态。
  50. 43. `ctx`:上下文对象,用于传递和管理命令执行的上下文信息。
  51. 44. `commands`:一个指针切片,包含此命令支持的子命令。
  52. 45. `parent`:指向该命令的父命令的指针。
  53. 46. `Max lengths` 相关字段:记录命令相关字符串的最大长度,用于格式化和展示。
  54. 47. `TraverseChildren`:一个布尔值,决定是否在执行子命令之前解析所有父命令的标志。
  55. 48. `Hidden`:如果为真,该命令将在可用命令列表中隐藏,不向用户显示。
  56. 49. `SilenceErrors`:一个布尔值,用于控制是否静默处理错误。
  57. 50. `SilenceUsage`:一个布尔值,用于控制在发生错误时是否静默使用信息。
  58. 51. `DisableFlagParsing`:如果为真,将禁用标志解析,所有标志将作为参数传递给命令。
  59. 52. `DisableAutoGenTag`:如果为真,在生成文档时将禁用自动生成的标签。
  60. 53. `DisableFlagsInUseLine`:如果为真,将在使用行中禁用标志的添加。
  61. 54. `DisableSuggestions`:如果为真,将禁用基于编辑距离的建议功能。
  62. 55. `SuggestionsMinimumDistance`:定义显示建议的最小编辑距离,必须大于 0 。

5 Resumo

Este artigo é principalmente uma explicação passo a passo de como usar a biblioteca cobra. Quando usamos esta biblioteca, podemos entender melhor seu princípio de funcionamento e, a seguir, com base em como usar os k8s específicos e o uso de campos específicos para. aproveite ao máximo os recursos e o valor do cmd