github.com/TeaOSLab/EdgeNode@v1.3.8/cmd/edge-node/main.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "flag" 6 "fmt" 7 "github.com/TeaOSLab/EdgeNode/internal/apps" 8 "github.com/TeaOSLab/EdgeNode/internal/configs" 9 teaconst "github.com/TeaOSLab/EdgeNode/internal/const" 10 "github.com/TeaOSLab/EdgeNode/internal/nodes" 11 "github.com/TeaOSLab/EdgeNode/internal/utils" 12 fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs" 13 "github.com/iwind/TeaGo/Tea" 14 _ "github.com/iwind/TeaGo/bootstrap" 15 "github.com/iwind/TeaGo/logs" 16 "github.com/iwind/TeaGo/maps" 17 "github.com/iwind/TeaGo/types" 18 "github.com/iwind/gosock/pkg/gosock" 19 "gopkg.in/yaml.v3" 20 "net" 21 "net/http" 22 _ "net/http/pprof" 23 "os" 24 "path/filepath" 25 "sort" 26 "strings" 27 "time" 28 ) 29 30 func main() { 31 var app = apps.NewAppCmd(). 32 Version(teaconst.Version). 33 Product(teaconst.ProductName). 34 Usage(teaconst.ProcessName + " [-v|start|stop|restart|status|quit|test|reload|service|daemon|config|pprof|accesslog|uninstall]"). 35 Usage(teaconst.ProcessName + " [trackers|goman|conns|gc|bandwidth|disk|cache.garbage]"). 36 Usage(teaconst.ProcessName + " [ip.drop|ip.reject|ip.remove|ip.close] IP") 37 38 app.On("start:before", func() { 39 // validate config 40 _, err := configs.LoadAPIConfig() 41 if err != nil { 42 // validate cluster config 43 _, clusterErr := configs.LoadClusterConfig() 44 if clusterErr != nil { // fail again 45 fmt.Println("[ERROR]start failed: load api config from '" + Tea.ConfigFile(configs.ConfigFileName) + "' failed: " + err.Error()) 46 os.Exit(0) 47 } 48 } 49 }) 50 app.On("uninstall", func() { 51 // service 52 fmt.Println("Uninstall service ...") 53 var manager = utils.NewServiceManager(teaconst.ProcessName, teaconst.ProductName) 54 go func() { 55 _ = manager.Uninstall() 56 }() 57 58 // stop 59 fmt.Println("Stopping ...") 60 _, _ = gosock.NewTmpSock(teaconst.ProcessName).SendTimeout(&gosock.Command{Code: "stop"}, 1*time.Second) 61 62 // delete files 63 var exe, _ = os.Executable() 64 if len(exe) == 0 { 65 return 66 } 67 68 var dir = filepath.Dir(filepath.Dir(exe)) // ROOT / bin / exe 69 70 // verify dir 71 { 72 fmt.Println("Checking '" + dir + "' ...") 73 for _, subDir := range []string{"bin/" + filepath.Base(exe), "configs", "logs"} { 74 _, err := os.Stat(dir + "/" + subDir) 75 if err != nil { 76 fmt.Println("[ERROR]program directory structure has been broken, please remove it manually.") 77 return 78 } 79 } 80 81 fmt.Println("Removing '" + dir + "' ...") 82 err := os.RemoveAll(dir) 83 if err != nil { 84 fmt.Println("[ERROR]remove failed: " + err.Error()) 85 } 86 } 87 88 // delete symbolic links 89 fmt.Println("Removing symbolic links ...") 90 _ = os.Remove("/usr/bin/" + teaconst.ProcessName) 91 _ = os.Remove("/var/log/" + teaconst.ProcessName) 92 93 // delete configs 94 // nothing to delete for EdgeNode 95 96 // delete sock 97 fmt.Println("Removing temporary files ...") 98 var tempDir = os.TempDir() 99 _ = os.Remove(tempDir + "/" + teaconst.ProcessName + ".sock") 100 _ = os.Remove(tempDir + "/" + teaconst.AccessLogSockName) 101 102 // cache ... 103 fmt.Println("Please delete cache directories by yourself.") 104 105 // done 106 fmt.Println("[DONE]") 107 }) 108 app.On("test", func() { 109 err := nodes.NewNode().Test() 110 if err != nil { 111 _, _ = os.Stderr.WriteString(err.Error()) 112 } 113 }) 114 app.On("reload", func() { 115 var sock = gosock.NewTmpSock(teaconst.ProcessName) 116 reply, err := sock.Send(&gosock.Command{Code: "reload"}) 117 if err != nil { 118 fmt.Println("[ERROR]" + err.Error()) 119 } else { 120 var params = maps.NewMap(reply.Params) 121 if params.Has("error") { 122 fmt.Println("[ERROR]" + params.GetString("error")) 123 } else { 124 fmt.Println("ok") 125 } 126 } 127 }) 128 app.On("daemon", func() { 129 nodes.NewNode().Daemon() 130 }) 131 app.On("service", func() { 132 err := nodes.NewNode().InstallSystemService() 133 if err != nil { 134 fmt.Println("[ERROR]install failed: " + err.Error()) 135 return 136 } 137 fmt.Println("done") 138 }) 139 app.On("quit", func() { 140 var sock = gosock.NewTmpSock(teaconst.ProcessName) 141 _, err := sock.Send(&gosock.Command{Code: "quit"}) 142 if err != nil { 143 fmt.Println("[ERROR]quit failed: " + err.Error()) 144 return 145 } 146 fmt.Println("done") 147 }) 148 app.On("pprof", func() { 149 var flagSet = flag.NewFlagSet("pprof", flag.ExitOnError) 150 var addr string 151 flagSet.StringVar(&addr, "addr", "", "") 152 _ = flagSet.Parse(os.Args[2:]) 153 154 if len(addr) == 0 { 155 addr = "127.0.0.1:6060" 156 } 157 logs.Println("starting with pprof '" + addr + "'...") 158 159 go func() { 160 err := http.ListenAndServe(addr, nil) 161 if err != nil { 162 logs.Println("[ERROR]" + err.Error()) 163 } 164 }() 165 166 var node = nodes.NewNode() 167 node.Start() 168 }) 169 app.On("dbstat", func() { 170 teaconst.EnableDBStat = true 171 172 var node = nodes.NewNode() 173 node.Start() 174 }) 175 app.On("trackers", func() { 176 var sock = gosock.NewTmpSock(teaconst.ProcessName) 177 reply, err := sock.Send(&gosock.Command{Code: "trackers"}) 178 if err != nil { 179 fmt.Println("[ERROR]" + err.Error()) 180 } else { 181 labelsMap, ok := reply.Params["labels"] 182 if ok { 183 labels, ok := labelsMap.(map[string]interface{}) 184 if ok { 185 if len(labels) == 0 { 186 fmt.Println("no labels yet") 187 } else { 188 var labelNames = []string{} 189 for label := range labels { 190 labelNames = append(labelNames, label) 191 } 192 sort.Strings(labelNames) 193 194 for _, labelName := range labelNames { 195 fmt.Println(labelName + ": " + fmt.Sprintf("%.6f", labels[labelName])) 196 } 197 } 198 } 199 } 200 } 201 }) 202 app.On("goman", func() { 203 var sock = gosock.NewTmpSock(teaconst.ProcessName) 204 reply, err := sock.Send(&gosock.Command{Code: "goman"}) 205 if err != nil { 206 fmt.Println("[ERROR]" + err.Error()) 207 } else { 208 instancesJSON, err := json.MarshalIndent(reply.Params, "", " ") 209 if err != nil { 210 fmt.Println("[ERROR]" + err.Error()) 211 } else { 212 fmt.Println(string(instancesJSON)) 213 } 214 } 215 }) 216 app.On("conns", func() { 217 var sock = gosock.NewTmpSock(teaconst.ProcessName) 218 reply, err := sock.Send(&gosock.Command{Code: "conns"}) 219 if err != nil { 220 fmt.Println("[ERROR]" + err.Error()) 221 } else { 222 resultJSON, err := json.MarshalIndent(reply.Params, "", " ") 223 if err != nil { 224 fmt.Println("[ERROR]" + err.Error()) 225 } else { 226 fmt.Println(string(resultJSON)) 227 } 228 } 229 }) 230 app.On("gc", func() { 231 var sock = gosock.NewTmpSock(teaconst.ProcessName) 232 reply, err := sock.Send(&gosock.Command{Code: "gc"}) 233 if err != nil { 234 fmt.Println("[ERROR]" + err.Error()) 235 } else { 236 if reply == nil { 237 fmt.Println("ok") 238 } else { 239 var paramMap = maps.NewMap(reply.Params) 240 var pauseMS = paramMap.GetFloat64("pauseMS") 241 var costMS = paramMap.GetFloat64("costMS") 242 fmt.Printf("ok, cost: %.4fms, pause: %.4fms", costMS, pauseMS) 243 } 244 } 245 }) 246 app.On("ip.drop", func() { 247 var args = os.Args[2:] 248 if len(args) == 0 { 249 fmt.Println("Usage: edge-node ip.drop IP [--timeout=SECONDS] [--async]") 250 return 251 } 252 var ip = args[0] 253 if len(net.ParseIP(ip)) == 0 { 254 fmt.Println("IP '" + ip + "' is invalid") 255 return 256 } 257 var timeoutSeconds = 0 258 var options = app.ParseOptions(args[1:]) 259 timeout, ok := options["timeout"] 260 if ok { 261 timeoutSeconds = types.Int(timeout[0]) 262 } 263 var async = false 264 _, ok = options["async"] 265 if ok { 266 async = true 267 } 268 269 fmt.Println("drop ip '" + ip + "' for '" + types.String(timeoutSeconds) + "' seconds") 270 var sock = gosock.NewTmpSock(teaconst.ProcessName) 271 reply, err := sock.Send(&gosock.Command{ 272 Code: "dropIP", 273 Params: map[string]interface{}{ 274 "ip": ip, 275 "timeoutSeconds": timeoutSeconds, 276 "async": async, 277 }, 278 }) 279 if err != nil { 280 fmt.Println("[ERROR]" + err.Error()) 281 } else { 282 var errString = maps.NewMap(reply.Params).GetString("error") 283 if len(errString) > 0 { 284 fmt.Println("[ERROR]" + errString) 285 } else { 286 fmt.Println("ok") 287 } 288 } 289 }) 290 app.On("ip.reject", func() { 291 var args = os.Args[2:] 292 if len(args) == 0 { 293 fmt.Println("Usage: edge-node ip.reject IP [--timeout=SECONDS]") 294 return 295 } 296 var ip = args[0] 297 if len(net.ParseIP(ip)) == 0 { 298 fmt.Println("IP '" + ip + "' is invalid") 299 return 300 } 301 var timeoutSeconds = 0 302 var options = app.ParseOptions(args[1:]) 303 timeout, ok := options["timeout"] 304 if ok { 305 timeoutSeconds = types.Int(timeout[0]) 306 } 307 308 fmt.Println("reject ip '" + ip + "' for '" + types.String(timeoutSeconds) + "' seconds") 309 310 var sock = gosock.NewTmpSock(teaconst.ProcessName) 311 reply, err := sock.Send(&gosock.Command{ 312 Code: "rejectIP", 313 Params: map[string]interface{}{ 314 "ip": ip, 315 "timeoutSeconds": timeoutSeconds, 316 }, 317 }) 318 if err != nil { 319 fmt.Println("[ERROR]" + err.Error()) 320 } else { 321 var errString = maps.NewMap(reply.Params).GetString("error") 322 if len(errString) > 0 { 323 fmt.Println("[ERROR]" + errString) 324 } else { 325 fmt.Println("ok") 326 } 327 } 328 }) 329 app.On("ip.close", func() { 330 var args = os.Args[2:] 331 if len(args) == 0 { 332 fmt.Println("Usage: edge-node ip.close IP") 333 return 334 } 335 var ip = args[0] 336 if len(net.ParseIP(ip)) == 0 { 337 fmt.Println("IP '" + ip + "' is invalid") 338 return 339 } 340 341 fmt.Println("close ip '" + ip) 342 343 var sock = gosock.NewTmpSock(teaconst.ProcessName) 344 reply, err := sock.Send(&gosock.Command{ 345 Code: "closeIP", 346 Params: map[string]any{ 347 "ip": ip, 348 }, 349 }) 350 if err != nil { 351 fmt.Println("[ERROR]" + err.Error()) 352 } else { 353 var errString = maps.NewMap(reply.Params).GetString("error") 354 if len(errString) > 0 { 355 fmt.Println("[ERROR]" + errString) 356 } else { 357 fmt.Println("ok") 358 } 359 } 360 }) 361 app.On("ip.remove", func() { 362 var args = os.Args[2:] 363 if len(args) == 0 { 364 fmt.Println("Usage: edge-node ip.remove IP") 365 return 366 } 367 var ip = args[0] 368 if len(net.ParseIP(ip)) == 0 { 369 fmt.Println("IP '" + ip + "' is invalid") 370 return 371 } 372 373 var sock = gosock.NewTmpSock(teaconst.ProcessName) 374 reply, err := sock.Send(&gosock.Command{ 375 Code: "removeIP", 376 Params: map[string]interface{}{ 377 "ip": ip, 378 }, 379 }) 380 if err != nil { 381 fmt.Println("[ERROR]" + err.Error()) 382 } else { 383 var errString = maps.NewMap(reply.Params).GetString("error") 384 if len(errString) > 0 { 385 fmt.Println("[ERROR]" + errString) 386 } else { 387 fmt.Println("ok") 388 } 389 } 390 }) 391 app.On("accesslog", func() { 392 // local sock 393 var tmpDir = os.TempDir() 394 var sockFile = tmpDir + "/" + teaconst.AccessLogSockName 395 _, err := os.Stat(sockFile) 396 if err != nil { 397 if !os.IsNotExist(err) { 398 fmt.Println("[ERROR]" + err.Error()) 399 return 400 } 401 } 402 403 var processSock = gosock.NewTmpSock(teaconst.ProcessName) 404 reply, err := processSock.Send(&gosock.Command{ 405 Code: "accesslog", 406 }) 407 if err != nil { 408 fmt.Println("[ERROR]" + err.Error()) 409 return 410 } 411 if reply.Code == "error" { 412 var errString = maps.NewMap(reply.Params).GetString("error") 413 if len(errString) > 0 { 414 fmt.Println("[ERROR]" + errString) 415 return 416 } 417 } 418 419 conn, err := net.Dial("unix", sockFile) 420 if err != nil { 421 fmt.Println("[ERROR]start reading access log failed: " + err.Error()) 422 return 423 } 424 defer func() { 425 _ = conn.Close() 426 }() 427 var buf = make([]byte, 1024) 428 for { 429 n, err := conn.Read(buf) 430 if n > 0 { 431 fmt.Print(string(buf[:n])) 432 } 433 if err != nil { 434 break 435 } 436 } 437 }) 438 app.On("bandwidth", func() { 439 var sock = gosock.NewTmpSock(teaconst.ProcessName) 440 reply, err := sock.Send(&gosock.Command{Code: "bandwidth"}) 441 if err != nil { 442 fmt.Println("[ERROR]" + err.Error()) 443 return 444 } 445 var statsMap = maps.NewMap(reply.Params).Get("stats") 446 statsJSON, err := json.MarshalIndent(statsMap, "", " ") 447 if err != nil { 448 fmt.Println("[ERROR]" + err.Error()) 449 return 450 } 451 fmt.Println(string(statsJSON)) 452 }) 453 app.On("disk", func() { 454 var args = os.Args[2:] 455 if len(args) > 0 { 456 switch args[0] { 457 case "speed": 458 speedMB, isFast, err := fsutils.CheckDiskIsFast() 459 if err != nil { 460 fmt.Println("[ERROR]" + err.Error()) 461 } else { 462 fmt.Printf("Speed: %.0fMB/s\n", speedMB) 463 if isFast { 464 fmt.Println("IsFast: true") 465 } else { 466 fmt.Println("IsFast: false") 467 } 468 } 469 default: 470 fmt.Println("Usage: edge-node disk [speed]") 471 } 472 } else { 473 fmt.Println("Usage: edge-node disk [speed]") 474 } 475 }) 476 app.On("cache.garbage", func() { 477 fmt.Println("scanning ...") 478 479 var shouldDelete bool 480 for _, arg := range os.Args { 481 if strings.TrimLeft(arg, "-") == "delete" { 482 shouldDelete = true 483 } 484 } 485 486 var progressSock = gosock.NewTmpSock(teaconst.CacheGarbageSockName) 487 progressSock.OnCommand(func(cmd *gosock.Command) { 488 var params = maps.NewMap(cmd.Params) 489 if cmd.Code == "progress" { 490 fmt.Printf("%.2f%% %d\n", params.GetFloat64("progress")*100, params.GetInt("count")) 491 _ = cmd.ReplyOk() 492 } 493 }) 494 go func() { 495 _ = progressSock.Listen() 496 }() 497 time.Sleep(1 * time.Second) 498 499 var sock = gosock.NewTmpSock(teaconst.ProcessName) 500 reply, err := sock.Send(&gosock.Command{ 501 Code: "cache.garbage", 502 Params: map[string]any{"delete": shouldDelete}, 503 }) 504 if err != nil { 505 fmt.Println("[ERROR]" + err.Error()) 506 return 507 } 508 509 var params = maps.NewMap(reply.Params) 510 if params.GetBool("isOk") { 511 var count = params.GetInt("count") 512 fmt.Println("found", count, "bad caches") 513 514 if count > 0 { 515 fmt.Println("======") 516 var sampleFiles = params.GetSlice("sampleFiles") 517 for _, file := range sampleFiles { 518 fmt.Println(types.String(file)) 519 } 520 if count > len(sampleFiles) { 521 fmt.Println("... more files") 522 } 523 } 524 } else { 525 fmt.Println("[ERROR]" + params.GetString("error")) 526 } 527 }) 528 app.On("config", func() { 529 var configString = os.Args[len(os.Args)-1] 530 if configString == "config" { 531 fmt.Println("Usage: edge-node config '\nrpc.endpoints: [\"...\"]\nnodeId: \"...\"\nsecret: \"...\"\n'") 532 return 533 } 534 535 var config = &configs.APIConfig{} 536 err := yaml.Unmarshal([]byte(configString), config) 537 if err != nil { 538 fmt.Println("[ERROR]decode config failed: " + err.Error()) 539 return 540 } 541 542 err = config.Init() 543 if err != nil { 544 fmt.Println("[ERROR]validate config failed: " + err.Error()) 545 return 546 } 547 548 // marshal again 549 configYAML, err := yaml.Marshal(config) 550 if err != nil { 551 fmt.Println("[ERROR]encode config failed: " + err.Error()) 552 return 553 } 554 555 err = os.WriteFile(Tea.Root + "/configs/api_node.yaml", configYAML, 0666) 556 if err != nil { 557 fmt.Println("[ERROR]write config failed: " + err.Error()) 558 return 559 } 560 561 fmt.Println("success") 562 }) 563 app.Run(func() { 564 var node = nodes.NewNode() 565 node.Start() 566 }) 567 }