first commit
This commit is contained in:
		
						commit
						e6be1c8f28
					
				|  | @ -0,0 +1,133 @@ | |||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"environmentCaptureAgent/config" | ||||
| 	"environmentCaptureAgent/internal/monitor" | ||||
| 	"fmt" | ||||
| 	"git.hpds.cc/pavement/hpds_node" | ||||
| 	"go.uber.org/zap" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.hpds.cc/Component/logging" | ||||
| 	"github.com/spf13/cobra" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	ConfigFileFlag string = "./config/config.yaml" | ||||
| 	logger         *logging.Logger | ||||
| ) | ||||
| 
 | ||||
| func must(err error) { | ||||
| 	if err != nil { | ||||
| 		fmt.Fprint(os.Stderr, err) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func NewStartCmd() *cobra.Command { | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:   "start", | ||||
| 		Short: "Start hpds environment capture agent", | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			var ( | ||||
| 				cfg *config.AgentConfig | ||||
| 				err error | ||||
| 			) | ||||
| 			//ctx, cancel := context.WithCancel(context.Background())
 | ||||
| 			//defer cancel()
 | ||||
| 			configFileFlag, err := cmd.Flags().GetString("c") | ||||
| 			if err != nil { | ||||
| 				fmt.Println("get local config err: ", err) | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 			must(err) | ||||
| 			cfg, err = config.ParseConfigByFile(configFileFlag) | ||||
| 			must(err) | ||||
| 			logger = LoadLoggerConfig(cfg.Logging) | ||||
| 			exitChannel := make(chan os.Signal) | ||||
| 			defer close(exitChannel) | ||||
| 
 | ||||
| 			// 退出信号监听
 | ||||
| 			go func(c chan os.Signal) { | ||||
| 				signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) | ||||
| 			}(exitChannel) | ||||
| 			ap := hpds_node.NewAccessPoint( | ||||
| 				cfg.Name, | ||||
| 				hpds_node.WithMqAddr(fmt.Sprintf("%s:%d", cfg.Node.Host, cfg.Node.Port)), | ||||
| 				hpds_node.WithCredential(cfg.Node.Token), | ||||
| 			) | ||||
| 			err = ap.Connect() | ||||
| 			must(err) | ||||
| 			//defer ap.Close()
 | ||||
| 			//for _, v := range cfg.Funcs {
 | ||||
| 			//	ap.SetDataTag(v.DataTag)
 | ||||
| 			//}
 | ||||
| 			ap.SetDataTag(18) | ||||
| 			node := monitor.GetHost() | ||||
| 			byteNode := node.ToByte() | ||||
| 			_ = generateAndSendData(ap, byteNode) | ||||
| 
 | ||||
| 			ticker := time.NewTicker(time.Duration(cfg.Delay) * time.Second) | ||||
| 			count := 0 | ||||
| 			//c := cron.New()
 | ||||
| 			//spec := fmt.Sprintf("*/%d * * * * *", cfg.Delay)
 | ||||
| 			//_, err = c.AddFunc(spec, func() {
 | ||||
| 			//	stat := monitor.GetState().ToByte()
 | ||||
| 			//	_ = generateAndSendData(ap, stat)
 | ||||
| 			//	logger.With(
 | ||||
| 			//		zap.String("agent", "发送状态信息"),
 | ||||
| 			//	)
 | ||||
| 			//})
 | ||||
| 			//must(err)
 | ||||
| 			//c.Start()
 | ||||
| 			for { | ||||
| 				select { | ||||
| 				case <-ticker.C: | ||||
| 					stat := monitor.GetState(node.NodeName).ToByte() | ||||
| 					go func() { | ||||
| 						_ = generateAndSendData(ap, stat) | ||||
| 					}() | ||||
| 				case errs := <-exitChannel: | ||||
| 					count++ | ||||
| 					if count > 3 { | ||||
| 						logger.With( | ||||
| 							zap.String("agent", "服务退出"), | ||||
| 						).Info(errs.String()) | ||||
| 						return | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}, | ||||
| 	} | ||||
| 	cmd.Flags().StringVar(&ConfigFileFlag, "c", "./config/config.yaml", "The configuration file path") | ||||
| 	return cmd | ||||
| } | ||||
| 
 | ||||
| func generateAndSendData(stream hpds_node.AccessPoint, data []byte) error { | ||||
| 	_, err := stream.Write(data) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	time.Sleep(1000 * time.Millisecond) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func LoadLoggerConfig(opt config.LogOptions) *logging.Logger { | ||||
| 	return logging.NewLogger( | ||||
| 		logging.SetPath(opt.Path), | ||||
| 		logging.SetPrefix(opt.Prefix), | ||||
| 		logging.SetDevelopment(opt.Development), | ||||
| 		logging.SetDebugFileSuffix(opt.DebugFileSuffix), | ||||
| 		logging.SetWarnFileSuffix(opt.WarnFileSuffix), | ||||
| 		logging.SetErrorFileSuffix(opt.ErrorFileSuffix), | ||||
| 		logging.SetInfoFileSuffix(opt.InfoFileSuffix), | ||||
| 		logging.SetMaxAge(opt.MaxAge), | ||||
| 		logging.SetMaxBackups(opt.MaxBackups), | ||||
| 		logging.SetMaxSize(opt.MaxSize), | ||||
| 		logging.SetLevel(logging.LogLevel["debug"]), | ||||
| 	) | ||||
| } | ||||
|  | @ -0,0 +1,62 @@ | |||
| package config | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
| 
 | ||||
| type AgentConfig struct { | ||||
| 	Name    string       `yaml:"name,omitempty"` | ||||
| 	Mode    string       `yaml:"mode,omitempty"` | ||||
| 	Delay   int          `yaml:"delay"` | ||||
| 	Logging LogOptions   `yaml:"logging"` | ||||
| 	Node    HpdsNode     `yaml:"node,omitempty"` | ||||
| 	Funcs   []FuncConfig `yaml:"functions,omitempty"` | ||||
| } | ||||
| 
 | ||||
| type FuncConfig struct { | ||||
| 	Name    string `yaml:"name"` | ||||
| 	DataTag uint8  `yaml:"dataTag"` | ||||
| } | ||||
| 
 | ||||
| type HpdsNode struct { | ||||
| 	Host  string `yaml:"host"` | ||||
| 	Port  int    `yaml:"port"` | ||||
| 	Token string `yaml:"token,omitempty"` | ||||
| } | ||||
| 
 | ||||
| type LogOptions struct { | ||||
| 	Path            string `yaml:"path" json:"path" toml:"path"`                                  // 文件保存地方
 | ||||
| 	Prefix          string `yaml:"prefix" json:"prefix" toml:"prefix"`                            // 日志文件前缀
 | ||||
| 	ErrorFileSuffix string `yaml:"errorFileSuffix" json:"errorFileSuffix" toml:"errorFileSuffix"` // error日志文件后缀
 | ||||
| 	WarnFileSuffix  string `yaml:"warnFileSuffix" json:"warnFileSuffix" toml:"warnFileSuffix"`    // warn日志文件后缀
 | ||||
| 	InfoFileSuffix  string `yaml:"infoFileSuffix" json:"infoFileSuffix" toml:"infoFileSuffix"`    // info日志文件后缀
 | ||||
| 	DebugFileSuffix string `yaml:"debugFileSuffix" json:"debugFileSuffix" toml:"debugFileSuffix"` // debug日志文件后缀
 | ||||
| 	Level           string `yaml:"level" json:"level" toml:"level"`                               // 日志等级
 | ||||
| 	MaxSize         int    `yaml:"maxSize" json:"maxSize" toml:"maxSize"`                         // 日志文件大小(M)
 | ||||
| 	MaxBackups      int    `yaml:"maxBackups" json:"maxBackups" toml:"maxBackups"`                // 最多存在多少个切片文件
 | ||||
| 	MaxAge          int    `yaml:"maxAge" json:"maxAge" toml:"maxAge"`                            // 保存的最大天数
 | ||||
| 	Development     bool   `yaml:"development" json:"development" toml:"development"`             // 是否是开发模式
 | ||||
| } | ||||
| 
 | ||||
| func ParseConfigByFile(path string) (cfg *AgentConfig, err error) { | ||||
| 	buffer, err := os.ReadFile(path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return load(buffer) | ||||
| } | ||||
| 
 | ||||
| func load(buf []byte) (cfg *AgentConfig, err error) { | ||||
| 	cViper := viper.New() | ||||
| 	cViper.SetConfigType("yaml") | ||||
| 	cfg = new(AgentConfig) | ||||
| 	cViper.ReadConfig(bytes.NewBuffer(buf)) | ||||
| 	err = cViper.Unmarshal(cfg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | @ -0,0 +1,21 @@ | |||
| name: capture-agent | ||||
| mode: dev | ||||
| delay: 15 | ||||
| node: | ||||
|   host: 114.55.236.153 | ||||
|   port: 9188 | ||||
|   token: 06d36c6f5705507dae778fdce90d0767 | ||||
| logging: | ||||
|   path: ./logs | ||||
|   prefix: capture-agent | ||||
|   errorFileSuffix: error.log | ||||
|   warnFileSuffix: warn.log | ||||
|   infoFileSuffix: info.log | ||||
|   debugFileSuffix: debug.log | ||||
|   maxSize: 100 | ||||
|   maxBackups: 3000 | ||||
|   maxAge: 30 | ||||
|   development: true | ||||
| functions: | ||||
|   - name: capture-agent | ||||
|     dataTag: 18 | ||||
|  | @ -0,0 +1,61 @@ | |||
| module environmentCaptureAgent | ||||
| 
 | ||||
| go 1.19 | ||||
| 
 | ||||
| require ( | ||||
| 	git.hpds.cc/Component/logging v0.0.0-20230106105738-e378e873921b | ||||
| 	git.hpds.cc/pavement/hpds_node v0.0.0-20221023053316-37f7ba99eab3 | ||||
| 	github.com/Erope/goss v0.0.0-20211230093305-df3c03fd1ed4 | ||||
| 	github.com/robfig/cron/v3 v3.0.1 | ||||
| 	github.com/shirou/gopsutil/v3 v3.23.1 | ||||
| 	github.com/spf13/cobra v1.6.1 | ||||
| 	github.com/spf13/viper v1.15.0 | ||||
| 	github.com/stretchr/testify v1.8.1 | ||||
| ) | ||||
| 
 | ||||
| require ( | ||||
| 	git.hpds.cc/Component/mq_coder v0.0.0-20221010064749-174ae7ae3340 // indirect | ||||
| 	git.hpds.cc/Component/network v0.0.0-20221012021659-2433c68452d5 // indirect | ||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
| 	github.com/fsnotify/fsnotify v1.6.0 // indirect | ||||
| 	github.com/go-ole/go-ole v1.2.6 // indirect | ||||
| 	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect | ||||
| 	github.com/golang/mock v1.6.0 // indirect | ||||
| 	github.com/hashicorp/hcl v1.0.0 // indirect | ||||
| 	github.com/inconshreveable/mousetrap v1.0.1 // indirect | ||||
| 	github.com/lucas-clemente/quic-go v0.29.1 // indirect | ||||
| 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect | ||||
| 	github.com/magiconair/properties v1.8.7 // indirect | ||||
| 	github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect | ||||
| 	github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect | ||||
| 	github.com/matoous/go-nanoid/v2 v2.0.0 // indirect | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||
| 	github.com/nxadm/tail v1.4.8 // indirect | ||||
| 	github.com/onsi/ginkgo v1.16.4 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.0.6 // indirect | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
| 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect | ||||
| 	github.com/spf13/afero v1.9.3 // indirect | ||||
| 	github.com/spf13/cast v1.5.0 // indirect | ||||
| 	github.com/spf13/jwalterweatherman v1.1.0 // indirect | ||||
| 	github.com/spf13/pflag v1.0.5 // indirect | ||||
| 	github.com/subosito/gotenv v1.4.2 // indirect | ||||
| 	github.com/tklauser/go-sysconf v0.3.11 // indirect | ||||
| 	github.com/tklauser/numcpus v0.6.0 // indirect | ||||
| 	github.com/yusufpapurcu/wmi v1.2.2 // indirect | ||||
| 	go.uber.org/atomic v1.9.0 // indirect | ||||
| 	go.uber.org/multierr v1.8.0 // indirect | ||||
| 	go.uber.org/zap v1.23.0 // indirect | ||||
| 	golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect | ||||
| 	golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect | ||||
| 	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect | ||||
| 	golang.org/x/net v0.4.0 // indirect | ||||
| 	golang.org/x/sys v0.4.0 // indirect | ||||
| 	golang.org/x/text v0.5.0 // indirect | ||||
| 	golang.org/x/tools v0.1.12 // indirect | ||||
| 	gopkg.in/ini.v1 v1.67.0 // indirect | ||||
| 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect | ||||
| 	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|  | @ -0,0 +1,210 @@ | |||
| package monitor | ||||
| 
 | ||||
| import ( | ||||
| 	"environmentCaptureAgent/model" | ||||
| 	"fmt" | ||||
| 	"github.com/shirou/gopsutil/v3/disk" | ||||
| 	"os/exec" | ||||
| 	"regexp" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/Erope/goss" | ||||
| 	"github.com/shirou/gopsutil/v3/cpu" | ||||
| 	"github.com/shirou/gopsutil/v3/host" | ||||
| 	"github.com/shirou/gopsutil/v3/load" | ||||
| 	"github.com/shirou/gopsutil/v3/mem" | ||||
| 	"github.com/shirou/gopsutil/v3/net" | ||||
| 	"github.com/shirou/gopsutil/v3/process" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	netInSpeed, netOutSpeed, netInTransfer, netOutTransfer, lastUpdateNetStats uint64 | ||||
| 	cachedBootTime                                                             time.Time | ||||
| 	expectDiskFsTypes                                                          = []string{ | ||||
| 		"apfs", "ext4", "ext3", "ext2", "f2fs", "reiserfs", "jfs", "btrfs", | ||||
| 		"fuseblk", "zfs", "simfs", "ntfs", "fat32", "exfat", "xfs", "fuse.rclone", | ||||
| 	} | ||||
| 	getMacDiskNo = regexp.MustCompile(`\/dev\/disk(\d)s.*`) | ||||
| 	Version      string | ||||
| ) | ||||
| 
 | ||||
| func GetHost() *model.Node { | ||||
| 	hi, _ := host.Info() | ||||
| 	var cpuType string | ||||
| 	if hi.VirtualizationSystem != "" { | ||||
| 		cpuType = "Virtual" | ||||
| 	} else { | ||||
| 		cpuType = "Physical" | ||||
| 	} | ||||
| 	cpuModelCount := make(map[string]int) | ||||
| 	ci, _ := cpu.Info() | ||||
| 	for i := 0; i < len(ci); i++ { | ||||
| 		cpuModelCount[ci[i].ModelName]++ | ||||
| 	} | ||||
| 	var cpus []string | ||||
| 	for model, count := range cpuModelCount { | ||||
| 		cpus = append(cpus, fmt.Sprintf("%s %d %s Core", model, count, cpuType)) | ||||
| 	} | ||||
| 	mv, _ := mem.VirtualMemory() | ||||
| 	diskTotal, _ := getDiskTotalAndUsed() | ||||
| 
 | ||||
| 	var swapMemTotal uint64 | ||||
| 	if runtime.GOOS == "windows" { | ||||
| 		ms, _ := mem.SwapMemory() | ||||
| 		swapMemTotal = ms.Total | ||||
| 	} else { | ||||
| 		swapMemTotal = mv.SwapTotal | ||||
| 	} | ||||
| 
 | ||||
| 	if cachedBootTime.IsZero() { | ||||
| 		cachedBootTime = time.Unix(int64(hi.BootTime), 0) | ||||
| 	} | ||||
| 
 | ||||
| 	return &model.Node{ | ||||
| 		NodeName:        hi.HostID, | ||||
| 		Platform:        hi.OS, | ||||
| 		PlatformVersion: hi.PlatformVersion, | ||||
| 		CPU:             cpus, | ||||
| 		MemTotal:        mv.Total, | ||||
| 		SwapTotal:       swapMemTotal, | ||||
| 		DiskTotal:       diskTotal, | ||||
| 		Arch:            hi.KernelArch, | ||||
| 		Virtualization:  hi.VirtualizationSystem, | ||||
| 		BootTime:        hi.BootTime, | ||||
| 		IP:              cachedIP, | ||||
| 		CountryCode:     strings.ToLower(cachedCountry), | ||||
| 		Version:         Version, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func GetState(nodeName string) *model.NodeState { | ||||
| 	procs, _ := process.Pids() | ||||
| 
 | ||||
| 	mv, _ := mem.VirtualMemory() | ||||
| 
 | ||||
| 	var swapMemUsed uint64 | ||||
| 	if runtime.GOOS == "windows" { | ||||
| 		// gopsutil 在 Windows 下不能正确取 swap
 | ||||
| 		ms, _ := mem.SwapMemory() | ||||
| 		swapMemUsed = ms.Used | ||||
| 	} else { | ||||
| 		swapMemUsed = mv.SwapTotal - mv.SwapFree | ||||
| 	} | ||||
| 
 | ||||
| 	var cpuPercent float64 | ||||
| 	cp, err := cpu.Percent(0, false) | ||||
| 	if err == nil { | ||||
| 		cpuPercent = cp[0] | ||||
| 	} | ||||
| 
 | ||||
| 	_, diskUsed := getDiskTotalAndUsed() | ||||
| 	loadStat, _ := load.Avg() | ||||
| 
 | ||||
| 	var tcpConnCount, udpConnCount uint64 | ||||
| 
 | ||||
| 	ssErr := true | ||||
| 	if runtime.GOOS == "linux" { | ||||
| 		tcpStat, errTcp := goss.ConnectionsWithProtocol(syscall.IPPROTO_TCP) | ||||
| 		udpStat, errUdp := goss.ConnectionsWithProtocol(syscall.IPPROTO_UDP) | ||||
| 		if errTcp == nil && errUdp == nil { | ||||
| 			ssErr = false | ||||
| 			tcpConnCount = uint64(len(tcpStat)) | ||||
| 			udpConnCount = uint64(len(udpStat)) | ||||
| 		} | ||||
| 	} | ||||
| 	if ssErr { | ||||
| 		conns, _ := net.Connections("all") | ||||
| 		for i := 0; i < len(conns); i++ { | ||||
| 			switch conns[i].Type { | ||||
| 			case syscall.SOCK_STREAM: | ||||
| 				tcpConnCount++ | ||||
| 			case syscall.SOCK_DGRAM: | ||||
| 				udpConnCount++ | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return &model.NodeState{ | ||||
| 		NodeName:       nodeName, | ||||
| 		CPU:            cpuPercent, | ||||
| 		MemUsed:        mv.Total - mv.Available, | ||||
| 		SwapUsed:       swapMemUsed, | ||||
| 		DiskUsed:       diskUsed, | ||||
| 		NetInTransfer:  netInTransfer, | ||||
| 		NetOutTransfer: netOutTransfer, | ||||
| 		NetInSpeed:     netInSpeed, | ||||
| 		NetOutSpeed:    netOutSpeed, | ||||
| 		Uptime:         uint64(time.Since(cachedBootTime).Seconds()), | ||||
| 		Load1:          loadStat.Load1, | ||||
| 		Load5:          loadStat.Load5, | ||||
| 		Load15:         loadStat.Load15, | ||||
| 		TcpConnCount:   tcpConnCount, | ||||
| 		UdpConnCount:   udpConnCount, | ||||
| 		ProcessCount:   uint64(len(procs)), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func getDiskTotalAndUsed() (total uint64, used uint64) { | ||||
| 	diskList, _ := disk.Partitions(false) | ||||
| 	devices := make(map[string]string) | ||||
| 	countedDiskForMac := make(map[string]struct{}) | ||||
| 	for _, d := range diskList { | ||||
| 		fsType := strings.ToLower(d.Fstype) | ||||
| 		// 不统计 K8s 的虚拟挂载点:https://github.com/shirou/gopsutil/issues/1007
 | ||||
| 		if devices[d.Device] == "" && isListContainsStr(expectDiskFsTypes, fsType) && !strings.Contains(d.Mountpoint, "/var/lib/kubelet") { | ||||
| 			devices[d.Device] = d.Mountpoint | ||||
| 		} | ||||
| 	} | ||||
| 	for device, mountPath := range devices { | ||||
| 		diskUsageOf, _ := disk.Usage(mountPath) | ||||
| 		// 这里是针对 Mac 机器的处理,https://github.com/giampaolo/psutil/issues/1509
 | ||||
| 		matches := getMacDiskNo.FindStringSubmatch(device) | ||||
| 		if len(matches) == 2 { | ||||
| 			if _, has := countedDiskForMac[matches[1]]; !has { | ||||
| 				countedDiskForMac[matches[1]] = struct{}{} | ||||
| 				total += diskUsageOf.Total | ||||
| 			} | ||||
| 		} else { | ||||
| 			total += diskUsageOf.Total | ||||
| 		} | ||||
| 		used += diskUsageOf.Used | ||||
| 	} | ||||
| 
 | ||||
| 	// Fallback 到这个方法,仅统计根路径,适用于OpenVZ之类的.
 | ||||
| 	if runtime.GOOS == "linux" { | ||||
| 		if total == 0 && used == 0 { | ||||
| 			cmd := exec.Command("df") | ||||
| 			out, err := cmd.CombinedOutput() | ||||
| 			if err == nil { | ||||
| 				s := strings.Split(string(out), "\n") | ||||
| 				for _, c := range s { | ||||
| 					info := strings.Fields(c) | ||||
| 					if len(info) == 6 { | ||||
| 						if info[5] == "/" { | ||||
| 							total, _ = strconv.ParseUint(info[1], 0, 64) | ||||
| 							used, _ = strconv.ParseUint(info[2], 0, 64) | ||||
| 							// 默认获取的是1K块为单位的.
 | ||||
| 							total = total * 1024 | ||||
| 							used = used * 1024 | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func isListContainsStr(list []string, str string) bool { | ||||
| 	for i := 0; i < len(list); i++ { | ||||
| 		if strings.Contains(str, list[i]) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | @ -0,0 +1,90 @@ | |||
| package monitor | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"environmentCaptureAgent/pkg/utils" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type geoIP struct { | ||||
| 	CountryCode string `json:"country_code,omitempty"` | ||||
| 	IP          string `json:"ip,omitempty"` | ||||
| 	Query       string `json:"query,omitempty"` | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	geoIPApiList = []string{ | ||||
| 		"https://api.ip.sb/geoip", | ||||
| 		"https://ip.seeip.org/geoip", | ||||
| 		"https://ipapi.co/json", | ||||
| 		"https://freegeoip.app/json/", | ||||
| 		"http://ip-api.com/json/", | ||||
| 		"https://extreme-ip-lookup.com/json/", | ||||
| 	} | ||||
| 	cachedIP, cachedCountry string | ||||
| 	httpClientV4            = utils.NewSingleStackHTTPClient(time.Second*20, time.Second*5, time.Second*10, false) | ||||
| 	httpClientV6            = utils.NewSingleStackHTTPClient(time.Second*20, time.Second*5, time.Second*10, true) | ||||
| ) | ||||
| 
 | ||||
| func UpdateIP() { | ||||
| 	for { | ||||
| 		ipv4 := fetchGeoIP(geoIPApiList, false) | ||||
| 		ipv6 := fetchGeoIP(geoIPApiList, true) | ||||
| 		if ipv4.IP == "" && ipv6.IP == "" { | ||||
| 			time.Sleep(time.Minute) | ||||
| 			continue | ||||
| 		} | ||||
| 		if ipv4.IP == "" || ipv6.IP == "" { | ||||
| 			cachedIP = fmt.Sprintf("%s%s", ipv4.IP, ipv6.IP) | ||||
| 		} else { | ||||
| 			cachedIP = fmt.Sprintf("%s/%s", ipv4.IP, ipv6.IP) | ||||
| 		} | ||||
| 		if ipv4.CountryCode != "" { | ||||
| 			cachedCountry = ipv4.CountryCode | ||||
| 		} else if ipv6.CountryCode != "" { | ||||
| 			cachedCountry = ipv6.CountryCode | ||||
| 		} | ||||
| 		time.Sleep(time.Minute * 30) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func fetchGeoIP(servers []string, isV6 bool) geoIP { | ||||
| 	var ip geoIP | ||||
| 	var resp *http.Response | ||||
| 	var err error | ||||
| 	for i := 0; i < len(servers); i++ { | ||||
| 		if isV6 { | ||||
| 			resp, err = httpClientV6.Get(servers[i]) | ||||
| 		} else { | ||||
| 			resp, err = httpClientV4.Get(servers[i]) | ||||
| 		} | ||||
| 		if err == nil { | ||||
| 			body, err := ioutil.ReadAll(resp.Body) | ||||
| 			if err != nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			resp.Body.Close() | ||||
| 			err = json.Unmarshal(body, &ip) | ||||
| 			if err != nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			if ip.IP == "" && ip.Query != "" { | ||||
| 				ip.IP = ip.Query | ||||
| 			} | ||||
| 			// 没取到 v6 IP
 | ||||
| 			if isV6 && !strings.Contains(ip.IP, ":") { | ||||
| 				continue | ||||
| 			} | ||||
| 			// 没取到 v4 IP
 | ||||
| 			if !isV6 && !strings.Contains(ip.IP, ".") { | ||||
| 				continue | ||||
| 			} | ||||
| 			return ip | ||||
| 		} | ||||
| 	} | ||||
| 	return ip | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"environmentCaptureAgent/config/cmd" | ||||
| 	"fmt" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	rootCmd = &cobra.Command{ | ||||
| 		Use:     "hpds_environment_capture_agent", | ||||
| 		Long:    "hpds_agent is a environment capture agent", | ||||
| 		Version: "0.1", | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	rootCmd.AddCommand(cmd.NewStartCmd()) | ||||
| } | ||||
| func main() { | ||||
| 
 | ||||
| 	if err := rootCmd.Execute(); err != nil { | ||||
| 		fmt.Fprint(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,56 @@ | |||
| package model | ||||
| 
 | ||||
| import "encoding/json" | ||||
| 
 | ||||
| // Node 节点信息
 | ||||
| type Node struct { | ||||
| 	NodeName        string   `json:"nodeName"` | ||||
| 	Platform        string   `json:"platform,omitempty"` | ||||
| 	PlatformVersion string   `json:"platformVersion,omitempty"` | ||||
| 	CPU             []string `json:"cpu,omitempty"` | ||||
| 	MemTotal        uint64   `json:"memTotal,omitempty"` | ||||
| 	DiskTotal       uint64   `json:"diskTotal,omitempty"` | ||||
| 	SwapTotal       uint64   `json:"swapTotal,omitempty"` | ||||
| 	Arch            string   `json:"arch,omitempty"` | ||||
| 	Virtualization  string   `json:"virtualization,omitempty"` | ||||
| 	BootTime        uint64   `json:"bootTime,omitempty"` | ||||
| 	IP              string   `json:"ip"` | ||||
| 	CountryCode     string   `json:"countryCode,omitempty"` | ||||
| 	Version         string   `json:"version,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (n Node) ToByte() []byte { | ||||
| 	data, err := json.Marshal(n) | ||||
| 	if err != nil { | ||||
| 		return []byte("") | ||||
| 	} | ||||
| 	return data | ||||
| } | ||||
| 
 | ||||
| // NodeState 节点状态信息
 | ||||
| type NodeState struct { | ||||
| 	NodeName       string  `json:"nodeName"` | ||||
| 	CPU            float64 `json:"cpu,omitempty"` | ||||
| 	MemUsed        uint64  `json:"memUsed,omitempty"` | ||||
| 	SwapUsed       uint64  `json:"swapUsed,omitempty"` | ||||
| 	DiskUsed       uint64  `json:"diskUsed,omitempty"` | ||||
| 	NetInTransfer  uint64  `json:"netInTransfer,omitempty"` | ||||
| 	NetOutTransfer uint64  `json:"netOutTransfer,omitempty"` | ||||
| 	NetInSpeed     uint64  `json:"netInSpeed,omitempty"` | ||||
| 	NetOutSpeed    uint64  `json:"netOutSpeed,omitempty"` | ||||
| 	Uptime         uint64  `json:"uptime,omitempty"` | ||||
| 	Load1          float64 `json:"load1,omitempty"` | ||||
| 	Load5          float64 `json:"load5,omitempty"` | ||||
| 	Load15         float64 `json:"load15,omitempty"` | ||||
| 	TcpConnCount   uint64  `json:"tcpConnCount,omitempty"` | ||||
| 	UdpConnCount   uint64  `json:"udpConnCount,omitempty"` | ||||
| 	ProcessCount   uint64  `json:"processCount,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (ns NodeState) ToByte() []byte { | ||||
| 	data, err := json.Marshal(ns) | ||||
| 	if err != nil { | ||||
| 		return []byte("") | ||||
| 	} | ||||
| 	return data | ||||
| } | ||||
|  | @ -0,0 +1,92 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func NewSingleStackHTTPClient(httpTimeout, dialTimeout, keepAliveTimeout time.Duration, ipv6 bool) *http.Client { | ||||
| 	dialer := &net.Dialer{ | ||||
| 		Timeout:   dialTimeout, | ||||
| 		KeepAlive: keepAliveTimeout, | ||||
| 	} | ||||
| 
 | ||||
| 	transport := &http.Transport{ | ||||
| 		Proxy:             http.ProxyFromEnvironment, | ||||
| 		ForceAttemptHTTP2: false, | ||||
| 		DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) { | ||||
| 			ip, err := resolveIP(addr, ipv6) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			return dialer.DialContext(ctx, network, ip) | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	return &http.Client{ | ||||
| 		Transport: transport, | ||||
| 		Timeout:   httpTimeout, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func resolveIP(addr string, ipv6 bool) (string, error) { | ||||
| 	url := strings.Split(addr, ":") | ||||
| 
 | ||||
| 	dnsServers := []string{"[2606:4700:4700::1001]", "[2001:4860:4860::8844]", "[2400:3200::1]", "[2400:3200:baba::1]"} | ||||
| 	if !ipv6 { | ||||
| 		dnsServers = []string{"1.0.0.1", "8.8.4.4", "223.5.5.5", "223.6.6.6"} | ||||
| 	} | ||||
| 
 | ||||
| 	res, err := net.LookupIP(url[0]) | ||||
| 	if err != nil { | ||||
| 		for i := 0; i < len(dnsServers); i++ { | ||||
| 			r := &net.Resolver{ | ||||
| 				PreferGo: true, | ||||
| 				Dial: func(ctx context.Context, network, address string) (net.Conn, error) { | ||||
| 					d := net.Dialer{ | ||||
| 						Timeout: time.Second * 10, | ||||
| 					} | ||||
| 					return d.DialContext(ctx, "udp", dnsServers[i]+":53") | ||||
| 				}, | ||||
| 			} | ||||
| 			res, err = r.LookupIP(context.Background(), "ip", url[0]) | ||||
| 			if err == nil { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	var ipv4Resolved, ipv6Resolved bool | ||||
| 
 | ||||
| 	for i := 0; i < len(res); i++ { | ||||
| 		ip := res[i].String() | ||||
| 		if strings.Contains(ip, ".") && !ipv6 { | ||||
| 			ipv4Resolved = true | ||||
| 			url[0] = ip | ||||
| 			break | ||||
| 		} | ||||
| 		if strings.Contains(ip, ":") && ipv6 { | ||||
| 			ipv6Resolved = true | ||||
| 			url[0] = "[" + ip + "]" | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if ipv6 && !ipv6Resolved { | ||||
| 		return "", errors.New("the AAAA record not resolved") | ||||
| 	} | ||||
| 
 | ||||
| 	if !ipv6 && !ipv4Resolved { | ||||
| 		return "", errors.New("the A record not resolved") | ||||
| 	} | ||||
| 
 | ||||
| 	return strings.Join(url, ":"), nil | ||||
| } | ||||
|  | @ -0,0 +1,66 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/md5" // #nosec
 | ||||
| 	"encoding/hex" | ||||
| 	"math/rand" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||
| const ( | ||||
| 	letterIdxBits = 6                    // 6 bits to represent a letter index
 | ||||
| 	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
 | ||||
| 	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
 | ||||
| ) | ||||
| 
 | ||||
| func RandStringBytesMaskImprSrcUnsafe(n int) string { | ||||
| 	var src = rand.NewSource(time.Now().UnixNano()) | ||||
| 	b := make([]byte, n) | ||||
| 
 | ||||
| 	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
 | ||||
| 	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; { | ||||
| 		if remain == 0 { | ||||
| 			cache, remain = src.Int63(), letterIdxMax | ||||
| 		} | ||||
| 		if idx := int(cache & letterIdxMask); idx < len(letterBytes) { | ||||
| 			b[i] = letterBytes[idx] | ||||
| 			i-- | ||||
| 		} | ||||
| 		cache >>= letterIdxBits | ||||
| 		remain-- | ||||
| 	} | ||||
| 
 | ||||
| 	return *(*string)(unsafe.Pointer(&b)) //#nosec
 | ||||
| } | ||||
| 
 | ||||
| func MD5(plantext string) string { | ||||
| 	hash := md5.New() // #nosec
 | ||||
| 	hash.Write([]byte(plantext)) | ||||
| 	return hex.EncodeToString(hash.Sum(nil)) | ||||
| } | ||||
| 
 | ||||
| func IsWindows() bool { | ||||
| 	return os.PathSeparator == '\\' && os.PathListSeparator == ';' | ||||
| } | ||||
| 
 | ||||
| var ipv4Re = regexp.MustCompile(`(\d*\.).*(\.\d*)`) | ||||
| 
 | ||||
| func ipv4Desensitize(ipv4Addr string) string { | ||||
| 	return ipv4Re.ReplaceAllString(ipv4Addr, "$1****$2") | ||||
| } | ||||
| 
 | ||||
| var ipv6Re = regexp.MustCompile(`(\w*:\w*:).*(:\w*:\w*)`) | ||||
| 
 | ||||
| func ipv6Desensitize(ipv6Addr string) string { | ||||
| 	return ipv6Re.ReplaceAllString(ipv6Addr, "$1****$2") | ||||
| } | ||||
| 
 | ||||
| func IPDesensitize(ipAddr string) string { | ||||
| 	ipAddr = ipv4Desensitize(ipAddr) | ||||
| 	ipAddr = ipv6Desensitize(ipAddr) | ||||
| 	return ipAddr | ||||
| } | ||||
|  | @ -0,0 +1,41 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| type testSt struct { | ||||
| 	input  string | ||||
| 	output string | ||||
| } | ||||
| 
 | ||||
| func TestNotification(t *testing.T) { | ||||
| 	cases := []testSt{ | ||||
| 		{ | ||||
| 			input:  "103.80.236.249/d5ce:d811:cdb8:067a:a873:2076:9521:9d2d", | ||||
| 			output: "103.****.249/d5ce:d811:****:9521:9d2d", | ||||
| 		}, | ||||
| 		{ | ||||
| 			input:  "3.80.236.29/d5ce::cdb8:067a:a873:2076:9521:9d2d", | ||||
| 			output: "3.****.29/d5ce::****:9521:9d2d", | ||||
| 		}, | ||||
| 		{ | ||||
| 			input:  "3.80.236.29/d5ce::cdb8:067a:a873:2076::9d2d", | ||||
| 			output: "3.****.29/d5ce::****::9d2d", | ||||
| 		}, | ||||
| 		{ | ||||
| 			input:  "3.80.236.9/d5ce::cdb8:067a:a873:2076::9d2d", | ||||
| 			output: "3.****.9/d5ce::****::9d2d", | ||||
| 		}, | ||||
| 		{ | ||||
| 			input:  "3.80.236.9/d5ce::cdb8:067a:a873:2076::9d2d", | ||||
| 			output: "3.****.9/d5ce::****::9d2d", | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, c := range cases { | ||||
| 		assert.Equal(t, IPDesensitize(c.input), c.output) | ||||
| 	} | ||||
| } | ||||
		Loading…
	
		Reference in New Issue