├── .gitignore ├── ChangeLog.md ├── Makefile ├── README.md ├── common ├── data.go ├── doc.go ├── env.go ├── error.go └── req_var.go ├── conf ├── app.ini └── controller.go ├── db └── db.go ├── go.mod ├── go.sum ├── handlers ├── agent │ ├── start_up │ │ └── register.go │ └── v1 │ │ ├── fact.go │ │ ├── hbs.go │ │ └── status.go ├── external │ └── v1 │ │ └── task.go ├── front │ └── v1 │ │ ├── instance.go │ │ ├── reg_agents.go │ │ └── task.go └── test │ └── v1 │ └── test_get.go ├── logger └── log.go ├── main.go ├── opsHeart.png ├── routers ├── middleware │ ├── check_front.go │ └── check_token.go └── router.go ├── server └── server.go ├── service ├── agent │ ├── db_handle.go │ ├── db_handle_test.go │ ├── hbs.go │ ├── models.go │ └── start_up.go ├── collection │ ├── collection.go │ ├── fact.go │ ├── fact_db.go │ ├── fact_test.go │ ├── models.go │ ├── tag.go │ └── tag_db.go ├── cron_task │ └── hbs.go └── task │ ├── cmd.go │ ├── cmd_db.go │ ├── cmd_db_test.go │ ├── cron.go │ ├── doc.go │ ├── ins.go │ ├── ins_db.go │ ├── ins_db_test.go │ ├── ins_test.go │ ├── models.go │ ├── script.go │ ├── script_db.go │ ├── stage_agent.go │ ├── stage_agent_test.go │ ├── sync_file.go │ ├── sync_file_db.go │ ├── task.go │ ├── task_db.go │ ├── task_db_test.go │ └── task_test.go └── utils ├── call_http ├── client.go └── client_test.go ├── cron ├── cron.go ├── cron_test.go └── func.go ├── rand_str ├── get_rand.go └── get_rand_test.go └── worker ├── worker.go └── worker_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | .idea -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | opsHeart ChangeLog 2 | 3 | ## Standard entrances 4 | + FEATURES 5 | + ENHANCEMENTS 6 | + DEPRECATED 7 | + ADDED 8 | + FIXED 9 | + CHANGED 10 | + REMOVED 11 | + OTHERS 12 | 13 | ## v0.05 (2020-08-22) 14 | ### CHANGED 15 | + refactor vgroup concurrent handle method; 16 | 17 | ## v0.04 (2020-07-26) 18 | ### FEATURES 19 | + User can supply variables to basic task; 20 | + Task could be supplied with customized variables when it be called by front or external system; 21 | ### ADDED 22 | + Add config for step pause; 23 | 24 | ## v0.03 (2020-07-16) 25 | ### FEATURES 26 | + Pause between steps when Canary release; 27 | 28 | ## v0.02 (2020-07-15) 29 | ### ADDED 30 | + Front and external system run task http router; 31 | 32 | ## v0.01 (2020-07-03) 33 | ### FEATURES 34 | + Init project; 35 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | 3 | BUILDTIME=$(shell date +%FT%T%z) 4 | GOVERSION=$(shell go version) 5 | 6 | all: build 7 | 8 | build: 9 | go build -ldflags "-X 'main.buildTime=${BUILDTIME}' -X 'main.goVersion=${GOVERSION}'" -o ./super_server . 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opsHeart(开发中) 2 | 3 | ## 一个全新的运维管理平台 4 | ### 具备以下特性: 5 | + 主机批量管理 6 | + 远程执行(命令、脚本) 7 | + 任务调度,支持以下功能 8 | - 串行任务 9 | - 并行任务 10 | - 任务组 11 | - 任务组嵌套 12 | - 任务依赖 13 | - 布尔条件任务 14 | - 灰度执行(各个维度的灰度) 15 | - 暂停后确认 16 | + 定时任务 17 | + agent启动任务 18 | + agent数据采集 19 | + 用户管理 20 | - 权限管理 21 | - cmd执行权限管理 22 | - 操作审计 23 | + 外部系统调用管理 24 | - 权限管理 25 | - 调用审计 26 | + 交互式前端cmd 27 | + agent组管理 28 | 29 | ### 开发进度 30 | ![avatar](./opsHeart.png) 31 | -------------------------------------------------------------------------------- /common/data.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | Version = "0.05" 5 | Author = "Jacenr" 6 | ReleaseTime = "2020-08-22" 7 | ) 8 | 9 | var ( 10 | BuildTime string 11 | GoVersion string 12 | ) 13 | -------------------------------------------------------------------------------- /common/doc.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // If there is a proxy between server and agent, it should set agent ip to "X-Forwarded-For". 4 | // And also set server ip to "X-Forwarded-For". 5 | // The proxy should set ip of self to "X-Proxy-IP". 6 | -------------------------------------------------------------------------------- /common/env.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "opsHeart_server/conf" 4 | 5 | var AgentPort string 6 | var StepPause bool 7 | 8 | func InitRunningEnv() { 9 | AgentPort = conf.GetAgentPort() 10 | StepPause = conf.GetStepPause() 11 | } 12 | -------------------------------------------------------------------------------- /common/error.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | type ErrorCode uint 4 | 5 | const ( 6 | // 0-999 7 | NoError ErrorCode = 0 8 | 9 | // 1000-1999 10 | BindPostDataErr ErrorCode = 1000 11 | 12 | // 2000-2999 13 | RunTaskErr ErrorCode = 2000 14 | QueryTaskByIDErr ErrorCode = 2001 15 | QueryInstanceByIDErr ErrorCode = 2002 16 | ) 17 | 18 | var ErrCodeDict = map[ErrorCode]string{ 19 | BindPostDataErr: "bind post data error", 20 | RunTaskErr: "run task err", 21 | QueryTaskByIDErr: "query task by id error", 22 | QueryInstanceByIDErr: "query instance by id error", 23 | } 24 | -------------------------------------------------------------------------------- /common/req_var.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | ProxyIpHeader string = "X-Proxy-IP" // reversed for proxy 5 | UUIDKey string = "AgentUUID" 6 | FrontTokenKey string = "X-Front-Token" 7 | 8 | AgentStartUpPath string = "/v1/start-up" 9 | ) 10 | -------------------------------------------------------------------------------- /conf/app.ini: -------------------------------------------------------------------------------- 1 | [app] 2 | mode = dev 3 | log_level = info 4 | role=server 5 | 6 | [server] 7 | http_port = 8080 8 | http_addr = 0.0.0.0 9 | uuid = 2a97d697-ca28-4f15-9754-ea8c392ab35d 10 | server_list = 192.168.0.102:8080 11 | front_token = 0BjjOuc0ZTq5kLI3jlYi 12 | agent_port = 8081 13 | proxy_port = 8082 14 | worker_concurrent = 100 15 | step_pause = 1 16 | 17 | [cluster] 18 | role=1 19 | 20 | [db] 21 | type=sqlite3 22 | url=/var/superops/test.db 23 | 24 | [proxy] 25 | http_port = 8082 26 | http_addr = 0.0.0.0 27 | uuid = 2a97d697-ca28-4f15-9754-ea8c392ab35d 28 | zone_code = network_zone1 29 | -------------------------------------------------------------------------------- /conf/controller.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import "github.com/go-ini/ini" 4 | 5 | var cfg *ini.File 6 | 7 | func InitCfg() error { 8 | var err error 9 | cfg, err = ini.Load("./conf/app.ini") 10 | return err 11 | } 12 | 13 | // GetPort get port config 14 | func GetPort() string { 15 | return cfg.Section("server").Key("http_port").String() 16 | } 17 | 18 | // GetAddr get addr config 19 | func GetAddr() string { 20 | return cfg.Section("server").Key("http_addr").String() 21 | } 22 | 23 | func GetLogLevel() string { 24 | return cfg.Section("app").Key("log_level").String() 25 | } 26 | 27 | func GetMode() string { 28 | return cfg.Section("app").Key("mode").String() 29 | } 30 | 31 | func GetDbType() string { 32 | return cfg.Section("db").Key("type").String() 33 | } 34 | 35 | func GetDbUrl() string { 36 | return cfg.Section("db").Key("url").String() 37 | } 38 | 39 | func GetUUID() string { 40 | return cfg.Section("server").Key("uuid").String() 41 | } 42 | 43 | func GetFrontToken() string { 44 | return cfg.Section("server").Key("front_token").String() 45 | } 46 | 47 | func GetAgentPort() string { 48 | return cfg.Section("server").Key("agent_port").String() 49 | } 50 | 51 | func GetServerRole() bool { 52 | return cfg.Section("cluster").Key("role").MustInt() == 1 53 | } 54 | 55 | func GetWorkerConcurrent() int { 56 | return cfg.Section("server").Key("worker_concurrent").MustInt() 57 | } 58 | 59 | func GetStepPause() bool { 60 | return cfg.Section("server").Key("step_pause").MustInt() == 1 61 | } 62 | -------------------------------------------------------------------------------- /db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | _ "github.com/jinzhu/gorm/dialects/sqlite" 6 | "opsHeart_server/conf" 7 | "opsHeart_server/logger" 8 | ) 9 | 10 | var DB *gorm.DB 11 | 12 | func InitDB() { 13 | dbType := conf.GetDbType() 14 | dbUrl := conf.GetDbUrl() 15 | 16 | var err error 17 | DB, err = gorm.Open(dbType, dbUrl) 18 | if err != nil { 19 | panic("failed to connect database: " + err.Error()) 20 | } else { 21 | logger.ServerLog.Infof("action=init db;status=success") 22 | DB.SingularTable(true) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module opsHeart_server 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect 7 | github.com/gin-gonic/gin v1.6.3 8 | github.com/go-ini/ini v1.57.0 9 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect 10 | github.com/jinzhu/gorm v1.9.14 11 | github.com/jonboulle/clockwork v0.1.0 // indirect 12 | github.com/lestrrat-go/file-rotatelogs v2.3.0+incompatible 13 | github.com/lestrrat-go/strftime v1.0.1 // indirect 14 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 15 | github.com/pkg/errors v0.9.1 // indirect 16 | github.com/satori/go.uuid v1.2.0 17 | github.com/sirupsen/logrus v1.6.0 18 | github.com/smartystreets/goconvey v1.6.4 // indirect 19 | github.com/tebeka/strftime v0.1.4 // indirect 20 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect 21 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 22 | gopkg.in/ini.v1 v1.57.0 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= 2 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= 7 | github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= 8 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= 9 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 10 | github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU= 11 | github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw= 12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 14 | github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= 15 | github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 16 | github.com/go-ini/ini v1.57.0 h1:Qwzj3wZQW+Plax5Ntj+GYe07DfGj1OH+aL1nMTMaNow= 17 | github.com/go-ini/ini v1.57.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 18 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 19 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 20 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 21 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 22 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 23 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 24 | github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= 25 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 26 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= 27 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 28 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= 29 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 30 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 31 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 32 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 33 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 34 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 35 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= 36 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= 37 | github.com/jinzhu/gorm v1.9.14 h1:Kg3ShyTPcM6nzVo148fRrcMO6MNKuqtOUwnzqMgVniM= 38 | github.com/jinzhu/gorm v1.9.14/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= 39 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 40 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 41 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= 42 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 43 | github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= 44 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 45 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 46 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 47 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 48 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 49 | github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= 50 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 51 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 52 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 53 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 54 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 55 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 56 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= 57 | github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= 58 | github.com/lestrrat-go/file-rotatelogs v2.3.0+incompatible h1:4mNlp+/SvALIPFpbXV3kxNJJno9iKFWGxSDE13Kl66Q= 59 | github.com/lestrrat-go/file-rotatelogs v2.3.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= 60 | github.com/lestrrat-go/strftime v1.0.1 h1:o7qz5pmLzPDLyGW4lG6JvTKPUfTFXwe+vOamIYWtnVU= 61 | github.com/lestrrat-go/strftime v1.0.1/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= 62 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= 63 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 64 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 65 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 66 | github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= 67 | github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= 68 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 69 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 70 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 71 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 72 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 73 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 74 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 75 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 76 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 77 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 78 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 79 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 80 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 81 | github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= 82 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 83 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 84 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 85 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 86 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 87 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 88 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 89 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 90 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 91 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 92 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 93 | github.com/tebeka/strftime v0.1.4 h1:e0FKSyxthD1Xk4cIixFPoyfD33u2SbjNngOaaC3ePoU= 94 | github.com/tebeka/strftime v0.1.4/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= 95 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 96 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 97 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 98 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 99 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 100 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 101 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= 102 | golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 103 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 104 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 105 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 106 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 107 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 108 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 109 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 110 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 111 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 112 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= 113 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 114 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= 115 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 116 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 117 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 118 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 119 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 120 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 121 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 122 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 123 | gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= 124 | gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 125 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 126 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 127 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 128 | -------------------------------------------------------------------------------- /handlers/agent/start_up/register.go: -------------------------------------------------------------------------------- 1 | package start_up 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/logger" 7 | "opsHeart_server/service/agent" 8 | ) 9 | 10 | // HandleAgentRegister used for agent register self to server 11 | func HandleAgentRegister(c *gin.Context) { 12 | ag := agent.Agent{} 13 | ag.Status = agent.REGISTER 14 | 15 | var agentIP string 16 | agentIP = c.ClientIP() 17 | 18 | err := c.ShouldBindJSON(&ag) 19 | if err != nil { 20 | c.JSON(http.StatusBadRequest, gin.H{ 21 | "error": err.Error(), 22 | }) 23 | logger.RigisterLog.Error("get register data err: %s, from: %s", err.Error(), agentIP) 24 | return 25 | } 26 | 27 | ag.RemoteAddr = agentIP 28 | 29 | //var tmp v1.Agent 30 | //err = db.DB.FirstOrCreate(&tmp, v1.Agent{UUID: ag.UUID}).Error 31 | if ag.IsExist() { 32 | err = ag.UpdateDat() 33 | } else { 34 | err = ag.InsertDat() 35 | } 36 | if err != nil { 37 | logger.ServerLog.Errorf("uuid=%s, hostname=%s, addr=%s, register_error=%s", 38 | ag.UUID, ag.Hostname, ag.RemoteAddr, err) 39 | c.JSON(http.StatusInternalServerError, gin.H{ 40 | "error": "internal error", 41 | }) 42 | return 43 | } 44 | 45 | c.JSON(200, gin.H{ 46 | "status": "success", // mean: got register request successfully. 47 | }) 48 | 49 | logger.RigisterLog.Infof("action=register agent;uuid=%s, hostname=%s, addr=%s, status=success", 50 | ag.UUID, ag.Hostname, ag.RemoteAddr) 51 | } 52 | -------------------------------------------------------------------------------- /handlers/agent/v1/fact.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func HandleFact(c *gin.Context) { 6 | //agentUUID := UUID.(string) 7 | 8 | } 9 | -------------------------------------------------------------------------------- /handlers/agent/v1/hbs.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/common" 7 | "opsHeart_server/logger" 8 | "opsHeart_server/service/agent" 9 | ) 10 | 11 | func HandleHbs(c *gin.Context) { 12 | var agentIP string 13 | agentIP = c.ClientIP() 14 | 15 | UUID, ok := c.Get(common.UUIDKey) 16 | if !ok { 17 | logger.HbsLog.Errorf("path=hbs;action=get uuid;ip=%s", agentIP) 18 | c.JSON(http.StatusInternalServerError, gin.H{ 19 | "error": "can not get uuid", 20 | }) 21 | return 22 | } 23 | 24 | agentUUID := UUID.(string) 25 | 26 | // save hbs time to db or redis, and log 27 | a := &agent.Agent{ 28 | UUID: agentUUID, 29 | } 30 | if err := a.UpdateHbs(); err != nil { 31 | logger.HbsLog.Errorf("action=hbs;do=update hbs;uuid=%s;ip=%s;err=%s", agentUUID, agentIP, err) 32 | c.JSON(http.StatusInternalServerError, gin.H{ 33 | "error": err.Error(), 34 | }) 35 | return 36 | } 37 | 38 | logger.HbsLog.Info("action=hbs;uuid=%s;ip=%s", agentUUID, agentIP) 39 | 40 | //response 41 | c.JSON(http.StatusOK, gin.H{ 42 | "msg": "status", 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /handlers/agent/v1/status.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/common" 7 | "opsHeart_server/db" 8 | "opsHeart_server/logger" 9 | "opsHeart_server/service/agent" 10 | ) 11 | 12 | func HandleStatus(c *gin.Context) { 13 | UUID, ok := c.Get(common.UUIDKey) 14 | if !ok { 15 | logger.HbsLog.Errorf("path=status;action=get uuid;ip=%s", c.ClientIP()) 16 | c.JSON(http.StatusInternalServerError, gin.H{ 17 | "error": "can not get uuid", 18 | }) 19 | return 20 | } 21 | agentUuid := UUID.(string) 22 | 23 | var q agent.Agent 24 | err := db.DB.Find(&q, "uuid = ?", agentUuid).Error 25 | if err != nil { 26 | logger.RigisterLog.Error("uuid=%s, remote_ip=%s, action=query db, err=%s", agentUuid, 27 | c.Request.RemoteAddr, err.Error()) 28 | c.JSON(http.StatusInternalServerError, gin.H{ 29 | "error": err.Error(), 30 | }) 31 | return 32 | } 33 | c.JSON(http.StatusOK, gin.H{ 34 | "status": q.Status, 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /handlers/external/v1/task.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/common" 7 | "opsHeart_server/logger" 8 | "opsHeart_server/service/task" 9 | ) 10 | 11 | func HandleRunTask(c *gin.Context) { 12 | var tk *task.Task 13 | err := c.ShouldBindJSON(&tk) 14 | if err != nil { 15 | logger.TaskLog.Errorf("action=handle front run task request;do=bind data;err=%s;err_code=%d", 16 | err.Error(), common.BindPostDataErr) 17 | c.JSON(http.StatusInternalServerError, gin.H{ 18 | "error": err.Error(), 19 | "err_code": common.BindPostDataErr, 20 | }) 21 | return 22 | } 23 | tkInDb, _ := task.GetTaskByID(tk.ID) 24 | 25 | // the customized target hosts 26 | if tk.CollectionType != 0 && tk.CollectionValue != "" { 27 | tkInDb.CollectionType = tk.CollectionType 28 | tkInDb.CollectionValue = tk.CollectionValue 29 | } 30 | 31 | insName := task.NewInsName(tk.ID) 32 | parentIns := task.TaskInstance{ 33 | Name: insName, 34 | } 35 | 36 | // get customized arguments 37 | argMap := make(map[string]task.TaskArg) 38 | for _, v := range tk.TaskArgs { 39 | argMap[v.ArgName] = v 40 | } 41 | 42 | // save instance args 43 | _ = tkInDb.InitInsArgs(argMap, insName) 44 | 45 | err = tkInDb.Run(&parentIns) 46 | if err != nil { 47 | logger.TaskLog.Errorf("action=handle front run task;do=run task;err=%s;err_code=%d", 48 | err.Error(), common.RunTaskErr) 49 | c.JSON(http.StatusInternalServerError, gin.H{ 50 | "error": err.Error(), 51 | "err_code": common.RunTaskErr, 52 | }) 53 | return 54 | } 55 | c.JSON(http.StatusOK, gin.H{ 56 | "status": "success", 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /handlers/front/v1/instance.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/common" 7 | "opsHeart_server/logger" 8 | "opsHeart_server/service/task" 9 | ) 10 | 11 | func HandleRunIns(c *gin.Context) { 12 | var tiTmp task.TaskInstance 13 | err := c.ShouldBindJSON(&tiTmp) 14 | if err != nil { 15 | logger.TaskLog.Errorf("action=handle run instance;do=bind tk data;err=%s;err_code=%d", 16 | err.Error(), common.BindPostDataErr) 17 | c.JSON(http.StatusInternalServerError, gin.H{ 18 | "error": err.Error(), 19 | "err_code": common.BindPostDataErr, 20 | }) 21 | return 22 | } 23 | 24 | tk, err := task.GetTaskByID(tiTmp.TaskID) 25 | if err != nil { 26 | logger.TaskLog.Errorf("action=handle run instance;do=get task;err=%s;err_code=%d", 27 | err.Error(), common.QueryTaskByIDErr) 28 | c.JSON(http.StatusInternalServerError, gin.H{ 29 | "error": err.Error(), 30 | "err_code": common.QueryTaskByIDErr, 31 | }) 32 | return 33 | } 34 | 35 | ti, err := task.GetInstanceByID(tiTmp.ID) 36 | if err != nil { 37 | logger.TaskLog.Errorf("action=handle run instance;do=get instance;err=%s;err_code=%d", 38 | err.Error(), common.QueryInstanceByIDErr) 39 | c.JSON(http.StatusInternalServerError, gin.H{ 40 | "error": err.Error(), 41 | "err_code": common.QueryInstanceByIDErr, 42 | }) 43 | return 44 | } 45 | 46 | go ti.StartStage(&tk) 47 | 48 | c.JSON(http.StatusOK, gin.H{ 49 | "err_code": common.NoError, 50 | "status": "success", 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /handlers/front/v1/reg_agents.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/logger" 7 | "opsHeart_server/service/agent" 8 | "opsHeart_server/utils/rand_str" 9 | ) 10 | 11 | func HandleQueryUnregAgents(c *gin.Context) { 12 | all, err := agent.GetAllUnreg() 13 | if err != nil { 14 | c.JSON(http.StatusInternalServerError, gin.H{ 15 | "error": err.Error(), 16 | }) 17 | return 18 | } 19 | c.JSON(http.StatusOK, gin.H{ 20 | "data": all, 21 | }) 22 | } 23 | 24 | func HandleStartAgents(c *gin.Context) { 25 | var a agent.Agent 26 | err := c.ShouldBindJSON(&a) 27 | if err != nil { 28 | // client ip: who request this url 29 | logger.RigisterLog.Errorf("action=start agent;do=bind json;client_ip=%s;err=%s", c.ClientIP(), err) 30 | c.JSON(http.StatusInternalServerError, gin.H{ 31 | "error": err.Error(), 32 | }) 33 | return 34 | } 35 | 36 | if a.Status == agent.ACCEPTED { 37 | a.Token = rand_str.GetStr(20) 38 | } 39 | 40 | // save to db 41 | if err = a.ChangeStatus(); err != nil { 42 | // agent ip: which will be start up 43 | logger.RigisterLog.Errorf("action=start agent;do=save db;agent_ip=%s;err=%s", a.RemoteAddr, err) 44 | c.JSON(http.StatusInternalServerError, gin.H{ 45 | "error": err.Error(), 46 | }) 47 | return 48 | } 49 | 50 | // tell agent the status: accept or deny 51 | err = a.StartUpAgent() 52 | if err != nil { 53 | logger.RigisterLog.Errorf("action=start agent;do=post agent;agent_ip=%s;err=%s", a.RemoteAddr, err) 54 | c.JSON(http.StatusInternalServerError, gin.H{ 55 | "error": err.Error(), 56 | }) 57 | return 58 | } 59 | 60 | c.JSON(http.StatusOK, gin.H{ 61 | "status": "success", 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /handlers/front/v1/task.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/common" 7 | "opsHeart_server/logger" 8 | "opsHeart_server/service/task" 9 | ) 10 | 11 | func HandleRunTask(c *gin.Context) { 12 | var tk task.Task 13 | err := c.ShouldBindJSON(&tk) 14 | if err != nil { 15 | logger.TaskLog.Errorf("action=handle front run task request;do=bind data;err=%s;err_code=%d", 16 | err.Error(), common.BindPostDataErr) 17 | c.JSON(http.StatusInternalServerError, gin.H{ 18 | "error": err.Error(), 19 | "err_code": common.BindPostDataErr, 20 | }) 21 | return 22 | } 23 | tkInDb, _ := task.GetTaskByID(tk.ID) 24 | 25 | // the customized target hosts 26 | if tk.CollectionType != 0 && tk.CollectionValue != "" { 27 | tkInDb.CollectionType = tk.CollectionType 28 | tkInDb.CollectionValue = tk.CollectionValue 29 | } 30 | 31 | insName := task.NewInsName(tk.ID) 32 | parentIns := task.TaskInstance{ 33 | Name: insName, 34 | } 35 | 36 | // get customized arguments 37 | argMap := make(map[string]task.TaskArg) 38 | for _, v := range tk.TaskArgs { 39 | argMap[v.ArgName] = v 40 | } 41 | 42 | // save instance args 43 | _ = tkInDb.InitInsArgs(argMap, insName) 44 | 45 | err = tkInDb.Run(&parentIns) 46 | if err != nil { 47 | logger.TaskLog.Errorf("action=handle front run task;do=run task;err=%s;err_code=%d", 48 | err.Error(), common.RunTaskErr) 49 | c.JSON(http.StatusInternalServerError, gin.H{ 50 | "error": err.Error(), 51 | "err_code": common.RunTaskErr, 52 | }) 53 | return 54 | } 55 | c.JSON(http.StatusOK, gin.H{ 56 | "err_code": common.NoError, 57 | "status": "success", 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /handlers/test/v1/test_get.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "io/ioutil" 7 | "net/http" 8 | "net/url" 9 | ) 10 | 11 | func TestGetHandler(c *gin.Context) { 12 | q, _ := url.ParseQuery(c.Request.URL.RawQuery) 13 | fmt.Println(q) 14 | h := gin.H{} 15 | for k, v := range q { 16 | h[k] = v 17 | } 18 | c.JSON(http.StatusOK, h) 19 | } 20 | 21 | func TestPostHandler(c *gin.Context) { 22 | d, _ := ioutil.ReadAll(c.Request.Body) 23 | defer func() { 24 | _ = c.Request.Body.Close() 25 | }() 26 | c.JSON(http.StatusOK, gin.H{ 27 | "data": string(d), 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /logger/log.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "fmt" 5 | rotatelogs "github.com/lestrrat-go/file-rotatelogs" 6 | log "github.com/sirupsen/logrus" 7 | cc "opsHeart_server/conf" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | var ( 13 | ServerLog = log.New() 14 | RigisterLog = log.New() 15 | HbsLog = log.New() 16 | TaskLog = log.New() 17 | ) 18 | 19 | var ( 20 | appMode string 21 | level log.Level 22 | 23 | // Add new log file here. 24 | logPathMap = map[string]*log.Logger{ 25 | "/var/log/superops/server.log": ServerLog, 26 | "/var/log/superops/register.log": RigisterLog, 27 | "/var/log/superops/hbs.log": HbsLog, 28 | "/var/log/superops/task.log": TaskLog, 29 | } 30 | ) 31 | 32 | func InitLogger() { 33 | loglevel := cc.GetLogLevel() 34 | appMode = cc.GetMode() 35 | 36 | switch strings.ToLower(loglevel) { 37 | case "info": 38 | level = log.InfoLevel 39 | case "warning": 40 | level = log.WarnLevel 41 | case "error": 42 | level = log.ErrorLevel 43 | default: 44 | level = log.DebugLevel 45 | } 46 | 47 | for k, v := range logPathMap { 48 | createLog(k, v) 49 | fmt.Printf("log: %s initd.\n", k) 50 | } 51 | } 52 | 53 | func createLog(path string, l *log.Logger) { 54 | loggerWriter, err := rotatelogs.New(fmt.Sprintf("%s.%%Y%%m%%d", path), rotatelogs.WithRotationCount(7)) 55 | if err != nil { 56 | log.Fatal(err.Error()) 57 | } 58 | if appMode == "dev" { 59 | l.SetOutput(os.Stdout) 60 | l.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableLevelTruncation: true}) 61 | } else { 62 | l.SetOutput(loggerWriter) 63 | l.SetFormatter(&log.JSONFormatter{}) 64 | } 65 | l.SetLevel(level) 66 | l.SetReportCaller(true) 67 | } 68 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | uuid "github.com/satori/go.uuid" 7 | "opsHeart_server/common" 8 | "opsHeart_server/conf" 9 | "opsHeart_server/db" 10 | "opsHeart_server/logger" 11 | "opsHeart_server/server" 12 | "opsHeart_server/service/agent" 13 | "opsHeart_server/service/collection" 14 | "opsHeart_server/service/cron_task" 15 | "os" 16 | "time" 17 | ) 18 | 19 | var ( 20 | h bool 21 | v bool 22 | u bool 23 | buildTime string 24 | goVersion string 25 | ) 26 | 27 | func init() { 28 | flag.BoolVar(&h, "h", false, "this help") 29 | flag.BoolVar(&v, "v", false, "show version, build info.") 30 | flag.BoolVar(&u, "u", false, "create uuid") 31 | flag.Usage = usage 32 | } 33 | 34 | func usage() { 35 | _, _ = fmt.Fprintf(os.Stderr, `Options: Build Time: %v`, buildTime) 36 | flag.PrintDefaults() 37 | } 38 | 39 | func main() { 40 | common.BuildTime = buildTime 41 | common.GoVersion = goVersion 42 | 43 | flag.Parse() 44 | 45 | if h { 46 | flag.Usage() 47 | return 48 | } 49 | 50 | if v { 51 | fmt.Println("Proxy Version: ", common.Version) 52 | fmt.Println("Build Time: ", common.BuildTime) 53 | fmt.Println("Go Version: ", common.GoVersion) 54 | return 55 | } 56 | 57 | if u { 58 | uuidObj := uuid.NewV4() 59 | uu, err := uuidObj.Value() 60 | 61 | if err != nil { 62 | fmt.Printf("create uuid err: %s\n", err) 63 | } else { 64 | fmt.Printf("uuid is: %s\n", uu) 65 | } 66 | return 67 | } 68 | 69 | // init conf 70 | err := conf.InitCfg() 71 | if err != nil { 72 | fmt.Printf("action=init conf;err=%s\n", err) 73 | return 74 | } 75 | 76 | logger.ServerLog.Info("action=load conf file;status=success") 77 | 78 | common.InitRunningEnv() 79 | 80 | logger.InitLogger() 81 | 82 | db.InitDB() 83 | defer func() { 84 | _ = db.DB.Close() 85 | }() 86 | 87 | // init model 88 | db.DB.AutoMigrate(agent.Agent{}) 89 | db.DB.AutoMigrate(collection.AgentFact{}) 90 | 91 | if conf.GetServerRole() { 92 | // start hbs check cron task 93 | go func() { 94 | time.Sleep(3 * time.Minute) 95 | err := cron_task.CheckHbs.Start() 96 | if err != nil { 97 | logger.HbsLog.Errorf("action=start check hbs cron;err=%s", err.Error()) 98 | } 99 | }() 100 | defer func() { 101 | _ = cron_task.CheckHbs.Stop() 102 | }() 103 | } 104 | 105 | server.Start() 106 | } 107 | -------------------------------------------------------------------------------- /opsHeart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ren-zc/opsHeart_server/4cdb09ed17132c1400d5b0411e79784d8f1ce721/opsHeart.png -------------------------------------------------------------------------------- /routers/middleware/check_front.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/common" 7 | "opsHeart_server/conf" 8 | ) 9 | 10 | func FrontToken() gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | frontToken := c.GetHeader(common.FrontTokenKey) 13 | if frontToken == "" || frontToken != conf.GetFrontToken() { 14 | c.JSON(http.StatusForbidden, gin.H{ 15 | "error": "token required.", 16 | }) 17 | c.Abort() 18 | return 19 | } 20 | c.Next() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /routers/middleware/check_token.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "opsHeart_server/common" 7 | "opsHeart_server/logger" 8 | "opsHeart_server/service/agent" 9 | ) 10 | 11 | func TokenChecker() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | agentUuid, token, ok := c.Request.BasicAuth() 14 | if !ok { 15 | logger.ServerLog.Errorf("action=auth check;uuid=%s;path=%s;client=%s;err=basic auth false", 16 | agentUuid, c.FullPath(), c.ClientIP()) 17 | c.JSON(http.StatusUnauthorized, gin.H{ 18 | "error": "auth required", 19 | }) 20 | c.Abort() 21 | return 22 | } 23 | 24 | var q agent.Agent 25 | // or query dat form redis 26 | err := q.QueryByUUID(agentUuid) 27 | if err != nil { 28 | logger.ServerLog.Errorf("action=auth check;uuid=%s;path=%s;client=%s;err=%s", 29 | agentUuid, c.FullPath(), c.ClientIP(), err) 30 | c.JSON(http.StatusUnauthorized, gin.H{ 31 | "error": err, 32 | }) 33 | c.Abort() 34 | return 35 | } 36 | 37 | if token != q.Token { 38 | logger.ServerLog.Errorf("action=auth check;uuid=%s;path=%s;client=%s;err=wrong token", 39 | agentUuid, c.FullPath(), c.ClientIP()) 40 | c.JSON(http.StatusUnauthorized, gin.H{ 41 | "error": "auth required", 42 | }) 43 | c.Abort() 44 | return 45 | } 46 | 47 | c.Set(common.UUIDKey, agentUuid) 48 | c.Next() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /routers/router.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "opsHeart_server/handlers/agent/start_up" 6 | agentV1 "opsHeart_server/handlers/agent/v1" 7 | externalV1 "opsHeart_server/handlers/external/v1" 8 | frontV1 "opsHeart_server/handlers/front/v1" 9 | testV1 "opsHeart_server/handlers/test/v1" 10 | "opsHeart_server/routers/middleware" 11 | ) 12 | 13 | var R *gin.Engine 14 | 15 | func init() { 16 | R = gin.Default() 17 | 18 | // for agent call 19 | agent := R.Group("/agent") 20 | { 21 | v1 := agent.Group("/v1") 22 | v1.Use(middleware.TokenChecker()) 23 | { 24 | v1.GET("/hbs", agentV1.HandleHbs) 25 | v1.GET("/status", agentV1.HandleStatus) 26 | v1.POST("/fact", agentV1.HandleFact) 27 | } 28 | startUp := agent.Group("/start-up") 29 | { 30 | startUp.POST("/register", start_up.HandleAgentRegister) 31 | } 32 | } 33 | 34 | // for front call 35 | front := R.Group("/front") 36 | front.Use(middleware.FrontToken()) 37 | { 38 | v1 := front.Group("/v1") 39 | { 40 | v1.GET("/register-agents", frontV1.HandleQueryUnregAgents) 41 | v1.POST("/register-agents", frontV1.HandleStartAgents) 42 | 43 | // manage token of external 44 | v1.POST("/manage-external", nil) 45 | 46 | // run task 47 | v1.POST("/task/run", frontV1.HandleRunTask) 48 | 49 | // run instance 50 | v1.POST("/instance/run", frontV1.HandleRunIns) 51 | } 52 | //col := v1.Group("/collection") 53 | } 54 | 55 | // for external system call 56 | external := R.Group("/external") 57 | { 58 | v1 := external.Group("/v1") 59 | { 60 | v1.GET("/test", nil) 61 | v1.POST("/task/run", externalV1.HandleRunTask) 62 | } 63 | } 64 | 65 | // for test 66 | test := R.Group("/test") 67 | { 68 | v1 := test.Group("/v1") 69 | { 70 | v1.GET("/do", testV1.TestGetHandler) 71 | v1.POST("/do", testV1.TestPostHandler) 72 | } 73 | } 74 | 75 | //external := R.Group("/external") 76 | //{ 77 | // v1 := external.Group("/v1") 78 | //} 79 | } 80 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "opsHeart_server/common" 6 | "opsHeart_server/conf" 7 | "opsHeart_server/logger" 8 | "opsHeart_server/routers" 9 | ) 10 | 11 | type Data struct { 12 | Version string 13 | BuildTime string 14 | GoVersion string 15 | Author string 16 | ReleaseTime string 17 | } 18 | 19 | var Svr Data 20 | 21 | func init() { 22 | Svr.Version = common.Version 23 | Svr.BuildTime = common.BuildTime 24 | Svr.Author = common.Author 25 | Svr.ReleaseTime = common.ReleaseTime 26 | } 27 | 28 | func Start() { 29 | ip := conf.GetAddr() 30 | port := conf.GetPort() 31 | addr := fmt.Sprintf("%s:%s", ip, port) 32 | err := routers.R.Run(addr) 33 | if err != nil { 34 | logger.ServerLog.Errorf("action=run http server;err=%s", err.Error()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /service/agent/db_handle.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "errors" 5 | "opsHeart_server/db" 6 | "time" 7 | ) 8 | 9 | func (a *Agent) InsertDat() error { 10 | rst := db.DB.Create(a) 11 | return rst.Error 12 | } 13 | 14 | func (a *Agent) IsExist() bool { 15 | var tmpA Agent 16 | db.DB.First(&tmpA, "uuid = ?", a.UUID) 17 | return tmpA.UUID != "" 18 | } 19 | 20 | func (a *Agent) UpdateDat() error { 21 | //return db.DB.Save(a).Error 22 | a.UpdatedAt = time.Now() 23 | return db.DB.Model(&Agent{}).Omit("uuid").UpdateColumns(a).Error 24 | } 25 | 26 | func (a *Agent) QueryByUUID(u string) error { 27 | err := db.DB.Find(a, "uuid = ?", u).Error 28 | if err != nil { 29 | return err 30 | } 31 | return nil 32 | } 33 | 34 | func (a *Agent) ChangeStatus() error { 35 | var t Agent 36 | d := db.DB.First(&t, "id = ?", a.ID) 37 | if t.ID == 0 { 38 | return errors.New("no record found in db") 39 | } 40 | d.Update("status", a.Status) 41 | if a.Status == ACCEPTED { 42 | d.Update("token", a.Token) 43 | } 44 | return d.Error 45 | } 46 | 47 | func GetAllUnreg() ([]Agent, error) { 48 | var all []Agent 49 | d := db.DB.Where("status = ?", REGISTER).Find(&all) 50 | return all, d.Error 51 | } 52 | -------------------------------------------------------------------------------- /service/agent/db_handle_test.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "opsHeart_server/conf" 5 | "opsHeart_server/db" 6 | "testing" 7 | ) 8 | 9 | func TestGetAllUnreg(t *testing.T) { 10 | _ = conf.InitCfg() 11 | db.InitDB() 12 | all, err := GetAllUnreg() 13 | if err != nil { 14 | t.Fatalf("fail: %s", err.Error()) 15 | } 16 | t.Logf("all: %v", all) 17 | } 18 | -------------------------------------------------------------------------------- /service/agent/hbs.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "opsHeart_server/db" 5 | "time" 6 | ) 7 | 8 | func (a *Agent) UpdateHbs() error { 9 | return db.DB.Model(&Agent{}).Where("uuid", a.UUID). 10 | Update("hbs_status", WORKING). 11 | Update("hbs_time", time.Now()).Error 12 | } 13 | -------------------------------------------------------------------------------- /service/agent/models.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "opsHeart_server/service/collection" 5 | "time" 6 | ) 7 | 8 | type regStatus int 9 | 10 | const ( 11 | DENIED regStatus = -1 12 | BLANK regStatus = 0 13 | REGISTER regStatus = 1 14 | ACCEPTED regStatus = 2 15 | ) 16 | 17 | type runTimeStatus int 18 | 19 | const ( 20 | WORKING runTimeStatus = 1 21 | OFFLINE runTimeStatus = -1 22 | ) 23 | 24 | type Agent struct { 25 | ID uint `json:"id" gorm:"primary_key"` 26 | UUID string `json:"uuid" gorm:"Size:50;unique_index"` 27 | Hostname string `json:"hostname" gorm:"Size:100"` 28 | RemoteAddr string `json:"remote_addr" gorm:"Size:50"` 29 | OsType string `json:"os_type" gorm:"Size:50"` 30 | OsVersion string `json:"os_version" gorm:"Size:200"` 31 | OsArch string `json:"os_arch" gorm:"Size:20"` 32 | AgentVersion string `json:"agent_version" gorm:"Size:20"` 33 | Token string `json:"token" gorm:"Size:50"` 34 | Status regStatus `json:"status" gorm:"type:tinyint(2);default:0"` 35 | HbsStatus runTimeStatus `gorm:"type:tinyint(2);default:0"` 36 | HbsTime time.Time `gorm:"index"` 37 | CreatedAt time.Time 38 | UpdatedAt time.Time 39 | AgentFacts []collection.AgentFact `json:"agent_facts" gorm:"foreignkey:UUID;association_foreignkey:UUID"` 40 | AgentTags []collection.AgentTag `json:"agent_tags" gorm:"foreignkey:UUID;association_foreignkey:UUID"` 41 | } 42 | -------------------------------------------------------------------------------- /service/agent/start_up.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "opsHeart_server/common" 8 | "opsHeart_server/utils/call_http" 9 | ) 10 | 11 | type tokenMsg struct { 12 | Status int `json:"status"` 13 | Msg string `json:"msg"` 14 | Token string `json:"token"` 15 | } 16 | 17 | func (a *Agent) StartUpAgent() error { 18 | host := fmt.Sprintf("%s:%s", a.RemoteAddr, common.AgentPort) 19 | var d tokenMsg 20 | if a.Status == ACCEPTED { 21 | d.Msg = "accepted" 22 | d.Status = 1 23 | d.Token = a.Token 24 | } else { 25 | d.Msg = "denied" 26 | d.Status = -1 27 | } 28 | 29 | b, err := json.Marshal(d) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | c, _, err := call_http.HttpPost(host, a.Token, common.AgentStartUpPath, b) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | if c != 200 { 40 | return errors.New("agent resp code is not 200") 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /service/collection/collection.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | import "opsHeart_server/db" 4 | 5 | func (ac *AgentCollection) GetAllIPs() ([]string, error) { 6 | return nil, nil 7 | } 8 | 9 | func QueryCollByName(name string) (ac *AgentCollection, err error) { 10 | err = db.DB.Model(&AgentCollection{}).Where("name = ?", name).First(ac).Error 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /service/collection/fact.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | import "opsHeart_server/db" 4 | 5 | func (af *AgentFact) IsExist() bool { 6 | var t AgentFact 7 | db.DB.Model(&AgentFact{}).First(&t, "uuid = ? and key = ?", af.UUID, af.Key) 8 | return t.ID != 0 9 | } 10 | 11 | func (af *AgentFact) Create() error { 12 | return db.DB.Model(&AgentFact{}).Create(af).Error 13 | } 14 | 15 | func (af *AgentFact) Update() error { 16 | return db.DB.Model(&AgentFact{}).Omit("uuid", "key").Updates(af).Error 17 | } 18 | 19 | func (af *AgentFact) DeleteUUIDAll() error { 20 | return db.DB.Model(&AgentFact{}). 21 | Where("uuid = ?", af.UUID). 22 | Delete(AgentFact{}).Error 23 | } 24 | 25 | func (af *AgentFact) DeleteAKey() error { 26 | return db.DB.Model(&AgentFact{}). 27 | Where("uuid = ? and key = ?", af.UUID, af.Key). 28 | Delete(AgentFact{}).Error 29 | } 30 | 31 | func (af *AgentFact) QueryValue() (string, error) { 32 | var t AgentFact 33 | d := db.DB.Model(&AgentFact{}). 34 | Where("uuid = ? and key = ?", af.UUID, af.Key). 35 | First(&t) 36 | return t.Value, d.Error 37 | } 38 | 39 | func (af *AgentFact) QueyAllKeyValueByUUID() ([]AgentFact, error) { 40 | var afs []AgentFact 41 | d := db.DB.Model(&AgentFact{}).Where("uuid = ?", af.UUID).Find(&afs) 42 | return afs, d.Error 43 | } 44 | -------------------------------------------------------------------------------- /service/collection/fact_db.go: -------------------------------------------------------------------------------- 1 | package collection 2 | -------------------------------------------------------------------------------- /service/collection/fact_test.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | import ( 4 | "opsHeart_server/conf" 5 | "opsHeart_server/db" 6 | "testing" 7 | ) 8 | 9 | func initAf() error { 10 | err := conf.InitCfg() 11 | if err != nil { 12 | return err 13 | } 14 | db.InitDB() 15 | db.DB.AutoMigrate(AgentFact{}) 16 | return nil 17 | } 18 | 19 | func TestAgentFact_Create(t *testing.T) { 20 | err := initAf() 21 | if err != nil { 22 | t.Fatalf("test create af, init cfg err: %s", err) 23 | } 24 | 25 | // The 2nd test will be failed. 26 | af1 := AgentFact{ 27 | UUID: "test-uuid1", 28 | Key: "test-key2", 29 | Value: "test-value3", 30 | } 31 | 32 | err = af1.Create() 33 | if err != nil { 34 | t.Fatalf("test create af1 fail: %s", err.Error()) 35 | } 36 | t.Logf("test create af1 success!") 37 | 38 | // The 2nd test will be failed. 39 | af2 := AgentFact{ 40 | UUID: "test-uuid2", 41 | Key: "test-key2", 42 | Value: "test-value2", 43 | } 44 | 45 | err = af2.Create() 46 | if err != nil { 47 | t.Fatalf("test create af2 err: %s", err.Error()) 48 | } 49 | t.Logf("test create af2 success!") 50 | } 51 | 52 | func TestAgentFact_IsExist(t *testing.T) { 53 | err := initAf() 54 | if err != nil { 55 | t.Fatalf("test create af, init cfg err: %s", err) 56 | } 57 | 58 | af1 := AgentFact{ 59 | UUID: "test-uuid1", 60 | Key: "test-key1", 61 | Value: "test-value1", 62 | } 63 | af2 := AgentFact{ 64 | UUID: "test-uuid2", 65 | Key: "test-key2", 66 | Value: "test-value2", 67 | } 68 | 69 | af1IsExist := af1.IsExist() 70 | if af1IsExist { 71 | t.Log("af1 is exist, af1 test ok!") 72 | } else { 73 | t.Error("af2 is not exist, af1 test fail!") 74 | } 75 | 76 | af2IsExist := af2.IsExist() 77 | if af2IsExist { 78 | t.Log("af2 is exist, af2 test ok!") 79 | } else { 80 | t.Error("af2 is not exist, af2 test fail!") 81 | } 82 | } 83 | 84 | func TestAgentFact_Update(t *testing.T) { 85 | err := initAf() 86 | if err != nil { 87 | t.Fatalf("test create af, init cfg err: %s", err) 88 | } 89 | 90 | af1 := AgentFact{ 91 | UUID: "test-uuid1", 92 | Key: "test-key1", 93 | Value: "test-value2", 94 | } 95 | 96 | err = af1.Update() 97 | if err != nil { 98 | t.Fatalf("test update err: %s", err) 99 | } 100 | 101 | // test query by the way. 102 | s, err := af1.QueryValue() 103 | if err != nil { 104 | t.Fatalf("test update and query err: %s", err) 105 | } 106 | t.Logf("test update query value: %s", s) 107 | } 108 | 109 | func TestAgentFact_QueyAllKeyValueByUUID(t *testing.T) { 110 | err := initAf() 111 | if err != nil { 112 | t.Fatalf("test create af, init cfg err: %s", err) 113 | } 114 | 115 | af1 := AgentFact{ 116 | UUID: "test-uuid1", 117 | Key: "test-key1", 118 | } 119 | 120 | afs, err := af1.QueyAllKeyValueByUUID() 121 | 122 | t.Logf("afs length: %d", len(afs)) 123 | 124 | if err != nil { 125 | t.Fatalf("test query all by uuid err: %s", err.Error()) 126 | } 127 | for _, a := range afs { 128 | t.Logf("uuid:%s, key:%s, value: %s", a.UUID, a.Key, a.Value) 129 | } 130 | } 131 | 132 | func TestAgentFact_DeleteAKey(t *testing.T) { 133 | err := initAf() 134 | if err != nil { 135 | t.Fatalf("test create af, init cfg err: %s", err) 136 | } 137 | 138 | af2 := AgentFact{ 139 | UUID: "test-uuid2", 140 | Key: "test-key2", 141 | Value: "test-value2", 142 | } 143 | 144 | err = af2.DeleteAKey() 145 | if err != nil { 146 | t.Fatalf("test delete a key err: %s", err.Error()) 147 | } 148 | t.Log("test delete a key success!") 149 | } 150 | 151 | func TestAgentFact_DeleteUUIDAll(t *testing.T) { 152 | err := initAf() 153 | if err != nil { 154 | t.Fatalf("test create af, init cfg err: %s", err) 155 | } 156 | 157 | af1 := AgentFact{ 158 | UUID: "test-uuid1", 159 | Key: "test-key1", 160 | Value: "test-value2", 161 | } 162 | 163 | err = af1.DeleteUUIDAll() 164 | if err != nil { 165 | t.Fatalf("test delete all by uuid err: %s", err) 166 | } 167 | t.Logf("test delete all by uuid success!") 168 | } 169 | -------------------------------------------------------------------------------- /service/collection/models.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | import "github.com/jinzhu/gorm" 4 | 5 | // Agent data, id, agent id, key,value 6 | // agent id, key: composite indexes 7 | type AgentFact struct { 8 | gorm.Model 9 | UUID string `json:"uuid" gorm:"Size:50;index:agent_uuid;UNIQUE_INDEX:agent_key"` 10 | Key string `json:"key" gorm:"UNIQUE_INDEX:agent_key;Size:50"` 11 | Value string `json:"value" gorm:"Size:200"` 12 | //AgentID int `json:"agent_id" gorm:"index:agent_id;index:agent_key"` 13 | } 14 | 15 | type tagStatus int 16 | 17 | const ( 18 | TAGAVALIABLED tagStatus = 0 19 | TAGDISABLED tagStatus = 1 20 | ) 21 | 22 | // settings for agent: 23 | // id, agent id, key, value 24 | // agent id, key: composite indexes 25 | type AgentTag struct { 26 | gorm.Model 27 | UUID string `json:"uuid" gorm:"Size:50;index:agent_uuid;UNIQUE_INDEX:agent_key"` 28 | Key string `json:"key" gorm:"UNIQUE_INDEX:agent_key;Size:50"` 29 | Value string `json:"value" gorm:"Size:200"` 30 | Status tagStatus `json:"status" gorm:"default:0"` 31 | //AgentID int `json:"agent_id" gorm:"index:agent_id;index:agent_key"` 32 | } 33 | 34 | type collType int 35 | 36 | const ( 37 | COLLENTRANCE collType = 1 38 | COLLGROUP collType = 2 39 | COLLIST collType = 3 40 | COLLFACT collType = 4 41 | COLLSETTING collType = 5 42 | ) 43 | 44 | type unionType int 45 | 46 | const ( 47 | UUNDEFINED unionType = 0 48 | UAND unionType = 1 49 | UOR unionType = 2 50 | ) 51 | 52 | type collStatus int 53 | 54 | const ( 55 | AVALIABELD collStatus = 0 56 | DISABLED collStatus = 1 57 | ) 58 | 59 | // type: 60 | // list: give a agent list saved in `value` 61 | // fact: filter agent by fact 62 | // tag: filter agent by user tags 63 | // starts: ip or uuid start with string of `value` 64 | // ends: ip or uuid end with string of `value` 65 | // keyword: ip or uuid contain string of `value` 66 | // equal: ip or uuid is string of `value` 67 | // when type is starts, ends, keyword or equal, key must be ip or uuid. 68 | // group: just a container which hold child collection 69 | // entrance: a root level of a collection 70 | // Define how agents are selected. 71 | type AgentCollection struct { 72 | // id 73 | // name(unique index) 74 | // type: list, fact, tag, starts, ends, keyword, equal, group, entrance 75 | // key 76 | // value 77 | // parent id 78 | // union type: `and`, `or`, `undefined`, available for group or entrance 79 | // group default: `and` 80 | // entrance default: `or` 81 | // status 82 | // desc 83 | gorm.Model 84 | Name string `json:"name" gorm:"Size:50;unique_index"` 85 | ColTYPE collType `json:"col_type" gorm:"default:1"` 86 | Key string `json:"key" gorm:"Size:50"` 87 | Value string `json:"value" gorm:"Type:MEDIUMTEXT"` 88 | ParentID uint `json:"parent_id"` 89 | UnionType unionType `json:"union_type"` 90 | Status collStatus `json:"status"` 91 | Desc string `json:"desc"` 92 | } 93 | -------------------------------------------------------------------------------- /service/collection/tag.go: -------------------------------------------------------------------------------- 1 | package collection 2 | 3 | func (at *AgentTag) Create() error { 4 | return nil 5 | } 6 | 7 | func (at *AgentTag) Update() error { 8 | return nil 9 | } 10 | 11 | func (at *AgentTag) DeleteOne() error { 12 | return nil 13 | } 14 | 15 | func (at *AgentTag) DeleteAllOneUUID() error { 16 | return nil 17 | } 18 | 19 | func (at *AgentTag) IsExist() error { 20 | return nil 21 | } 22 | 23 | func (at *AgentTag) QueryValue() (string, error) { 24 | return "", nil 25 | } 26 | 27 | func (at *AgentTag) QueryAllValueOfUUID() ([]AgentTag, error) { 28 | return nil, nil 29 | } 30 | -------------------------------------------------------------------------------- /service/collection/tag_db.go: -------------------------------------------------------------------------------- 1 | package collection 2 | -------------------------------------------------------------------------------- /service/cron_task/hbs.go: -------------------------------------------------------------------------------- 1 | package cron_task 2 | 3 | import ( 4 | "opsHeart_server/db" 5 | "opsHeart_server/logger" 6 | "opsHeart_server/service/agent" 7 | "opsHeart_server/utils/cron" 8 | "time" 9 | ) 10 | 11 | var CheckHbs *cron.Cr 12 | 13 | func FindOffinedAgent() error { 14 | t := time.Now().Add(-3 * time.Minute) 15 | return db.DB.Model(&agent.Agent{}). 16 | Where("hbs_time < ? AND hbs_status = ?", t, agent.WORKING). 17 | Update("hbs_status", agent.OFFLINE).Error 18 | } 19 | 20 | func init() { 21 | var err error 22 | CheckHbs, err = cron.NewCron(FindOffinedAgent, nil, 3*time.Minute, 1) 23 | if err != nil { 24 | logger.HbsLog.Errorf("action=start check hbs cron;err=%s", err.Error()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /service/task/cmd.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | "opsHeart_server/logger" 6 | ) 7 | 8 | func (c *TaskCmd) start(agents *[]string) error { 9 | //return errors.New("test") 10 | logger.TaskLog.Infof("CMD run, cmd:%s, opt:%s.\n", c.Cmd, c.Opt) 11 | for _, v := range *agents { 12 | fmt.Printf("\t%s\n", v) 13 | //fmt.Printf("args: %v\n", c.Args) 14 | for _, a := range c.Args { 15 | fmt.Printf("\targ name: %s, arg type: %d, arg value: %s\n", 16 | a.ArgName, a.ArgType, a.ArgValue) 17 | } 18 | } 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /service/task/cmd_db.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import "opsHeart_server/db" 4 | 5 | func (c *TaskCmd) QueryByTaskID() (err error) { 6 | err = db.DB.Model(c).Where("task_id = ?", c.TaskID).First(c).Error 7 | return 8 | } 9 | 10 | func (c *TaskCmd) Create() (err error) { 11 | err = db.DB.Create(c).Error 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /service/task/cmd_db_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "opsHeart_server/conf" 5 | "opsHeart_server/db" 6 | "testing" 7 | ) 8 | 9 | func TestTaskCmd_QueryByTaskID(t *testing.T) { 10 | err := conf.InitCfg() 11 | if err != nil { 12 | t.Fatalf("init conf err: %s", err.Error()) 13 | } 14 | db.InitDB() 15 | db.DB.AutoMigrate(&TaskCmd{}) 16 | 17 | c := TaskCmd{ 18 | TaskID: 2, 19 | Cmd: "echo test", 20 | } 21 | err = c.QueryByTaskID() 22 | if err != nil { 23 | t.Fatalf("test query cmd task by task id err: %s", err.Error()) 24 | } 25 | 26 | t.Logf("task id:%d, cmd:%s, opt:%s, timeout:%d", 27 | c.TaskID, c.Cmd, c.Opt, c.Timeout) 28 | } 29 | -------------------------------------------------------------------------------- /service/task/cron.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "opsHeart_server/logger" 5 | "time" 6 | ) 7 | 8 | func VgroupChildChecker(ins *TaskInstance, tk *Task) { 9 | for { 10 | time.Sleep(2 * time.Second) 11 | allChild, err := ins.GetAllChildInsWithUnfinished() 12 | if err != nil { 13 | logger.TaskLog.Errorf("action=cron check vgroup;ins_id=%d;err=%s", 14 | ins.ID, err.Error()) 15 | } 16 | 17 | // unfinished children length 18 | n := len(allChild) 19 | 20 | if n > 0 { 21 | continue 22 | } 23 | 24 | if n == 0 { 25 | ins.StageFinish(tk) 26 | logger.TaskLog.Infof("action=vgroup end;ins_id=%d;task_id=%d", ins.ID, tk.ID) 27 | break 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /service/task/doc.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | // TASK NOTE 4 | 5 | // Task type: ROOT, H_GROUP, V_GROUP, X_GROUP, CMD, SCRIPT, SYNC FILE; 6 | // Status: created, approved, disabled, deleted; 7 | 8 | // Type HGROUP: child tasks run one by one; 9 | // Type VGROUP: all child tasks run concomitantly; 10 | // the seq num of all childes must be 1; 11 | // Type XGROUP: only contain two tasks, they are exclusion, 12 | // in XGROUP: the firs child will run when condition is success, 13 | // the second child will run when condition is failed. 14 | // That is to say only one task to be called in this group; 15 | 16 | // Only ROOT need approved, and it is a task entrance; 17 | // ROOT, H_GROUP and V_GROUP has more than one child; 18 | // Type CMD, SCRIPT and SYNC FILE no child; 19 | 20 | // Type H_GROUP, V_GROUP, CMD, SCRIPT and SYNC FILE has priority on one level; 21 | 22 | // `agent_collect_type`: `list`, `collection_name`, `inherit_num`, `inherit_percent` 23 | // `agent_collect_value`: `["0.0.0.0","1.1.1.1"]` or 24 | // a agent collection name which defined at `AgentCollection` table 25 | // or 100 agents or 20% agents 26 | // `inherit_num`, `inherit_percent`: inherit agents ips from parent. 27 | 28 | // step_type: null, num or percent 29 | // null: all agent will be run 30 | // num: run agent by NUM list of steps 31 | // percent: run agent by PERCENT list of steps 32 | // for example 33 | // The total number of agents get from `agent_collect_type` and `agent_collect_value` is 1000 34 | // `step_type`: num; `stages`: [100, 200, 300, 400]; 100 + 200 + 300 + 400 = 1000 35 | // `step_type`: percent; `stages`: [10%, 20%, 30%, 40%]; 10% + 20% + 30% + 40% = 100% 36 | 37 | // A task instance will get all agents from `agent_collect_*`; 38 | // If `stage_type` not blank, The task instance will split the number of agents by the `stages`; 39 | // If blank, will not split. 40 | 41 | // `continue_by_task`: check the specific task before start; 42 | // the task must be a brother task 43 | // `continue_by_rst`: the rst of the task of `continue_by_task` 44 | // default: success 45 | // if continue check failed, the task will be set to failed, and all tasks of later won't be running, 46 | // so if you want tasks of later could be run, you can set `ContinueOnFail` to 1 for this task. 47 | 48 | // `ContinueByTask` must be brother node id. 49 | 50 | // ContinueOnFail: continue next task even failed. 51 | // ParentXGROUP: parent task is a XGROUP task. 52 | 53 | // INSTANCE NOTE 54 | 55 | // When instance run, first check if there is a stage need to run, than check next task. 56 | // ParentIsX: this is a instance of task whose parent is a a XGROUP task. 57 | // ContinueOnFail: continue next task even this instance is failed. 58 | 59 | // only cmd and script task can have arguments. 60 | -------------------------------------------------------------------------------- /service/task/ins.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math" 7 | "opsHeart_server/common" 8 | "opsHeart_server/db" 9 | "opsHeart_server/logger" 10 | "time" 11 | ) 12 | 13 | // GetAllInsIPs get instance id stage all related agent IPs 14 | func (ins *TaskInstance) GetAllInsIPs() (allIPs []string, err error) { 15 | stageAgents, err := ins.GetAllAgents() 16 | for _, v := range stageAgents { 17 | allIPs = append(allIPs, v.IP) 18 | } 19 | return 20 | } 21 | 22 | // GetAllInsIPsByPercentOrNum get instance related agent IPs by percent or number 23 | func (ins *TaskInstance) GetAllInsIPsByPercentOrNum(p int, t StType, cs splitColl) ([]string, error) { 24 | var stageAgents []TaskStageAgent 25 | var agents []string 26 | 27 | err := db.DB.Model(ins).Related(&stageAgents, "TaskStageAgents").Error 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | var n int 33 | if t == StagePercent { 34 | n = int(math.Ceil((float64(p) / 100) * float64(len(stageAgents)))) 35 | } else { 36 | n = p 37 | } 38 | 39 | for _, v := range stageAgents { 40 | if cs == DoSplit && v.ChildUse == IsUsed { 41 | continue 42 | } 43 | if n == 0 { 44 | break 45 | } 46 | agents = append(agents, v.IP) 47 | if cs == DoSplit { 48 | err := v.Update("child_use", IsUsed) 49 | if err != nil { 50 | return nil, err 51 | } 52 | } 53 | n-- 54 | } 55 | return agents, nil 56 | } 57 | 58 | func (ins *TaskInstance) logFailMsg(status StageStatus, msg string, t *Task) { 59 | logger.TaskLog.Debugf(">>> ins: %d, update status in log fail: %d\n", ins.ID, status) 60 | 61 | _ = ins.Update([]string{"status", "ins_msg", "end_at"}, status, msg, time.Now()) 62 | logger.TaskLog.Error(msg) 63 | 64 | ins.Status = status 65 | 66 | if ins.ContinueOnFail == 1 { 67 | ins.StageFinish(t) 68 | } else { 69 | ins.CallbackParentStage() 70 | } 71 | } 72 | 73 | func (ins *TaskInstance) runChildTask(tk *Task, seq uint) { 74 | c, err := tk.GetTheSeqChild(seq) 75 | if err != nil { 76 | errMsg := fmt.Sprintf("action=start stage;do=get seq %d child task;err=%s", 77 | seq, err.Error()) 78 | ins.logFailMsg(STAGEFAILED, errMsg, tk) 79 | return 80 | } 81 | if len(c) != 1 { 82 | err = errors.New("multi child task exist") 83 | errMsg := fmt.Sprintf("action=start stage;do=get seq %d child task;err=%s", 84 | seq, err.Error()) 85 | ins.logFailMsg(STAGEFAILED, errMsg, tk) 86 | return 87 | } 88 | err = c[0].Run(ins) 89 | if err != nil { 90 | errMsg := fmt.Sprintf("action=start stage;do=run seq %d child task;err=%s", 91 | seq, err.Error()) 92 | ins.logFailMsg(STAGEFAILED, errMsg, tk) 93 | } 94 | } 95 | 96 | func (ins *TaskInstance) StartStage(t *Task) { 97 | tm := time.Now() 98 | logger.TaskLog.Debugf(">>> ins: %d, update status in start stage: %d\n", 99 | ins.ID, STAGERUNNING) 100 | err := ins.Update([]string{"status", "start_at"}, STAGERUNNING, tm) 101 | if err != nil { 102 | logger.TaskLog. 103 | Errorf("action=start stage;do=save stage status;ins_id=%d;ins_name=%s;task_id=%d;err=%s", 104 | ins.ID, ins.Name, ins.TaskID, err.Error()) 105 | return 106 | } 107 | 108 | ins.Status = STAGERUNNING 109 | ins.StartAt = tm 110 | 111 | // check continue condition 112 | ok, err := ins.checkContinueCondition(t) 113 | if err != nil { 114 | errMsg := fmt.Sprintf("action=start stage;do=check continue condition;ins_id=%d;ins_name=%s;task_id=%d;err=%s", 115 | ins.ID, ins.Name, ins.TaskID, err.Error()) 116 | ins.logFailMsg(STAGEFAILED, errMsg, t) 117 | return 118 | } 119 | 120 | if !ok { 121 | errMsg := fmt.Sprintf("action=start stage;do=check continue condition;ins_id=%d;ins_name=%s;task_id=%d;err=continue check fail", 122 | ins.ID, ins.Name, ins.TaskID) 123 | ins.logFailMsg(STAGEFAILED, errMsg, t) 124 | return 125 | } 126 | 127 | switch t.TkType { 128 | case TASKROOT: 129 | // get the first child task to run 130 | ins.runChildTask(t, 1) 131 | return 132 | case HGROUP: 133 | // get the first child task to run 134 | ins.runChildTask(t, 1) 135 | return 136 | case VGROUP: 137 | // get all child task to run 138 | allChilds, err := t.GetAllChildTask() 139 | if err != nil { 140 | errMsg := fmt.Sprintf("action=start stage;do=get child tasks;ins_id=%d;ins_name=%s;err=%s", 141 | ins.ID, ins.Name, err.Error()) 142 | ins.logFailMsg(STAGEFAILED, errMsg, t) 143 | return 144 | } 145 | 146 | cl := len(allChilds) 147 | for _, c := range allChilds { 148 | tk := c 149 | tk.ParentVGROUP = 1 150 | tk.ChildesNum = cl 151 | err := tk.Run(ins) 152 | if err != nil { 153 | errMsg := fmt.Sprintf("action=start stage;do=start child task;ins_id=%d;ins_name=%s;child_task_id=%d;task_id=%s;err=%s", 154 | ins.ID, ins.Name, tk.ID, t.Name, err.Error()) 155 | ins.logFailMsg(STAGEFAILED, errMsg, t) 156 | return 157 | } 158 | } 159 | 160 | // start cron 161 | VgroupChildChecker(ins, t) 162 | 163 | case XGROUP: 164 | // get the previous task result and determine which child task to run 165 | rst, err := ins.getPreviousInsRst(t) 166 | if err != nil { 167 | errMsg := fmt.Sprintf("action=start stage;do=get brother task stages;ins_id=%d;ins_name=%s", 168 | ins.ID, ins.Name) 169 | ins.logFailMsg(STAGEFAILED, errMsg, t) 170 | return 171 | } 172 | 173 | // get all child task 174 | childes, err := t.GetAllChildTask() 175 | if err != nil { 176 | errMsg := fmt.Sprintf("action=start stage;do=xgroup get all childs;stage=%d;ins_id=%d;ins_name=%s", 177 | ins.Stage, ins.ID, ins.Name) 178 | ins.logFailMsg(STAGEFAILED, errMsg, t) 179 | return 180 | } 181 | 182 | if len(childes) != 2 { 183 | errMsg := fmt.Sprintf("action=start stage;do=check xgroup childs length;stage=%d;ins_id=%d;ins_name=%s", 184 | ins.Stage, ins.ID, ins.Name) 185 | ins.logFailMsg(STAGEFAILED, errMsg, t) 186 | return 187 | } 188 | 189 | var tSuccess Task 190 | var tFailed Task 191 | for _, v := range childes { 192 | if v.SeqNum == 1 { 193 | tSuccess = v 194 | tSuccess.ParentXGROUP = 1 195 | } 196 | if v.SeqNum == 2 { 197 | tFailed = v 198 | tFailed.ParentXGROUP = 1 199 | } 200 | } 201 | 202 | // choose one to run 203 | if rst == STAGESUCCESS { 204 | err := tSuccess.Run(ins) 205 | if err != nil { 206 | errMsg := fmt.Sprintf("action=start stage;task_id=%d;task_name=%s;do=run 1st task;ins_id=%d;ins_name=%s;err=%s", 207 | tSuccess.ID, tSuccess.Name, ins.ID, ins.Name, err.Error()) 208 | ins.logFailMsg(STAGEFAILED, errMsg, t) 209 | } 210 | return 211 | } 212 | if rst == STAGEFAILED { 213 | err := tFailed.Run(ins) 214 | if err != nil { 215 | errMsg := fmt.Sprintf("action=start stage;task_id=%d;task_name=%s;do=run 2nd task;ins_id=%d;ins_name=%s;err=%s", 216 | tSuccess.ID, tSuccess.Name, ins.ID, ins.Name, err.Error()) 217 | ins.logFailMsg(STAGEFAILED, errMsg, t) 218 | return 219 | } 220 | } 221 | 222 | default: 223 | ins.startBasicTask(t) 224 | } 225 | } 226 | 227 | func (ins *TaskInstance) startBasicTask(tk *Task) { 228 | var bt basicTask 229 | args, _ := ins.GetAllArgs() 230 | switch tk.TkType { 231 | case TASKCMD: 232 | tc := &TaskCmd{} 233 | tc.TaskID = ins.TaskID 234 | tc.Args = args 235 | bt = tc 236 | case TASKSCRIPT: 237 | ts := &TaskScript{} 238 | ts.TaskID = ins.TaskID 239 | ts.Args = args 240 | bt = ts 241 | case SYNCFILE: 242 | tsf := &TaskSyncFile{} 243 | tsf.TaskID = ins.TaskID 244 | bt = tsf 245 | default: 246 | return 247 | } 248 | 249 | // start task and block until task finish 250 | err := bt.QueryByTaskID() 251 | if err != nil { 252 | errMsg := fmt.Sprintf("action=start stage;do=query cmd task;ins_id=%d;ins_name=%s;task_id=%d;err=%s", 253 | ins.ID, ins.Name, ins.TaskID, err.Error()) 254 | ins.logFailMsg(STAGEFAILED, errMsg, tk) 255 | return 256 | } 257 | agents, _ := ins.GetAllInsIPs() 258 | err = bt.start(&agents) 259 | if err != nil { 260 | errMsg := fmt.Sprintf("action=start stage;do=start cmd task;ins_id=%d;ins_name=%s;task_id=%d;err=%s", 261 | ins.ID, ins.Name, ins.TaskID, err.Error()) 262 | ins.logFailMsg(STAGEFAILED, errMsg, tk) 263 | return 264 | } 265 | 266 | ins.StageFinish(tk) 267 | } 268 | 269 | // getPreviousInsRst get the status of previous task 270 | func (ins *TaskInstance) getPreviousInsRst(t *Task) (StageStatus, error) { 271 | allStages, err := ins.getAllStagesOfBrotherTask(t) 272 | if err != nil { 273 | return 0, err 274 | } 275 | 276 | rst := STAGESUCCESS 277 | for _, v := range allStages { 278 | if v.Status != STAGESUCCESS { 279 | rst = STAGEFAILED 280 | break 281 | } 282 | } 283 | return rst, nil 284 | } 285 | 286 | func (ins *TaskInstance) checkContinueCondition(t *Task) (bool, error) { 287 | // no continue task be set 288 | if t.ContinueByTask == 0 { 289 | return true, nil 290 | } 291 | 292 | allBrothers, err := ins.GetBrotherInsByTaskID(t.ContinueByTask) 293 | if err != nil { 294 | return false, err 295 | } 296 | for _, v := range allBrothers { 297 | if v.Status != t.ContinueRst { 298 | return false, errors.New("mismatched continue result") 299 | } 300 | } 301 | 302 | return true, nil 303 | } 304 | 305 | func (ins *TaskInstance) getAllStagesOfBrotherTask(t *Task) (ai []TaskInstance, err error) { 306 | tb, err := t.GetOldBrotherTask() 307 | if err != nil { 308 | return 309 | } 310 | return ins.GetAllStagesOfTask(tb.ID) 311 | } 312 | 313 | // StageFinish: when stage finished, it will be called 314 | func (ins *TaskInstance) StageFinish(t *Task) { 315 | childIns, _ := ins.GetAllChildIns() 316 | 317 | // get the status 318 | insStatus := STAGESUCCESS 319 | for _, v := range childIns { 320 | if v.Status == STAGEFAILED { 321 | insStatus = STAGEFAILED 322 | break 323 | } 324 | } 325 | 326 | // save status to db 327 | if ins.Status < STAGEFAILED { 328 | var err error 329 | //var callback bool 330 | logger.TaskLog.Debugf(">>> ins: %d, update status in finish stage: %d\n", 331 | ins.ID, insStatus) 332 | err = ins.Update([]string{"status", "end_at"}, insStatus, time.Now()) 333 | if err != nil { 334 | logger.TaskLog.Errorf("action=start stage;do=update success;ins_id=%d;ins_name=%s;err=%s", 335 | ins.ID, ins.Name, err.Error()) 336 | return 337 | } 338 | } 339 | 340 | if insStatus == STAGEFAILED && ins.ContinueOnFail != 1 { 341 | ins.CallbackParentStage() 342 | return 343 | } 344 | 345 | // start next stage 346 | if !common.StepPause { 347 | runNextIns := ins.RunNextStage(t) 348 | if !runNextIns { 349 | return 350 | } 351 | } 352 | 353 | // if task in a vgroup task, return 354 | if ins.ParentIsV == 1 { 355 | return 356 | } 357 | 358 | // if it's a task root, exit, because all stage finished. 359 | if t.TkType == TASKROOT { 360 | logger.TaskLog.Debugf("*** ins: %d, task id: %d, task is root, return.\n", 361 | ins.ID, t.ID) 362 | return 363 | } 364 | 365 | // if no next stage, call next task 366 | parentIns, _ := GetInstanceByID(ins.ParentInsID) 367 | 368 | // if parent instance is a xgroup task, it can only call finish to callback parent 369 | //if ins.ParentIsX != 1 { 370 | //} 371 | continueOn := ins.RunNextTask(t, &parentIns) 372 | if !continueOn { 373 | return 374 | } 375 | 376 | logger.TaskLog.Debugf("*** ins: %d, callback parent 1: %d\n", ins.ID, parentIns.ID) 377 | 378 | // call parent instance stage finish 379 | parentTk, _ := GetTaskByID(parentIns.TaskID) 380 | parentIns.StageFinish(&parentTk) 381 | return 382 | } 383 | 384 | func (ins *TaskInstance) RunNextTask(t *Task, parentIns *TaskInstance) (continueOn bool) { 385 | nextTk, _ := t.GetNextTask() 386 | nextTkL := len(nextTk) 387 | if nextTkL == 1 { 388 | logger.TaskLog.Debugf("*** ins: %d, next task: %d\n", ins.ID, nextTk[0].ID) 389 | err := nextTk[0].Run(parentIns) 390 | if err != nil { 391 | logger.TaskLog.Errorf("action=stage finish;do=start task;task_id=%d;task_name=%s;ins_id=%d;ins_name=%s;err=%s;", 392 | nextTk[0].ID, nextTk[0].Name, ins.ID, ins.Name, err.Error()) 393 | return false 394 | } 395 | return false 396 | } 397 | if nextTkL > 1 { 398 | logger.TaskLog.Errorf("action=stage finish;do=start task;ins_id=%d;ins_name=%s;err=more task found", 399 | ins.ID, ins.Name) 400 | return false 401 | } 402 | return true 403 | } 404 | 405 | func (ins *TaskInstance) RunNextStage(t *Task) (nextTask bool) { 406 | nextIns, _ := ins.GetNextStageInstance() 407 | nextInsL := len(nextIns) 408 | fmt.Printf("next ins length: %d\n", nextInsL) 409 | if nextInsL == 1 { 410 | if nextIns[0].Status >= STAGERUNNING { 411 | logger.TaskLog.Debugf("*** ins: %d, next ins: %d, next ins is running, return\n", 412 | ins.ID, nextIns[0].ID) 413 | return 414 | } 415 | logger.TaskLog.Debugf("*** ins: %d, next ins: %d\n", ins.ID, nextIns[0].ID) 416 | 417 | // stage not auto start but step pause is false set by config file. 418 | nextIns[0].StartStage(t) 419 | logger.TaskLog.Infof("action=run next stage;task_name=%s;task_id=%d;ins=%d;next_ins=%d", 420 | t.Name, t.ID, ins.ID, nextIns[0].ID) 421 | 422 | return false 423 | } 424 | if nextInsL > 1 { 425 | logger.TaskLog.Errorf("action=stage finish;ins_id=%d;ins_name=%s;err=there are %d stage for seq %d", 426 | ins.ID, ins.Name, nextInsL, ins.StageSeq+1) 427 | return false 428 | } 429 | return true 430 | } 431 | 432 | func (ins *TaskInstance) CallbackParentStage() { 433 | parentIns, _ := GetInstanceByID(ins.ParentInsID) 434 | parentTk, _ := GetTaskByID(parentIns.TaskID) 435 | logger.TaskLog.Debugf("*** ins: %d, callback parent 0: %d\n", ins.ID, parentIns.ID) 436 | if parentIns.ID == 0 { 437 | return 438 | } 439 | parentIns.StageFinish(&parentTk) 440 | } 441 | -------------------------------------------------------------------------------- /service/task/ins_db.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "errors" 5 | "opsHeart_server/db" 6 | ) 7 | 8 | func (ins *TaskInstance) Create() error { 9 | return db.DB.Model(&TaskInstance{}).Create(ins).Error 10 | } 11 | 12 | func (ins *TaskInstance) Update(fields []string, values ...interface{}) error { 13 | if len(fields) != len(values) { 14 | return errors.New("length of fields not equal to length of values") 15 | } 16 | m := make(map[string]interface{}) 17 | for i, f := range fields { 18 | m[f] = values[i] 19 | } 20 | return db.DB.Model(ins).Updates(m).Error 21 | } 22 | 23 | func (ins *TaskInstance) UpdateAndCheckCallbackVGROUP(fields []string, values ...interface{}) (c bool, err error) { 24 | if len(fields) != len(values) { 25 | err = errors.New("length of fields not equal to length of values") 26 | return 27 | } 28 | m := make(map[string]interface{}) 29 | for i, f := range fields { 30 | m[f] = values[i] 31 | } 32 | 33 | tx := db.DB.Begin() 34 | 35 | err = tx.Model(ins).Updates(m).Error 36 | if err != nil { 37 | return 38 | } 39 | 40 | var allBrother []TaskInstance 41 | err = tx.Model(ins).Where("parent_ins_id = ?", ins.ParentInsID). 42 | Find(&allBrother).Error 43 | if err != nil { 44 | return 45 | } 46 | 47 | tx.Commit() 48 | 49 | if len(allBrother) < ins.BrotherNum { 50 | return 51 | } 52 | 53 | last := 0 54 | for _, v := range allBrother { 55 | //fmt.Printf("continue: %d, status: %d\n", v.ContinueOnFail, v.Status) 56 | if v.Status < STAGEFAILED { 57 | last++ 58 | } 59 | } 60 | 61 | c = last == 0 62 | return 63 | } 64 | 65 | func (ins *TaskInstance) GetAllAgents() (sa []TaskStageAgent, err error) { 66 | err = db.DB.Model(ins).Related(&sa, "TaskStageAgents").Error 67 | return 68 | } 69 | 70 | func (ins *TaskInstance) GetAllStagesOfTask(tID uint) (ai []TaskInstance, err error) { 71 | err = db.DB.Model(ins).Where("parent_ins_id = ? and task_id = ?", 72 | ins.ParentInsID, tID).Find(&ai).Error 73 | return 74 | } 75 | 76 | func (ins *TaskInstance) GetBrotherInsByTaskID(i uint) (all []TaskInstance, err error) { 77 | err = db.DB.Model(ins).Where("parent_ins_id = ? and task_id = ?", ins.ParentInsID, i). 78 | Find(&all).Error 79 | return 80 | } 81 | 82 | func (ins *TaskInstance) GetNextStageInstance() (ti []TaskInstance, err error) { 83 | err = db.DB.Model(ins). 84 | Where("name = ? and parent_ins_id = ? and task_id = ?", 85 | ins.Name, ins.ParentInsID, ins.TaskID). 86 | Find(&ti, "stage_seq = ?", ins.StageSeq+1).Error 87 | return 88 | } 89 | 90 | func (ins *TaskInstance) GetAllChildIns() (all []TaskInstance, err error) { 91 | err = db.DB.Model(ins).Where("parent_ins_id = ?", ins.ID).Find(&all).Error 92 | return 93 | } 94 | 95 | func (ins *TaskInstance) GetAllChildInsWithUnfinished() (all []TaskInstance, err error) { 96 | err = db.DB.Model(ins).Where("parent_ins_id = ?", ins.ID). 97 | Where("status < ?", STAGEFAILED). 98 | Find(&all).Error 99 | return 100 | } 101 | 102 | func (ins *TaskInstance) GetAllArgs() (args []InsArg, err error) { 103 | err = db.DB.Model(&InsArg{}).Where("ins_name = ? and task_id = ?", ins.Name, ins.TaskID). 104 | Find(&args).Error 105 | return 106 | } 107 | 108 | func (ia *InsArg) Create() error { 109 | return db.DB.Model(ia).Create(ia).Error 110 | } 111 | 112 | func GetAllInsByNameAndTaskID(name string, i uint) (allIns []TaskInstance, err error) { 113 | err = db.DB.Model(&TaskInstance{}).Where("name = ? and task_id = ?", name, i). 114 | Find(&allIns).Error 115 | return 116 | } 117 | 118 | func GetInstanceByID(id uint) (p TaskInstance, err error) { 119 | err = db.DB.Model(&TaskInstance{}). 120 | Where("id = ?", id).First(&p).Error 121 | return 122 | } 123 | 124 | func GetAllInsByName(name string) (all []TaskInstance, err error) { 125 | err = db.DB.Model(&TaskInstance{}).Find(&all, "name = ?", name).Error 126 | return 127 | } 128 | -------------------------------------------------------------------------------- /service/task/ins_db_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "opsHeart_server/conf" 5 | "opsHeart_server/db" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestInsArg_Create(t *testing.T) { 11 | err := conf.InitCfg() 12 | if err != nil { 13 | t.Fatalf("init conf err: %s", err.Error()) 14 | } 15 | db.InitDB() 16 | db.DB.AutoMigrate(&InsArg{}) 17 | 18 | ia := InsArg{ 19 | InsName: "a test ins name 999", 20 | TaskID: 99999999, 21 | ArgName: "TestArgName999", 22 | ArgType: COMMONSTR, 23 | } 24 | 25 | err = ia.Create() 26 | if err != nil { 27 | t.Fatalf("fail: %s", err) 28 | } 29 | t.Log("success!") 30 | } 31 | 32 | func TestTaskInstance_Create(t *testing.T) { 33 | err := conf.InitCfg() 34 | if err != nil { 35 | t.Fatalf("init conf err: %s", err.Error()) 36 | } 37 | db.InitDB() 38 | db.DB.AutoMigrate(&TaskInstance{}) 39 | 40 | ins1 := TaskInstance{ 41 | Name: NewInsName(12), 42 | TaskID: 12, 43 | StageSeq: 1, 44 | StageAgents: "12-100-vUNq", 45 | StartAt: time.Now(), 46 | } 47 | err = ins1.Create() 48 | if err != nil { 49 | t.Fatalf("test create instance fail: %s", err.Error()) 50 | } 51 | t.Logf("success: %v", ins1) 52 | 53 | ins2 := TaskInstance{ 54 | Name: ins1.Name, 55 | TaskID: 2, 56 | StageSeq: 1, 57 | StageAgents: "12-100-vUNq", 58 | StartAt: time.Now(), 59 | } 60 | ins2.ParentInsID = ins1.ID 61 | err = ins2.Create() 62 | if err != nil { 63 | t.Fatalf("test create instance fail: %s", err.Error()) 64 | } 65 | t.Logf("success: %v", ins2) 66 | 67 | ins3 := TaskInstance{ 68 | Name: ins1.Name, 69 | TaskID: 3, 70 | StageSeq: 1, 71 | StageAgents: "12-100-vUNq", 72 | StartAt: time.Now(), 73 | } 74 | ins3.ParentInsID = ins1.ID 75 | err = ins3.Create() 76 | if err != nil { 77 | t.Fatalf("test create instance fail: %s", err.Error()) 78 | } 79 | t.Logf("success: %v", ins3) 80 | } 81 | 82 | func TestTaskInstance_Update(t *testing.T) { 83 | err := conf.InitCfg() 84 | if err != nil { 85 | t.Fatalf("init conf err: %s", err.Error()) 86 | } 87 | db.InitDB() 88 | db.DB.AutoMigrate(&TaskInstance{}) 89 | 90 | ins := TaskInstance{} 91 | ins.ID = 1 92 | 93 | err = ins.Update([]string{"status", "start_at"}, STAGERUNNING, time.Now()) 94 | if err != nil { 95 | t.Fatalf("test update fail: %s", err.Error()) 96 | } 97 | 98 | t.Logf("success: %v", ins) 99 | } 100 | 101 | func TestGetAllInsByNameAndTaskID(t *testing.T) { 102 | err := conf.InitCfg() 103 | if err != nil { 104 | t.Fatalf("init conf err: %s", err.Error()) 105 | } 106 | db.InitDB() 107 | db.DB.AutoMigrate(&TaskInstance{}) 108 | 109 | all, err := GetAllInsByNameAndTaskID("12-20206230516-efRX", 2) 110 | if err != nil { 111 | t.Fatalf("get all instance fail: %s", err.Error()) 112 | } 113 | t.Logf("test success: %v", all) 114 | } 115 | 116 | func TestTaskInstance_GetNextInstance(t *testing.T) { 117 | err := conf.InitCfg() 118 | if err != nil { 119 | t.Fatalf("init conf err: %s", err.Error()) 120 | } 121 | db.InitDB() 122 | db.DB.AutoMigrate(&TaskInstance{}) 123 | 124 | ins := TaskInstance{ 125 | ParentInsID: 1, 126 | TaskID: 3, 127 | StageSeq: 0, 128 | } 129 | 130 | next, err := ins.GetNextStageInstance() 131 | if err != nil { 132 | t.Fatalf("test fail: %s", err.Error()) 133 | } 134 | 135 | //t.Logf("test success: %v", next) 136 | t.Logf("test next len: %d", len(next)) 137 | for _, v := range next { 138 | t.Logf("test success: %v", v) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /service/task/ins_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "opsHeart_server/conf" 5 | "opsHeart_server/db" 6 | "testing" 7 | ) 8 | 9 | func TestTaskInstance_GetIPsByStageName(t *testing.T) { 10 | err := conf.InitCfg() 11 | if err != nil { 12 | t.Fatalf("init conf err: %s", err.Error()) 13 | } 14 | db.InitDB() 15 | ins := TaskInstance{} 16 | db.DB.Model(&TaskInstance{}).Where("stage_agents = ?", "12-100-vUNq"). 17 | First(&ins) 18 | t.Logf("ins id: %d, ins stage_agents: %s", ins.ID, ins.StageAgents) 19 | ips, err := ins.GetAllInsIPs() 20 | if err != nil { 21 | t.Fatalf("test get ips fail: %s", err.Error()) 22 | } 23 | t.Logf("success: %v", ips) 24 | } 25 | 26 | func TestTaskInstance_GetAllInsIPsByPercent(t *testing.T) { 27 | err := conf.InitCfg() 28 | if err != nil { 29 | t.Fatalf("init conf err: %s", err.Error()) 30 | } 31 | db.InitDB() 32 | ins := TaskInstance{} 33 | db.DB.Model(&TaskInstance{}).Where("stage_agents = ?", "12-100-vUNq"). 34 | First(&ins) 35 | t.Logf("ins id: %d, ins stage_agents: %s", ins.ID, ins.StageAgents) 36 | //ips, err := ins.GetAllInsIPsByPercent(2, StageNumber) 37 | ips, err := ins.GetAllInsIPsByPercentOrNum(50, StagePercent, DoSplit) 38 | if err != nil { 39 | t.Fatalf("test get ips fail: %s", err.Error()) 40 | } 41 | t.Logf("success: %v", ips) 42 | } 43 | 44 | func TestTaskInstance_GetAllStagesOfBrotherTask(t *testing.T) { 45 | err := conf.InitCfg() 46 | if err != nil { 47 | t.Fatalf("init conf err: %s", err.Error()) 48 | } 49 | db.InitDB() 50 | db.DB.AutoMigrate(&TaskInstance{}) 51 | 52 | ins := TaskInstance{} 53 | ins.TaskID = 3 54 | ins.ParentInsID = 1 55 | 56 | tk := Task{} 57 | tk.ParentTaskID = 1 58 | tk.SeqNum = 2 59 | 60 | allBt, err := ins.getAllStagesOfBrotherTask(&tk) 61 | if err != nil { 62 | t.Fatalf("test failed: %s", err.Error()) 63 | } 64 | 65 | t.Logf("test success: %v", allBt) 66 | } 67 | -------------------------------------------------------------------------------- /service/task/models.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "os" 6 | "time" 7 | ) 8 | 9 | type Status int 10 | 11 | const ( 12 | StatusAvaliabled Status = 1 13 | StatusInvilid Status = 0 14 | ) 15 | 16 | type Rst int 17 | 18 | const ( 19 | RstSuccess Rst = 1 20 | RstFailed Rst = 0 21 | ) 22 | 23 | type AgentCollectType int 24 | 25 | const ( 26 | //CollInherit AgentCollectType = 2 27 | CollInheritPercent AgentCollectType = 0 28 | CollInheritNum AgentCollectType = 1 29 | CollName AgentCollectType = 2 30 | CollList AgentCollectType = 3 31 | ) 32 | 33 | type StType int 34 | 35 | const ( 36 | StageNumber StType = 1 37 | StagePercent StType = 0 38 | ) 39 | 40 | type TType int 41 | 42 | const ( 43 | TASKROOT TType = 0 44 | HGROUP TType = 1 45 | VGROUP TType = 2 46 | XGROUP TType = 3 47 | TASKCMD TType = 4 48 | TASKSCRIPT TType = 5 49 | SYNCFILE TType = 6 50 | ) 51 | 52 | type splitColl uint 53 | 54 | const ( 55 | DoSplit splitColl = 1 56 | DoNotSplit splitColl = 0 57 | ) 58 | 59 | // args type 60 | type ArgsType uint 61 | 62 | const ( 63 | COMMONSTR ArgsType = 0 // common string argument 64 | AGENTFACT ArgsType = 1 // a agent fact key which map to a value 65 | AGENTTAG ArgsType = 2 // a agent tag key which map to a value 66 | //ROOTARGS ArgsType = 3 // inherit arguments from task root 67 | ) 68 | 69 | type TaskArg struct { 70 | gorm.Model 71 | TaskID uint `json:"task_id" gorm:"index"` 72 | TaskName string `json:"task_name" gorm:"index;UNIQUE_INDEX:task_name_arg"` 73 | ArgName string `json:"arg_name" gorm:"UNIQUE_INDEX:task_name_arg"` 74 | ArgType ArgsType `json:"arg_type"` 75 | ArgValue string `json:"arg_value"` // default value, it can be reset when start run 76 | } 77 | 78 | type Task struct { 79 | gorm.Model 80 | Name string `json:"name" gorm:"size:100;index"` 81 | TkType TType `json:"tk_type" gorm:"default:0"` 82 | ParentTaskID uint `json:"parent_task_id" gorm:"default:0;index"` 83 | SeqNum uint `json:"seq_num" gorm:"default:1"` 84 | ContinueByTask uint `json:"continue_by_task" gorm:"default:0"` 85 | ContinueRst StageStatus `json:"status" gorm:"default:5"` 86 | CollectionType AgentCollectType `json:"collection_type" gorm:"default:0"` 87 | CollectionValue string `json:"collection_value" gorm:"type:MEDIUMTEXT;default:100"` 88 | SplitParent splitColl `json:"child_split_coll" gorm:"default:0"` 89 | StageType StType `json:"stage_type" gorm:"default:0"` // default: percent 90 | Stages string `json:"stages" gorm:"size:500;default:'[100]'"` 91 | CreateBy string `json:"create_by" gorm:"size:50"` 92 | Desc string `json:"desc" gorm:"size:500"` 93 | Status Status `json:"status" gorm:"default:1"` 94 | Comments string `json:"comments" gorm:"size:500"` 95 | TaskArgs []TaskArg `json:"task_args"` 96 | ContinueOnFail uint `json:"continue_on_fail" gorm:"default:0"` // 0: no, 1: yes. 97 | ParentVGROUP uint `gorm:"-"` // 0: no, 1: yes. 98 | ParentXGROUP uint `gorm:"-"` // 0: no, 1: yes. 99 | ChildesNum int `gorm:"-"` 100 | } 101 | 102 | // task type cmd 103 | type TaskCmd struct { 104 | gorm.Model 105 | TaskID uint `json:"task_id" gorm:"not null;index"` 106 | Cmd string `json:"cmd" gorm:"not null;size:50"` 107 | Opt string `json:"opt" gorm:"size:1000"` 108 | Timeout uint `json:"timeout"` 109 | Args []InsArg `gorm:"-"` 110 | } 111 | 112 | // task type script 113 | type TaskScript struct { 114 | gorm.Model 115 | TaskID uint `json:"task_id" gorm:"not null;index"` 116 | Shell string `json:"shell" gorm:"size:100"` 117 | Name string `json:"name" gorm:"size:100"` 118 | RunAs string `json:"run_as" gorm:"size:50"` 119 | Timeout uint `json:"timeout"` 120 | Args []InsArg `gorm:"-"` 121 | } 122 | 123 | // task type sync file 124 | type TaskSyncFile struct { 125 | gorm.Model 126 | TaskID uint `json:"task_id" gorm:"not null;index"` 127 | Src string `json:"src"` 128 | Dst string `json:"dst"` 129 | User string `json:"user"` 130 | Group string `json:"group"` 131 | Perm os.FileMode `json:"perm"` 132 | } 133 | 134 | type StageStatus int 135 | 136 | const ( 137 | // start: 0-19 138 | STAGEREADY StageStatus = 0 139 | STAGENEEDCONFIRM StageStatus = 1 140 | 141 | // middle: 20-39 142 | STAGERUNNING StageStatus = 20 143 | STAGEPAUSED StageStatus = 21 144 | STAGESTOPPED StageStatus = 22 145 | 146 | // end: 40-59 147 | STAGEFAILED StageStatus = 40 148 | STAGESUCCESS StageStatus = 41 149 | ) 150 | 151 | type TaskInstance struct { 152 | gorm.Model 153 | Name string `json:"name" gorm:"index;size:50;index:name_parent_task"` 154 | ParentInsID uint `json:"parent_ins_id" gorm:"index;default:0;index:parent_task;index:name_parent_task"` 155 | TaskID uint `json:"task_id" gorm:"index;index:parent_task;index:name_parent_task"` 156 | Stage int `json:"stage" gorm:"default:100"` 157 | StageSeq uint `json:"stage_seq"` 158 | StageAgents string `json:"stage_agents" gorm:"size:50"` 159 | StartAt time.Time `json:"start_at"` 160 | EndAt time.Time `json:"end_at"` 161 | RunBy string `json:"run_by" gorm:"size:50"` 162 | Status StageStatus `json:"status" gorm:"default:0"` 163 | InsMsg string `json:"ins_msg" gorm:"size:500"` 164 | ParentIsV uint `json:"parent_is_v" gorm:"default:0"` // 0: no, 1: yes. 165 | ParentIsX uint `json:"parent_is_x" gorm:"default:0"` // 0: no, 1: yes. 166 | ContinueOnFail uint `json:"continue_on_fail" gorm:"default:0"` // 0: no, 1: yes. 167 | BrotherNum int `json:"brother_num" gorm:"default:0"` 168 | CallbackVGROUP bool `gorm:"-"` 169 | TaskStageAgents []TaskStageAgent `json:"task_stage_agents" gorm:"foreignkey:stageName;association_foreignkey:stageAgents"` 170 | TaskLogs []TaskLog 171 | //ParentIsH uint `json:"parent_is_h" gorm:"default:0"` // 0: no, 1: yes. 172 | } 173 | 174 | type InsArg struct { 175 | gorm.Model 176 | InsName string `json:"ins_name" gorm:"index:ins_task_arg"` 177 | TaskID uint `json:"task_id" gorm:"index:ins_task_arg"` 178 | ArgName string `json:"arg_name"` 179 | ArgType ArgsType `json:"arg_type"` 180 | ArgValue string `json:"arg_value"` 181 | } 182 | 183 | type ChildUsed int 184 | 185 | const ( 186 | IsUsed = 1 187 | NotUsed = 0 188 | ) 189 | 190 | // task instance stage ips 191 | type TaskStageAgent struct { 192 | gorm.Model 193 | StageName string `json:"stage_name" gorm:"size:50"` 194 | IP string `json:"ip" gorm:"size:20"` 195 | ChildUse ChildUsed `json:"child_use" gorm:"default:0"` // default 0 196 | } 197 | 198 | // task run logs 199 | type TaskLog struct { 200 | // id, ins id (index), task id (index), agent, status,duration, msg 201 | gorm.Model 202 | TaskInstanceID uint 203 | } 204 | 205 | type basicTask interface { 206 | QueryByTaskID() error 207 | start(agents *[]string) error 208 | } 209 | -------------------------------------------------------------------------------- /service/task/script.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "opsHeart_server/logger" 7 | "strings" 8 | ) 9 | 10 | func (s *TaskScript) start(agents *[]string) error { 11 | if strings.Contains(s.Shell, "zsh") { 12 | return errors.New("test zsh") 13 | } 14 | logger.TaskLog.Infof("script run, shell:%s, name:%s.\n", s.Shell, s.Name) 15 | for _, v := range *agents { 16 | fmt.Printf("\t%s\n", v) 17 | for _, a := range s.Args { 18 | fmt.Printf("\targ name: %s, arg type: %d, arg value: %s\n", 19 | a.ArgName, a.ArgType, a.ArgValue) 20 | } 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /service/task/script_db.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import "opsHeart_server/db" 4 | 5 | func (s *TaskScript) QueryByTaskID() (err error) { 6 | err = db.DB.Model(s).Where("task_id = ?", s.TaskID).First(s).Error 7 | return 8 | } 9 | 10 | func (s *TaskScript) Create() (err error) { 11 | err = db.DB.Create(s).Error 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /service/task/stage_agent.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import "opsHeart_server/db" 4 | 5 | func (sa *TaskStageAgent) Update(f string, v interface{}) error { 6 | return db.DB.Model(&TaskStageAgent{}). 7 | Where("id = ?", sa.ID).Update(f, v).Error 8 | } 9 | 10 | func (sa *TaskStageAgent) Create() error { 11 | return db.DB.Model(&TaskStageAgent{}).Create(sa).Error 12 | } 13 | -------------------------------------------------------------------------------- /service/task/stage_agent_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "opsHeart_server/conf" 5 | "opsHeart_server/db" 6 | "opsHeart_server/utils/rand_str" 7 | "testing" 8 | ) 9 | 10 | func TestTaskStageAgent_Create(t *testing.T) { 11 | err := conf.InitCfg() 12 | if err != nil { 13 | t.Fatalf("init conf err: %s", err.Error()) 14 | } 15 | db.InitDB() 16 | db.DB.AutoMigrate(&TaskStageAgent{}) 17 | n := 10 18 | sn := NewStageName(12, 100) 19 | for i := 0; i < n; i++ { 20 | sa := TaskStageAgent{ 21 | StageName: sn, 22 | IP: rand_str.GetStr(4), 23 | } 24 | err := sa.Create() 25 | if err != nil { 26 | t.Fatalf("create sa: %v, err: %s, n: %d", sa, err.Error(), n) 27 | } 28 | } 29 | } 30 | 31 | func TestTaskStageAgent_Update(t *testing.T) { 32 | err := conf.InitCfg() 33 | if err != nil { 34 | t.Fatalf("init conf err: %s", err.Error()) 35 | } 36 | db.InitDB() 37 | db.DB.AutoMigrate(&TaskStageAgent{}) 38 | 39 | var allSa []TaskStageAgent 40 | err = db.DB.Model(&TaskStageAgent{}).Where("child_use = ?", IsUsed).Find(&allSa).Error 41 | if err != nil { 42 | t.Fatalf("query all sa err: %s", err.Error()) 43 | } 44 | 45 | for _, v := range allSa { 46 | err := v.Update("child_use", NotUsed) 47 | if err != nil { 48 | t.Fatalf("update sa err: %s", err) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /service/task/sync_file.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | func (sf *TaskSyncFile) start(agents *[]string) error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /service/task/sync_file_db.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import "opsHeart_server/db" 4 | 5 | func (sf *TaskSyncFile) QueryByTaskID() (err error) { 6 | err = db.DB.Model(sf).Where("task_id = ?", sf.TaskID).First(sf).Error 7 | return 8 | } 9 | 10 | func (sf *TaskSyncFile) Create() (err error) { 11 | err = db.DB.Create(sf).Error 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /service/task/task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "math" 8 | "opsHeart_server/logger" 9 | "opsHeart_server/service/collection" 10 | "opsHeart_server/utils/rand_str" 11 | "strconv" 12 | "time" 13 | ) 14 | 15 | type StageAgentsMap struct { 16 | stage int 17 | agents []string 18 | } 19 | 20 | // NewInsName create a instance name by task id 21 | func NewInsName(taskID uint) string { 22 | randStr := rand_str.GetStr(4) 23 | tm := time.Now() 24 | InsName := fmt.Sprintf("%d-%d%d%d%d%d%d-%s", 25 | taskID, tm.Year(), tm.Month(), tm.Day(), 26 | tm.Hour(), tm.Minute(), tm.Second(), randStr) 27 | return InsName 28 | } 29 | 30 | // NewStageName create a stage name by task id 31 | func NewStageName(taskID uint, stg int) string { 32 | return fmt.Sprintf("%d-%d-%s", taskID, stg, rand_str.GetStr(4)) 33 | } 34 | 35 | // getCollection get all target agents IPs by collection definition. 36 | func (t *Task) getCollection(parentIns *TaskInstance) ([]string, error) { 37 | ct := t.CollectionType 38 | cv := t.CollectionValue 39 | 40 | switch ct { 41 | case CollList: 42 | var allIPs []string 43 | err := json.Unmarshal([]byte(cv), &allIPs) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return allIPs, nil 48 | case CollName: 49 | // get collection by agent collection rule 50 | ac, err := collection.QueryCollByName(cv) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return ac.GetAllIPs() 55 | case CollInheritPercent: 56 | n, err := strconv.Atoi(cv) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return parentIns.GetAllInsIPsByPercentOrNum(n, StagePercent, t.SplitParent) 61 | case CollInheritNum: 62 | n, err := strconv.Atoi(cv) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return parentIns.GetAllInsIPsByPercentOrNum(n, StageNumber, t.SplitParent) 67 | } 68 | return nil, nil 69 | } 70 | 71 | // SplitIPsList split IPs list to several parts by stage list. 72 | // return map[stage_num][]IP 73 | func SplitIPsList(st StType, sNum []int, ips []string) (sam []StageAgentsMap, err error) { 74 | ipsL := len(ips) 75 | sNumL := len(sNum) 76 | if ipsL == 0 { 77 | return nil, errors.New("ip list 0 length") 78 | } 79 | if sNumL == 0 { 80 | return nil, errors.New("stage list 0 length") 81 | } 82 | 83 | sNewNum := make([]int, len(sNum)) 84 | 85 | if st == StagePercent { 86 | for i, v := range sNum { 87 | f := float64(v) / 100 88 | n := int(math.Ceil(f * float64(len(ips)))) 89 | if n >= ipsL { 90 | sNewNum[i] = ipsL 91 | break 92 | } 93 | sNewNum[i] = n 94 | ipsL -= n 95 | } 96 | } 97 | 98 | if st == StageNumber { 99 | for i, v := range sNum { 100 | if v >= ipsL { 101 | sNewNum[i] = ipsL 102 | break 103 | } 104 | sNewNum[i] = v 105 | ipsL -= v 106 | } 107 | } 108 | 109 | startOffset := 0 110 | for i, v := range sNewNum { 111 | sa := StageAgentsMap{ 112 | stage: sNum[i], 113 | agents: ips[startOffset : startOffset+v], 114 | } 115 | sam = append(sam, sa) 116 | startOffset += v 117 | } 118 | 119 | return 120 | } 121 | 122 | // init instance arguments 123 | func (t *Task) InitInsArgs(argMap map[string]TaskArg, insName string) error { 124 | args, err := t.GetAllArgsByTaskName() 125 | if err != nil { 126 | return err 127 | } 128 | for _, v := range args { 129 | insArg := InsArg{ 130 | InsName: insName, 131 | TaskID: v.TaskID, 132 | ArgName: v.ArgName, 133 | ArgType: v.ArgType, 134 | ArgValue: v.ArgValue, 135 | } 136 | argV, ok := argMap[v.ArgName] 137 | if ok { 138 | insArg.ArgType = argV.ArgType 139 | insArg.ArgValue = argV.ArgValue 140 | } 141 | 142 | // ERROR IGNORED 143 | err := insArg.Create() 144 | if err != nil { 145 | return err 146 | } 147 | } 148 | return nil 149 | } 150 | 151 | // Run start a task 152 | // before Run, should run NewInsName to get a instance name 153 | func (t *Task) Run(parentIns *TaskInstance) error { 154 | st := t.StageType 155 | sg := t.Stages 156 | var stages []int 157 | err := json.Unmarshal([]byte(sg), &stages) 158 | if err != nil { 159 | return err 160 | } 161 | 162 | // get ips 163 | IPs, err := t.getCollection(parentIns) 164 | if err != nil { 165 | return err 166 | } 167 | 168 | // split IP list by stage list 169 | samList, err := SplitIPsList(st, stages, IPs) 170 | if err != nil { 171 | return err 172 | } 173 | 174 | // create stage name and save to stage agent data db 175 | var firstStageIns TaskInstance 176 | var stageSeqNum uint 177 | for _, v := range samList { 178 | // save stage agent ip to db 179 | stageRef := NewStageName(t.ID, v.stage) 180 | IPList := v.agents 181 | for _, ip := range IPList { 182 | sa := TaskStageAgent{ 183 | StageName: stageRef, 184 | IP: ip, 185 | } 186 | err := sa.Create() 187 | if err != nil { 188 | return err 189 | } 190 | } 191 | 192 | // save task stage ins data to db 193 | stageSeqNum++ 194 | ti := TaskInstance{ 195 | Name: parentIns.Name, 196 | ParentInsID: parentIns.ID, 197 | TaskID: t.ID, 198 | Stage: v.stage, 199 | StageSeq: stageSeqNum, 200 | StageAgents: stageRef, 201 | RunBy: "reserved", 202 | ParentIsX: t.ParentXGROUP, 203 | ParentIsV: t.ParentVGROUP, 204 | BrotherNum: t.ChildesNum, 205 | ContinueOnFail: t.ContinueOnFail, 206 | //IsVGROUP: parentIns.IsVGROUP, 207 | } 208 | 209 | err := ti.Create() 210 | if err != nil { 211 | return err 212 | } 213 | 214 | if stageSeqNum == 1 { 215 | firstStageIns = ti 216 | } 217 | } 218 | 219 | // start first stage 220 | logger.TaskLog.Debugf("*** task run: %d, first stage: %d\n", t.ID, firstStageIns.ID) 221 | 222 | go firstStageIns.StartStage(t) 223 | 224 | logger.TaskLog.Infof("action=run task;task_name=%s;task_id=%d;ins_name=%s;", 225 | t.Name, t.ID, parentIns.Name) 226 | 227 | return nil 228 | } 229 | -------------------------------------------------------------------------------- /service/task/task_db.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "errors" 5 | "opsHeart_server/db" 6 | ) 7 | 8 | // GetTheSeqChild get the seq-th child of task t 9 | func (t *Task) GetTheSeqChild(seq uint) ([]Task, error) { 10 | var childs []Task 11 | err := db.DB.Model(t).Where("parent_task_id = ?", t.ID). 12 | Find(&childs, "seq_num = ?", seq).Error 13 | return childs, err 14 | } 15 | 16 | func GetTaskByID(i uint) (t Task, err error) { 17 | db.DB.Model(t).First(&t, "id = ?", i) 18 | return 19 | } 20 | 21 | func (t *Task) GetOldBrotherTask() (bt Task, err error) { 22 | if t.SeqNum == 1 { 23 | err = errors.New("the seq 1 invalid for finding previous brother task") 24 | return 25 | } 26 | err = db.DB.Model(t).Where("parent_task_id = ?", t.ParentTaskID). 27 | First(&bt, "seq_num = ?", t.SeqNum-1).Error 28 | return 29 | } 30 | 31 | func (t *Task) GetAllChildTask() (allChild []Task, err error) { 32 | err = db.DB.Model(t).Where("parent_task_id = ?", t.ID). 33 | Find(&allChild).Error 34 | return 35 | } 36 | 37 | func (t *Task) CheckRootNameIsExist() bool { 38 | var tmp Task 39 | db.DB.Model(t).Where("name = ? and tk_type = ?", t.Name, 0).First(&tmp) 40 | return tmp.Name == t.Name 41 | } 42 | 43 | // Before create, if continue depending is set, should check the depended task is a brother node. 44 | func (t *Task) Create() error { 45 | if t.TkType == TASKROOT { 46 | if t.CheckRootNameIsExist() { 47 | return errors.New("root task has exist") 48 | } 49 | } 50 | return db.DB.Create(t).Error 51 | } 52 | 53 | func (t *Task) GetNextTask() (next []Task, err error) { 54 | err = db.DB.Model(t).Where("parent_task_id = ?", t.ParentTaskID). 55 | Find(&next, "seq_num = ?", t.SeqNum+1).Error 56 | return 57 | } 58 | 59 | func (t *Task) GetAllTaskArgs() (args []TaskArg, err error) { 60 | err = db.DB.Model(t).Related(&args, "TaskArgs").Error 61 | return 62 | } 63 | 64 | func (t *Task) GetAllArgsByTaskName() (args []TaskArg, err error) { 65 | err = db.DB.Model(&TaskArg{}).Find(&args, "task_name = ?", t.Name).Error 66 | return 67 | } 68 | 69 | // CheckNameIsExist check root task name is unique 70 | // for `root` task, task name must be unique 71 | func CheckNameIsExist(n string) (ok bool) { 72 | t := Task{} 73 | err := db.DB.Model(&t).Where("tk_type = 0").First(&t, "name = ?", n).Error 74 | if err != nil { 75 | return false 76 | } 77 | if t.Name == n { 78 | return true 79 | } 80 | return false 81 | } 82 | -------------------------------------------------------------------------------- /service/task/task_db_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "opsHeart_server/conf" 5 | "opsHeart_server/db" 6 | "testing" 7 | ) 8 | 9 | func TestTask_Create2(t *testing.T) { 10 | err := conf.InitCfg() 11 | if err != nil { 12 | t.Fatalf("init conf err: %s", err.Error()) 13 | } 14 | db.InitDB() 15 | db.DB.AutoMigrate(&Task{}) 16 | db.DB.AutoMigrate(&TaskArg{}) 17 | 18 | tk := Task{ 19 | Name: "test 02", 20 | TkType: TASKROOT, 21 | CollectionType: CollList, 22 | CollectionValue: "[\"A\",\"B\"]", 23 | CreateBy: "jacen", 24 | Desc: "just a test task", 25 | TaskArgs: []TaskArg{ 26 | { 27 | TaskName: "test 02", 28 | ArgName: "var1", 29 | }, 30 | { 31 | TaskName: "test 02", 32 | ArgName: "var2", 33 | }, 34 | }, 35 | } 36 | 37 | err = tk.Create() 38 | if err != nil { 39 | t.Fatalf("fail: %s", err.Error()) 40 | } 41 | t.Log("success!") 42 | } 43 | 44 | func TestTask_GetAllArgsPerTask(t *testing.T) { 45 | err := conf.InitCfg() 46 | if err != nil { 47 | t.Fatalf("init conf err: %s", err.Error()) 48 | } 49 | db.InitDB() 50 | db.DB.AutoMigrate(&Task{}) 51 | db.DB.AutoMigrate(&TaskArg{}) 52 | 53 | tk := Task{} 54 | tk.ID = 397 55 | 56 | args, err := tk.GetAllTaskArgs() 57 | if err != nil { 58 | t.Fatalf("fail: %s", err.Error()) 59 | } 60 | 61 | for _, v := range args { 62 | t.Log(v) 63 | } 64 | t.Log("success!") 65 | } 66 | 67 | func TestTask_GetAllArgsByTaskName(t *testing.T) { 68 | err := conf.InitCfg() 69 | if err != nil { 70 | t.Fatalf("init conf err: %s", err.Error()) 71 | } 72 | db.InitDB() 73 | db.DB.AutoMigrate(&Task{}) 74 | db.DB.AutoMigrate(&TaskArg{}) 75 | 76 | tk := Task{ 77 | Name: "test 02", 78 | } 79 | args, err := tk.GetAllArgsByTaskName() 80 | if err != nil { 81 | t.Fatalf("faile: %s", err.Error()) 82 | } 83 | 84 | for _, v := range args { 85 | t.Log(v) 86 | } 87 | t.Log("success!") 88 | } 89 | 90 | func TestTask_Create(t *testing.T) { 91 | err := conf.InitCfg() 92 | if err != nil { 93 | t.Fatalf("init conf err: %s", err.Error()) 94 | } 95 | db.InitDB() 96 | db.DB.AutoMigrate(&Task{}) 97 | 98 | tk := Task{ 99 | Name: "a test task 1", 100 | TkType: TASKROOT, 101 | CollectionType: CollList, 102 | CollectionValue: "[\"A\",\"B\"]", 103 | CreateBy: "jacen", 104 | Desc: "just a test task", 105 | } 106 | 107 | err = tk.Create() 108 | if err != nil { 109 | t.Fatalf("test fail, create db data err: %s", err.Error()) 110 | } 111 | 112 | tkc1 := Task{ 113 | Name: tk.Name, 114 | ParentTaskID: tk.ID, 115 | TkType: TASKCMD, 116 | CreateBy: "jacen", 117 | Desc: "a child task", 118 | SeqNum: 1, 119 | } 120 | err = tkc1.Create() 121 | if err != nil { 122 | t.Fatalf("test fail, create tkc1 err: %s", err.Error()) 123 | } 124 | 125 | tkc2 := Task{ 126 | Name: tk.Name, 127 | ParentTaskID: tk.ID, 128 | TkType: TASKCMD, 129 | CreateBy: "jacen", 130 | Desc: "a child task", 131 | SeqNum: 2, 132 | } 133 | err = tkc2.Create() 134 | if err != nil { 135 | t.Fatalf("test fail, create tkc2 err: %s", err.Error()) 136 | } 137 | } 138 | 139 | func TestTask_GetTheSeqChild(t *testing.T) { 140 | err := conf.InitCfg() 141 | if err != nil { 142 | t.Fatalf("init conf err: %s", err.Error()) 143 | } 144 | db.InitDB() 145 | db.DB.AutoMigrate(&Task{}) 146 | 147 | tk := Task{} 148 | tk.ID = 1 149 | 150 | var seq uint = 1 151 | tkc1, err := tk.GetTheSeqChild(seq) 152 | if err != nil { 153 | t.Fatalf("test get the seq %d child err: %s", seq, err.Error()) 154 | } 155 | t.Logf("test success %v", tkc1) 156 | } 157 | 158 | func TestTask_GetAllChildTask(t *testing.T) { 159 | err := conf.InitCfg() 160 | if err != nil { 161 | t.Fatalf("init conf err: %s", err.Error()) 162 | } 163 | db.InitDB() 164 | db.DB.AutoMigrate(&Task{}) 165 | 166 | tk := Task{} 167 | tk.ID = 1 168 | 169 | allChild, err := tk.GetAllChildTask() 170 | if err != nil { 171 | t.Fatalf("tast gat all child task failed: %s", err) 172 | } 173 | 174 | for _, v := range allChild { 175 | t.Logf("test success, task: %v", v) 176 | } 177 | } 178 | 179 | func TestTask_GetBrotherTask(t *testing.T) { 180 | err := conf.InitCfg() 181 | if err != nil { 182 | t.Fatalf("init conf err: %s", err.Error()) 183 | } 184 | db.InitDB() 185 | db.DB.AutoMigrate(&Task{}) 186 | 187 | tk := Task{} 188 | tk.ParentTaskID = 1 189 | tk.SeqNum = 2 190 | 191 | tb, err := tk.GetOldBrotherTask() 192 | if err != nil { 193 | t.Fatalf("test get brother task failed: %s", err.Error()) 194 | } 195 | t.Logf("test success, task id: %d, task seq: %d", tb.ID, tb.SeqNum) 196 | } 197 | 198 | func TestGetTaskByID(t *testing.T) { 199 | err := conf.InitCfg() 200 | if err != nil { 201 | t.Fatalf("init conf err: %s", err.Error()) 202 | } 203 | db.InitDB() 204 | db.DB.AutoMigrate(&Task{}) 205 | 206 | tk, err := GetTaskByID(1) 207 | if err != nil { 208 | t.Fatalf("test err: %s", err.Error()) 209 | } 210 | t.Logf("test success, tk id: %d, tk name: %s", tk.ID, tk.Name) 211 | } 212 | 213 | func TestCheckNameIsExist(t *testing.T) { 214 | err := conf.InitCfg() 215 | if err != nil { 216 | t.Fatalf("init conf err: %s", err.Error()) 217 | } 218 | db.InitDB() 219 | db.DB.AutoMigrate(&Task{}) 220 | 221 | n1 := "a test name" 222 | n2 := "a test task 1" 223 | 224 | c1 := CheckNameIsExist(n1) 225 | t.Logf("test n1 rst: %v", c1) 226 | 227 | c2 := CheckNameIsExist(n2) 228 | t.Logf("test n1 rst: %v", c2) 229 | } 230 | 231 | func TestTask_GetNextTask(t *testing.T) { 232 | err := conf.InitCfg() 233 | if err != nil { 234 | t.Fatalf("init conf err: %s", err.Error()) 235 | } 236 | db.InitDB() 237 | db.DB.AutoMigrate(&Task{}) 238 | 239 | tk := Task{} 240 | tk.ParentTaskID = 1 241 | tk.SeqNum = 2 242 | 243 | next, err := tk.GetNextTask() 244 | if err != nil { 245 | t.Fatalf("test fail: %s", err.Error()) 246 | } 247 | 248 | t.Logf("test success len: %d", len(next)) 249 | for _, v := range next { 250 | t.Logf("test success: %v", v) 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /service/task/task_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "encoding/json" 5 | "opsHeart_server/common" 6 | "opsHeart_server/conf" 7 | "opsHeart_server/db" 8 | "opsHeart_server/logger" 9 | "opsHeart_server/utils/rand_str" 10 | "strconv" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | func TestSplitIPSList(t *testing.T) { 16 | ipsL := 83 17 | ips := make([]string, 0) 18 | for i := 0; i < ipsL; i++ { 19 | rs := rand_str.GetStr(4) 20 | ips = append(ips, rs) 21 | } 22 | t.Logf("ips len: %d\n", len(ips)) 23 | t.Logf("ips: %v\n", ips) 24 | 25 | type SplitTest struct { 26 | t StType 27 | s []int 28 | } 29 | 30 | t.Log("=======t1 number======") 31 | // test 1 32 | t1 := SplitTest{ 33 | t: StageNumber, 34 | s: []int{10, 20, 30, 50}, 35 | } 36 | tm1, err := SplitIPsList(t1.t, t1.s, ips) 37 | if err != nil { 38 | t.Fatalf("t1 test: %s\n", err) 39 | } 40 | //t.Logf("t1 test: %v\n", tm1) 41 | for _, v := range tm1 { 42 | t.Log(v.stage) 43 | t.Logf("len: %d, list: %v", len(v.agents), v.agents) 44 | } 45 | 46 | t.Log("=======t2 number======") 47 | // test2 48 | t2 := SplitTest{ 49 | t: StageNumber, 50 | s: []int{10, 20, 9}, 51 | } 52 | tm2, err := SplitIPsList(t2.t, t2.s, ips) 53 | if err != nil { 54 | t.Fatalf("t2 test: %s\n", err) 55 | } 56 | //t.Logf("t2 test: %v\n", tm2) 57 | for _, v := range tm2 { 58 | t.Log(v.stage) 59 | t.Logf("len: %d, list: %v", len(v.agents), v.agents) 60 | } 61 | 62 | t.Log("=======t3 percent======") 63 | // test3 64 | t3 := SplitTest{ 65 | t: StagePercent, 66 | s: []int{10, 30, 40, 50}, 67 | } 68 | tm3, err := SplitIPsList(t3.t, t3.s, ips) 69 | if err != nil { 70 | t.Fatalf("t3 test: %s\n", err) 71 | } 72 | //t.Logf("t3 test: %v\n", tm3) 73 | for _, v := range tm3 { 74 | t.Log(v.stage) 75 | t.Logf("len: %d, list: %v", len(v.agents), v.agents) 76 | } 77 | 78 | t.Log("=======t4 percent======") 79 | // test4 80 | t4 := SplitTest{ 81 | t: StagePercent, 82 | s: []int{10, 30, 20, 5, 8}, 83 | } 84 | tm4, err := SplitIPsList(t4.t, t4.s, ips) 85 | if err != nil { 86 | t.Fatalf("t3 test: %s\n", err) 87 | } 88 | //t.Logf("t4 test: %v\n", tm4) 89 | for _, v := range tm4 { 90 | t.Log(v.stage) 91 | t.Logf("len: %d, list: %v", len(v.agents), v.agents) 92 | } 93 | 94 | t.Log("=======t5 default percent======") 95 | // test5 96 | t5 := SplitTest{ 97 | t: StagePercent, 98 | s: []int{100}, 99 | } 100 | tm5, err := SplitIPsList(t5.t, t5.s, ips) 101 | if err != nil { 102 | t.Fatalf("t5 test: %s\n", err) 103 | } 104 | //t.Logf("t5 test: %v\n", tm5) 105 | for _, v := range tm5 { 106 | t.Log(v.stage) 107 | t.Logf("len: %d, list: %v", len(v.agents), v.agents) 108 | } 109 | } 110 | 111 | func TestTask_Run(t *testing.T) { 112 | err := conf.InitCfg() 113 | if err != nil { 114 | t.Fatalf("init conf err: %s", err.Error()) 115 | } 116 | common.InitRunningEnv() 117 | db.InitDB() 118 | db.DB.AutoMigrate(&Task{}) 119 | db.DB.AutoMigrate(&TaskInstance{}) 120 | db.DB.AutoMigrate(&TaskCmd{}) 121 | db.DB.AutoMigrate(&TaskScript{}) 122 | db.DB.AutoMigrate(&TaskStageAgent{}) 123 | db.DB.AutoMigrate(&TaskArg{}) 124 | db.DB.AutoMigrate(&InsArg{}) 125 | 126 | ipList := make([]string, 0) 127 | for i := 0; i < 20; i++ { 128 | rands := rand_str.GetStr(4) 129 | ipList = append(ipList, rands) 130 | } 131 | 132 | collValByte, _ := json.Marshal(ipList) 133 | stageInt := []int{100} 134 | sg, _ := json.Marshal(stageInt) 135 | 136 | trStage := []int{50, 50} 137 | trStageByte, _ := json.Marshal(trStage) 138 | 139 | // *** ROOT TASK *** 140 | tr := Task{ 141 | Name: "test task 93", 142 | TkType: TASKROOT, 143 | CollectionType: CollList, 144 | CollectionValue: string(collValByte), 145 | Stages: string(trStageByte), // 50% 50% 146 | //Stages: string(sg), // 100% 147 | Desc: "a basic test task", 148 | //ContinueOnFail: 1, 149 | } 150 | err = tr.Create() 151 | if err != nil { 152 | t.Fatalf("test create tr err: %s", err.Error()) 153 | } 154 | 155 | // *** THE FIRST TASK *** 156 | t1Stages := []int{50, 50} 157 | stagesByte, _ := json.Marshal(t1Stages) 158 | 159 | t1 := Task{ 160 | Name: tr.Name, 161 | TkType: TASKCMD, 162 | ParentTaskID: tr.ID, 163 | SeqNum: 1, 164 | CollectionValue: strconv.Itoa(50), 165 | Stages: string(stagesByte), 166 | SplitParent: DoSplit, 167 | ContinueOnFail: 1, 168 | TaskArgs: []TaskArg{ 169 | { 170 | TaskName: tr.Name, 171 | ArgName: "t1Var1", 172 | ArgType: COMMONSTR, 173 | ArgValue: "t1Var1Value", 174 | }, 175 | { 176 | TaskName: tr.Name, 177 | ArgName: "t1Var2", 178 | ArgType: AGENTFACT, 179 | ArgValue: "t1Var2Value", 180 | }, 181 | { 182 | TaskName: tr.Name, 183 | ArgName: "t1Var3", 184 | ArgType: AGENTTAG, 185 | ArgValue: "t1Var3Value", 186 | }, 187 | }, 188 | } 189 | err = t1.Create() 190 | if err != nil { 191 | t.Fatalf("test create t1 err: %s", err.Error()) 192 | } 193 | c1 := TaskCmd{ 194 | TaskID: t1.ID, 195 | Cmd: "echo1", 196 | Opt: "123 ok", 197 | Timeout: 60, 198 | } 199 | err = c1.Create() 200 | if err != nil { 201 | t.Fatalf("test create c1 err: %s", err.Error()) 202 | } 203 | 204 | // *** THE SECOND TASK *** 205 | t2Stage := []int{30, 70} 206 | t2stageByte, _ := json.Marshal(t2Stage) 207 | 208 | t2 := Task{ 209 | //Name: "tmp-not-used", 210 | Name: tr.Name, 211 | TkType: VGROUP, 212 | //ParentTaskID: 1000000, 213 | ParentTaskID: tr.ID, 214 | SeqNum: 2, 215 | Stages: string(t2stageByte), 216 | SplitParent: DoNotSplit, 217 | //ContinueOnFail: 1, 218 | } 219 | err = t2.Create() 220 | if err != nil { 221 | t.Fatalf("test create t2 err: %s", err.Error()) 222 | } 223 | 224 | t21 := Task{ 225 | Name: tr.Name, 226 | TkType: TASKCMD, 227 | ParentTaskID: t2.ID, 228 | SeqNum: 1, 229 | CollectionValue: strconv.Itoa(50), 230 | Stages: string(sg), 231 | SplitParent: DoSplit, 232 | } 233 | err = t21.Create() 234 | if err != nil { 235 | t.Fatalf("test create t21 err: %s", err.Error()) 236 | } 237 | c21 := TaskCmd{ 238 | TaskID: t21.ID, 239 | Cmd: "test21", 240 | //Opt: "123 ok test", 241 | Timeout: 60, 242 | } 243 | err = c21.Create() 244 | if err != nil { 245 | t.Fatalf("test create c21 err: %s", err.Error()) 246 | } 247 | 248 | t22 := Task{ 249 | Name: tr.Name, 250 | TkType: TASKSCRIPT, 251 | ParentTaskID: t2.ID, 252 | SeqNum: 1, 253 | CollectionValue: strconv.Itoa(50), 254 | Stages: string(sg), 255 | SplitParent: DoSplit, 256 | TaskArgs: []TaskArg{ 257 | { 258 | TaskName: tr.Name, 259 | ArgName: "t22Var1", 260 | ArgType: COMMONSTR, 261 | ArgValue: "t22Var1Value", 262 | }, 263 | { 264 | TaskName: tr.Name, 265 | ArgName: "t22Var2", 266 | ArgType: AGENTFACT, 267 | ArgValue: "t22Var2Value", 268 | }, 269 | { 270 | TaskName: tr.Name, 271 | ArgName: "t22Var3", 272 | ArgType: AGENTTAG, 273 | ArgValue: "t22Var3Value", 274 | }, 275 | }, 276 | } 277 | err = t22.Create() 278 | if err != nil { 279 | t.Fatalf("test create t22 err: %s", err.Error()) 280 | } 281 | s22 := TaskScript{ 282 | TaskID: t22.ID, 283 | Shell: "/bin/t22sh", 284 | Name: "/tmp/test123.sh", 285 | RunAs: "jacenr", 286 | Timeout: 60, 287 | } 288 | err = s22.Create() 289 | if err != nil { 290 | t.Fatalf("test create s22 err: %s", err.Error()) 291 | } 292 | 293 | // *** THE THIRD TASK *** 294 | stageInt6040 := []int{40, 60} 295 | sg6040, _ := json.Marshal(stageInt6040) 296 | 297 | t3 := Task{ 298 | Name: tr.Name, 299 | TkType: HGROUP, 300 | ParentTaskID: tr.ID, 301 | SeqNum: 3, 302 | //SeqNum: 2, 303 | CollectionValue: strconv.Itoa(50), 304 | Stages: string(sg6040), // 40% 60% 305 | SplitParent: DoSplit, 306 | ContinueOnFail: 1, 307 | } 308 | err = t3.Create() 309 | if err != nil { 310 | t.Fatalf("test create t3 err: %s", err.Error()) 311 | } 312 | 313 | t31 := Task{ 314 | Name: tr.Name, 315 | TkType: TASKCMD, 316 | ParentTaskID: t3.ID, 317 | SeqNum: 1, 318 | Stages: string(sg), // 100% 319 | SplitParent: DoNotSplit, 320 | ContinueOnFail: 1, 321 | TaskArgs: []TaskArg{ 322 | { 323 | TaskName: tr.Name, 324 | ArgName: "t31Var1", 325 | ArgType: COMMONSTR, 326 | ArgValue: "t31Var1Value", 327 | }, 328 | { 329 | TaskName: tr.Name, 330 | ArgName: "t31Var2", 331 | ArgType: AGENTFACT, 332 | ArgValue: "t31Var2Value", 333 | }, 334 | { 335 | TaskName: tr.Name, 336 | ArgName: "t31Var3", 337 | ArgType: AGENTTAG, 338 | ArgValue: "t31Var3Value", 339 | }, 340 | }, 341 | } 342 | err = t31.Create() 343 | if err != nil { 344 | t.Fatalf("test create t31 err: %s", err.Error()) 345 | } 346 | c31 := TaskCmd{ 347 | TaskID: t31.ID, 348 | Cmd: "echo31", 349 | Opt: "123 ok test", 350 | Timeout: 60, 351 | } 352 | err = c31.Create() 353 | if err != nil { 354 | t.Fatalf("test create c31 err: %s", err.Error()) 355 | } 356 | 357 | t32 := Task{ 358 | Name: tr.Name, 359 | TkType: TASKSCRIPT, 360 | ParentTaskID: t3.ID, 361 | SeqNum: 2, 362 | Stages: string(sg), // 100% 363 | SplitParent: DoNotSplit, 364 | } 365 | err = t32.Create() 366 | if err != nil { 367 | t.Fatalf("test create t32 err: %s", err.Error()) 368 | } 369 | s32 := TaskScript{ 370 | TaskID: t32.ID, 371 | Shell: "/bin/csh", 372 | Name: "/tmp/test123.sh", 373 | RunAs: "jacenr", 374 | Timeout: 60, 375 | } 376 | err = s32.Create() 377 | if err != nil { 378 | t.Fatalf("test create s32 err: %s", err.Error()) 379 | } 380 | 381 | t33 := Task{ 382 | Name: tr.Name, 383 | TkType: TASKSCRIPT, 384 | ParentTaskID: t3.ID, 385 | SeqNum: 3, 386 | Stages: string(sg), // 100% 387 | SplitParent: DoNotSplit, 388 | ContinueByTask: t31.ID, 389 | ContinueRst: STAGEFAILED, 390 | ContinueOnFail: 1, 391 | } 392 | err = t33.Create() 393 | if err != nil { 394 | t.Fatalf("test create t33 err: %s", err.Error()) 395 | } 396 | s33 := TaskScript{ 397 | TaskID: t33.ID, 398 | Shell: "/bin/bash", 399 | Name: "/tmp/test.sh", 400 | RunAs: "jacenr", 401 | Timeout: 60, 402 | } 403 | err = s33.Create() 404 | if err != nil { 405 | t.Fatalf("test create s33 err: %s", err.Error()) 406 | } 407 | 408 | t.Log("All tasks are created.") 409 | 410 | insName := NewInsName(tr.ID) 411 | ins := TaskInstance{ 412 | Name: insName, 413 | } 414 | 415 | argMap := make(map[string]TaskArg) 416 | err = tr.InitInsArgs(argMap, insName) 417 | if err != nil { 418 | t.Fatalf("fail: %s", err) 419 | } 420 | 421 | err = tr.Run(&ins) 422 | if err != nil { 423 | t.Fatalf("test run tr fail: %s", err.Error()) 424 | } 425 | 426 | allIns, err := GetAllInsByName(tr.Name) 427 | if err != nil { 428 | t.Fatalf("test run task fail: %s", err) 429 | } 430 | for _, v := range allIns { 431 | t.Logf("id:%d, parent_id:%d, task_id:%d, stage:%d, stage_seq:%d, start_at:%v, end_at:%v, status: %d, msg:%s\n", 432 | v.ID, v.ParentInsID, v.TaskID, v.Stage, v.StageSeq, v.StartAt, v.EndAt, v.Status, v.InsMsg) 433 | } 434 | 435 | time.Sleep(10 * time.Second) 436 | } 437 | 438 | func TestTask_Run2(t *testing.T) { 439 | err := conf.InitCfg() 440 | if err != nil { 441 | t.Fatalf("init conf err: %s", err.Error()) 442 | } 443 | db.InitDB() 444 | db.DB.AutoMigrate(&Task{}) 445 | db.DB.AutoMigrate(&TaskInstance{}) 446 | db.DB.AutoMigrate(&TaskCmd{}) 447 | db.DB.AutoMigrate(&TaskScript{}) 448 | db.DB.AutoMigrate(&TaskStageAgent{}) 449 | 450 | tr, _ := GetTaskByID(325) 451 | insName := NewInsName(325) 452 | parentIns := TaskInstance{ 453 | Name: insName, 454 | } 455 | err = tr.Run(&parentIns) 456 | if err != nil { 457 | t.Fatalf("err: %s", err.Error()) 458 | } 459 | time.Sleep(10 * time.Second) 460 | } 461 | 462 | func TestTask_Run3(t *testing.T) { 463 | err := conf.InitCfg() 464 | if err != nil { 465 | t.Fatalf("init conf err: %s", err.Error()) 466 | } 467 | common.InitRunningEnv() 468 | logger.InitLogger() 469 | db.InitDB() 470 | db.DB.AutoMigrate(&Task{}) 471 | db.DB.AutoMigrate(&TaskInstance{}) 472 | db.DB.AutoMigrate(&TaskCmd{}) 473 | db.DB.AutoMigrate(&TaskScript{}) 474 | db.DB.AutoMigrate(&TaskStageAgent{}) 475 | db.DB.AutoMigrate(&TaskArg{}) 476 | db.DB.AutoMigrate(&InsArg{}) 477 | 478 | ipList := make([]string, 0) 479 | for i := 0; i < 20; i++ { 480 | rands := rand_str.GetStr(4) 481 | ipList = append(ipList, rands) 482 | } 483 | 484 | collValByte, _ := json.Marshal(ipList) 485 | stage100 := []int{100} 486 | stg100, _ := json.Marshal(stage100) 487 | 488 | stage5050 := []int{50, 50} 489 | stg5050, _ := json.Marshal(stage5050) 490 | 491 | // *** ROOT TASK *** 492 | tr := Task{ 493 | Name: "test run3 task 28", 494 | TkType: TASKROOT, 495 | CollectionType: CollList, 496 | CollectionValue: string(collValByte), 497 | Stages: string(stg100), 498 | Desc: "a basic test task run3", 499 | //Stages: string(sg), // 100% 500 | //ContinueOnFail: 1, 501 | } 502 | err = tr.Create() 503 | if err != nil { 504 | t.Fatalf("test create tr err: %s", err.Error()) 505 | } 506 | 507 | // *** TEST CMD *** 508 | // *** stage: 100% *** 509 | // *** split: 50% *** 510 | t1 := Task{ 511 | Name: tr.Name, 512 | TkType: TASKCMD, 513 | ParentTaskID: tr.ID, 514 | SeqNum: 1, 515 | Stages: string(stg100), 516 | ContinueOnFail: 1, 517 | SplitParent: DoSplit, 518 | CollectionValue: strconv.Itoa(50), 519 | } 520 | err = t1.Create() 521 | if err != nil { 522 | t.Fatalf("test create t1 err: %s", err.Error()) 523 | } 524 | c1 := TaskCmd{ 525 | TaskID: t1.ID, 526 | Cmd: "echo1", 527 | Opt: "123 ok", 528 | Timeout: 60, 529 | } 530 | err = c1.Create() 531 | if err != nil { 532 | t.Fatalf("test create c1 err: %s", err.Error()) 533 | } 534 | 535 | // *** TEST SCRIPT *** 536 | // *** stage: 50% 50% *** 537 | // *** split: 50% *** 538 | t2 := Task{ 539 | Name: tr.Name, 540 | TkType: TASKSCRIPT, 541 | ParentTaskID: tr.ID, 542 | SeqNum: 2, 543 | Stages: string(stg5050), 544 | SplitParent: DoSplit, 545 | CollectionValue: strconv.Itoa(50), 546 | } 547 | err = t2.Create() 548 | if err != nil { 549 | t.Fatalf("test create t2 err: %s", err.Error()) 550 | } 551 | s2 := TaskScript{ 552 | TaskID: t2.ID, 553 | Shell: "/bin/csh2", 554 | Name: "/tmp/test123.sh", 555 | RunAs: "jacenr", 556 | Timeout: 60, 557 | } 558 | err = s2.Create() 559 | if err != nil { 560 | t.Fatalf("test create s2 err: %s", err.Error()) 561 | } 562 | 563 | // *** TEST HGROUP *** 564 | // *** stage 40% 60% *** 565 | stageInt6040 := []int{40, 60} 566 | stg6040, _ := json.Marshal(stageInt6040) 567 | 568 | t3 := Task{ 569 | Name: tr.Name, 570 | TkType: HGROUP, 571 | ParentTaskID: tr.ID, 572 | SeqNum: 3, 573 | Stages: string(stg6040), // 40% 60% 574 | ContinueOnFail: 1, 575 | //CollectionValue: strconv.Itoa(50), 576 | //SplitParent: DoSplit, 577 | } 578 | err = t3.Create() 579 | if err != nil { 580 | t.Fatalf("test create t3 err: %s", err.Error()) 581 | } 582 | 583 | // *** split 50% *** 584 | // *** test variables *** 585 | t31 := Task{ 586 | Name: tr.Name, 587 | TkType: TASKCMD, 588 | ParentTaskID: t3.ID, 589 | SeqNum: 1, 590 | CollectionValue: strconv.Itoa(50), 591 | Stages: string(stg100), // 100% 592 | SplitParent: DoSplit, 593 | ContinueOnFail: 1, 594 | TaskArgs: []TaskArg{ 595 | { 596 | TaskName: tr.Name, 597 | ArgName: "t31Var1", 598 | ArgType: COMMONSTR, 599 | ArgValue: "t31Var1Value", 600 | }, 601 | { 602 | TaskName: tr.Name, 603 | ArgName: "t31Var2", 604 | ArgType: AGENTFACT, 605 | ArgValue: "t31Var2Value", 606 | }, 607 | { 608 | TaskName: tr.Name, 609 | ArgName: "t31Var3", 610 | ArgType: AGENTTAG, 611 | ArgValue: "t31Var3Value", 612 | }, 613 | }, 614 | } 615 | err = t31.Create() 616 | if err != nil { 617 | t.Fatalf("test create t31 err: %s", err.Error()) 618 | } 619 | c31 := TaskCmd{ 620 | TaskID: t31.ID, 621 | Cmd: "echo31", 622 | Opt: "123 ok test", 623 | Timeout: 60, 624 | } 625 | err = c31.Create() 626 | if err != nil { 627 | t.Fatalf("test create c31 err: %s", err.Error()) 628 | } 629 | 630 | t32 := Task{ 631 | Name: tr.Name, 632 | TkType: TASKSCRIPT, 633 | ParentTaskID: t3.ID, 634 | SeqNum: 2, 635 | CollectionValue: strconv.Itoa(50), 636 | Stages: string(stg100), // 100% 637 | SplitParent: DoSplit, 638 | } 639 | err = t32.Create() 640 | if err != nil { 641 | t.Fatalf("test create t32 err: %s", err.Error()) 642 | } 643 | s32 := TaskScript{ 644 | TaskID: t32.ID, 645 | Shell: "/bin/csh32", 646 | Name: "/tmp/test123.sh", 647 | RunAs: "jacenr", 648 | Timeout: 60, 649 | } 650 | err = s32.Create() 651 | if err != nil { 652 | t.Fatalf("test create s32 err: %s", err.Error()) 653 | } 654 | 655 | // *** test continue depend task *** 656 | t33 := Task{ 657 | Name: tr.Name, 658 | TkType: TASKSCRIPT, 659 | ParentTaskID: t3.ID, 660 | SeqNum: 3, 661 | Stages: string(stg100), // 100% 662 | SplitParent: DoNotSplit, 663 | ContinueByTask: t31.ID, 664 | ContinueRst: STAGEFAILED, 665 | ContinueOnFail: 1, 666 | } 667 | err = t33.Create() 668 | if err != nil { 669 | t.Fatalf("test create t33 err: %s", err.Error()) 670 | } 671 | s33 := TaskScript{ 672 | TaskID: t33.ID, 673 | Shell: "/bin/bash33", 674 | Name: "/tmp/test.sh", 675 | RunAs: "jacenr", 676 | Timeout: 60, 677 | } 678 | err = s33.Create() 679 | if err != nil { 680 | t.Fatalf("test create s33 err: %s", err.Error()) 681 | } 682 | 683 | // *** test XGROUP *** 684 | t4 := Task{ 685 | Name: tr.Name, 686 | TkType: XGROUP, 687 | ParentTaskID: tr.ID, 688 | SeqNum: 4, 689 | Stages: string(stg100), 690 | ContinueOnFail: 1, 691 | //CollectionValue: strconv.Itoa(50), 692 | //SplitParent: DoSplit, 693 | } 694 | err = t4.Create() 695 | if err != nil { 696 | t.Fatalf("test create t4 err: %s", err.Error()) 697 | } 698 | 699 | t41 := Task{ 700 | Name: tr.Name, 701 | TkType: TASKCMD, 702 | ParentTaskID: t4.ID, 703 | SeqNum: 1, 704 | Stages: string(stg100), 705 | ContinueOnFail: 1, 706 | } 707 | err = t41.Create() 708 | if err != nil { 709 | t.Fatalf("test create t41 err: %s", err.Error()) 710 | } 711 | c41 := TaskCmd{ 712 | TaskID: t41.ID, 713 | Cmd: "echo41", 714 | Opt: "123 ok", 715 | Timeout: 60, 716 | } 717 | err = c41.Create() 718 | if err != nil { 719 | t.Fatalf("test create c41 err: %s", err.Error()) 720 | } 721 | 722 | t42 := Task{ 723 | Name: tr.Name, 724 | TkType: TASKCMD, 725 | ParentTaskID: t4.ID, 726 | SeqNum: 2, 727 | Stages: string(stg100), 728 | ContinueOnFail: 1, 729 | } 730 | err = t42.Create() 731 | if err != nil { 732 | t.Fatalf("test create t42 err: %s", err.Error()) 733 | } 734 | c42 := TaskCmd{ 735 | TaskID: t42.ID, 736 | Cmd: "echo42", 737 | Opt: "123 ok", 738 | Timeout: 60, 739 | } 740 | err = c42.Create() 741 | if err != nil { 742 | t.Fatalf("test create c42 err: %s", err.Error()) 743 | } 744 | 745 | // *** test VGROUP *** 746 | // *** stage 100% *** 747 | t5 := Task{ 748 | Name: tr.Name, 749 | TkType: VGROUP, 750 | ParentTaskID: tr.ID, 751 | SeqNum: 5, 752 | Stages: string(stg100), 753 | ContinueOnFail: 1, 754 | } 755 | err = t5.Create() 756 | if err != nil { 757 | t.Fatalf("test create t5 err: %s", err.Error()) 758 | } 759 | 760 | t51 := Task{ 761 | Name: tr.Name, 762 | TkType: TASKCMD, 763 | ParentTaskID: t5.ID, 764 | SeqNum: 1, 765 | Stages: string(stg100), 766 | ContinueOnFail: 1, 767 | } 768 | err = t51.Create() 769 | if err != nil { 770 | t.Fatalf("test create t51 err: %s", err.Error()) 771 | } 772 | c51 := TaskCmd{ 773 | TaskID: t51.ID, 774 | Cmd: "echo51", 775 | Opt: "123 ok", 776 | Timeout: 60, 777 | } 778 | err = c51.Create() 779 | if err != nil { 780 | t.Fatalf("test create c51 err: %s", err.Error()) 781 | } 782 | 783 | t52 := Task{ 784 | Name: tr.Name, 785 | TkType: TASKCMD, 786 | ParentTaskID: t5.ID, 787 | SeqNum: 2, 788 | Stages: string(stg100), 789 | ContinueOnFail: 1, 790 | } 791 | err = t52.Create() 792 | if err != nil { 793 | t.Fatalf("test create t52 err: %s", err.Error()) 794 | } 795 | c52 := TaskCmd{ 796 | TaskID: t52.ID, 797 | Cmd: "echo52", 798 | Opt: "123 ok", 799 | Timeout: 60, 800 | } 801 | err = c52.Create() 802 | if err != nil { 803 | t.Fatalf("test create c52 err: %s", err.Error()) 804 | } 805 | 806 | t.Log("All tasks are created.") 807 | 808 | insName := NewInsName(tr.ID) 809 | ins := TaskInstance{ 810 | Name: insName, 811 | } 812 | 813 | argMap := make(map[string]TaskArg) 814 | err = tr.InitInsArgs(argMap, insName) 815 | if err != nil { 816 | t.Fatalf("fail: %s", err) 817 | } 818 | 819 | err = tr.Run(&ins) 820 | if err != nil { 821 | t.Fatalf("test run tr fail: %s", err.Error()) 822 | } 823 | 824 | allIns, err := GetAllInsByName(tr.Name) 825 | if err != nil { 826 | t.Fatalf("test run task fail: %s", err) 827 | } 828 | for _, v := range allIns { 829 | t.Logf("id:%d, parent_id:%d, task_id:%d, stage:%d, stage_seq:%d, start_at:%v, end_at:%v, status: %d, msg:%s\n", 830 | v.ID, v.ParentInsID, v.TaskID, v.Stage, v.StageSeq, v.StartAt, v.EndAt, v.Status, v.InsMsg) 831 | } 832 | 833 | time.Sleep(10 * time.Second) 834 | } 835 | -------------------------------------------------------------------------------- /utils/call_http/client.go: -------------------------------------------------------------------------------- 1 | package call_http 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "opsHeart_server/conf" 9 | "time" 10 | ) 11 | 12 | var C *http.Client 13 | 14 | func init() { 15 | C = &http.Client{ 16 | Timeout: 10 * time.Second, 17 | } 18 | } 19 | 20 | func HttpGet(host string, token string, path string, para string) (int, []byte, error) { 21 | urlWithPara := fmt.Sprintf("http://%s%s?%s", host, path, para) 22 | //fmt.Printf("%s", urlWithPara) 23 | req, _ := http.NewRequest("GET", urlWithPara, nil) 24 | 25 | // get uuid of self from conf 26 | u := conf.GetUUID() 27 | 28 | // set basic auth 29 | req.SetBasicAuth(u, token) 30 | 31 | resp, err := C.Do(req) 32 | if err != nil { 33 | return 0, nil, err 34 | } 35 | defer func() { 36 | _ = resp.Body.Close() 37 | }() 38 | b, err := ioutil.ReadAll(resp.Body) 39 | if err != nil { 40 | return 0, nil, err 41 | } 42 | return resp.StatusCode, b, nil 43 | } 44 | 45 | func HttpPost(host string, token string, path string, para []byte) (int, []byte, error) { 46 | url := fmt.Sprintf("http://%s%s", host, path) 47 | req, _ := http.NewRequest("POST", url, bytes.NewReader(para)) 48 | 49 | // get uuid of self from conf 50 | u := conf.GetUUID() 51 | 52 | // set basic auth 53 | req.SetBasicAuth(u, token) 54 | 55 | resp, err := C.Do(req) 56 | if err != nil { 57 | return 0, nil, err 58 | } 59 | defer func() { 60 | _ = resp.Body.Close() 61 | }() 62 | b, err := ioutil.ReadAll(resp.Body) 63 | if err != nil { 64 | return 0, nil, err 65 | } 66 | return resp.StatusCode, b, nil 67 | } 68 | -------------------------------------------------------------------------------- /utils/call_http/client_test.go: -------------------------------------------------------------------------------- 1 | package call_http 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | ) 7 | 8 | func TestHttpGet(t *testing.T) { 9 | path := "/test/v1/do" 10 | para := "p1=testv1&p2=测试" 11 | _, s, err := HttpGet("localhost:8080", "", path, para) 12 | if err != nil { 13 | t.Fatalf("err: %s", err.Error()) 14 | } 15 | t.Logf("success: %s", s) 16 | } 17 | 18 | func TestHttpPost(t *testing.T) { 19 | type TestS struct { 20 | Name string 21 | Age int 22 | } 23 | 24 | d := TestS{ 25 | Name: "testName", 26 | Age: 20, 27 | } 28 | 29 | b, _ := json.Marshal(d) 30 | path := "/test/v1/do" 31 | 32 | _, s, err := HttpPost("localhost:8080", "", path, b) 33 | if err != nil { 34 | t.Fatalf("err: %s", err.Error()) 35 | } 36 | t.Logf("success: %s", s) 37 | } 38 | -------------------------------------------------------------------------------- /utils/cron/cron.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "reflect" 7 | "time" 8 | ) 9 | 10 | type Status int 11 | 12 | const ( 13 | STARTED Status = 1 14 | STOPPED Status = 0 15 | ) 16 | 17 | type Cr struct { 18 | f interface{} // the func called by period 19 | args []interface{} // func arguments 20 | d time.Duration // the duration 21 | c int // the max concurrent 22 | r int // number in running 23 | s Status // cron status 24 | //ch chan int // handle r 25 | ctx context.Context 26 | cancel context.CancelFunc 27 | } 28 | 29 | // init cron object 30 | func NewCron(f interface{}, args []interface{}, d time.Duration, c int) (*Cr, error) { 31 | if !IsFunc(f) { 32 | return nil, errors.New("func is not callable function") 33 | } 34 | 35 | o := Cr{ 36 | f: f, 37 | args: args, 38 | d: d, 39 | c: c, 40 | } 41 | 42 | ctxB := context.Background() 43 | ctx, cancel := context.WithCancel(ctxB) 44 | 45 | o.ctx = ctx 46 | o.cancel = cancel 47 | 48 | return &o, nil 49 | } 50 | 51 | // start cron object 52 | func (c *Cr) Start() error { 53 | if c.s == STARTED { 54 | return errors.New("cron had started, do not start again") 55 | } 56 | c.s = STARTED 57 | 58 | // init a channel for manager of Cr.r 59 | //ch := make(chan int) 60 | //c.ch = ch 61 | //go c.handleConcurrentNum() 62 | 63 | go func() { 64 | t := time.NewTicker(c.d) 65 | defer t.Stop() 66 | 67 | f := reflect.ValueOf(c.f) 68 | rargs := make([]reflect.Value, len(c.args)) 69 | for i, a := range c.args { 70 | rargs[i] = reflect.ValueOf(a) 71 | } 72 | 73 | fc := func() { 74 | //fmt.Println(c.r) // test 75 | // may need to be optimized: 76 | // locker, token channel or a goroutine whose role is a manager 77 | c.r++ 78 | f.Call(rargs) 79 | c.r-- 80 | } 81 | 82 | go fc() 83 | 84 | for { 85 | select { 86 | case <-c.ctx.Done(): 87 | return 88 | case <-t.C: 89 | if c.r >= c.c { 90 | continue 91 | } 92 | go fc() 93 | } 94 | } 95 | }() 96 | return nil 97 | } 98 | 99 | // stop the cron 100 | func (c *Cr) Stop() error { 101 | if c.s == STOPPED { 102 | return errors.New("cron had stop, do not stop again") 103 | } 104 | c.cancel() 105 | c.s = STOPPED 106 | return nil 107 | } 108 | 109 | // A goroutine whose role is manager of Cr.r 110 | //func (c *Cr) handleConcurrentNum() { 111 | // for { 112 | // i, ok := <-c.ch 113 | // if !ok { 114 | // break 115 | // } 116 | // c.r += i 117 | // } 118 | //} 119 | -------------------------------------------------------------------------------- /utils/cron/cron_test.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func printNS(i int64, s string) { 10 | time.Sleep(time.Duration(i) * time.Second) 11 | fmt.Printf("n is: %d, s is: %s\n", i, s) 12 | } 13 | 14 | func printFunc() { 15 | time.Sleep(time.Duration(1) * time.Second) 16 | fmt.Printf("this func no argments.\n") 17 | } 18 | 19 | // test cron usage with arguments 20 | func TestCr_Start1(t *testing.T) { 21 | argsList := []interface{}{int64(2), "a"} 22 | c, err := NewCron(printNS, argsList, 3*time.Second, 1) 23 | if err != nil { 24 | t.Fatalf("fail: %s\n", err.Error()) 25 | } 26 | _ = c.Start() 27 | time.Sleep(15 * time.Second) 28 | _ = c.Stop() 29 | time.Sleep(5 * time.Second) // wait last task complete. 30 | } 31 | 32 | // test cron with func which no argments 33 | func TestCr_Start2(t *testing.T) { 34 | c, err := NewCron(printFunc, nil, 3*time.Second, 1) 35 | if err != nil { 36 | t.Fatalf("fail: %s\n", err.Error()) 37 | } 38 | _ = c.Start() 39 | time.Sleep(15 * time.Second) 40 | _ = c.Stop() 41 | time.Sleep(5 * time.Second) // wait last task complete. 42 | } 43 | 44 | // test start twice and stop twice 45 | func TestCr_Stop(t *testing.T) { 46 | argsList := []interface{}{int64(3), "a"} 47 | c, err := NewCron(printNS, argsList, 2*time.Second, 1) 48 | if err != nil { 49 | t.Fatalf("fail: %s\n", err.Error()) 50 | } 51 | 52 | _ = c.Start() 53 | err = c.Start() 54 | if err != nil { 55 | t.Logf("start err: %s\n", err.Error()) 56 | } 57 | 58 | time.Sleep(15 * time.Second) 59 | 60 | _ = c.Stop() 61 | err = c.Stop() 62 | if err != nil { 63 | t.Logf("stop err: %s\n", err.Error()) 64 | } 65 | 66 | time.Sleep(5 * time.Second) 67 | } 68 | -------------------------------------------------------------------------------- /utils/cron/func.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import "reflect" 4 | 5 | // IsFunc check v if a func 6 | func IsFunc(v interface{}) bool { 7 | return reflect.TypeOf(v).Kind() == reflect.Func 8 | } 9 | 10 | // [ NOT USED ] Invoke call func interface 11 | func Invoke(fn interface{}, args ...interface{}) { 12 | v := reflect.ValueOf(fn) 13 | rargs := make([]reflect.Value, len(args)) 14 | for i, a := range args { 15 | rargs[i] = reflect.ValueOf(a) 16 | } 17 | v.Call(rargs) 18 | } 19 | -------------------------------------------------------------------------------- /utils/rand_str/get_rand.go: -------------------------------------------------------------------------------- 1 | package rand_str 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | const ( 9 | ALLCHARS = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" 10 | ALLWORDCHAR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 11 | ) 12 | 13 | func GetStrWithSymbol(l int) string { 14 | rand.Seed(time.Now().Unix()) 15 | randChars := make([]byte, 0) 16 | rang := len(ALLCHARS) 17 | for i := 0; i < l; i++ { 18 | randChars = append(randChars, ALLCHARS[rand.Intn(rang)]) 19 | } 20 | return string(randChars) 21 | } 22 | 23 | func GetStr(l int) string { 24 | rand.Seed(time.Now().UnixNano()) 25 | randChars := make([]byte, 0) 26 | rang := len(ALLWORDCHAR) 27 | for i := 0; i < l; i++ { 28 | randChars = append(randChars, ALLWORDCHAR[rand.Intn(rang)]) 29 | } 30 | return string(randChars) 31 | } 32 | -------------------------------------------------------------------------------- /utils/rand_str/get_rand_test.go: -------------------------------------------------------------------------------- 1 | package rand_str 2 | 3 | import "testing" 4 | 5 | func TestGetStrWithSymbol(t *testing.T) { 6 | s1 := GetStrWithSymbol(20) 7 | t.Log(s1) 8 | s2 := GetStr(20) 9 | t.Log(s2) 10 | } 11 | -------------------------------------------------------------------------------- /utils/worker/worker.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import "sync" 4 | 5 | // WorkerManager a worker manager object 6 | type Manager struct { 7 | Num int // the number of workers 8 | Ch chan func() // func channel from which worker get work to run 9 | Cl chan struct{} // to end all worker by close chan 10 | WG sync.WaitGroup 11 | //Cx context.Context // to concel all wokers by context 12 | } 13 | 14 | // NewWorker used for init WokerManager 15 | func NewWorker(n int) *Manager { 16 | wm := &Manager{} 17 | wm.Num = n 18 | //if ctx != nil { 19 | // wm.Cx = ctx 20 | //} 21 | c := make(chan func(), 0) 22 | wm.Ch = c 23 | 24 | cl := make(chan struct{}, 0) 25 | wm.Cl = cl 26 | 27 | wm.WG = sync.WaitGroup{} 28 | 29 | return wm 30 | } 31 | 32 | // StartWork used for start workers 33 | func (wm *Manager) StartWork() { 34 | for i := 0; i < wm.Num; i++ { 35 | wm.WG.Add(1) 36 | go func() { 37 | defer wm.WG.Done() 38 | for { 39 | select { 40 | case f, ok := <-wm.Ch: 41 | if !ok { 42 | return 43 | } 44 | f() 45 | //case <-wm.Cx.Done(): 46 | // return 47 | case <-wm.Cl: 48 | return 49 | } 50 | } 51 | }() 52 | } 53 | } 54 | 55 | // ForceEndWorker force to end workers 56 | func (wm *Manager) ForceEndWorker() { 57 | close(wm.Cl) 58 | } 59 | 60 | // EndWorkerAndWait wait all worker complete 61 | func (wm *Manager) EndWorkerAndWait() { 62 | close(wm.Ch) 63 | wm.WG.Wait() 64 | } 65 | -------------------------------------------------------------------------------- /utils/worker/worker_test.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestNewWorker(t *testing.T) { 10 | printStr := func(s string) { 11 | time.Sleep(1 * time.Second) 12 | fmt.Println(s) 13 | } 14 | 15 | w1 := NewWorker(10) 16 | w1.StartWork() 17 | defer w1.EndWorkerAndWait() 18 | //defer w1.ForceEndWorker() 19 | for i := 'A'; i <= 'z'; i++ { 20 | func(n string) { 21 | w1.Ch <- func() { 22 | printStr(n) 23 | } 24 | }(string(i)) 25 | } 26 | } 27 | --------------------------------------------------------------------------------