大家好, 我是老麦, 我将每天 早上9点 为你分享一篇好文章。
原文链接: https://tangx.in/posts/2023/01/26/devopscamp-cobra-interactive-survey/
本文为 DevOpsCamp 实战训练作业 cobra - 03 配置文件的读取与写入(简单) 的解题答案
DevoOpsCamp 作业地址:https://www.devopscamp.cc/semi-plan-202301-2/posts/homework/cobra03/
要求:
除了官方效果之外, 我还发现了 aliyun
命令行工具在配置账户的时候使用的是 交互式 , 如下
为了更好的体现 实战性, 我们将以 aliyun configure --profile
的作为例子, 并进行一些优化。
survey
依赖这是一个意外收获, survey 库的 Github 地址与 go module
名称不一致。同时, survey
版本还是 v2 了。
关于 go module version
, 参考文章:https://go.dev/doc/modules/version-numbers
话说回来, 虽然 github 仓库地址是 https://github.com/go-survey/survey
, 但安装库需要使用命令
$ go get -u github.com/AlecAivazis/survey/v2
在 go.mod
第一行中, 也可以看到 module 的名称
module github.com/AlecAivazis/survey/v2
平时在使用的时候, 应该多注意官方文档的 Usage
、 exmaple
或者 _test.go
等。
survey
提供了很多组件类型以及 Option
参数、 验证器 等功能,非常全面。在这里简单介绍常用的几种
Input
组件:普通输入框, 输入什么就显示什么。Password
组件:密码输入框, 输入的内容不直接显示, 使用 *
替代。Select
组件:单选框。MultiSelect
组件:多选框, 结果为 切片 类型。Confirm
组件:确认框, 结果为 布尔 类型。更多其它组件, 可以参考官方文档。
参考 aliyun 命令行, 我们自己实现的功能需要以下字段。
代码中, 创建了 匿名 struct , 并创建 实例 赋值给 answers
answers := struct {
ID string
Key string
ChinaRegion string `survey:"region"`
Language []string
}{}
其中 ChinaRegion
字段通过 tag survey:"region"
指定了一个映射名字 region
。 回想一下, 这种用法是不是和上一篇配置文件中的 json, yaml
字段的映射名字用法一样?
另一方面, 我们还准备了一系列问题, 引导用户输入
// the questions to ask
var qs = []*survey.Question{
{
// 1. Input 输入框
Name: "id",
Prompt: &survey.Input{
Message: "Access Secret ID: ",
},
Validate: survey.Required,
},
{
// 2. Password 密码输入框
Name: "key",
Prompt: &survey.Password{
Message: "Access Secret Key: ",
},
Validate: survey.Required,
},
{
// 3. Select 单选框
Name: "region",
Prompt: &survey.Select{
Message: "Choose a region:",
Options: []string{"cn-shanghai", "cn-hangzhou"},
Default: "cn-hangzhou",
},
},
{
// 4. MultiSelect 多选框
Name: "language",
Prompt: &survey.MultiSelect{
Message: "Supported Configure Language: ",
Options: []string{"zh", "en", "jp"},
},
},
}
qs
中的 Name
名称与 answers
中的字段名称都是一一对应的。id
和 key
字段, 设置了验证器, 要求 必须提供。region
字段, 设置 cn-hangzhou
为默认值, 虽然在切片中排在第二位。另外, 我们还使用 Confirm
组件引导用户确认是否将输入内容保存到文件中。由于 保存确认 并不需要保存到配置文件中, 因此我们将其单独封装在了 confirm
函数中。
func confirm() bool {
ok := false
// 5. Confirm 确认框
prompt := &survey.Confirm{
Message: "是否保存文件?",
}
survey.AskOne(prompt, &ok)
return ok
}
JSON MashralIndent
为了更好的可读性, 这次在保存配置文件的时候, 使用了 MarshalIndent
方法。
{
"ID": "AKID-demodemo-adsfasdf",
"Key": "flasjdflaksdjf",
"ChinaRegion": "cn-shanghai",
"Language": [
"zh",
"en"
]
}
profile
字段你可能已经注意到了, 目前所有的代码都在 main
包下面, 并没有 划分目录结构。
var profile string
因此定义的 profile
是全局变量, 可以在 任意位置 直接使用。
但是我们在使用的时候并没有在函数中直接使用, 而是通过 函数参数 的方式传递下去的。这是我们 刻意 回避直接在 dumpConfig
中直接使用 全局的profile
的。
关于 目录结构 我们将会在后面的作业中提到。
var root = &cobra.Command{
Use: "aliyunx",
Short: "aliyun 配置中心",
Run: func(cmd *cobra.Command, args []string) {
// 1. 使用全局 profile
interactive(profile)
},
}
func interactive(profile string) {
// 2. 参数传递
dumpConfig(profile, answers)
}
func dumpConfig(profile string, answer any) {
// 3. 参数传递
name := fmt.Sprintf("%s.config.json", profile)
err2 := os.WriteFile(name, b, os.ModePerm)
if err2 != nil {
panic(err2)
}
}
关联文章
1. 《DevOpsCamp作业: cobra - 01 实现编译与参数绑定(简单)》 解题答案 2. DevopsCamp 第一期作业: 《cobra - 02 配置文件的读取与保存(简单)》 解题答案