浏览代码

add /v2/casaos/health/ports to get ports in use (#1023)

Signed-off-by: Tiger Wang <tigerwang@outlook.com>
Tiger Wang 2 年之前
父节点
当前提交
8e1b9b82c1
共有 8 个文件被更改,包括 191 次插入20 次删除
  1. 39 0
      api/casaos/openapi.yaml
  2. 47 16
      codegen/casaos_api.go
  3. 6 1
      go.mod
  4. 4 0
      go.sum
  5. 4 3
      route/v2.go
  6. 17 0
      route/v2/health.go
  7. 56 0
      service/health.go
  8. 18 0
      service/health_test.go

+ 39 - 0
api/casaos/openapi.yaml

@@ -47,6 +47,19 @@ paths:
           $ref: "#/components/responses/GetHealthServicesOK"
         "500":
           $ref: "#/components/responses/ResponseInternalServerError"
+
+  /health/ports:
+    get:
+      tags:
+        - Health methods
+      summary: Get port in use
+      operationId: getHealthPorts
+      responses:
+        "200":
+          $ref: "#/components/responses/GetHealthPortsOK"
+        "500":
+          $ref: "#/components/responses/ResponseInternalServerError"
+
   /file/test:
     get:
       tags:
@@ -93,6 +106,17 @@ components:
                   data:
                     $ref: "#/components/schemas/HealthServices"
 
+    GetHealthPortsOK:
+      description: OK
+      content:
+        application/json:
+          schema:
+            allOf:
+              - $ref: "#/components/schemas/BaseResponse"
+              - properties:
+                  data:
+                    $ref: "#/components/schemas/HealthPorts"
+
   schemas:
     BaseResponse:
       properties:
@@ -114,3 +138,18 @@ components:
           items:
             type: string
             example: "casaos.service"
+
+    HealthPorts:
+      properties:
+        tcp:
+          type: array
+          items:
+            type: integer
+            example: 80
+          x-go-name: TCP
+        udp:
+          type: array
+          items:
+            type: integer
+            example: 53
+          x-go-name: UDP

+ 47 - 16
codegen/casaos_api.go

@@ -26,12 +26,26 @@ type BaseResponse struct {
 	Message *string `json:"message,omitempty"`
 }
 
+// HealthPorts defines model for HealthPorts.
+type HealthPorts struct {
+	TCP *[]int `json:"tcp,omitempty"`
+	UDP *[]int `json:"udp,omitempty"`
+}
+
 // HealthServices defines model for HealthServices.
 type HealthServices struct {
 	NotRunning *[]string `json:"not_running,omitempty"`
 	Running    *[]string `json:"running,omitempty"`
 }
 
+// GetHealthPortsOK defines model for GetHealthPortsOK.
+type GetHealthPortsOK struct {
+	Data *HealthPorts `json:"data,omitempty"`
+
+	// Message message returned by server side if there is any
+	Message *string `json:"message,omitempty"`
+}
+
 // GetHealthServicesOK defines model for GetHealthServicesOK.
 type GetHealthServicesOK struct {
 	Data *HealthServices `json:"data,omitempty"`
@@ -51,6 +65,9 @@ type ServerInterface interface {
 	// Test file methods
 	// (GET /file/test)
 	GetFileTest(ctx echo.Context) error
+	// Get port in use
+	// (GET /health/ports)
+	GetHealthPorts(ctx echo.Context) error
 	// Get service status
 	// (GET /health/services)
 	GetHealthServices(ctx echo.Context) error
@@ -72,6 +89,17 @@ func (w *ServerInterfaceWrapper) GetFileTest(ctx echo.Context) error {
 	return err
 }
 
+// GetHealthPorts converts echo context to params.
+func (w *ServerInterfaceWrapper) GetHealthPorts(ctx echo.Context) error {
+	var err error
+
+	ctx.Set(Access_tokenScopes, []string{""})
+
+	// Invoke the callback with all the unmarshalled arguments
+	err = w.Handler.GetHealthPorts(ctx)
+	return err
+}
+
 // GetHealthServices converts echo context to params.
 func (w *ServerInterfaceWrapper) GetHealthServices(ctx echo.Context) error {
 	var err error
@@ -112,6 +140,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
 	}
 
 	router.GET(baseURL+"/file/test", wrapper.GetFileTest)
+	router.GET(baseURL+"/health/ports", wrapper.GetHealthPorts)
 	router.GET(baseURL+"/health/services", wrapper.GetHealthServices)
 
 }
@@ -119,22 +148,24 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
 // Base64 encoded, gzipped, json marshaled Swagger object
 var swaggerSpec = []string{
 
-	"H4sIAAAAAAAC/7xW70/jRhD9V1bTfoDKxBGoUmXpPnC9wiFUpSpIrUSi3GY9sfewd92ZcSBF/t+rXZtL",
-	"SCiC649PiffHe2/e7szsAxhfN96hE4bsAQi58Y4xfpyjfERdSXmFtLIGeXIZho13gk7CX900lTVarHfp",
-	"Z/YujLEpsdZxtqomS8huHuBbwiVk8E26YUv7dZy+14y/DrTQJQ/QkG+QxPYici0R7CWIpyqh67pZ13UJ",
-	"5MiGbBPkQQaTS+gSeKS6cILkdBV2If1E5OlNwb0+pH0lj9yqJ1c9+5a4Nxr9T7QEV7pkAIuOP9mR7Z5H",
-	"jcy6iBNPgYYJRSgtOczVYq24j49tjsoulZRIqCwr7daQAN7ruqkQMoAECHU+cdUaMqEWE5B1E2ZYyLqi",
-	"F75zzHvSnJc5tc6FDdkDWME6jm94jGbtecQ9BOyxfBnQRHodvl+Dd1RowTu9fj1uDIfRtGRlfRWs7yPQ",
-	"xiDzXPwtxiO2wdgSdY4ECThdB4zTVkpP9s94GzZcurGXuO6dsm7p909o2o7HJ6axRlrC+IFTp5RS/QT7",
-	"lgyqGnOr303hoCFcIvGR8ZWno3hBMFO5ptvDKSgmwyjvplCKNJylKem7UWGlbBctIw13d2R8nV4Y/K3U",
-	"FV6jKdPKFz6ttXVpb97wM19o55DmAX7ubFHK/IfxuLkfNa6YwteKrQLQf6hW7mykmC+qFl8WbOtC6SpI",
-	"+FGznlz1ov5/Rb2adOcWTF2vSp3+cqEa8iubI6vassGq0g59y6pGKX3OaulJ5Xa5REInig06TdbzKKCc",
-	"eVKWucWQ47nKLZuW2XrHiWoq1IxqZdlKKAXq5tzKx3ahCBvPVjytZwePbvRO7IffyzxUntRnb5268S2p",
-	"D5aNp3yzO+8HRkWR3ro/TheL9wv8/XA0jeliJebuJmBIYIXEfZKsjkO6+gadbixkcDIaj04ggUZLGXM0",
-	"XdoKU0GOhblA2U+0a2RRYdmjZyOIkBRT9iKHLPTWMxtiYonFb6vtHo/Hf1fUv6xLtzpFl8D3b9nyXOeL",
-	"9aita03r5/QH23TBkN3A2fbwLOxLy1iXU94qzM/aco6ihnqqWLS0rPxSoTal+jRU0u8+qQHmWct2OsDX",
-	"GPfco+bfdzCEOgQyhLplYc+/beJWN4jvpad94GbWzcKCQMZxvqUKMkhXx0P2Q1gwwO+6fnA9+TA53LSP",
-	"Hfbw4np5w5MTD0T3R6KLc/Jt0/MN637euyt7gc66vwIAAP//o5zNVnEKAAA=",
+	"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",
+	"BwAA///xoj5w+wwAAA==",
 }
 
 // GetSwagger returns the content of the embedded swagger specification file

+ 6 - 1
go.mod

@@ -33,15 +33,18 @@ require (
 	github.com/patrickmn/go-cache v2.1.0+incompatible
 	github.com/pkg/errors v0.9.1
 	github.com/robfig/cron/v3 v3.0.1
+	github.com/samber/lo v1.38.1
 	github.com/satori/go.uuid v1.2.0
 	github.com/shirou/gopsutil/v3 v3.23.2
 	github.com/sirupsen/logrus v1.9.0
+	github.com/stretchr/testify v1.8.2
 	github.com/tidwall/gjson v1.14.4
 	go.uber.org/goleak v1.2.1
 	go.uber.org/zap v1.24.0
 	golang.org/x/crypto v0.7.0
 	golang.org/x/oauth2 v0.6.0
 	golang.org/x/sync v0.1.0
+	golang.org/x/sys v0.6.0
 	gorm.io/gorm v1.24.6
 	gotest.tools v2.2.0+incompatible
 )
@@ -53,6 +56,7 @@ require (
 	github.com/bytedance/sonic v1.8.5 // indirect
 	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
 	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
 	github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
 	github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
@@ -100,6 +104,7 @@ require (
 	github.com/pelletier/go-toml/v2 v2.0.7 // indirect
 	github.com/perimeterx/marshmallow v1.1.4 // indirect
 	github.com/pierrec/lz4/v4 v4.1.17 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 	github.com/tidwall/match v1.1.1 // indirect
@@ -116,9 +121,9 @@ require (
 	go.uber.org/atomic v1.10.0 // indirect
 	go.uber.org/multierr v1.10.0 // indirect
 	golang.org/x/arch v0.3.0 // indirect
+	golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
 	golang.org/x/image v0.6.0 // indirect
 	golang.org/x/net v0.8.0 // indirect
-	golang.org/x/sys v0.6.0 // indirect
 	golang.org/x/text v0.8.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect

+ 4 - 0
go.sum

@@ -255,6 +255,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
+github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
+github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
 github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU=
@@ -324,6 +326,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
+golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
 golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4=
 golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0=

+ 4 - 3
route/v2.go

@@ -71,10 +71,10 @@ func InitV2Router() http.Handler {
 	e.Use(echo_middleware.JWTWithConfig(echo_middleware.JWTConfig{
 		Skipper: func(c echo.Context) bool {
 			return c.RealIP() == "::1" || c.RealIP() == "127.0.0.1"
-			//return true
+			// return true
 		},
 		ParseTokenFunc: func(token string, c echo.Context) (interface{}, error) {
-			claims, code := jwt.Validate(token)
+			claims, code := jwt.Validate(token) // TODO - needs JWT validation
 			if code != common_err.SUCCESS {
 				return nil, echo.ErrUnauthorized
 			}
@@ -137,6 +137,7 @@ func InitV2DocRouter(docHTML string, docYAML string) http.Handler {
 		}
 	})
 }
+
 func InitFile() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		filePath := r.URL.Query().Get("path")
@@ -174,7 +175,7 @@ func InitDir() http.Handler {
 		// handles only single files not folders and multiple files
 		//		if len(list) == 1 {
 
-		//filePath := list[0]
+		// filePath := list[0]
 		//			info, err := os.Stat(filePath)
 		//			if err != nil {
 

+ 17 - 0
route/v2/health.go

@@ -24,3 +24,20 @@ func (s *CasaOS) GetHealthServices(ctx echo.Context) error {
 		},
 	})
 }
+
+func (s *CasaOS) GetHealthPorts(ctx echo.Context) error {
+	tcpPorts, udpPorts, err := service.MyService.Health().Ports()
+	if err != nil {
+		message := err.Error()
+		return ctx.JSON(http.StatusInternalServerError, codegen.ResponseInternalServerError{
+			Message: &message,
+		})
+	}
+
+	return ctx.JSON(http.StatusOK, codegen.GetHealthPortsOK{
+		Data: &codegen.HealthPorts{
+			TCP: &tcpPorts,
+			UDP: &udpPorts,
+		},
+	})
+}

+ 56 - 0
service/health.go

@@ -1,11 +1,21 @@
 package service
 
 import (
+	"bufio"
+	"errors"
+	"fmt"
+	"os"
+	"strconv"
+	"strings"
+
+	"github.com/samber/lo"
+
 	"github.com/IceWhaleTech/CasaOS-Common/utils/systemctl"
 )
 
 type HealthService interface {
 	Services() (map[bool]*[]string, error)
+	Ports() ([]int, []int, error)
 }
 
 type service struct{}
@@ -34,6 +44,52 @@ func (s *service) Services() (map[bool]*[]string, error) {
 	return result, nil
 }
 
+func (s *service) Ports() ([]int, []int, error) {
+	usedPorts := map[string]map[int]struct{}{
+		"tcp": {},
+		"udp": {},
+	}
+
+	for _, protocol := range []string{"tcp", "udp"} {
+		filename := fmt.Sprintf("/proc/net/%s", protocol)
+
+		file, err := os.Open(filename)
+		if err != nil {
+			return nil, nil, errors.New("Failed to open " + filename)
+		}
+		defer file.Close()
+
+		scanner := bufio.NewScanner(file)
+		for scanner.Scan() {
+			line := scanner.Text()
+			fields := strings.Fields(line)
+			if len(fields) < 2 {
+				continue
+			}
+
+			localAddress := fields[1]
+			addressParts := strings.Split(localAddress, ":")
+			if len(addressParts) < 2 {
+				continue
+			}
+
+			portHex := addressParts[1]
+			port, err := strconv.ParseInt(portHex, 16, 0)
+			if err != nil {
+				continue
+			}
+
+			usedPorts[protocol][int(port)] = struct{}{}
+		}
+
+		if err := scanner.Err(); err != nil {
+			return nil, nil, errors.New("Error reading from " + filename)
+		}
+	}
+
+	return lo.Keys(usedPorts["tcp"]), lo.Keys(usedPorts["udp"]), nil
+}
+
 func NewHealthService() HealthService {
 	return &service{}
 }

+ 18 - 0
service/health_test.go

@@ -0,0 +1,18 @@
+package service_test
+
+import (
+	"testing"
+
+	"github.com/IceWhaleTech/CasaOS/service"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPorts(t *testing.T) {
+	service := service.NewHealthService()
+
+	tcpPorts, udpPorts, err := service.Ports()
+	assert.NoError(t, err)
+
+	assert.NotEmpty(t, tcpPorts)
+	assert.NotEmpty(t, udpPorts)
+}