前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Go构建一个Data Thrashing CLI工具

使用Go构建一个Data Thrashing CLI工具

作者头像
大数据弄潮儿
发布2018-05-30 11:20:29
8730
发布2018-05-30 11:20:29
举报
文章被收录于专栏:大数据大数据

因为我需要一个工具来做一些测试。所以用Go的一些库快速地开发了一个这样的工具。

以下所撰就是这个快速上手的项目。最后,我将在容器中搭建服务,并将其放入Kubernetes(k8s)集群中,不过下面操作都是在CLI环境下进行的。

在使用Cobra之前,需要安装Go的依赖管理工具dep,使用下面的命令来安装或者升级。

代码语言:txt
复制
brew install dep
brew upgrade dep

如果你用的不是MacOS或者brew命令 ,你也可以通过 go get的方式来安装,执行go get -u github.com/golang/dep/cmd/dep。对你的项目的根目录的初始化命令是dep Init接下来会详细说明。

首先,我使用Cobra CLI为我自己的CLI生成初始代码。若要安装Cobra,也需要使用go get,命令为go get -u github.com/spf13/cobra/cobra。执行cobra init github.com/adron/thrasher来生成CLI的初始框架。

/src/github.com/adron/blueland-cli/是我按照标准在Go目录下生成的。

我现在进入到该目录并获取一些命令集来启动我的项目。

我使我的CLI能够有以下的操作命令。

此命令来启动调用数据的API接口:

代码语言:txt
复制
thrasher thrash

此命令用来检查请求将发送到的URI:

代码语言:txt
复制
thrasher config set

此命令可以用来检查请求将被发送的URI:

代码语言:txt
复制
thrasher config view

此命令用于验证已配置的URI是否被设置正确:

代码语言:txt
复制
thrasher config verify

以上所述都是我所想要的命令。下面将使用cobra add来发布我的命令。

代码语言:txt
复制
cobra add thrash
cobra add config
cobra add set -p 'configCmd'
cobra add view -p 'configCmd'
cobra add verify -p 'configCmd'

下一步测试这些命令并验证核心代码是否正确,先在根目录下执行一个简单的命令go build来构建CLI 。结果如下所示:

代码语言:txt
复制
$ go build
$ ./thrasher thrashthrash called
$ ./thrasher configconfig called
$ ./thrasher config set
set called
$ ./thrasher config view
view called
$ ./thrasher config verify
verify called
$

现在,立即执行git init进行git的初始化,除了.gitignore这个文件,包含的内容如下:

代码语言:txt
复制
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

.idea
.DS_Store

打包,上传代码到仓库

接下来,我使用了dep(包括dep init命令)来设置项目,第一次拉取时依赖关系如下:

当我执行这个命令时,dep完成了以下的操作。

  1. 查找已经存在的管理依赖关系文件进行转换。
  2. 检查依赖项是否使用了dep
  3. 确定依赖关系。
  4. 备份现有的vendor目录(如果存在的话)到_vendor-TIMESTAMP/目录下。
  5. 为每个依赖项选择最高兼容版本。
  6. 生成Gopkg.tom(清单)和lGopkg.lock文件。
  7. vendor/目录下安装依赖关系。

有关dep更多信息,请查看github上的项目README

我下一个添加依赖项是faker,使用的dep命令如下。

代码语言:txt
复制
dep ensure -add github.com/bxcodec/faker

现在,我开始做一些真正的实现。首先,我们来设置配置文件。这是一个需要在配置文件中检索到配置项,然后进行一个简单的配置。

管理配置

CLI有两个状态,启动时选择其一。它有一个测试的URI,CLI可以选择进行测试或不测试。一个命令被执行CLI都需要检查。如果找不到配置文件,CLI会弹出一条消息,提示没有配置文件,然后退出。如果配置文件存在,那么可以继续下面操作。

为此,我做了一个快速搜索,用到了Viper项目。这个项目已经包含在Cobra项目中,所以可以直接进入这个项目,下面我会详细地阐述下面我说编写的代码内容。

首先,打开CLI主要的代码,用这些代码来尝试一些模拟测试。以下就是使用Cobra生成的代码。在cmd的目录下会有叫cmdroot.go的文件

代码语言:txt
复制
// Copyright © 2017 Adron Hall <adronhall@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
    "fmt"
    "os"

    homedir "github.com/mitchellh/go-homedir"
    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var cfgFile string

// RootCmd 表示在没有其他的子命令的情况下的基本命令。
var RootCmd = &cobra.Command{
    Use:   "thrasher",
    Short: "This CLI is built around testing API end points by submitting data, " +
        "etc, generated at time of testing.",
    Long: `This CLI is built around sending randomly generated data at API end points for testing.
The idea is for the data to be issued against end points using various HTTP verbs such as GET, POST, or others.

For example a similar command using curl.
  curl -d "param1=value1&param2=value2" -X POST http://localhost:3000/data
  curl -d "param1=value1&param2=value2" -H "Content-Type: application/x-www-form-urlencoded" -X POST http://localhost:3000/data`,
}

// Execute 将所有子命令添加到根命令,并合理地设置标志。
// Execute 被main.main()函数调用. rootCmd只会执行一次.
func Execute() {
    if err := RootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

func init() { 
    cobra.OnInitialize(initConfig)

    // 在这里配置你的flag和configuration settings。
    // Cobra支持持久flag,可以这里定义,
    // 对应用进行全局设定。
    RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.thrasher.yaml)")

    // Cobra 也支持本地flag, 只有当这个action被直接调用时才会运行。
    RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

// initConfig读取配置文件和ENV变量。
func initConfig() {
    if cfgFile != "" {
        // 使用来自flag的配置文件。.
        viper.SetConfigFile(cfgFile)
    } else {
        // 寻找主目录。
        home, err := homedir.Dir()
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }

        // 在主目录中搜索名为“.thrasher” 的配置文件。(不包含拓展).
        viper.AddConfigPath(home)
        viper.SetConfigName(".thrasher")
    }

    viper.AutomaticEnv() // read in environment variables that match

    // 如果找到了配置文件,执行导入。
    if err := viper.ReadInConfig(); err == nil {
        fmt.Println("Using config file:", viper.ConfigFileUsed())
    }

在这个文件中,有很多需要注意的地方,之后再进行讨论。现在,我继续在init函数中添加一些代码来初始化并设置环境变量的默认值。现在init函数代码如下:

代码语言:txt
复制
func init() { 
    cobra.OnInitialize(initConfig)

    defaultUri := "localhost:3000"
    viper.SetEnvPrefix("thrasher")  // Uppercased automatically? Maybe just make it uppercase?
    viper.BindEnv("uri")

    uri := viper.Get("uri")

    if uri == nil {
        os.Setenv("THRASHER_URI", defaultUri)
        fmt.Printf("Set environment variable THRASHER_URI to %s.\n\n", defaultUri)
    }

    // 在这里配置你的flag和configuration settings。
    // Cobra支持持久flag,可以这里定义,
    // 对应用进行全局设定。
    RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.thrasher.yaml)")

   // Cobra 也支持本地flag, 只有当这个action被直接调用时才会运行。
    RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

使用Viper的过程中,我给thrasher设置了一个环境变量前缀。这意味着,我设置的任何带有thrasher前缀的变量都将作为环境变量,只针对我的应用程序。然后,我将其中一个绑定到我想要命名uri的环境变量中。我会得到一个叫做uri的环境变量的值。实际上需要设置值是THRASHER_URI,因为Viper库会自动启动环境变量。这有点难以理解,我最后在我的~/.bashrc or ~/.bash_profile目录下设置了一个本地的环境变量。如果我不设置它,If的条件判断会将THRASHER_URI设置为我在代码localhost:3000中设置的默认值。

还要注意的是,在上面的代码中,生成的目录下默认有$HOME/.thrasher.yaml的配置文件。目前,我没修改这个默认值,所以它还是默认的配置。

我想要用thrasher config view命令找到我的环境变量的配置。为此,我在view.go文件中添加了几行代码。在view.go文件中,有一个分配给这个命令的指针位置的变量,我把这个变量用作来打印URI设置。

代码语言:txt
复制
var viewCmd = &cobra.Command{
    Use:   "view",
    Short: "The view command is used to retrieve the configuration settings.",
    Long: `The view command can be used to retrieve and view the configuration
        settings that the thrasher CLI currently has active and in use.`,
    Run: func(cmd *cobra.Command, args []string) {
        uri := viper.Get("uri")
        fmt.Printf("Current URI to issue commands and data against is %s.", uri)
    },
}

好的,view大功告成。现在,开始构建verify。在verify中,我想通过HTTP请求和API的响应来验URI。如果验证没有出现错误,我想要它来处理接下来问题。

我要添加的第一个代码是使用viper来获取URI路径,并使用get来对URI路径发起一个请求。我将它添加到verify命令的指针函数中。

代码语言:txt
复制
uri := viper.GetString("uri")
resp, err := http.Get(uri)

我想要获取内容,所以我写了一个简单的函数来处理返回的内容。该代码如下所示:

代码语言:txt
复制
func keepTopVariableLines(s string, n int) string {
    bodyResult := strings.Join(strings.Split(s, "\n")[:n], "\n")
    return strings.Replace(bodyResult, "\r", "", -1)
}

现在,我在代码中加了一些if-else语句,当运行错误的时候即可返回响应的错误信息。

代码语言:txt
复制
if err != nil {
    fmt.Printf("Configured URI has errors: \n\n%s", err)
} else {
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)

    if err != nil {
        fmt.Printf("Response body caused error: %s", err)
    } else {
        fmt.Println("Get:\n", keepTopVariableLines(string(body), 2))
        fmt.Println("\n\nURI has been retrieved, URI verified.\n\n")
    }
}

现在,我已经把verify的所有部分都做好了。完成的keeptTopVaribleLinesverify命令的函数现在是这样的。

代码语言:txt
复制
func keepTopVariableLines(s string, n int) string {
    bodyResult := strings.Join(strings.Split(s, "\n")[:n], "\n")
    return strings.Replace(bodyResult, "\r", "", -1)
}

// verifyCmd表示验证命令。
var verifyCmd = &cobra.Command{
    Use:   "verify",
    Short: "Verify will test the URI end point with a get call issued.",
    Long: `Verify will test the URI end point with an HTTP get call request against the end point.`,
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("verify called")

        uri := viper.GetString("uri")
        resp, err := http.Get(uri)

        if err != nil {
            fmt.Printf("Configured URI has errors: \n\n%s", err)
        } else {
            defer resp.Body.Close()

            body, err := ioutil.ReadAll(resp.Body)

            if err != nil {
                fmt.Printf("Response body caused error: %s", err)
            } else {
                fmt.Println("Get:\n", keepTopVariableLines(string(body), 3))
                fmt.Println("\n\nURI has been retrieved, URI verified.\n\n")
            }
        }
    },
}

现在,立即执行go build。就会得到一个有一些功能的CLI工具。

在下一篇文章中,我会进行总结,并发布来发布针对端的数据。

happy Go hacking!

如果你有什么问题的话请通过Twitters @Adron发送给我。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 管理配置
相关产品与服务
大数据
全栈大数据产品,面向海量数据场景,帮助您 “智理无数,心中有数”!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档