linux 查找最近修改的文件

按时间查找可以使用参数:

  • -atime 访问时间
  • -ctime 改变状态的时间
  • -mtime修改的时间
find ./ -mtime 0 #返回最近24小时内修改过的文件。
find ./ -mtime 1 #返回的是前48~24小时修改过的文件,注意不是48小时以内修改过的文件。
find . -name '*.php' -mmin -30 #查找最近30分钟修改的当前目录下的.php文件
find . -name '*.inc' -mtime 0 -ls #查找最近24小时修改的当前目录下的.php文件,并列出详细信息

golang 创建错误

Golang 中的错误也是一种类型。错误用内置的 error 类型表示。源码定义如下:

// src/builtin/builtin.go

type error interface {
    Error() string
}

error 为 nil 代表没有错误。

error 创建方式有两种:

  • errors.New()
  • fmt.Errorf(),增加上下文的信息,更精确的描述错误。
err1 := errors.New("abcde")
err2 := fmt.Errorf("math: square root of negative number %g", x)

参考资料


kubernetes 开发之 client-go 学习

3月份已经记录了一些开发与 kubernetes 交互的文章,由于工期比较紧,虽然开发完成了,对client-go的学习还是不彻底。这一篇我记录一些我在 kubernetes 开发中的备忘。

访问 k8s 集群获取资源的几种方式:

  1. 命令行 kubectl

  2. http k8s REST API

  3. 代码库 client-go

    1. ClientSet

      ClientSet 可以对k8s的标准资源进行CURD

    2. Dynamic Client

      它能处理 kubernetes 所有的资源,控制所有的 API,处理CRD一般都是用它。请求返回的对象 unstructured.Unstructured 是一个 map[string]interface{}

    3. DiscoveryClient

      这是一种发现客户端,在前面的客户端中需要知道资源的 Resource 和 Version 才能找到你想要的,

      这些信息太多很难全部记住,这个客户端用于获取资源组、版本等信息。

    4. RESTClient

      最基础的客户端,对HTTP Request进行了封装,实现了RESTFul风格的API。

    5. informer

其中,ClientSet、DynamicClient、DiscoveryClient 都是基于 RESTClient 封装的。

一、背景

Kubernetes 官方从2016年8月份开始,将Kubernetes资源操作相关的核心源码抽取出来,独立出来一个项目Client-go,作为官方提供的Go client。

Kubernetes的 部分代码也是基于这个client实现的。

引入包:

go1.16+:

go get k8s.io/client-go@latest

go1.11+ 使用特定版本:

go get k8s.io/client-go@v0.20.4

更多内容参考官方安装手册

二、构造k8s配置文件,生成clientset和dynamic client

无论是使用 ClientSet 还是 Dynamic Client,第一步都是构建 k8s 的配置文件。

通过参数(master的url或者kubeconfig路径)和BuildConfigFromFlags方法来获取rest.Config对象。

kubeconfig路径一般路径为$HOME/.kube/config,用来配置本地连接的kubernetes集群。

package main

import (
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/dynamic"    
    "log"
    "os"
    "path/filepath"
)

// 从文件 ~/.kube/config 中构造config
func getConfigFromFile() (*restclient.Config, error){
    kubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config")
    config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
    if err != nil {
        log.Fatal(err)
    }
    
    return config, err
}

const (
	// High enough QPS to fit all expected use cases.
	DefaultQPS = 1e6
	// High enough Burst to fit all expected use cases.
	DefaultBurst = 1e6

)

// 从数据库存储的多集群信息,包括masterIPkubeconfig和元数据信息中初始化,参考了[k8s多集群go-client实现](https://blog.csdn.net/weixin_43908373/article/details/95618989)

func InitClient(clusterName string) (*kubernetes.Clientset, *rest.Config, dynamic.Interface, error) {
	//数据库取出集群信息
	master, kubeconfig, err := GetClusterInfo(clusterName)
	if err != nil {
		logs.Error("get db for cluster kubeconfig error. %v ", err)
		return nil, nil, err
	}
	kubeconfigJson, err := yaml.YAMLToJSON([]byte(kubeconfig))
	if err != nil {
		logs.Error("yaml to json err")
	}
	configV1 := clientcmdapiv1.Config{}
	err = json.Unmarshal(kubeconfigJson, &configV1)
	if err != nil {
		logs.Error("json unmarshal kubeconfig error. %v ", err)
		return nil, nil, err
	}
	// 切换匹配的版本
	configObject, err := clientcmdlatest.Scheme.ConvertToVersion(&configV1, clientcmdapi.SchemeGroupVersion)
	if err != nil {
		logs.Error("ConvertToVersion error. %v ", err)
		return nil, nil, err
	}
	configInternal := configObject.(*clientcmdapi.Config)

	// 实例化配置信息
	clientConfig, err := clientcmd.NewDefaultClientConfig(*configInternal, &clientcmd.ConfigOverrides{
		ClusterDefaults: clientcmdapi.Cluster{Server: master},
	}).ClientConfig()

	if err != nil {
		logs.Error("build client config error. %v ", err)
		return nil, nil, err
	}
	clientConfig.QPS = defaultQPS
	clientConfig.Burst = defaultBurst
	
	// 实例化客户端
	clientSet, err := kubernetes.NewForConfig(clientConfig)

	if err != nil {
		logs.Error("(%s) kubernetes.NewForConfig(%v) error.%v", master, err, clientConfig)
		return nil, nil, err
	}
	
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		logrus.Error(err)
		return nil, nil, err
	}

	return clientSet, clientConfig, dynamicClient, nil
}

golang 文件路径相关操作

获取当前目录:

pwd, err := os.Getwd()

获取当前目录下的文件:

filepathNames, err := filepath.Glob(filepath.Join(pwd,"*"))

获取当前目录下的文件(包括子目录):

filepath.Walk(pwd,func(path string, info os.FileInfo, err error) error{
		fmt.Println(path) //打印path信息
		fmt.Println(info.Name()) //打印文件或目录名
		return nil
	})

参考资料


golang 编译备忘

这篇文章记录 golang 编译的一些知识备忘。

一、背景

go中没有项目的说法,只有包, 其中有两个重要的路径,GOROOTGOPATH

  • GOROOT:Go的安装目录,(类似于java的JDK)
  • GOPATH:GOPATH 是我们的工作空间,保存go项目代码和第三方依赖包

我们可以使用 go env 查看相关设置信息:

image-20210428162240487

二、历史

1. GOPATH

使用 GOPATH 时,GO 会在以下目录中搜索包:

  1. GOROOT/src:该目录保存了Go标准库代码。
  2. GOPATH/src:该目录保存了应用自身的代码和第三方依赖的代码。

程序中引入包时,Go会先去 GOROOT的scr目录 中查找,然后在 GOPATH的src目录 找,如果该目录不存在,会报错找不到package。

现在已不再推荐使用 GOPATH 来构建应用了。

2. vendor

Vendor 目录是 Golang 从 1.5 版本开始引入的,为项目开发提供了一种离线保存第三方依赖包的方法。

1.5 版本需要手动设置环境变量 GO15VENDOREXPERIMENT=1

在执行 go buildgo run 命令时,会按照以下顺序去查找包:

  • 当前包下的 vendor 目录
  • 向上级目录查找,直到找到 src 下的 vendor 目录
  • 在 GOROOT 目录下查找
  • 在 GOPATH 下面查找依赖包

在发布 1.6 版本时,该环境变量的值已经默认设置为 1 了,该值可以使用 go env 命令查看。

在发布 1.7 版本时,已去掉该环境变量,默认开启 vendor 特性。

3. vendor管理工具混战

主要是一下三个工具:

godep

godep 通过 godep save 把第三包的版本依赖信息记录在Godeps.json下,并且把第三包完整拷贝一份到vendor下面。通过对 Godeps.json 文件进行版本管理即可以管理整个项目的第三方包依赖信息。

govendor

govendor init之后会在根目录下生成一个vendor文件夹。只需要对vendor/vendor.json进行版本控制,即可对第三包依赖关系进行控制。

glide

glide 通过glide createglide init命令初始化第三方包管理,会在项目根目录下生成一个glide.yaml,这个文件记录用到的第三方包的依赖关系,支持编辑修改。 glide通过glide install, 会把所有缺少的第三方包都下载到vendor文件夹下,并且会在glide.yaml中添加所有依赖的第三方包名称,在glide.lock文件中记录具体的版本管理信息。

4. module

2018年 8 月, Go 发布了 1.11版本,引入Module功能,新增了一组go mod命令,可以通过go help mod查看使用帮助,目前一共有8个子命令:

image-20210428183831784

从 Go 1.13 版本开始,go module 是Go语言默认的依赖管理工具。

三、go module使用

1. 配置

GO111MODULE

设置环境变量 GO111MODULE=on ,就可以使用 go module 了:

  1. GO111MODULE=off 禁用模块支持,编译时会从GOPATHvendor文件夹中查找包。
  2. GO111MODULE=on 启用模块支持,编译时会忽略GOPATHvendor文件夹,只根据 go.mod下载依赖,将依赖下载至%GOPATH%/pkg/mod/ 目录下。
  3. GO111MODULE=auto,当项目在$GOPATH/src外且项目根目录有go.mod文件时,开启支持。

go module 管理依赖后会在项目根目录下生成两个文件go.modgo.sum

GOPROXY

Go1.13 之后 GOPROXY 默认值为https://proxy.golang.org,在国内无法访问,要设置成国内的代理地址,我是用这个域名:

export GOPROXY=https://goproxy.cn
go mod 命令
go mod download    下载依赖的module到本地cache(默认为$GOPATH/pkg/mod目录)
go mod edit        编辑go.mod文件
go mod graph       打印模块依赖图
go mod init        初始化当前文件夹, 创建go.mod文件
go mod tidy        增加缺少的module,删除无用的module
go mod vendor      将依赖复制到vendor下
go mod verify      校验依赖
go mod why         解释为什么需要依赖
go.mod 文件

依赖信息大致如下:

image-20210428192727148

  • module用来定义包名
  • require用来定义依赖包及版本
  • indirect表示间接引用

在国内访问 golang.org/x 的各个包都需要翻墙,我们可以在 go.mod 中使用 replace 替换成 github 上对应的库:

replace (
    golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac => github.com/golang/crypto v0.0.0-20180820150726-614d502a4dac
    golang.org/x/net v0.0.0-20180821023952-922f4815f713 => github.com/golang/net v0.0.0-20180826012351-8a410e7b638d
    golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0
)
go get

在项目中执行go get命令可以下载依赖包,并且还可以指定下载的版本。

  1. 运行go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
  2. 运行go get -u=patch将会升级到最新的修订版本
  3. 运行go get package@version将会升级到指定的版本号version

如果下载所有依赖可以使用go mod download命令。

go mod vendor

go mod vendor 会复制modules下载到vendor中, 只会下载你代码中引用的库,而不是 go.mod 中定义全部的module。用在 module 和 go vendor 混用的情况。

2. 初始化

我这边用 cobra 创建了个简单的项目,参考另一篇文章《golang Cobra 包介绍》

image-20210428155039659

对其做 module 初始化:

$ go mod init appname // 初始化当前文件夹, 创建go.mod文件
$ go mod tidy // 增加缺少的module,删除无用的module

使用 go mod init 要带上package的名字,比如我这里用了appname,这样当其它应用引用包时,就可以直接引用了。

image-20210429111556570


golang Cobra 包简单使用

Cobra 是一个 Golang 包,提供了简单的接口来创建命令行程序。同时也很容易的生成应用程序和命令,cobra create appnamecobra add cmdname 等。

一、概念

cobra 中几个个重要的概念,分别是 commands、arguments 和 flags:

  • commands 代表行为
  • arguments 就是命令行参数(或者称为位置参数)
  • flags 代表对行为的改变(命令行选项)

二、初步使用

1. 安装

$ go get -u github.com/spf13/cobra/cobra

2. 创建应用

$ cobra init --pkg-name appname

image-20210428154327426

3. 增加命令

$ cobra add eula
$ cobra add kazuha

image-20210428155039659

这两条命令生成了两个相关文件。

4. 尝试使用

首先确定包名,我姑且设置为

kelu.org/apptest

对 import 做个修改。

image-20210429112413943

初始化 go mod:

go mod init kelu.org/apptest
go mod tidy

image-20210429112607233

编译

go build

在本目录下生成了 apptest 可执行文件,运行:

./apptest
./apptest eula
./apptest kazuha

image-20210429112731078

image-20210429113707028

5. 代码结构

对着几个子命令文件 var kazuhaCmd = &cobra.Command{ 以下的代码看下就能知道大致的开发套路了。

image-20210429113106610

每个命令的内容都相对简单:

image-20210429113254806