golang调用shell实现kubeconfig文件有效期监测和监控,代码采用cobra命令行工具库编写。其主要功能是:
项目名称为:sretools
/*
Copyright © 2023 NAME HERE
*/
package main
import "sretools/cmd"
func main() {
cmd.Execute()
}
/*
Copyright © 2023 NAME HERE
*/
package cmd
import (
"strconv"
"strings"
"fmt"
"time"
"github.com/spf13/cobra"
"sretools/lib"
)
const (
STANDARD_DIR="/root/kubeconfig_monitor"
CERT_PEM_FILE_NAME="client-cert.pem"
DAY_SECOND=86400
DING_TALK_URL="钉钉群机器人地址"
)
// kubeconfcheckCmd represents the kubeconfcheck command
var kubeconfcheckCmd = &cobra.Command{
Use: "kubeconfcheck",
Short: "kubeconfig expire time verificate",
Example: "sretools kubeconfcheck --conf ./root/.kube/config",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
CheckKubeConfig(conf,app)
},
}
func init() {
rootCmd.AddCommand(kubeconfcheckCmd)
kubeconfcheckCmd.Flags().StringVarP(&conf, "conf", "c", "", "kubeconfig path,example:/root/.kube/config")
kubeconfcheckCmd.MarkFlagRequired("conf")
kubeconfcheckCmd.Flags().StringVarP(&app, "app", "a", "", "oceanbase public cloud app,example:ocp,oms...")
kubeconfcheckCmd.MarkFlagRequired("app")
kubeconfcheckCmd.Flags().StringVarP(®ion, "region", "r", "", "oceanbase public cloud region,please refer to:https://help.aliyun.com/document_detail/40654.html?spm=a2c4g.750001.0.i2")
kubeconfcheckCmd.MarkFlagRequired("region")
kubeconfcheckCmd.Flags().StringVarP(&zone, "zone", "z", "", "oceanbase public cloud region zone,example:zeus(宙斯区)、obvpc(obvpc区)")
kubeconfcheckCmd.MarkFlagRequired("zone")
kubeconfcheckCmd.Flags().StringVarP(&days, "days", "d", "", "expire days,example:1、7、30、180、365")
kubeconfcheckCmd.MarkFlagRequired("days")
}
func CheckKubeConfig(file,app string) {
var err error
//校验配置文件是否存在
if !lib.IsExist(file) {
fmt.Printf("file:%v node exist",file)
return
}
//校验kubeconfig文件到期时间
//读取文件内容
fileContentCmd := fmt.Sprintf("cat %v | grep client-certificate-data | awk -F ' ' '{print $2}' |base64 -d",file)
content,err := lib.RunCmd(fileContentCmd)
if err != nil {
fmt.Printf("read file content failed:%v\n",err)
return
}
// 判断路径是否存在
err = lib.DirNotExistAndCreate(STANDARD_DIR)
if err != nil {
fmt.Printf("create dir :%v failed:%v\n",STANDARD_DIR,err)
return
}
pemfile := fmt.Sprintf("%v/%v-%v",STANDARD_DIR,app,CERT_PEM_FILE_NAME)
err = lib.WriteToFile(pemfile,content)
if err != nil {
return
}
checkCertCmd := fmt.Sprintf("openssl x509 -in %v -noout -dates| grep After| awk -F '=' '{print $2}'|awk -F 'GMT' '{print $1}'",pemfile)
//fmt.Printf("%v\n",checkCertCmd)
certTime,err := lib.RunCmd(checkCertCmd)
if err != nil {
fmt.Printf("read file content failed:%v\n",err)
return
}
//fmt.Printf("cert time:%v\n",strings.Replace(certTime,"\n","",-1))
timeToUnix := fmt.Sprintf("date -d '%v' %v",strings.Replace(certTime,"\n","",-1),"+%s")
//fmt.Printf("%v\n",timeToUnix)
unixTime,err := lib.RunCmd(timeToUnix)
if err != nil {
fmt.Printf("read file content failed:%v\n",err)
return
}
//fmt.Printf("cert time:%v\n",certTime)
now := time.Now().Unix()
utime,_ := strconv.ParseInt(strings.Replace(unixTime,"\n","",-1), 10, 64)
expiretime := utime - now
msgContent := fmt.Sprintf("标题:kubeconfig有效期巡检\n检测时间:%v\nregion:%v\n功能区:%v\n巡检内容:kubeconfig文件【%v】有效期不足%v天,请及时替换!\n到期时间:%v",time.Non,zone,file,days,certTime)
day,_ := strconv.ParseInt(days, 10, 64)
if expiretime <= day * DAY_SECOND {
err := lib.SendMsg(msgContent,DING_TALK_URL)
if err != nil {
fmt.Printf("send msg to dingding failed:%v\n",err)
return
}
}
return
}
package cmd
var (
process string
conf string
app string
region string
zone string
days string
env string
)
package lib
import (
"fmt"
"os/exec"
"bytes"
)
func RunCmd(cmdstring string) (string, error) {
var out bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command("/bin/sh", "-c", cmdstring)
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
fmt.Printf("err:%v\n",err)
return fmt.Sprintf("%s",stderr.String()),err
}
return fmt.Sprintf("%v",out.String()),nil
}
package lib
import (
"io/fs"
"io/ioutil"
"fmt"
"os"
)
func ReadFile(path string) (string, error) {
content, err := os.ReadFile(path)
if err != nil {
return "",err
}
return string(content),nil
}
func IsExist(filePath string) bool {
_, err := os.Stat(filePath)
return err == nil || os.IsExist(err)
}
func CreateFile(filepath string) error {
file, err := os.Create(filepath)
if err != nil {
fmt.Println(err)
return err
}
// 关流(不关流会长时间占用内存)
defer file.Close()
return nil
}
func WriteToFile(file,content string) error {
err := ioutil.WriteFile(file,[]byte(content),0666)
if err != nil {
fmt.Printf("write to file failed:%v\n",err)
return err
}
return nil
}
func CheckDirExist(path string) error {
var err error
_, err = os.ReadDir(path)
if err != nil {
fmt.Printf("dir:%v not exist:%v\n",path,err)
return err
}
return nil
}
func CreateDir(path string) error {
var err error
err = os.MkdirAll(path, fs.ModePerm)
if err != nil {
fmt.Printf("create dir :%v failed:%v\n",path,err)
return err
}
return nil
}
func DirNotExistAndCreate(path string) error {
// 判断路径是否存在
err := CheckDirExist(path)
if err != nil {
// 不存在就创建
err = CreateDir(path)
if err != nil {
fmt.Printf("create dir :%v failed:%v\n",path,err)
return err
}
}
return nil
}
package lib
import (
"fmt"
"time"
)
func Printf(msg,level string) {
fmt.Printf("[%v] [%v] %v\n",time.Now().Format("2006-01-02 15:04:05"),level,msg)
}
package lib
import (
"net"
"fmt"
)
//获取本机ip地址
func GetIps() (ips []string) {
interfaceAddr, err := net.InterfaceAddrs()
if err != nil {
fmt.Printf("fail to get net interfaces ipAddress: %v\n", err)
return ips
}
for _, address := range interfaceAddr {
ipNet, isVailIpNet := address.(*net.IPNet)
if isVailIpNet && !ipNet.IP.IsLoopback() {
if ipNet.IP.To4() != nil {
ips = append(ips, ipNet.IP.String())
}
}
}
return ips
}
func GetEth0Ip() string {
ips := GetIps()
if len(ips) > 0 {
return ips[0]
}
return ""
}
package lib
import (
"fmt"
//"time"
"encoding/json"
"strings"
"net/http"
"io/ioutil"
"errors"
)
func SendMsg(content,url string) error {
var msg *Message = &Message{
Msgtype: "text",
Text: &Msgtext{
Content: content,
},
}
err := HttpPost(msg,url)
if err != nil {
message := fmt.Sprintf("发送钉钉消息失败,失败原因:%v",err)
Printf(message,"ERROR")
return err
}
return nil
}
type Message struct {
Msgtype string `json:"msgtype"`
Text *Msgtext `json:"text"`
}
type Msgtext struct {
Content string `json:"content"`
}
type Response struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
func HttpPost(param *Message,url string) error {
var (
err error
message string
)
// json.Marshal
reqParam, err := json.Marshal(¶m)
if err != nil {
message = fmt.Sprintf("解析请求参数失败:%v",err)
Printf(message,"ERROR")
return err
}
reqBody := strings.NewReader(string(reqParam))
httpReq, err := http.NewRequest("POST", url, reqBody)
if err != nil {
message = fmt.Sprintf("发起http post请求失败,url: %s, reqBody: %s,失败原因:%v",url, reqBody,err)
Printf(message,"ERROR")
return err
}
httpReq.Header.Add("Content-Type", "application/json")
// DO: HTTP请求
httpRsp, err := http.DefaultClient.Do(httpReq)
if err != nil {
message = fmt.Sprintf("http post请求处理失败, url: %s, reqBody: %s, 失败原因:%v", url, reqBody, err)
Printf(message,"ERROR")
return err
}
defer httpRsp.Body.Close()
// Read: HTTP结果
rspBody, err := ioutil.ReadAll(httpRsp.Body)
if err != nil {
message = fmt.Sprintf("获取http post响应失败, url: %s, reqBody: %s, 失败原因: %v", url, reqBody, err)
Printf(message,"ERROR")
return err
}
var result Response
if err = json.Unmarshal(rspBody, &result); err != nil {
message = fmt.Sprintf("解析http响应失败, 失败原因:%v", err)
Printf(message,"ERROR")
return err
}
if result.Errcode != 0 {
message = fmt.Sprintf("http post fail, url: %s, reqBody: %s, ErrorMsg:%s", url, reqBody, result.Errmsg)
Printf(message,"ERROR")
return errors.New(result.Errmsg)
}
return nil
}
3 使用
30 9 * * * /usr/bin/sretools kubeconfcheck -a appname -c /root/.kube/config -r region-id -z zonename -d 30 >> /dev/null 2>&1