1
0
mirror of https://github.com/khorevaa/kubodin.git synced 2025-01-17 18:44:40 +02:00

alfa version

This commit is contained in:
khorevaa 2021-03-04 16:09:08 +03:00
parent f694075ba8
commit 3409afff64
54 changed files with 22577 additions and 111 deletions

View File

@ -23,12 +23,9 @@ jobs:
name: Docker Login
if: success() && startsWith(github.ref, 'refs/tags/v')
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
run: |
echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin
echo "${GITHUB_TOKEN}" | docker login ghcr.io --username $GITHUB_ACTOR --password-stdin
echo "${GITHUB_TOKEN}" | docker login ghcr.io --username $GITHUB_ACTOR --password-stdin
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
@ -37,6 +34,7 @@ jobs:
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
-
name: Clear
if: always() && startsWith(github.ref, 'refs/tags/v')

2
.gitignore vendored
View File

@ -2,7 +2,7 @@
*.gz
*.zip
api/.sequence
dist
.idea
*.logs

View File

@ -1,11 +1,15 @@
project_name: app-template
project_name: kubodin
env:
- GO111MODULE=on
- GOPRIVATE=github.com/khorevaa/ras-client
# - GOPROXY=https://goproxy.io
before:
hooks:
- git config --global url."https://khorevaa:{{ .Env.ACCESS_TOKEN }}@github.com/khorevaa/".insteadOf "https://github.com/khorevaa/"
- go mod tidy
- go get -u github.com/swaggo/swag/cmd/swag
- swag init --parseDependency -g ./main.go
builds:
- env:
- CGO_ENABLED=0
@ -32,25 +36,22 @@ changelog:
- Merge branch
dockers:
- image_templates:
- 'khorevaa/{{.ProjectName}}:{{ .Tag }}'
- 'khorevaa/{{.ProjectName}}:v{{ .Major }}.{{ .Minor }}'
- 'khorevaa/{{.ProjectName}}:latest'
- 'ghcr.io/khorevaa/{{.ProjectName}}:{{ .Tag }}'
- 'ghcr.io/khorevaa/{{.ProjectName}}:v{{ .Major }}.{{ .Minor }}'
- 'ghcr.io/khorevaa/{{.ProjectName}}:latest'
dockerfile: Dockerfile
binaries:
- app-template
- kubodin
build_flag_templates:
- "--label=org.label-schema.schema-version=1.0"
- "--label=org.label-schema.version={{.Version}}"
- "--label=org.label-schema.name={{.ProjectName}}"
- "--label=com.github.actions.name={{.ProjectName}}"
- "--label=com.github.actions.description=Setup new app-template description"
- "--label=com.github.actions.description=API server for Kubernetes & 1C.Enterprise"
- "--label=com.github.actions.icon=terminal"
- "--label=com.github.actions.color=blue"
- "--label=repository=http://github.com/khorevaa/app-template"
- "--label=homepage=http://github.com/khorevaa/app-template"
- "--label=repository=http://github.com/khorevaa/kubodin"
- "--label=homepage=http://github.com/khorevaa/kubodin"
- "--label=maintainerAleksey Khorev khorevaa@gmail.com"
archives:
- name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'

View File

@ -1,3 +1,3 @@
FROM scratch
COPY app-template /
ENTRYPOINT ["/app-template"]
COPY kubodin /
ENTRYPOINT ["/kubodin"]

91
api/admin.go Normal file
View File

@ -0,0 +1,91 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type agentApi struct {
service service.Service
}
func (a *agentApi) Routes(r fiber.Router) {
r.Get("/agent/version", withClient(a.Version))
r.Get("/agent/admins", withClient(a.List))
r.Post("/agent/admins", withClient(a.RegAgentAdmin))
r.Delete("/agent/admins/:admin", withClient(a.UnregAgentAdmin))
}
// List получение списка администраторов агента на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка администраторов агента на сервере 1С Предприятие
// @Description получение списка администраторов агента на сервере 1С Предприятие
// @Tags admins,agent
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.UsersList}
// @Failure 500 {object} Response
// @Router /app/{app}/agent/admins [get]
func (a *agentApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Version получение версии агента на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение версии агента на сервере 1С Предприятие
// @Description получение версии агента на сервере 1С Предприятие
// @Tags agent
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=string}
// @Failure 500 {object} Response
// @Router /app/{app}/agent/version [get]
func (a *agentApi) Version(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// RegAgentAdmin выполняет регистрацию нового адмнимистратор на агенте сервера 1С Предприятие
// Swagger-spec:
// @Summary выполняет регистрацию нового адмнимистратор на агенте сервера 1С Предприятиеи
// @Description выполняет регистрацию нового адмнимистратор на агенте сервера 1С Предприятие
// @Tags admins,agent
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param req body serialize.UserInfo true "user info"
// @Success 200 {object} Response{data=serialize.UserInfo}
// @Failure 404 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/agent/admins [post]
func (a *agentApi) RegAgentAdmin(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// UnregAgentAdmin Удаление администратора агента на сервере 1С Предприятие
// Swagger-spec:
// @Summary Удаление администратора агента на сервере 1С Предприятие
// @Description Удаление администратора агента на сервере 1С Предприятие
// @Tags admins,agent
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param admin path string true "admin name"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=string}
// @Failure 500 {object} Response
// @Router /app/{app}/agent/admins/{admin} [delete]
func (a *agentApi) UnregAgentAdmin(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}

140
api/app.go Normal file
View File

@ -0,0 +1,140 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type AppApi struct {
service service.Service
}
func (a *AppApi) Routes(r fiber.Router) {
r.Get("/app", a.List)
r.Post("/app", a.Create)
r.Get("/app/:app", a.Info)
r.Post("/app/:app", a.Update)
r.Delete("/app/:app", a.Delete)
router := r.Group("/app/:app")
parentApi := []route{
&clusterApi{service: a.service},
&infobasesApi{service: a.service},
&sessionsApi{service: a.service},
&connectionsApi{service: a.service},
&managersApi{service: a.service},
&servicesApi{service: a.service},
&locksApi{service: a.service},
&blockerApi{service: a.service},
&processesApi{service: a.service},
&licensesApi{service: a.service},
&healthAppApi{service: a.service},
&agentApi{service: a.service},
}
for _, api := range parentApi {
api.Routes(router)
}
}
// List Получение списка зарегистрированных серверов 1С.Предприятие
// Swagger-spec:
// @Summary Получение списка зарегистрированных серверов 1С.Предприятие
// @Description Получение списка зарегистрированных серверов 1С.Предприятие
// @Tags app
// @Accept json
// @Produce json
// @Success 200 {object} Response{data=[]models.AppServer}
// @Failure 400 {object} Response
// @Router /app [get]
func (a *AppApi) List(ctx *fiber.Ctx) error {
apps, err := a.service.GetAppServers()
if err != nil {
return ErrorResponse(ctx, err, "error get app list")
}
return SuccessResponse(ctx, apps)
}
// Info получение информации о зарегистрированном сервере 1С.Предприятие
// Swagger-spec:
// @Summary Получение информации о зарегистрированном сервере 1С.Предприятие
// @Description Получение информации о зарегистрированном сервере 1С.Предприятие
// @Tags app
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Success 200 {object} Response{data=models.AppServer}
// @Failure 400 {object} Response
// @Router /app/{app} [get]
func (a *AppApi) Info(ctx *fiber.Ctx) error {
name := ctx.Params("app")
app, err := a.service.GetAppServer(name)
if err != nil {
return ErrorResponse(ctx, err, "error get app info")
}
return SuccessResponse(ctx, app)
}
// Update обновление информации о зарегистрированном сервере 1С.Предприятие
// Swagger-spec:
// @Summary Обновление информации о зарегистрированном сервере 1С.Предприятие
// @Description Обновление информации о зарегистрированном сервере 1С.Предприятие
// @Tags app
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param req body models.AppServer true "app info"
// @Success 200 {object} Response{data=models.AppServer}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app} [post]
func (a *AppApi) Update(ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Create выполняет регистрацию сервера 1С.Предприятие в приложении
// Swagger-spec:
// @Summary выполняет регистрацию сервера 1С.Предприятие в приложении
// @Description выполняет регистрацию сервера 1С.Предприятие в приложении
// @Tags app
// @Accept json
// @Produce json
// @Param req body models.AppServer true "app info"
// @Success 200 {object} Response{data=models.AppServer}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app [post]
func (a *AppApi) Create(ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Delete выполняет отмену зарегистрирации сервера 1С.Предприятие
// Swagger-spec:
// @Summary Удаление информации о регистрации сервера приложений 1С.Предприятие
// @Description Удаление информации о регистрации сервера приложений 1С.Предприятие
// @Tags app
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Success 200 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app} [delete]
func (a *AppApi) Delete(ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}

131
api/app_test.go Normal file
View File

@ -0,0 +1,131 @@
package api
import (
"github.com/khorevaa/kubodin/models"
"github.com/steinfletcher/apitest"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
"github.com/stretchr/testify/suite"
"net/http"
"testing"
)
type appSuite struct {
baseSuite
}
func TestAppTestSuite(t *testing.T) {
suite.Run(t, new(appSuite))
}
func (s *appSuite) TestAppList() {
s.api().
Post("/api/v1/app").
JSON(&models.AppServer{
Name: "test",
Addr: "localhost",
Port: "1545",
}).
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).End()
s.api().Report(apitest.SequenceDiagram()).
Get("/api/v1/app").
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).
End()
}
func (s *appSuite) TestAppReg() {
s.api().
Post("/api/v1/app").
JSON(&models.AppServer{
Name: "test",
Addr: "localhost",
Port: "1545",
}).
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).End()
}
func (s *appSuite) TestAppUnreg() {
s.api().
Postf("/api/v1/app/%s", "test").
JSON(&models.AppServer{
Name: "test",
Addr: "localhost",
Port: "1546",
}).
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).
End()
s.api().
Deletef("/api/v1/app/%s", "test").
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).End()
}
func (s *appSuite) TestHealthStatus() {
s.api().
Get("/api/v1/health").
Expect(s.T()).
Assert(jsonpath.Equal(`$.status`, true)).
Status(http.StatusOK).
End()
}
func (s *appSuite) TestHealthReadinessStatus() {
s.api().
Postf("/api/v1/app/%s", "test").
JSON(&models.AppServer{
Name: "test",
Addr: "localhost",
Port: "1546",
}).
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).End()
s.api().
Get("/api/v1/health/readiness").
Expect(s.T()).
Assert(jsonpath.Equal(`$.status`, true)).
Status(http.StatusOK).
End()
s.api().
Deletef("/api/v1/app/%s", "test").
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).End()
}
func (s *appSuite) TestHealthReadinessStatusBad() {
s.api().
Postf("/api/v1/app/%s", "test").
JSON(&models.AppServer{
Name: "test",
Addr: "localhost",
Port: "1545",
}).
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).End()
s.api().
Get("/api/v1/health/readiness").
Expect(s.T()).
Assert(jsonpath.Equal(`$.status`, false)).
Status(http.StatusServiceUnavailable).
End()
}

326
api/blocker.go Normal file
View File

@ -0,0 +1,326 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type blockerApi struct {
service service.Service
}
func (a *blockerApi) Routes(r fiber.Router) {
r.Get("/block", withClient(a.GetBlock))
r.Post("/block", withClient(a.PostBlock))
r.Get("/unblock", withClient(a.GetUnblock))
r.Post("/unblock", withClient(a.PostUnblock))
}
// Block установка блокировки на информационную базу на кластере
// Swagger-spec:
// @Summary Установка блокировки на информационную базу на кластере
// @Description установка блокировки на информационную базу на кластере
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase query string true "infobase uuid or name"
// @Param sessions-deny query string true "session deny"
// @Param message query string false "message to user"
// @Param permission-code query string false "permission code"
// @Param denied-parameter query string false "denied parameter"
// @Param permission-code query string false "permission code"
// @Param scheduled-jobs-deny query bool false "scheduled jobs deny"
// @Param denied-from query string false "denied from time"
// @Param denied-to query string false "denied to time"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=models.InfobaseUnblocker}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/block [get]
func (a *blockerApi) GetBlock(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Block Установка блокировки конкретную на информационную базу на кластере
// Swagger-spec:
// @Summary Установка блокировки на выбранную информационную базу на кластере
// @Description Установка блокировки на выбранную информационную базу на кластере
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase path string true "infobase uuid or name"
// @Param sessions-deny query string true "session deny"
// @Param message query string false "message to user"
// @Param permission-code query string false "permission code"
// @Param denied-parameter query string false "denied parameter"
// @Param permission-code query string false "permission code"
// @Param scheduled-jobs-deny query bool false "scheduled jobs deny"
// @Param denied-from query string false "denied from time"
// @Param denied-to query string false "denied to time"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=models.InfobaseUnblocker}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/infobases/{infobase}/block [get]
func (a *blockerApi) GetBlockInfobase() {}
// Block Установка блокировки на произвольную информационную базу на сервер 1С Предприятие
// Swagger-spec:
// @Summary Установка блокировки на произвольную информационную базу на сервер 1С Предприятие
// @Description Установка блокировки на произвольную информационную базу на сервер 1С Предприятие
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase-id query string true "infobase uuid or name"
// @Param sessions-deny query string true "session deny"
// @Param message query string false "message to user"
// @Param permission-code query string false "permission code"
// @Param denied-parameter query string false "denied parameter"
// @Param permission-code query string false "permission code"
// @Param scheduled-jobs-deny query bool false "scheduled jobs deny"
// @Param denied-from query string false "denied from time" default("now")
// @Param denied-to query string false "denied to time"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=models.InfobaseUnblocker}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/block [get]
func (a *blockerApi) GetlockApp() {}
// Block Установка блокировки конкретную на информационную базу на сервер 1С Предприятие
// Swagger-spec:
// @Summary Установка блокировки конкретную на информационную базу на сервер 1С Предприятие
// @Description Установка блокировки конкретную на информационную базу на сервер 1С Предприятие
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase path string true "infobase uuid or name"
// @Param sessions-deny query string true "session deny"
// @Param message query string false "message to user"
// @Param permission-code query string false "permission code"
// @Param denied-parameter query string false "denied parameter"
// @Param permission-code query string false "permission code"
// @Param scheduled-jobs-deny query bool false "scheduled jobs deny"
// @Param denied-from query string false "denied from time"
// @Param denied-to query string false "denied to time"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=models.InfobaseUnblocker}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/infobases/{infobase}/block [get]
func (a *blockerApi) GetBlockAppInfobase() {}
// Block установка блокировки на информационную базу на кластере
// Swagger-spec:
// @Summary Установка блокировки на информационную базу на кластере
// @Description установка блокировки на информационную базу на кластере
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param body body models.InfobaseBlocker true "block info"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=models.InfobaseUnblocker}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/block [post]
func (a *blockerApi) PostBlock(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Block установка блокировки на конкретную информационную базу на кластере
// Swagger-spec:
// @Summary Установка блокировки на конкретную информационную базу на кластере
// @Description установка блокировки на конкретную информационную базу на кластере
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase path string true "infobase uuid or name"
// @Param body body models.InfobaseBlocker true "block info"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=models.InfobaseUnblocker}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/infobases/{infobase}/block [post]
func (a *blockerApi) PostBlockInfobase() {}
// Block Установка блокировки на произвольную информационную базу на сервер 1С Предприятие
// Swagger-spec:
// @Summary Установка блокировки на произвольную информационную базу на сервер 1С Предприятие
// @Description Установка блокировки на произвольную информационную базу на сервер 1С Предприятие
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param body body models.InfobaseBlocker true "block info"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=models.InfobaseUnblocker}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/block [post]
func (a *blockerApi) PostBlockApp() {}
// Block Установка блокировки конкретную на информационную базу на сервер 1С Предприятие
// Swagger-spec:
// @Summary Установка блокировки конкретную на информационную базу на сервер 1С Предприятие
// @Description Установка блокировки конкретную на информационную базу на сервер 1С Предприятие
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase path string true "infobase uuid or name"
// @Param body body models.InfobaseBlocker true "block info"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=models.InfobaseUnblocker}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/infobases/{infobase}/block [post]
func (a *blockerApi) PostBlockAppInfobase() {}
// GetUnblock Снятие блокировки на информационную базу на кластере
// Swagger-spec:
// @Summary Снятие блокировки на информационную базу на кластере
// @Description Снятие блокировки на информационную базу на кластере
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase query string true "infobase uuid or name"
// @Param sessions-deny query string true "session deny"
// @Param denied-parameter query string false "denied parameter"
// @Param permission-code query string false "permission code"
// @Param scheduled-jobs-deny query bool false "scheduled jobs deny"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.InfobaseSummaryInfo}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/unblock [get]
func (a *blockerApi) GetUnblock(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// GetUnblock Снятие блокировки на информационную базу на кластере
// Swagger-spec:
// @Summary Снятие блокировки на информационную базу на кластере
// @Description Снятие блокировки на информационную базу на кластере
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase query string true "infobase uuid or name"
// @Param sessions-deny query string true "session deny"
// @Param denied-parameter query string false "denied parameter"
// @Param permission-code query string false "permission code"
// @Param scheduled-jobs-deny query bool false "scheduled jobs deny"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.InfobaseSummaryInfo}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/unblock [get]
func (a *blockerApi) GetUnblockApp() {}
// PostUnblock Снимает блокировку на информационную базу на кластере
// Swagger-spec:
// @Summary Снимает блокировку на информационную базу на кластере
// @Description Снимает блокировку на информационную базу на кластере
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param body body models.InfobaseUnblocker true "unblock info"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.InfobaseSummaryInfo}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/unblock [post]
func (a *blockerApi) PostUnblock(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// PostUnblockApp Снимает блокировку на информационную базу на сервере 1С Прдприятие
// Swagger-spec:
// @Summary Снимает блокировку на информационную базу на сервере 1С Прдприятие
// @Description Снимает блокировку на информационную базу на сервере 1С Прдприятие
// @Tags blocker
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param body body models.InfobaseUnblocker true "unblock info"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.InfobaseSummaryInfo}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/unblock [post]
func (a *blockerApi) PostUnblockApp() {}

93
api/cluster.go Normal file
View File

@ -0,0 +1,93 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type clusterApi struct {
service service.Service
}
func (a *clusterApi) Routes(r fiber.Router) {
r.Get("/clusters", withClient(a.List))
r.Get("/clusters/:cluster", withClient(a.Info))
r.Post("/clusters", withClient(a.Reg))
r.Delete("/clusters/:cluster", withClient(a.Unreg))
router := r.Group("/clusters/:cluster")
parentApi := []route{
&infobasesApi{service: a.service},
&sessionsApi{service: a.service},
&connectionsApi{service: a.service},
&managersApi{service: a.service},
&servicesApi{service: a.service},
&locksApi{service: a.service},
&processesApi{service: a.service},
&licensesApi{service: a.service},
&clusterAdminApi{service: a.service},
}
for _, api := range parentApi {
api.Routes(router)
}
}
// List получение списка кластеров на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка кластеров на сервере 1С Предприятие
// @Description получение списка кластеров на сервере 1С Предприятие
// @Tags clusters
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=[]serialize.ClusterInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters [get]
func (a *clusterApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
list, err := a.service.GetClusters(client)
if err != nil {
return ErrorResponse(ctx, err, "error get clusters")
}
return SuccessResponse(ctx, list)
}
// Info получение информации о кластере на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение информации о кластере на сервере 1С Предприятие
// @Description получение информации о кластере на сервере 1С Предприятие
// @Tags clusters
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.ClusterInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster} [get]
func (a *clusterApi) Info(client service.ClientContext, ctx *fiber.Ctx) error {
val, err := a.service.GetClusterInfo(client)
if err != nil {
return ErrorResponse(ctx, err, "error get cluster info")
}
return SuccessResponse(ctx, val)
}
func (a *clusterApi) Reg(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
func (a *clusterApi) Unreg(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}

73
api/clusterAdmin.go Normal file
View File

@ -0,0 +1,73 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type clusterAdminApi struct {
service service.Service
}
func (a *clusterAdminApi) Routes(r fiber.Router) {
r.Get("/admins", withClient(a.List))
r.Post("/admins", withClient(a.Create))
r.Delete("/admins/:admin", withClient(a.Delete))
}
// List получение списка администраторов кластера
// Swagger-spec:
// @Summary получение списка администраторов кластера
// @Description получение списка администраторов кластера
// @Tags admins,clusters
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.UsersList}
// @Failure 500 {object} Response
// @Router /app/{app}/cluster/{cluster}/admins [get]
func (a *clusterAdminApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Create выполняет регистрацию нового администратор на кластере
// Swagger-spec:
// @Summary выполняет регистрацию нового администратор на кластере
// @Description выполняет регистрацию нового администратор на кластере
// @Tags admins,clusters
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param req body serialize.UserInfo true "user info"
// @Success 200 {object} Response{data=serialize.UserInfo}
// @Failure 404 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/cluster/{cluster}/admins [post]
func (a *clusterAdminApi) Create(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Delete Удаление администратора агента на кластере
// Swagger-spec:
// @Summary Удаление администратора агента на кластере
// @Description Удаление администратора агента на кластере
// @Tags admins,clusters
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param admin path string true "admin name"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=string}
// @Failure 500 {object} Response
// @Router /app/{app}/cluster/{cluster}/admins/{admin} [delete]
func (a *clusterAdminApi) Delete(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}

176
api/connections.go Normal file
View File

@ -0,0 +1,176 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type connectionsApi struct {
service service.Service
}
func (a *connectionsApi) Routes(r fiber.Router) {
r.Get("/connections", withClient(a.List))
r.Post("/connections/terminate", withClient(a.Terminate))
r.Delete("/connections/:connection.:process", withClient(a.TerminateOne))
}
// List получение списка подключений на кластере
// Swagger-spec:
// @Summary получение списка подключений на кластере
// @Description пполучение списка подключений на кластере
// @Tags connections
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Success 200 {object} Response{data=serialize.ConnectionShortInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/connections [get]
func (a *connectionsApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// List получение списка подключений для информационной базы на кластере
// Swagger-spec:
// @Summary получение списка подключений для информационной базы на кластере
// @Description получение списка подключений для информационной базы на кластере
// @Tags connections
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase path string true "infobase uuid or name"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.ConnectionShortInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/infobases/{infobase}/connections [get]
func (a *connectionsApi) ListClusterInfobase() {}
// List получение списка подключений на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка подключений на сервере 1С Предприятие
// @Description получение списка подключений на сервере 1С Предприятие
// @Tags connections
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.ConnectionShortInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/connections [get]
func (a *connectionsApi) ListApp() {}
// List получение списка подключений для информационной базы на сервер 1С Предприятие
// Swagger-spec:
// @Summary получение списка подключений для информационной базы на сервер 1С Предприятие
// @Description получение списка подключений для информационной базы на сервер 1С Предприятие
// @Tags connections
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase path string true "infobase uuid or name"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.ConnectionShortInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/infobases/{infobase}/connections [get]
func (a *connectionsApi) ListAppInfobase() {}
// TerminateOne отключение подключения на кластере
// Swagger-spec:
// @Summary отключение подключения на кластере
// @Description отключение подключения на кластере
// @Tags connections
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param connection path string true "connection uuid"
// @Param process path string true "process uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Success 200 {object} Response{data=models.TerminateConnectionSig}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/connections/{connection}.{process} [delete]
func (a *connectionsApi) TerminateOne(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// TerminateOne отключение подключения на сервере 1С Предприятие
// Swagger-spec:
// @Summary отключение подключения на сервере 1С Предприятие
// @Description отключение подключения на сервере 1С Предприятие
// @Tags connections
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param connection path string true "connection uuid"
// @Param process path string true "process uuid"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Success 200 {object} Response{data=models.TerminateConnectionSig}
// @Failure 500 {object} Response
// @Router /app/{app}/connections/{connection}.{process} [delete]
func (a *connectionsApi) TerminateOneApp() {}
// Terminate отключение списка подключений на кластере
// Swagger-spec:
// @Summary отключение списка подключений на кластере
// @Description отключение списка подключений или по информационной базе на кластере
// @Tags connections
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param req body models.TerminateConnectionsRequest true "request"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Success 200 {object} Response{data=models.TerminateConnectionsResponse}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/connections/terminate [post]
func (a *connectionsApi) Terminate(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Terminate отключение списка подключений на сервере 1С Предприятие
// Swagger-spec:
// @Summary отключение списка подключений на сервере 1С Предприятие
// @Description отключение списка подключений или по информационной базе на сервере 1С Предприятие
// @Tags connections
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param req body models.TerminateConnectionsRequest true "request"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Success 200 {object} Response{data=models.TerminateConnectionsResponse}
// @Failure 400 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/connections/terminate [post]
func (a *connectionsApi) TerminateApp() {}

18
api/handlers.go Normal file
View File

@ -0,0 +1,18 @@
package api
import (
"github.com/gofiber/fiber/v2"
)
// NotFound returns custom 404 page
func NotFound(c *fiber.Ctx) error {
return c.Status(404).JSON(Response{
Code: 404,
Message: "not found",
})
}
type HTTPError struct {
Status string
Message string
}

103
api/health.go Normal file
View File

@ -0,0 +1,103 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/models"
"github.com/khorevaa/kubodin/service"
"time"
)
type healthApi struct {
name string
version string
route string
service service.Service
}
func (a *healthApi) Routes(r fiber.Router) {
r.Get("/health", a.health)
r.Get("/health/readiness", a.readiness)
}
// health запрос о состонии приложения
// Swagger-spec:
// @Summary запрос о состонии приложения
// @Description запрос о состонии приложения
// @Tags health
// @Accept json
// @Produce json
// @Success 200 {object} StatusResponse
// @Failure 500 {object} StatusResponse
// @Router /health [get]
func (a *healthApi) health(ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// readiness запрос подробного состония приложения
// Swagger-spec:
// @Summary запрос подробного состония приложения
// @Description запрос подробного состония приложения
// @Tags health
// @Accept json
// @Produce json
// @Success 200 {object} ReadinessCheckStatus
// @Failure 500 {object} ReadinessCheckStatus
// @Router /health/readiness [get]
func (a *healthApi) readiness(ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
type readinessCheck struct {
Name string
Version string
service service.Service
Apps []AppServiceCheckConfig
}
type ReadinessCheckStatus struct {
Name string `json:"name"`
Version string `json:"version"`
Status bool `json:"status"`
Apps []AppServiceStatus `json:"apps"`
}
type serviceStatus struct {
Status bool `json:"status"`
Error string `json:"errors,omitempty"`
}
type AppServiceCheckConfig struct {
App *models.AppServer
Host string
TimeOut time.Duration `json:"timeout,omitempty"` // default value: 10
Headers []HTTPHeader `json:"headers,omitempty"`
Ctx *fiber.Ctx
}
type AppServiceStatus struct {
Name string `json:"name"`
Host string `json:"host"`
Status bool `json:"status"`
ResponseTime float64 `json:"response_time"`
URL string `json:"url"`
Error string `json:"errors,omitempty"`
}
// HTTPHeader used to setup webservices integrations
type HTTPHeader struct {
Key string `json:"key,omitempty"`
Value string `json:"Value,omitempty"`
}
type StatusResponse struct {
Status bool `json:"status"`
Err string `json:"errors,omitempty"`
}
func (r StatusResponse) Error() string {
return r.Err
}

31
api/healthApp.go Normal file
View File

@ -0,0 +1,31 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type healthAppApi struct {
service service.Service
}
func (a *healthAppApi) Routes(r fiber.Router) {
r.Get("/health", withClient(a.health))
}
// health запрос о состонии сервера приложений 1С Предприятие
// Swagger-spec:
// @Summary запрос о состонии сервера приложений 1С Предприятие
// @Description запрос о состонии сервера приложений 1С Предприятие
// @Tags app
// @Accept json
// @Produce json
// @Success 200 {object} StatusResponse
// @Failure 500 {object} StatusResponse
// @Router /app/{app}/health [get]
func (a *healthAppApi) health(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}

21
api/helpers.go Normal file
View File

@ -0,0 +1,21 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
func withClient(fn func(client service.ClientContext, ctx *fiber.Ctx) error) fiber.Handler {
return func(ctx *fiber.Ctx) error {
client, err := service.GetClientContext(ctx)
if err != nil {
return ErrorResponse(ctx, err, "error get context client")
}
return fn(client, ctx)
}
}

480
api/helpers_test.go Normal file
View File

@ -0,0 +1,480 @@
package api
import (
"context"
"encoding/json"
db "github.com/khorevaa/kubodin/database"
"github.com/khorevaa/kubodin/models"
"github.com/khorevaa/kubodin/ras"
"github.com/khorevaa/kubodin/service"
"github.com/khorevaa/kubodin/service/cache"
rclient "github.com/khorevaa/ras-client"
"github.com/khorevaa/ras-client/serialize"
uuid "github.com/satori/go.uuid"
"github.com/steinfletcher/apitest"
"time"
)
// WithRecorderRepository wraps an existing driver with a Recorder
func WithRecorderRepository(repository db.Repository, recorder *apitest.Recorder) db.Repository {
recordingDriver := &recordingRepository{
sourceName: "pudge DB",
repository: repository,
recorder: recorder,
}
return recordingDriver
}
// WithRecorderService wraps an existing driver with a Recorder
func WithRecorderService(serv service.Service, recorder *apitest.Recorder) service.Service {
recordingDriver := &recordingService{
sourceName: "service",
service: serv,
recorder: recorder,
}
return recordingDriver
}
// WithRecorderService wraps an existing driver with a Recorder
func WithRecorderCache(c cache.Cache, recorder *apitest.Recorder) cache.Cache {
recordingDriver := &recordingCache{
sourceName: "cache",
c: c,
recorder: recorder,
}
return recordingDriver
}
// WithRecorderService wraps an existing driver with a Recorder
func WithRecorderClient(c rclient.Api, recorder *apitest.Recorder) rclient.Api {
recordingDriver := &recordingClient{
sourceName: "ras",
Api: c,
recorder: recorder,
}
return recordingDriver
}
// WithRecorderService wraps an existing driver with a Recorder
func RecorderStorage(recorder *apitest.Recorder) ras.Storage {
recordingDriver := &recordingStorage{
sourceName: "storage",
recorder: recorder,
}
return recordingDriver
}
type recordingStorage struct {
recorder *apitest.Recorder
sourceName string
}
func (r recordingStorage) Store(id string, addr string, version string) (rclient.Api, bool) {
client := WithRecorderClient(rclient.NewClient(addr, rclient.WithVersion(version)), r.recorder)
return client, false
}
func (r recordingStorage) LoadOrInit(id string, addr string, version string) (rclient.Api, bool) {
client := WithRecorderClient(rclient.NewClient(addr, rclient.WithVersion(version)), r.recorder)
return client, false
}
func (r recordingStorage) Load(id string) (interface{}, bool) {
return nil, false
}
type recordingClient struct {
rclient.Api
recorder *apitest.Recorder
sourceName string
}
func (r recordingClient) addReq(header string, body ...interface{}) {
var data string
if len(body) > 0 {
data = toJson(body[0])
}
r.recorder.AddMessageRequest(apitest.MessageRequest{
Source: "service",
Target: r.sourceName,
Header: header,
Body: data,
Timestamp: time.Now().UTC(),
})
}
func (r recordingClient) addResp(header string, body interface{}, err error) {
var data string
if err != nil {
data = err.Error()
} else {
data = toJson(body)
}
r.recorder.AddMessageResponse(apitest.MessageResponse{
Source: r.sourceName,
Target: "service",
Header: header,
Body: data,
Timestamp: time.Now().UTC(),
})
}
func (r recordingClient) GetClusters(ctx context.Context) ([]*serialize.ClusterInfo, error) {
r.addReq("GetClusters")
ok, err := r.Api.GetClusters(ctx)
r.addResp("GetClusters", ok, err)
return ok, err
}
func (r recordingClient) GetClusterServices(ctx context.Context, cluster uuid.UUID) ([]*serialize.ServiceInfo, error) {
r.addReq("GetClusterServices",
map[string]interface{}{"cluster": cluster})
ok, err := r.Api.GetClusterServices(ctx, cluster)
r.addResp("GetClusterServices", ok, err)
return ok, err
}
type recordingCache struct {
c cache.Cache
recorder *apitest.Recorder
sourceName string
}
func (r recordingCache) Connect() {
r.Connect()
}
func (r recordingCache) Get(key string) (interface{}, bool) {
r.addReq("cache get", key)
data, ok := r.c.Get(key)
r.addResp("cache resp",
map[string]interface{}{"data": data, "ok": ok},
nil)
return data, ok
}
func (r recordingCache) Set(key string, value interface{}) {
r.addReq("cache set", map[string]interface{}{key: value})
r.c.Set(key, value)
}
func (r recordingCache) Clear(key string) {
r.addReq("cache clear", key)
r.c.Clear(key)
}
func (r recordingCache) HealthCheck() (bool, error) {
r.addReq("cache HealthCheck")
ok, err := r.c.HealthCheck()
r.addResp("cache HealthCheck", ok, err)
return ok, err
}
func (r recordingCache) addReq(header string, body ...interface{}) {
var data string
if len(body) > 0 {
data = toJson(body[0])
}
r.recorder.AddMessageRequest(apitest.MessageRequest{
Source: "service",
Target: r.sourceName,
Header: header,
Body: data,
Timestamp: time.Now().UTC(),
})
}
func (r recordingCache) addResp(header string, body interface{}, err error) {
var data string
if err != nil {
data = err.Error()
} else {
data = toJson(body)
}
r.recorder.AddMessageResponse(apitest.MessageResponse{
Source: r.sourceName,
Target: "service",
Header: header,
Body: data,
Timestamp: time.Now().UTC(),
})
}
type recordingService struct {
service service.Service
recorder *apitest.Recorder
sourceName string
}
func (r recordingService) GetCache() cache.Cache {
panic("implement me")
}
func (r recordingService) License() {
panic("implement me")
}
func (r recordingService) addReq(header string, body ...interface{}) {
var data string
if len(body) > 0 {
data = toJson(body[0])
}
r.recorder.AddMessageRequest(apitest.MessageRequest{
Source: apitest.SystemUnderTestDefaultName,
Target: r.sourceName,
Header: header,
Body: data,
Timestamp: time.Now().UTC(),
})
}
func (r recordingService) addResp(header string, body interface{}, err error) {
var data string
if err != nil {
data = err.Error()
} else {
data = toJson(body)
}
r.recorder.AddMessageResponse(apitest.MessageResponse{
Source: r.sourceName,
Target: apitest.SystemUnderTestDefaultName,
Header: header,
Body: data,
Timestamp: time.Now().UTC(),
})
}
func (r recordingService) Repository() db.Repository {
return r.service.Repository()
}
func (r recordingService) GetInfobase(ctt service.ClientContext) (*serialize.InfobaseInfo, error) {
panic("implement me")
}
func (r recordingService) CreateInfobase(ctt service.ClientContext, info *serialize.InfobaseInfo, createDB bool) (*serialize.InfobaseInfo, error) {
panic("implement me")
}
func (r recordingService) UpdateInfobase(ctt service.ClientContext, info *serialize.InfobaseInfo) (*serialize.InfobaseInfo, error) {
panic("implement me")
}
func (r recordingService) DropInfobase(ctt service.ClientContext, deleteDB bool) error {
panic("implement me")
}
func (r recordingService) GetSessions(ctt service.ClientContext) (serialize.SessionInfoList, error) {
panic("implement me")
}
func (r recordingService) TerminateSession(ctt service.ClientContext, sessionID uuid.UUID, msg string) error {
panic("implement me")
}
func (r recordingService) GetClusters(ctt service.ClientContext) ([]*serialize.ClusterInfo, error) {
panic("implement me")
}
func (r recordingService) GetClusterInfo(ctt service.ClientContext) (*serialize.ClusterInfo, error) {
panic("implement me")
}
func (r recordingService) GetManagers(ctt service.ClientContext) ([]*serialize.ManagerInfo, error) {
panic("implement me")
}
func (r recordingService) GetInfobases(ctt service.ClientContext) (serialize.InfobaseSummaryList, error) {
panic("implement me")
}
func (r recordingService) GetLocks(ctt service.ClientContext) (serialize.LocksList, error) {
panic("implement me")
}
func (r recordingService) GetConnections(ctt service.ClientContext) (serialize.ConnectionShortInfoList, error) {
panic("implement me")
}
func (r recordingService) GetInfobaseConnections(client service.ClientContext, infobase string) (serialize.ConnectionShortInfoList, error) {
panic("implement me")
}
func (r recordingService) Block(ctt service.ClientContext, blocker *models.InfobaseBlocker) (*models.InfobaseUnblocker, error) {
panic("implement me")
}
func (r recordingService) Unblock(ctt service.ClientContext, unblocker *models.InfobaseUnblocker) (*serialize.InfobaseSummaryInfo, error) {
panic("implement me")
}
func (r recordingService) GetProcesses(ctt service.ClientContext) (serialize.ProcessInfoList, error) {
panic("implement me")
}
func (r recordingService) GetProcessInfo(ctt service.ClientContext) (*serialize.ProcessInfo, error) {
panic("implement me")
}
func (r recordingService) TerminateConnection(ctt service.ClientContext, processID, connectionsID uuid.UUID) (models.ConnectionSig, error) {
panic("implement me")
}
func (r recordingService) GetAppServers() (apps []*models.AppServer, err error) {
r.addReq("GetAppServers")
apps, err = r.service.GetAppServers()
r.addResp("GetAppServers", apps, err)
return
}
func (r recordingService) GetAppServer(name string) (*models.AppServer, error) {
r.addReq("GetAppServer", name)
app, err := r.service.GetAppServer(name)
r.addResp("GetAppServer", app, err)
return app, err
}
type recordingRepository struct {
repository db.Repository
recorder *apitest.Recorder
sourceName string
}
func (r recordingRepository) addReq(header string, body interface{}) {
var data string
errBody, ok := body.(error)
if ok {
data = errBody.Error()
} else {
data = toJson(body)
}
r.recorder.AddMessageRequest(apitest.MessageRequest{
Source: "service",
Target: r.sourceName,
Header: header,
Body: data,
Timestamp: time.Now().UTC(),
})
}
func (r recordingRepository) addResp(header string, body interface{}, err error) {
var data string
if err != nil {
data = err.Error()
} else {
data = toJson(body)
}
r.recorder.AddMessageResponse(apitest.MessageResponse{
Source: r.sourceName,
Target: "service",
Header: header,
Body: data,
Timestamp: time.Now().UTC(),
})
}
func (r recordingRepository) AddAppServer(app models.AppServer) error {
err := r.repository.AddAppServer(app)
return err
}
func (r recordingRepository) Clear() error {
panic("implement me")
}
func (r recordingRepository) Db() string {
return r.repository.Db()
}
func (r recordingRepository) GetAppServers() (apps []*models.AppServer, err error) {
r.addReq("GetAppServers", nil)
apps, err = r.repository.GetAppServers()
r.addResp("GetAppServers", apps, err)
return
}
func (r recordingRepository) GetAppServer(name string) (*models.AppServer, error) {
r.addReq("GetAppServer", name)
app, err := r.repository.GetAppServer(name)
r.addResp("GetAppServer", app, err)
return app, err
}
func (r recordingRepository) DeleteAppServer(appName string) error {
r.addReq("DeleteAppServer", appName)
err := r.repository.DeleteAppServer(appName)
r.addResp("DeleteAppServer", nil, err)
return err
}
func toJson(data interface{}) string {
raw, err := json.Marshal(data)
if err != nil {
return err.Error()
}
return string(raw)
}

284
api/infobases.go Normal file
View File

@ -0,0 +1,284 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
"github.com/khorevaa/ras-client/serialize"
"strconv"
)
type infobasesApi struct {
service service.Service
}
func (a *infobasesApi) Routes(r fiber.Router) {
r.Get("/infobases", withClient(a.List))
r.Post("/infobases", withClient(a.Create))
r.Get("/infobases/:infobase", withClient(a.Info))
r.Post("/infobases/:infobase", withClient(a.Update))
r.Delete("/infobases/:infobase", withClient(a.Drop))
router := r.Group("/infobases/:infobase")
parentApi := []route{
&sessionsApi{service: a.service},
&connectionsApi{service: a.service},
&blockerApi{service: a.service},
&locksApi{service: a.service},
}
for _, api := range parentApi {
api.Routes(router)
}
}
// List получение списка информационных баз с кластера
// Swagger-spec:
// @Summary получение списка информационных баз с кластера
// @Description получение списка информационных баз с кластера
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.InfobaseSummaryList}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/infobases [get]
func (a *infobasesApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
list, err := a.service.GetInfobases(client)
if err != nil {
return ErrorResponse(ctx, err, "error get infobases")
}
return SuccessResponse(ctx, list)
}
// List получение списка информационных баз с сервера 1С Предприятие
// Swagger-spec:
// @Summary получение списка информационных баз с сервера 1С Предприятие
// @Description получение списка информационных баз с сервера 1С Предприятие
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.InfobaseSummaryList}
// @Failure 500 {object} Response
// @Router /app/{app}/infobases [get]
func (a *infobasesApi) ListApp() {}
// Info получение информации об информационной базе с кластера
// Swagger-spec:
// @Summary получение информации об информационной базе с кластера
// @Description получение информации об информационной базе с кластера
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase path string true "infobase name or uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.InfobaseInfo}
// @Failure 500 {object} Response{data=string}
// @Router /app/{app}/clusters/{cluster}/infobases/{infobase} [get]
func (a *infobasesApi) Info(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Info получение информации об информационной базе с сервера 1С Предприятие
// Swagger-spec:
// @Summary получение информации об информационной базе с сервера 1С Предприятие
// @Description получение информации об информационной базе с сервера 1С Предприятие
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase path string true "infobase name or uuid"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.InfobaseInfo}
// @Failure 500 {object} Response{data=string}
// @Router /app/{app}/infobases/{infobase} [get]
func (a *infobasesApi) InfoApp() {}
// Update обновление информации об информационной базе на кластере
// Swagger-spec:
// @Summary обновление информации об информационной базе на кластере
// @Description побновление информации об информационной базе на кластере
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase path string true "infobase name or uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Param info body serialize.InfobaseInfo true "new info"
// @Success 200 {object} Response{data=string}
// @Failure 400 {object} Response{data=string}
// @Failure 500 {object} Response{data=string}
// @Router /app/{app}/clusters/{cluster}/infobases/{infobase} [post]
func (a *infobasesApi) Update(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Update обновление информации об информационной базе на сервера 1С Предприятие
// Swagger-spec:
// @Summary обновление информации об информационной базе на сервера 1С Предприятие
// @Description обновление информации об информационной базе на сервера 1С Предприятие
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase path string true "infobase name or uuid"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Param info body serialize.InfobaseInfo true "new info"
// @Success 200 {object} Response{data=string}
// @Failure 400 {object} Response{data=string}
// @Failure 500 {object} Response{data=string}
// @Router /app/{app}/infobases/{infobase} [post]
func (a *infobasesApi) UpdateApp() {}
// Create создание информационной базы на кластере
// Swagger-spec:
// @Summary оздание информационной базы на кластере
// @Description оздание информационной базы на кластере
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param create-db query bool false "create server db"
// @Param info body serialize.InfobaseInfo true "new info"
// @Success 200 {object} Response{data=serialize.InfobaseInfo}
// @Failure 400 {object} Response{data=string}
// @Failure 500 {object} Response{data=string}
// @Router /app/{app}/clusters/{cluster}/infobases [post]
func (a *infobasesApi) Create(client service.ClientContext, ctx *fiber.Ctx) error {
var body serialize.InfobaseInfo
err := ctx.BodyParser(&body)
if err != nil {
return ErrorResponse(ctx, err, "error body parse infobase info")
}
//err = validateCreateInfobaseInfo(body)
//if err != nil {
// return ErrorResponse(ctx, err, "validate error")
//}
createDB, _ := strconv.ParseBool(ctx.Query("create-db", "false"))
info, err := a.service.CreateInfobase(client, &body, createDB)
if err != nil {
return ErrorResponse(ctx, err, "create infobase error")
}
return SuccessResponse(ctx, info)
}
// Create создание информационной базы на сервере 1С Предприятие
// Swagger-spec:
// @Summary создание информационной базы на сервере 1С Предприятие
// @Description создание информационной базы на сервере 1С Предприятие
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param create-db query bool false "create server db"
// @Param info body serialize.InfobaseInfo true "new info"
// @Success 200 {object} Response{data=serialize.InfobaseInfo}
// @Failure 400 {object} Response{data=string}
// @Failure 500 {object} Response{data=string}
// @Router /app/{app}/infobases [post]
func (a *infobasesApi) CreateApp() {}
// Drop удаляет информационную базу с сервера кластера
// Swagger-spec:
// @Summary удаляет информационную базу с сервера кластера
// @Description удаляет информационную базу с сервера кластера
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase path string true "infobase name or uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param delete-db query bool false "delete server db"
// @Success 200 {object} Response{data=string}
// @Failure 500 {object} Response{data=string}
// @Router /app/{app}/clusters/{cluster}/infobases/{infobase} [delete]
func (a *infobasesApi) Drop(client service.ClientContext, ctx *fiber.Ctx) error {
deleteDB, _ := strconv.ParseBool(ctx.Query("delete-db", "false"))
err := a.service.DropInfobase(client, deleteDB)
if err != nil {
return ErrorResponse(ctx, err, "drop infobase error")
}
return SuccessResponse(ctx, nil)
}
// DropApp удаляет информационную базу с сервера 1С Предприятие
// Swagger-spec:
// @Summary удаляет информационную базу с сервера 1С Предприятие
// @Description удаляет информационную базу с сервера 1С Предприятие
// @Tags infobases
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase path string true "infobase name or uuid"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param delete-db query bool false "delete server db"
// @Success 200 {object} Response{data=string}
// @Failure 500 {object} Response{data=string}
// @Router /app/{app}/infobases/{infobase} [delete]
func (a *infobasesApi) DropApp() {}

54
api/licenses.go Normal file
View File

@ -0,0 +1,54 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type licensesApi struct {
service service.Service
}
func (a *licensesApi) Routes(r fiber.Router) {
r.Get("/licenses", withClient(a.List))
}
// List получение списка лицензий на кластере
// Swagger-spec:
// @Summary получение списка лицензий на кластере
// @Description получение списка лицензий на кластере
// @Tags licenses
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.LicenseInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/licenses [get]
func (a *licensesApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// List получение списка лицензий на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка лицензий на сервере 1С Предприятие
// @Description получение списка лицензий на сервере 1С Предприятие
// @Tags licenses
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.LicenseInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/licenses [get]
func (a *licensesApi) ListApp() {}

91
api/locks.go Normal file
View File

@ -0,0 +1,91 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type locksApi struct {
service service.Service
}
func (a *locksApi) Routes(r fiber.Router) {
r.Get("/locks", withClient(a.List))
}
// List получение списка блокировок на кластере
// Swagger-spec:
// @Summary получение списка блокировок на кластере
// @Description получение списка блокировок на кластере
// @Tags locks
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Success 200 {object} Response{data=serialize.LocksList}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/locks [get]
func (a *locksApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// List получение списка блокировок на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка блокировок на сервере 1С Предприятие
// @Description получение списка блокировок на сервере 1С Предприятие
// @Tags locks
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Success 200 {object} Response{data=serialize.LocksList}
// @Failure 500 {object} Response
// @Router /app/{app}/locks [get]
func (a *locksApi) ListApp() {}
// List получение списка блокировок для информационной базы на сервере 1С Предприятие
// Swagger-spec:
// @Summary пполучение списка блокировок для информационной базы на сервере 1С Предприятие
// @Description получение списка блокировок для информационной базы на сервере 1С Предприятие
// @Tags locks
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase path string true "infobase name or uuid"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.LocksList}
// @Failure 500 {object} Response
// @Router /app/{app}/infobases/{infobase}/locks [get]
func (a *locksApi) ListAppInfobase() {}
// List получение списка блокировок для информационной базы на кластере
// Swagger-spec:
// @Summary получение списка блокировок для информационной базы на кластере
// @Description получение списка блокировок для информационной базы на кластере
// @Tags locks
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase path string true "infobase name or uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param infobase-usr query string false "infobase user"
// @Param infobase-pwd query string false "infobase password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.LocksList}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/infobases/{infobase}/locks [get]
func (a *locksApi) LisClusterInfobase() {}

58
api/managers.go Normal file
View File

@ -0,0 +1,58 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type managersApi struct {
service service.Service
}
func (a *managersApi) Routes(r fiber.Router) {
r.Get("/managers", withClient(a.List))
r.Get("/managers/:manager", withClient(a.Info))
}
// List получение списка менеджеров на кластере
// Swagger-spec:
// @Summary получение списка менеджеров на кластере
// @Description получение списка менеджеров на кластере
// @Tags managers
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=[]serialize.ManagerInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/managers [get]
func (a *managersApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// List получение списка менеджеров на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка менеджеров на сервере 1С Предприятие
// @Description получение списка менеджеров на сервере 1С Предприятие
// @Tags managers
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=[]serialize.ManagerInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/managers [get]
func (a *managersApi) ListApp() {}
func (a *managersApi) Info(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}

91
api/processes.go Normal file
View File

@ -0,0 +1,91 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type processesApi struct {
service service.Service
}
func (a *processesApi) Routes(r fiber.Router) {
r.Get("/processes", withClient(a.List))
r.Get("/processes/:process", withClient(a.Info))
}
// List получение списка процессов на кластере
// Swagger-spec:
// @Summary получение списка процессов на кластере
// @Description получение списка процессов на кластере
// @Tags processes
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=[]serialize.ProcessInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/processes [get]
func (a *processesApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// List получение списка процессов на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка процессов на сервере 1С Предприятие
// @Description получение списка процессов на сервере 1С Предприятие
// @Tags processes
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=[]serialize.ProcessInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/processes [get]
func (a *processesApi) ListApp() {}
// Info получение информации опроцессе на кластере
// Swagger-spec:
// @Summary получение информации опроцессе на кластере
// @Description получение информации опроцессе на кластере
// @Tags processes
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param process path string true "uuid process"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Success 200 {object} Response{data=serialize.ProcessInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/processes/{process} [get]
func (a *processesApi) Info(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// Info получение информации опроцессе на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение информации опроцессе на сервере 1С Предприятие
// @Description получение информации опроцессе на сервере 1С Предприятие
// @Tags processes
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param process path string true "uuid process"
// @Param cluster-id query string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Success 200 {object} Response{data=serialize.ProcessInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/processes/{process} [get]
func (a *processesApi) InfoApp() {}

110
api/response.go Normal file
View File

@ -0,0 +1,110 @@
package api
import (
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/errors"
"github.com/khorevaa/ras-client/messages"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
} // @Name Response
func (r *Response) Error() error {
err, ok := r.Data.(error)
if !ok {
return nil
}
return err
}
func ErrorResponse(ctx *fiber.Ctx, err error, msgAndArgs ...string) error {
var data interface{}
message := messageFromMsgAndArgs(msgAndArgs)
switch typed := err.(type) {
case *messages.EndpointFailure:
data = typed
case *messages.EndpointMessageFailure:
data = typed
case *messages.UnknownMessageError:
data = typed
default:
data = err.Error()
}
code := fiber.StatusInternalServerError
switch errors.GetType(err) {
case errors.BadRequest:
code = fiber.StatusBadRequest
case errors.Other:
data = err.Error()
}
return ctx.Status(code).JSON(&Response{
Code: code,
Message: message,
Data: data,
})
}
func NotImplemented(ctx *fiber.Ctx) error {
return ErrorResponse(ctx, nil)
}
func SuccessResponse(ctx *fiber.Ctx, data interface{}) error {
return ctx.Status(fiber.StatusOK).JSON(&Response{
Code: fiber.StatusOK,
Message: successMessage,
Data: data,
})
}
const successMessage = "success"
func HttpResponse(ctx *fiber.Ctx, data interface{}, err error, errMsgAndArgs ...string) error {
if err != nil {
return ErrorResponse(ctx, err, errMsgAndArgs...)
}
return SuccessResponse(ctx, data)
}
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
if len(msgAndArgs) == 0 {
return ""
}
if len(msgAndArgs) == 1 {
msg := msgAndArgs[0]
if msgAsStr, ok := msg.(string); ok {
return msgAsStr
}
return fmt.Sprintf("%+v", msg)
}
if len(msgAndArgs) > 1 {
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
}
return ""
}
func NoAllowResponse(ctx *fiber.Ctx) error {
return ctx.Status(fiber.StatusNotImplemented).JSON(&Response{
Code: fiber.StatusNotImplemented,
Message: "This api is now allow in Kubodin. Pls buy full version of ODIN",
Data: "",
})
}

31
api/routes.go Normal file
View File

@ -0,0 +1,31 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type route interface {
Routes(r fiber.Router)
}
func Routes(app *fiber.App, s service.Service) {
// Create a /api/v1 endpoint
v1 := app.Group("/api/v1")
routes := []route{
&AppApi{
service: s,
},
&healthApi{
service: s,
name: "API Remote Administration for 1S.Enterprise Application Servers",
version: "1.0",
route: "/api/v1",
},
}
for _, r := range routes {
r.Routes(v1)
}
}

87
api/server_test.go Normal file
View File

@ -0,0 +1,87 @@
package api
import (
"github.com/gofiber/fiber/v2"
db "github.com/khorevaa/kubodin/database"
"github.com/khorevaa/kubodin/ras"
"github.com/khorevaa/kubodin/service"
"github.com/khorevaa/kubodin/service/cache"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"io"
"net/http"
"sync"
"time"
)
var once = sync.Once{}
type baseSuite struct {
suite.Suite
}
func (s *baseSuite) r() *require.Assertions {
return s.Require()
}
func (s *baseSuite) api(recorder ...*apitest.Recorder) *apitest.APITest {
rec := apitest.NewTestRecorder()
if len(recorder) > 0 {
rec = recorder[0]
}
once.Do(func() {
})
return apitest.New().
HandlerFunc(FiberToHandlerFunc(newTestApp(rec))).
//Debug().
//Report(apitest.SequenceDiagram())
Recorder(rec)
}
func newTestApp(rec *apitest.Recorder) *fiber.App {
server := fiber.New()
memoryCache := &cache.Memory{
Expiration: 30 * time.Minute,
}
memoryCache.Connect()
ras.SetLocalStorage(RecorderStorage(rec))
rep := WithRecorderRepository(db.NewMemoryRepository(), rec)
s, _ := service.NewService(WithRecorderCache(memoryCache, rec), rep)
serv := WithRecorderService(s, rec)
server.Use(service.Middleware(serv))
Routes(server, serv)
return server
}
func FiberToHandlerFunc(app *fiber.App) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
resp, err := app.Test(r)
if err != nil {
panic(err)
}
// copy headers
for k, vv := range resp.Header {
for _, v := range vv {
w.Header().Add(k, v)
}
}
w.WriteHeader(resp.StatusCode)
if _, err := io.Copy(w, resp.Body); err != nil {
panic(err)
}
}
}

58
api/services.go Normal file
View File

@ -0,0 +1,58 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type servicesApi struct {
service service.Service
}
func (a *servicesApi) Routes(r fiber.Router) {
r.Get("/services", withClient(a.List))
r.Get("/services/:service", withClient(a.Info))
}
// List получение списка сервисов на кластере
// Swagger-spec:
// @Summary получение списка сервисов на кластере
// @Description получение списка сервисов на кластере
// @Tags services
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=[]serialize.ServiceInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/services [get]
func (a *servicesApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// List получение списка сервисов на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка сервисов на сервере 1С Предприятие
// @Description получение списка сервисов на сервере 1С Предприятие
// @Tags services
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=[]serialize.ServiceInfo}
// @Failure 500 {object} Response
// @Router /app/{app}/services [get]
func (a *servicesApi) ListApp() {}
func (a *servicesApi) Info(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}

41
api/services_test.go Normal file
View File

@ -0,0 +1,41 @@
package api
import (
"github.com/khorevaa/kubodin/models"
"github.com/steinfletcher/apitest"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
"github.com/stretchr/testify/suite"
"net/http"
"testing"
)
type servicesSuite struct {
baseSuite
}
func TestServiceTestSuite(t *testing.T) {
suite.Run(t, new(servicesSuite))
}
func (s *servicesSuite) SetupSuite() {
s.api().
Post("/api/v1/app").
JSON(&models.AppServer{
Name: "test",
Addr: "localhost",
Port: "1546",
}).
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).End()
}
func (s *servicesSuite) TestServiceList() {
s.api().
Debug().Report(apitest.SequenceDiagram()).
Get("/api/v1/app/test/services").
Expect(s.T()).
Assert(jsonpath.Equal(`$.message`, "success")).
Status(http.StatusOK).
End()
}

144
api/sessions.go Normal file
View File

@ -0,0 +1,144 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/service"
)
type sessionsApi struct {
service service.Service
}
func (a *sessionsApi) Routes(r fiber.Router) {
r.Get("/sessions", withClient(a.List))
//r.Get("/sessions/:session", withClient(a.Info))
r.Delete("/sessions/:session", withClient(a.TerminateOne))
r.Post("/sessions/terminate", withClient(a.Terminate))
}
// List получение списка сессий на кластере
// Swagger-spec:
// @Summary получение списка сессий на кластере
// @Description получение списка сессий на кластере
// @Tags sessions
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.SessionInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/sessions [get]
func (a *sessionsApi) List(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// List получение списка сессий информационной базы на кластере
// Swagger-spec:
// @Summary получение списка сессий информационной базы на кластере
// @Description получение списка сессий информационной базы на кластере
// @Tags sessions
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param infobase path string true "infobase uuid or name"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.SessionInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/infobases/{infobase}/sessions [get]
func (a *sessionsApi) ListInfobase() {}
// List получение списка сессий информационной базы на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка сессий информационной базы на сервере 1С Предприятие
// @Description получение списка сессий информационной базы на сервере 1С Предприятие
// @Tags sessions
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param infobase path string true "infobase uuid or name"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.SessionInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/infobases/{infobase}/sessions [get]
func (a *sessionsApi) ListAppInfobase() {}
// List получение списка сессий на сервере 1С Предприятие
// Swagger-spec:
// @Summary получение списка сессий на сервере 1С Предприятие
// @Description получение списка сессий на сервере 1С Предприятие
// @Tags sessions
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response{data=serialize.SessionInfoList}
// @Failure 500 {object} Response
// @Router /app/{app}/sessions [get]
func (a *sessionsApi) ListApp() {}
func (a *sessionsApi) Info(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
func (a *sessionsApi) Terminate(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// TerminateOne отключение сессии на кластере
// Swagger-spec:
// @Summary отключение сессии на кластере
// @Description отключение сессии на кластере
// @Tags sessions
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param cluster path string true "cluster uuid"
// @Param session path string true "session uuid"
// @Param msg query string false "message to user"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/clusters/{cluster}/sessions/{session} [delete]
func (a *sessionsApi) TerminateOne(client service.ClientContext, ctx *fiber.Ctx) error {
return NoAllowResponse(ctx)
}
// TerminateOne отключение сессии на сервер 1С Предприятие
// Swagger-spec:
// @Summary отключение сессии на сервер 1С Предприятие
// @Description отключение сессии на сервер 1С Предприятие
// @Tags sessions
// @Accept json
// @Produce json
// @Param app path string true "app name"
// @Param session path string true "session uuid"
// @Param msg query string false "message to user"
// @Param cluster-id query string false "cluster uuid"
// @Param cluster-usr query string false "cluster user"
// @Param cluster-pwd query string false "cluster password"
// @Param force query bool false "force update ignore cache"
// @Success 200 {object} Response
// @Failure 500 {object} Response
// @Router /app/{app}/sessions/{session} [delete]
func (a *sessionsApi) TerminateOneApp() {}

View File

@ -1,19 +0,0 @@
package cmd
import (
"github.com/urfave/cli/v2"
)
var Commands = []Command{
&someCommand{},
//&commandWithSub{
// sub: []Command{
// &subCommand{},
// },
//},
}
type Command interface {
Cmd() *cli.Command
}

262
cmd/mainCommand.go Normal file
View File

@ -0,0 +1,262 @@
package cmd
import (
"fmt"
"github.com/gofiber/fiber/v2/middleware/pprof"
"github.com/khorevaa/kubodin/api"
db "github.com/khorevaa/kubodin/database"
"github.com/khorevaa/kubodin/models"
"github.com/khorevaa/kubodin/service"
"github.com/khorevaa/kubodin/service/cache"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
"log"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
swagger "github.com/arsmn/fiber-swagger/v2"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
rec "github.com/gofiber/fiber/v2/middleware/recover"
_ "github.com/khorevaa/kubodin/docs" // docs is generated by Swag CLI, you have to import it.
)
type MainCommand struct {
debug bool
port string
appServer string
Version string
BuildBy string
Date string
}
func (c *MainCommand) Run(context *cli.Context) error {
server := fiber.New(fiber.Config{
DisableStartupMessage: true,
})
memoryCache := &cache.Memory{
Expiration: 30 * time.Minute,
}
memoryCache.Connect()
rep := db.NewMemoryRepository()
addr, port := parseAddr(c.appServer)
err := rep.AddAppServer(models.AppServer{
Name: "default",
Addr: addr,
Port: port,
Description: "default app server",
})
if err != nil {
log.Println("ERROR: Connect to ras server")
}
serv, err := service.NewService(memoryCache, rep)
if err != nil {
return err
}
// Middleware
server.Use(rec.New())
server.Use(service.Middleware(serv))
if c.debug {
server.Use(logger.New())
server.Use(pprof.New())
}
api.Routes(server, serv)
server.Use("/docs", swagger.Handler) // default
// Handle not founds
server.Use(api.NotFound)
go func() {
c.startupMessage(c.port, false, c.debug, serv)
log.Fatal(server.Listen(c.port)) // go run main.go -port=:3000
}()
gracefullyShutdownInit(server)
return nil
}
func (c *MainCommand) Flags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Destination: &c.port, Name: "port",
Value: "localhost:3001", Usage: "port to listen on"},
&cli.StringFlag{
Destination: &c.appServer, Name: "server",
Value: "localhost:1545", Usage: "ras client address with port"},
&cli.BoolFlag{
Destination: &c.debug, Name: "debug",
Value: false, Usage: "debug mode"},
}
}
func (c *MainCommand) startupMessage(addr string, tls bool, debug bool, serv service.Service) {
if len(os.Getenv("FIBER_PREFORK_CHILD")) > 0 {
// Для подчиненных процессов мы не выводим сообщение
return
}
var logo string
logo += "%s"
logo += " ┌───────────────────────────────────────────────────┐\n"
logo += " │ %s │\n"
logo += " │ %s │\n"
logo += " │ %s │\n"
logo += " │ %s │\n"
logo += " │ %s │\n"
logo += " │ %s │\n"
logo += " │ │\n"
logo += " │ Built by: %s Built at: %s │\n"
logo += " │ Cache: %s DB: %s │\n"
logo += " └───────────────────────────────────────────────────┘"
logo += "%s"
const (
cBlack = "\u001b[90m"
// cRed = "\u001b[91m"
cCyan = "\u001b[96m"
// cGreen = "\u001b[92m"
cYellow = "\u001b[93m"
// cBlue = "\u001b[94m"
// cMagenta = "\u001b[95m"
// cWhite = "\u001b[97m"
cReset = "\u001b[0m"
)
value := func(s string, width int) string {
pad := width - len(s)
str := ""
for i := 0; i < pad; i++ {
str += "."
}
if s == "Disabled" {
str += " " + s
} else {
str += fmt.Sprintf(" %s%s%s", cYellow, s, cBlack)
}
return str
}
center := func(s string, width int) string {
pad := strconv.Itoa((width - len(s)) / 2)
str := fmt.Sprintf("%"+pad+"s", " ")
str += s
str += fmt.Sprintf("%"+pad+"s", " ")
if len(str) < width {
str += " "
}
return str
}
centerValue := func(s string, width int) string {
pad := strconv.Itoa((width - len(s)) / 2)
str := fmt.Sprintf("%"+pad+"s", " ")
str += fmt.Sprintf("%s%s%s", cCyan, s, cBlack)
str += fmt.Sprintf("%"+pad+"s", " ")
if len(str)-10 < width {
str += " "
}
return str
}
host, port := parseAddr(addr)
if host == "" || host == "0.0.0.0" {
host = "127.0.0.1"
}
addr = "http://" + host + ":" + port
if tls {
addr = "https://" + host + ":" + port
}
t, _ := time.Parse(time.RFC3339, c.Date)
cacheInfo := "redis"
switch serv.GetCache().(type) {
case *cache.Memory:
cacheInfo = "in memory"
}
dbInfo := "pudge"
pprofInfo := ""
if debug {
pprofInfo = " Pprof: " + addr + "/debug/pprof"
}
switch serv.Repository().(type) {
case *db.InMemory:
dbInfo = "in memory"
}
mainLogo := fmt.Sprintf(logo,
cBlack,
centerValue(" KUBODIN "+c.Version, 49),
center("Remote Administration", 49),
center("for 1S.Enterprise Application Servers", 49),
centerValue(" API: "+addr+"/api/v1", 49),
centerValue(" Docs: "+addr+"/docs", 49),
centerValue(pprofInfo, 49),
value(c.BuildBy, 13), value(t.Format("2006-01-02"), 13),
value(cacheInfo, 16), value(dbInfo, 19),
cReset,
)
out := colorable.NewColorableStdout()
if os.Getenv("TERM") == "dumb" ||
(!isatty.IsTerminal(os.Stdout.Fd()) &&
!isatty.IsCygwinTerminal(os.Stdout.Fd())) {
out = colorable.NewNonColorable(os.Stdout)
}
fmt.Fprintln(out, mainLogo)
}
func parseAddr(raw string) (host, port string) {
if i := strings.LastIndex(raw, ":"); i != -1 {
return raw[:i], raw[i+1:]
}
return raw, ""
}
func gracefullyShutdownInit(app *fiber.App) {
// Wait for interrupt signal to gracefully shutdown the server with
// setup signal catching
quit := make(chan os.Signal, 1)
// catch all signals since not explicitly listing
signal.Notify(quit, syscall.SIGQUIT, os.Interrupt, os.Kill)
q := <-quit
if q != nil {
println(fmt.Sprintf("RECEIVED SIGNAL: %s", q))
}
println("Shutdown Server ...")
if err := app.Shutdown(); err != nil {
log.Println("Server Shutdown:", err)
}
time.Sleep(2 * time.Second)
_ = cli.Exit("Shutdown complete", 0)
}

View File

@ -1,61 +0,0 @@
package cmd
import (
"github.com/urfave/cli/v2"
)
type someCommand struct {
}
func (c *someCommand) run(context *cli.Context) error {
return nil
}
func (c *someCommand) Cmd() *cli.Command {
cmd := &cli.Command{
//Category: "some_category",
Name: "some_command",
Usage: "Usage decription",
Description: `Full usage description `,
Action: c.run,
Flags: []cli.Flag{
//&cli.StringFlag{
// Destination: &c.cluster, Name: "cluster-id",
// Value: "", Usage: "cluster uuid for join new work server"},
//&cli.StringFlag{
// Destination: &c.Name, Name: "name", Aliases: []string{"N"},
// Value: "", Usage: "work server name", EnvVars: []string{"SERVER_NAME"}, Required: true},
//&cli.StringFlag{
// Destination: &c.AgentHost, Name: "host",
// Value: "", Usage: "work server agent host", EnvVars: []string{"SERVER_AGENT_HOST"}, Required: true},
//&cli.IntFlag{
// Destination: &c.AgentPort, Name: "port",
// Value: 1540, Usage: "work server agent port", EnvVars: []string{"SERVER_AGENT_HOST"}, DefaultText: "1540"},
//&cli.StringFlag{
// Destination: &c.PortRange, Name: "port-range",
// Value: "1560:1591", Usage: "work server port range", EnvVars: []string{"SERVER_PORT_RANGE"}, DefaultText: "1560:1591"},
//&cli.StringFlag{
// Destination: &c.Using, Name: "using",
// Value: "normal", Usage: "variant of using work server (main, normal)", EnvVars: []string{"SERVER_USING"}, DefaultText: "normal"},
//&cli.StringFlag{
// Destination: &c.DedicateManagers, Name: "dedicate-managers",
// Value: "none", Usage: "вариант размещения менеджеров сервисов (all, none)", EnvVars: []string{"SERVER_DEDICATE_MANAGERS"}, DefaultText: "none"},
//&cli.IntFlag{
// Destination: &c.ClusterPort, Name: "cluster-port",
// Value: 1541, Usage: "номер порта главного менеджера кластера", EnvVars: []string{"SERVER_CLUSTER_PORT"}, DefaultText: "1541"},
//&cli.Int64Flag{
// Destination: &c.MemoryLimit, Name: "memory-limit",
// Value: 0, Usage: "предел использования памяти рабочими процессами (kilobytes)", EnvVars: []string{"SERVER_MEMORY_LIMIT"}},
//&cli.Int64Flag{
// Destination: &c.ConnectionsLimit, Name: "connections-limit",
// Value: 128, Usage: "максимальное количество соединения на рабочий процесс", EnvVars: []string{"SERVER_CONNECTIONS_LIMIT"}, DefaultText: "128"},
//&cli.Int64Flag{
// Destination: &c.CriticalTotalMemory, Name: "total-memory",
// Value: 0, Usage: "максимальный объем памяти процессов рабочего сервера (bytes)", EnvVars: []string{"SERVER_TOTAL_MEMORY"}},
},
}
return cmd
}

55
database/app.go Normal file
View File

@ -0,0 +1,55 @@
package database
import (
"context"
"github.com/khorevaa/kubodin/errors"
"github.com/khorevaa/kubodin/models"
"time"
)
type Repository interface {
GetAppServers() (apps []*models.AppServer, err error)
GetAppServer(name string) (*models.AppServer, error)
AddAppServer(app models.AppServer) error
DeleteAppServer(appName string) error
Db() string
Clear() error
}
var (
ErrorNotFound = errors.Internal.New("app by id not found")
)
func prepareAppServer(app *models.AppServer) error {
if len(app.Port) == 0 {
app.Port = "1545"
}
if len(app.Name) == 0 {
app.Name = app.Addr
}
client, err := app.Client()
if err != nil {
return err
}
ctx, _ := context.WithTimeout(context.Background(), time.Second*10)
version, err := client.GetAgentVersion(ctx)
app.Version = client.Version()
if err != nil {
// При первом получении версии только определяется версия сервиса.
// Надо ждать изменений в ras-client, получение версии сервиса в отдельном потоке, а не с первой папыткой открытия endpoint
version, err = client.GetAgentVersion(ctx)
}
app.AgentVersion = version
return err
}

75
database/memory.go Normal file
View File

@ -0,0 +1,75 @@
package database
import (
"github.com/khorevaa/kubodin/models"
"sync"
)
var _ Repository = (*InMemory)(nil)
type InMemory struct {
m *sync.Map
}
func (i *InMemory) GetAppServers() (apps []*models.AppServer, err error) {
i.m.Range(func(_, val interface{}) bool {
app := val.(models.AppServer)
apps = append(apps, &app)
return true
})
return
}
func (i *InMemory) GetAppServer(name string) (*models.AppServer, error) {
app, ok := i.m.Load(name)
if !ok {
return nil, ErrorNotFound
}
appServer := app.(models.AppServer)
return &appServer, nil
}
func (i InMemory) SetAppServer(app models.AppServer) error {
i.m.Store(app.Name, app)
return nil
}
func (i *InMemory) AddAppServer(app models.AppServer) error {
err := prepareAppServer(&app)
i.m.Store(app.Name, app)
return err
}
func (i *InMemory) DeleteAppServer(appName string) error {
_, ok := i.m.LoadAndDelete(appName)
if !ok {
return ErrorNotFound
}
return nil
}
func (i *InMemory) Db() string {
return ""
}
func (i *InMemory) Clear() error {
return nil
}
func NewMemoryRepository() Repository {
return &InMemory{
m: &sync.Map{},
}
}

6815
docs/docs.go Normal file

File diff suppressed because it is too large Load Diff

6753
docs/swagger.json Normal file

File diff suppressed because it is too large Load Diff

4347
docs/swagger.yaml Normal file

File diff suppressed because it is too large Load Diff

178
errors/error.go Normal file
View File

@ -0,0 +1,178 @@
package errors
import (
"fmt"
"github.com/pkg/errors"
)
var (
_ error = (*Error)(nil)
)
type Kind uint
const (
Other Kind = iota // Unclassified error. This value is not printed in the error message.
Invalid // Invalid operation for this type of item.
Permission // Permission denied.
IO // External I/O error such as network failure.
Exist // Item already exists.
Private // Information withheld.
Internal // Internal error or inconsistency.
Timeout // Link target does not exist.
Codec
BadRequest
BadCommand
Runtime
ToManyClusters
ToManyInfobases
)
type ErrorType uint
func (k Kind) String() string {
switch k {
case Other:
return "other error"
case Invalid:
return "invalid operation"
case Permission:
return "permission denied"
case IO:
return "I/O error"
case Exist:
return "item already exists"
case Private:
return "information withheld"
case Internal:
return "internal error"
case Codec:
return "codec error"
case Timeout:
return "timeout error"
case BadRequest:
return "bad request"
}
return "unknown error kind"
}
type Error struct {
kind Kind
err error
contextInfo errorContext
}
type errorContext struct {
Field string
Message string
}
func (e Error) Error() string {
return e.err.Error()
}
func (e Error) WithContext(field, message string) error {
context := errorContext{Field: field, Message: message}
return Error{kind: e.kind, err: e.err, contextInfo: context}
}
func (e *Error) IsZero() bool {
return e.err == nil && e.kind == 0
}
// New creates a new Error
func (e Kind) New(msg string) Error {
return Error{kind: e, err: errors.New(msg)}
}
// New creates a new Error with formatted message
func (e Kind) Newf(msg string, args ...interface{}) Error {
err := fmt.Errorf(msg, args...)
return Error{kind: e, err: err}
}
// Wrap creates a new wrapped error
func (e Kind) Wrap(err error, msg string) Error {
return e.Wrapf(err, msg)
}
// Wrap creates a new wrapped error with formatted message
func (e Kind) Wrapf(err error, msg string, args ...interface{}) Error {
newErr := errors.Wrapf(err, msg, args...)
return Error{kind: e, err: newErr}
}
// Cause gives the original error
func Cause(err error) error {
return errors.Cause(err)
}
// Wrapf wraps an error with format string
func Wrapf(err error, msg string, args ...interface{}) error {
if err == nil {
return err
}
wrappedError := errors.Wrapf(err, msg, args...)
if customErr, ok := err.(Error); ok {
return Error{
kind: customErr.kind,
err: wrappedError,
contextInfo: customErr.contextInfo,
}
}
return Error{kind: Other, err: wrappedError}
}
// AddErrorContext adds a context to an error
func AddErrorContext(err error, field, message string) error {
context := errorContext{Field: field, Message: message}
if customErr, ok := err.(Error); ok {
return Error{kind: customErr.kind, err: customErr.err, contextInfo: context}
}
return Error{kind: Other, err: err, contextInfo: context}
}
// GetErrorContext returns the error context
func GetErrorContext(err error) map[string]string {
emptyContext := errorContext{}
if customErr, ok := err.(Error); ok || customErr.contextInfo != emptyContext {
return map[string]string{"field": customErr.contextInfo.Field, "message": customErr.contextInfo.Message}
}
return nil
}
// GetType returns the error type
func GetType(err error) Kind {
if customErr, ok := err.(Error); ok {
return customErr.kind
}
return Other
}
// Is reports whether err is an *Error of the given Kind.
// If err is nil then Is returns false.
func Is(kind Kind, err error) bool {
e, ok := err.(*Error)
if !ok {
return false
}
if e.kind != Other {
return e.kind == kind
}
if e.err != nil {
return Is(kind, e.err)
}
return false
}

23
go.mod Normal file
View File

@ -0,0 +1,23 @@
module github.com/khorevaa/kubodin
go 1.16
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/arsmn/fiber-swagger/v2 v2.3.0
github.com/gofiber/fiber/v2 v2.5.0
github.com/khorevaa/ras-client v0.0.0-20201104084928-a9228766f6ed
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.12
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/recoilme/pudge v1.0.3
github.com/satori/go.uuid v1.2.0
github.com/steinfletcher/apitest v1.5.2
github.com/steinfletcher/apitest-jsonpath v1.6.0
github.com/stretchr/testify v1.7.0
github.com/swaggo/swag v1.7.0
github.com/urfave/cli/v2 v2.3.0
golang.org/x/sys v0.0.0-20210112080510-489259a85091 // indirect
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e // indirect
)

171
go.sum Normal file
View File

@ -0,0 +1,171 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PaesslerAG/gval v1.0.0 h1:GEKnRwkWDdf9dOmKcNrar9EA1bz1z9DqPIO1+iLzhd8=
github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk=
github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/arsmn/fiber-swagger/v2 v2.3.0 h1:FC6RvYCBQlV0rbAEuCgxHilWLLNGmN8ClFRlY5l8RSs=
github.com/arsmn/fiber-swagger/v2 v2.3.0/go.mod h1:bScnIE8qvQF5/wvsewuwXPkLN23eGQZqqNIyBA6Xd2E=
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA=
github.com/go-openapi/spec v0.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I=
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY=
github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
github.com/gofiber/fiber/v2 v2.3.0/go.mod h1:f8BRRIMjMdRyt2qmJ/0Sea3j3rwwfufPrh9WNBRiVZ0=
github.com/gofiber/fiber/v2 v2.5.0 h1:yml405Um7b98EeMjx63OjSFTATLmX985HPWFfNUPV0w=
github.com/gofiber/fiber/v2 v2.5.0/go.mod h1:f8BRRIMjMdRyt2qmJ/0Sea3j3rwwfufPrh9WNBRiVZ0=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
github.com/k0kubun/pp/v3 v3.0.3/go.mod h1:2ol0zQBSPTermAo8igHVJ4d5vTiNmBkCrUdu7wZp4aI=
github.com/khorevaa/ras-client v0.0.0-20201104084928-a9228766f6ed h1:D02yuRwXP5R0p1OkmCsaJ7REhuRG0NI1lcLHfr7M8vI=
github.com/khorevaa/ras-client v0.0.0-20201104084928-a9228766f6ed/go.mod h1:GYAPHlMkTyZdkgqNWmCfwWsAS8BQfBjxg2r3yQ3rQnY=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc=
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/recoilme/pudge v1.0.3 h1:h/9dEv5fRqtzM4lnO69kUoN+k7ukxxrW9NGb9ug0grM=
github.com/recoilme/pudge v1.0.3/go.mod h1:VMvxBLVkrSStldckzCsETBXox3pfovfrnEchafXk8qA=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/steinfletcher/apitest v1.4.10/go.mod h1:0MT98QwexQVvf5pIn3fqiC/+8Nyd7A4RShxuSjnpOcE=
github.com/steinfletcher/apitest v1.5.2 h1:o5R0km8ZI6xooSDwsHdDCD9OpEXda7CJeQwyoSrJmPM=
github.com/steinfletcher/apitest v1.5.2/go.mod h1:TrZemFOZ1yNgKoAeAsth3Z3vEavTloE1hP/U2PSd3w0=
github.com/steinfletcher/apitest-jsonpath v1.6.0 h1:9LsuXIw6Kn66n59UZ8aSvx3AwXNeKW70c/Ds4dU2SL4=
github.com/steinfletcher/apitest-jsonpath v1.6.0/go.mod h1:0XJfDbARuf72hqzj5rPz2ou5rZRJkvbFGFuR+OVosO4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E=
github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.18.0 h1:IV0DdMlatq9QO1Cr6wGJPVW1sV1Q8HvZXAIcjorylyM=
github.com/valyala/fasthttp v1.18.0/go.mod h1:jjraHZVbKOXftJfsOYoAjaeygpj5hr8ermTRJNroD7A=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/xelaj/go-dry v0.0.0-20201004191957-aab3eecf0604 h1:lg5YBv2+xr3j2lfcfahTa+9msjETFlB/b9cRBOe5A/8=
github.com/xelaj/go-dry v0.0.0-20201004191957-aab3eecf0604/go.mod h1:6rEJfrv43LMiKiRYh7Zzhv6eystOVUhoYa2YCq2gI4k=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201210223839-7e3030f88018/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201217165654-008e477491be/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

51
main.go
View File

@ -1,7 +1,8 @@
package main
import (
"github.com/khorevaa/go-app-template/cmd"
"fmt"
"github.com/khorevaa/kubodin/cmd"
"github.com/urfave/cli/v2"
"log"
"os"
@ -15,23 +16,38 @@ var (
builtBy = ""
)
// main
// @title KUBOdin: Remote Administration for 1S.Enterprise Application Servers
// @version 1.0
// @description KUBOdin Swagger UI
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.email khorevaa@yandex.ru
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host localhost:3001
// @BasePath /api/v1
func main() {
app := &cli.App{
Name: "go-app-template",
mainCmd := &cmd.MainCommand{
Version: version,
BuildBy: builtBy,
Date: date,
}
app := &cli.App{
Name: "kubodin",
Version: buildVersion(),
Authors: []*cli.Author{
{
Name: "Aleksey Khorev",
},
},
Usage: "Description for go-app-template",
Copyright: "(c) 2021 Khorevaa",
//Description: "Command line utilities for server 1S.Enterprise",
}
for _, command := range cmd.Commands {
app.Commands = append(app.Commands, command.Cmd())
Usage: "Start API server for kubernetes & 1C.Enterprise",
Copyright: "(c) 2021 Khorevaa",
Description: "API server for Kubernetes & 1C.Enterprise",
Flags: mainCmd.Flags(),
Action: mainCmd.Run,
}
err := app.Run(os.Args)
@ -39,3 +55,18 @@ func main() {
log.Fatal(err)
}
}
func buildVersion() string {
var result = version
if commit != "" {
result = fmt.Sprintf("%s\ncommit: %s", result, commit)
}
if date != "" {
result = fmt.Sprintf("%s\nbuilt at: %s", result, date)
}
if builtBy != "" {
result = fmt.Sprintf("%s\nbuilt by: %s", result, builtBy)
}
return result
}

49
models/appServer.go Normal file
View File

@ -0,0 +1,49 @@
package models
import (
"github.com/khorevaa/kubodin/ras"
rclient "github.com/khorevaa/ras-client"
"net"
)
type AppServer struct {
Name string `json:"name"`
Addr string `json:"addr"`
Port string `json:"port"`
Version string `json:"version"`
Description string `json:"descr,omitempty" yaml:"descr"`
AgentUsr string `json:"agent_usr,omitempty"`
AgentPwd string `json:"agent_pwd,omitempty"`
AgentVersion string `json:"agent_version,omitempty"`
Properties map[string]string `json:"properties,omitempty"`
} // @Name AppServer
func (a *AppServer) init(api rclient.Api, inited bool) {
if inited {
return
}
if len(a.AgentUsr) > 0 {
api.AuthenticateAgent(a.AgentUsr, a.AgentPwd)
}
}
func (a *AppServer) Client() (rclient.Api, error) {
api, inited := ras.LoadOrInit(a.Name, net.JoinHostPort(a.Addr, a.Port), a.Version)
a.init(api, inited)
return api, nil
}
func (a *AppServer) Reload() {
c, err := a.Client()
if err == nil {
_ = c.Close()
}
api, inited := ras.Store(a.Name, net.JoinHostPort(a.Addr, a.Port), a.Version)
a.init(api, inited)
}

37
models/blocker.go Normal file
View File

@ -0,0 +1,37 @@
package models
import (
uuid "github.com/satori/go.uuid"
"time"
)
type InfobaseBlocker struct {
Infobase string `query:"infobase" json:"infobase" example:"testdb2"`
ClusterID string `query:"cluster-id" json:"cluster_id" example:"80f7f2f6-2feb-46bf-92f4-19294a2f5dc7"`
Message string `query:"message" json:"message" example:"Обновление информационной базы"`
PermissionCode string `query:"permission-code" json:"permission_code" example:"123"`
DeniedParameter string `query:"denied-parameter" json:"denied_parameter" example:"code"`
ScheduledJobsDeny bool `query:"scheduled-jobs-deny" json:"scheduled_jobs_deny"`
SessionsDeny bool `query:"sessions-deny,required" json:"sessions_deny"`
Reload bool `query:"reload" json:"reload"`
DeniedFrom time.Time `query:"denied-from" json:"denied_from" example:"2020-10-01T08:30:00Z"`
DeniedTo time.Time `query:"denied-to" json:"denied_to" example:"2020-10-01T08:30:00Z"`
}
func (b *InfobaseBlocker) Empty() bool {
return !b.SessionsDeny && b.DeniedFrom.IsZero() && b.DeniedTo.IsZero() &&
len(b.Message) == 0 && len(b.PermissionCode) == 0
}
type InfobaseUnblocker struct {
Infobase string `query:"infobase-name" json:"infobase" example:"testdb2"`
InfobaseID uuid.UUID `query:"infobase-id" json:"infobase_id" example:"80f7f2f6-2feb-46bf-92f4-19294a2f5dc7"`
ClusterID uuid.UUID `query:"cluster-id" json:"cluster_id" example:"80f7f2f6-2feb-46bf-92f4-19294a2f5dc7"`
PermissionCode string `query:"permission-code" json:"permission_code" example:""`
DeniedParameter string `query:"denied-parameter" json:"denied_parameter" example:""`
ScheduledJobsDeny bool `query:"scheduled-jobs-deny" json:"scheduled_jobs_deny"`
SessionsDeny bool `query:"sessions-deny,required" json:"sessions_deny"`
}

45
models/connectionSig.go Normal file
View File

@ -0,0 +1,45 @@
package models
import uuid "github.com/satori/go.uuid"
type ConnectionSig struct {
ClusterID uuid.UUID `json:"cluster_id" example:"6d6958e1-a96c-4999-a995-698a0298161e"`
InfobaseID uuid.UUID `json:"infobase_id" example:"6d6958e1-a96c-4999-a995-698a0298161e"`
Process uuid.UUID `json:"process" example:"6d6958e1-a96c-4999-a995-698a0298161e"`
UUID uuid.UUID `json:"uuid" example:"6d6958e1-a96c-4999-a995-698a0298161e"`
}
type TerminateConnectionsRequest struct {
InfobaseID string `json:"infobase_id" example:"6d6958e1-a96c-4999-a995-698a0298161e or testib2"`
Connections []ConnectionSig `json:"connections"`
}
type TerminateConnectionSig struct {
ConnectionSig
Terminated bool `json:"terminated" example:"false"`
Err string `json:"err,omitempty" example:"error terminate connection"`
}
type TerminateConnectionsResponse struct {
Count int `json:"count" example:"0"`
Connections []TerminateConnectionSig `json:"connections,omitempty"`
}
func (r *TerminateConnectionsResponse) AddResult(sig ConnectionSig, err error) {
msg := ""
terminated := true
if err != nil {
terminated = false
msg = err.Error()
}
r.Connections = append(r.Connections, TerminateConnectionSig{
ConnectionSig: sig,
Terminated: terminated,
Err: msg,
})
r.Count++
}

72
ras/client.go Normal file
View File

@ -0,0 +1,72 @@
package ras
import (
rclient "github.com/khorevaa/ras-client"
"sync"
)
type Storage interface {
Store(id string, addr string, version string) (rclient.Api, bool)
LoadOrInit(id string, addr string, version string) (rclient.Api, bool)
Load(id string) (interface{}, bool)
}
var localStorage = newStorage()
func newStorage() Storage {
return &storage{
sMap: &sync.Map{},
}
}
type storage struct {
sMap *sync.Map
}
func (s *storage) Store(id string, addr string, version string) (rclient.Api, bool) {
client := rclient.NewClient(addr, rclient.WithVersion(version))
s.sMap.Store(id, client)
return client, false
}
func (s *storage) LoadOrInit(id string, addr string, version string) (rclient.Api, bool) {
client, ok := s.sMap.Load(id)
if !ok {
return Store(id, addr, version)
}
c := client.(rclient.Api)
return c, true
}
func (s *storage) Load(id string) (interface{}, bool) {
return s.sMap.Load(id)
}
func SetLocalStorage(s Storage) {
localStorage = s
}
func Store(id string, addr string, version string) (rclient.Api, bool) {
client, _ := localStorage.Store(id, addr, version)
return client, false
}
func LoadOrInit(id string, addr string, version string) (rclient.Api, bool) {
client, ok := localStorage.Load(id)
if !ok {
return localStorage.Store(id, addr, version)
}
c := client.(rclient.Api)
return c, true
}

View File

@ -1,10 +1,10 @@
# go-app-template
# kubodin
[![Release](https://img.shields.io/github/release/khorevaa/go-app-template.svg?style=for-the-badge)](https://github.com/khorevaa/go-app-template/releases/latest)
[![Release](https://img.shields.io/github/release/khorevaa/kubodin.svg?style=for-the-badge)](https://github.com/khorevaa/kubodin/releases/latest)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=for-the-badge)](/LICENSE.md)
[![Build status](https://img.shields.io/github/workflow/status/khorevaa/go-app-template/build?style=for-the-badge)](https://github.com/khorevaa/go-app-template/actions?workflow=releaser)
[![Codecov branch](https://img.shields.io/codecov/c/github/khorevaa/go-app-template/master.svg?style=for-the-badge)](https://codecov.io/gh/khorevaa/go-app-template)
[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](http://godoc.org/github.com/khorevaa/go-app-template)
[![Build status](https://img.shields.io/github/workflow/status/khorevaa/kubodin/build?style=for-the-badge)](https://github.com/khorevaa/kubodin/actions?workflow=releaser)
[![Codecov branch](https://img.shields.io/codecov/c/github/khorevaa/kubodin/master.svg?style=for-the-badge)](https://codecov.io/gh/khorevaa/kubodin)
[![Go Doc](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](http://godoc.org/github.com/khorevaa/kubodin)
[![SayThanks.io](https://img.shields.io/badge/SayThanks.io-%E2%98%BC-1EAEDB.svg?style=for-the-badge)](https://saythanks.io/to/khorevaa)
[![Powered By: GoReleaser](https://img.shields.io/badge/powered%20by-goreleaser-green.svg?style=for-the-badge)](https://github.com/goreleaser)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=for-the-badge)](https://conventionalcommits.org)

39
service/cache.go Normal file
View File

@ -0,0 +1,39 @@
package service
import (
"fmt"
"github.com/khorevaa/ras-client/serialize"
)
func (s service) getFromCache(key string) (interface{}, bool) {
//fmt.Sprintf("%s.clusters", ctt.App.Name)
return s.cache.Get(key)
}
func (s service) getCacheClusters(appName string) ([]*serialize.ClusterInfo, bool) {
if clusters, ok := s.cache.Get(fmt.Sprintf(clustersTpl, appName)); ok {
return clusters.([]*serialize.ClusterInfo), ok
}
return nil, false
}
func (s service) setCacheClusters(appName string, clusters []*serialize.ClusterInfo) {
s.cache.Set(fmt.Sprintf(clustersTpl, appName), clusters)
}
func (s service) getCacheInfobases(cluster string) (serialize.InfobaseSummaryList, bool) {
if list, ok := s.cache.Get(fmt.Sprintf(infobasesTpl, cluster)); ok {
return list.(serialize.InfobaseSummaryList), ok
}
return nil, false
}
func (s service) setCacheInfobases(cluster string, list serialize.InfobaseSummaryList) {
s.cache.Set(fmt.Sprintf(infobasesTpl, cluster), list)
}
func (s service) clearCacheInfobases(cluster string) {
s.cache.Clear(fmt.Sprintf(infobasesTpl, cluster))
}

9
service/cache/cache.go vendored Normal file
View File

@ -0,0 +1,9 @@
package cache
type Cache interface {
Connect()
Get(key string) (interface{}, bool)
Set(key string, value interface{})
Clear(key string)
HealthCheck() (bool, error)
}

32
service/cache/memory.go vendored Normal file
View File

@ -0,0 +1,32 @@
package cache
import (
"time"
memoryCache "github.com/patrickmn/go-cache"
)
type Memory struct {
Expiration time.Duration
cache *memoryCache.Cache
}
func (m *Memory) HealthCheck() (bool, error) {
return true, nil
}
func (m *Memory) Connect() {
m.cache = memoryCache.New(m.Expiration*time.Minute, m.Expiration*time.Minute)
//log.Println("Using in-memory cache")
}
func (m *Memory) Get(key string) (interface{}, bool) {
return m.cache.Get(key)
}
func (m *Memory) Set(key string, value interface{}) {
m.cache.Set(key, value, time.Duration(m.Expiration)*time.Minute)
}
func (m *Memory) Clear(key string) {
m.cache.Delete(key)
}

367
service/clientContext.go Normal file
View File

@ -0,0 +1,367 @@
package service
import (
"context"
"github.com/gofiber/fiber/v2"
"github.com/khorevaa/kubodin/errors"
"github.com/khorevaa/kubodin/models"
rclient "github.com/khorevaa/ras-client"
"github.com/khorevaa/ras-client/serialize"
"github.com/recoilme/pudge"
uuid "github.com/satori/go.uuid"
)
//var _ rclient.Api = (ClientContext)(nil)
type ClientContext struct {
client rclient.Api
App *models.AppServer
ctx context.Context
requestCtx *fiber.Ctx
force bool
}
func (c ClientContext) GetApiClient() rclient.Api {
return c.client
}
func (c ClientContext) Version() string {
return c.client.Version()
}
func (c ClientContext) Close() error {
return c.client.Close()
}
func (c ClientContext) AuthenticateAgent(user, password string) {
c.client.AuthenticateAgent(user, password)
}
func (c ClientContext) AuthenticateCluster(cluster uuid.UUID, user, password string) {
c.client.AuthenticateCluster(cluster, user, password)
}
func (c ClientContext) AuthenticateInfobase(infobase uuid.UUID, user, password string) {
c.client.AuthenticateInfobase(infobase, user, password)
}
func (c ClientContext) GetClusters() ([]*serialize.ClusterInfo, error) {
return c.client.GetClusters(c.ctx)
}
func (c ClientContext) GetAgentAdmins() (serialize.UsersList, error) {
return c.client.GetAgentAdmins(c.ctx)
}
func (c ClientContext) GetAgentVersion() (string, error) {
return c.client.GetAgentVersion(c.ctx)
}
func (c ClientContext) RegAgentAdmin(user serialize.UserInfo) error {
return c.client.RegAgentAdmin(c.ctx, user)
}
func (c ClientContext) UnregAgentAdmin(user string) error {
return c.client.UnregAgentAdmin(c.ctx, user)
}
func (c ClientContext) GetClusterAdmins(cluster uuid.UUID) (serialize.UsersList, error) {
c.AddAuth(cluster)
return c.client.GetClusterAdmins(c.ctx, cluster)
}
func (c ClientContext) RegClusterAdmin(cluster uuid.UUID, user serialize.UserInfo) error {
c.AddAuth(cluster)
return c.client.RegClusterAdmin(c.ctx, cluster, user)
}
func (c ClientContext) UnregClusterAdmin(cluster uuid.UUID, user string) error {
c.AddAuth(cluster)
return c.client.UnregClusterAdmin(c.ctx, cluster, user)
}
func (c ClientContext) GetClusterInfo(cluster uuid.UUID) (serialize.ClusterInfo, error) {
c.AddAuth(cluster)
return c.client.GetClusterInfo(c.ctx, cluster)
}
func (c ClientContext) GetClusterInfobases(cluster uuid.UUID) (serialize.InfobaseSummaryList, error) {
c.AddAuth(cluster)
return c.client.GetClusterInfobases(c.ctx, cluster)
}
func (c ClientContext) GetClusterServices(cluster uuid.UUID) ([]*serialize.ServiceInfo, error) {
c.AddAuth(cluster)
return c.client.GetClusterServices(c.ctx, cluster)
}
func (c ClientContext) GetClusterManagers(cluster uuid.UUID) ([]*serialize.ManagerInfo, error) {
c.AddAuth(cluster)
return c.client.GetClusterManagers(c.ctx, cluster)
}
func (c ClientContext) GetClusterSessions(cluster uuid.UUID) (serialize.SessionInfoList, error) {
c.AddAuth(cluster)
return c.client.GetClusterSessions(c.ctx, cluster)
}
func (c ClientContext) GetInfobaseSessions(cluster uuid.UUID, infobase uuid.UUID) (serialize.SessionInfoList, error) {
c.AddAuth(cluster, infobase)
return c.client.GetInfobaseSessions(c.ctx, cluster, infobase)
}
func (c ClientContext) TerminateSession(cluster uuid.UUID, session uuid.UUID, msg string) error {
c.AddAuth(cluster)
return c.client.TerminateSession(c.ctx, cluster, session, msg)
}
func (c ClientContext) GetClusterLocks(cluster uuid.UUID) (serialize.LocksList, error) {
c.AddAuth(cluster)
return c.client.GetClusterLocks(c.ctx, cluster)
}
func (c ClientContext) GetInfobaseLocks(cluster uuid.UUID, infobase uuid.UUID) (serialize.LocksList, error) {
c.AddAuth(cluster, infobase)
return c.client.GetInfobaseLocks(c.ctx, cluster, infobase)
}
func (c ClientContext) GetSessionLocks(cluster uuid.UUID, infobase uuid.UUID, session uuid.UUID) (serialize.LocksList, error) {
c.AddAuth(cluster, infobase)
return c.client.GetSessionLocks(c.ctx, cluster, infobase, session)
}
func (c ClientContext) GetConnectionLocks(cluster uuid.UUID, connection uuid.UUID) (serialize.LocksList, error) {
c.AddAuth(cluster)
return c.client.GetConnectionLocks(c.ctx, cluster, connection)
}
func (c ClientContext) GetClusterConnections(cluster uuid.UUID) (serialize.ConnectionShortInfoList, error) {
c.AddAuth(cluster)
return c.client.GetClusterConnections(c.ctx, cluster)
}
func (c ClientContext) GetInfobaseConnections(cluster uuid.UUID, infobase uuid.UUID) (serialize.ConnectionShortInfoList, error) {
c.AddAuth(cluster, infobase)
return c.client.GetInfobaseConnections(c.ctx, cluster, infobase)
}
func (c ClientContext) DisconnectConnection(cluster uuid.UUID, process uuid.UUID, connection uuid.UUID, infobase uuid.UUID) error {
c.AddAuth(cluster, infobase)
return c.client.DisconnectConnection(c.ctx, cluster, process, connection, infobase)
}
func (c ClientContext) CreateInfobase(cluster uuid.UUID, infobase serialize.InfobaseInfo, mode int) (serialize.InfobaseInfo, error) {
c.AddAuth(cluster)
return c.client.CreateInfobase(c.ctx, cluster, infobase, mode)
}
func (c ClientContext) UpdateSummaryInfobase(cluster uuid.UUID, infobase serialize.InfobaseSummaryInfo) error {
c.AddAuth(cluster, infobase.UUID)
return c.client.UpdateSummaryInfobase(c.ctx, cluster, infobase)
}
func (c ClientContext) UpdateInfobase(cluster uuid.UUID, infobase serialize.InfobaseInfo) error {
c.AddAuth(cluster, infobase.UUID)
return c.client.UpdateInfobase(c.ctx, cluster, infobase)
}
func (c ClientContext) DropInfobase(cluster uuid.UUID, infobase uuid.UUID, mode int) error {
c.AddAuth(cluster, infobase)
return c.client.DropInfobase(c.ctx, cluster, infobase, mode)
}
func (c ClientContext) GetInfobaseInfo(cluster uuid.UUID, infobase uuid.UUID) (serialize.InfobaseInfo, error) {
c.AddAuth(cluster, infobase)
return c.client.GetInfobaseInfo(c.ctx, cluster, infobase)
}
func (c ClientContext) GetWorkingProcesses(cluster uuid.UUID) (serialize.ProcessInfoList, error) {
c.AddAuth(cluster)
return c.client.GetWorkingProcesses(c.ctx, cluster)
}
func (c ClientContext) GetWorkingProcessInfo(cluster, process uuid.UUID) (*serialize.ProcessInfo, error) {
c.AddAuth(cluster)
return c.client.GetWorkingProcessInfo(c.ctx, cluster, process)
}
func (c ClientContext) GetWorkingServers(cluster uuid.UUID) ([]*serialize.ServerInfo, error) {
c.AddAuth(cluster)
return c.client.GetWorkingServers(c.ctx, cluster)
}
func (c ClientContext) GetWorkingServerInfo(cluster, serverID uuid.UUID) (*serialize.ServerInfo, error) {
c.AddAuth(cluster)
return c.client.GetWorkingServerInfo(c.ctx, cluster, serverID)
}
func (c ClientContext) RegWorkingServer(cluster uuid.UUID, info *serialize.ServerInfo) (*serialize.ServerInfo, error) {
c.AddAuth(cluster)
return c.client.RegWorkingServer(c.ctx, cluster, info)
}
func (c ClientContext) UnRegWorkingServer(cluster, serverID uuid.UUID) error {
c.AddAuth(cluster)
return c.client.UnRegWorkingServer(c.ctx, cluster, serverID)
}
func (c ClientContext) Force() bool {
return c.force
}
func (c ClientContext) Context() context.Context {
return c.ctx
}
func appServerFromContext(ctx *fiber.Ctx) (*models.AppServer, error) {
name := ctx.Params("app")
serviceInterface := ctx.Context().UserValue("service")
s, ok := serviceInterface.(Service)
if !ok {
return nil, errors.Internal.New("cannot get service from context ")
}
app, err := s.GetAppServer(name)
if err != nil {
if err == pudge.ErrKeyNotFound {
return nil, errors.BadRequest.Newf("app <%s> not registered", name)
}
return nil, errors.BadRequest.Wrapf(err, "cannot get app <%s>", name)
}
return app, err
}
func GetClientContext(ctx *fiber.Ctx) (ClientContext, error) {
app, err := appServerFromContext(ctx)
if err != nil {
return ClientContext{}, err
}
apiClient, err := app.Client()
if err != nil {
return ClientContext{}, err
}
client := ClientContext{
App: app,
client: apiClient,
requestCtx: ctx,
ctx: ctx.Context(),
force: GetContextValueOrNil(ctx, "force").Bool(false),
}
return client, nil
}
func NewClientContext(app *models.AppServer, ctx *fiber.Ctx) *ClientContext {
apiClient, _ := app.Client()
return &ClientContext{
App: app,
client: apiClient,
requestCtx: ctx,
ctx: ctx.Context(),
force: true,
}
}
func (c ClientContext) GetContextValue(name string, unescape ...bool) (ContextValue, bool) {
return GetContextValue(c.requestCtx, name, unescape...)
}
var NeedClusterID = errors.BadRequest.New("need set cluster id")
func (c ClientContext) GetClusterID() (uuid.UUID, bool) {
value := GetContextValueOrNil(c.requestCtx, "cluster cluster-id", true)
id, err := value.UUID()
if err != nil {
return id, false
}
return id, true
}
func (c ClientContext) GetClusterIDOrNil() uuid.UUID {
id, _ := c.GetClusterID()
return id
}
func (c ClientContext) GetInfobaseID() (ContextValue, bool) {
val := GetContextValueOrNil(c.requestCtx, "infobase infobase-id", true)
return val, !val.Empty()
}
func (c ClientContext) AddAuth(cluster uuid.UUID, infobase ...uuid.UUID) {
c.authCluster(cluster)
if len(infobase) == 1 {
c.authInfobase(infobase[0])
}
}
func (c ClientContext) authCluster(cluster uuid.UUID) {
if cluster == uuid.Nil {
return
}
user, _ := c.GetContextValue("cluster-usr", true)
if len(user) == 0 {
return
}
pwd, _ := c.GetContextValue("cluster-pwd", true)
c.client.AuthenticateCluster(cluster, user.String(), pwd.String())
}
func (c ClientContext) authInfobase(infobase uuid.UUID) {
if infobase == uuid.Nil {
return
}
user, _ := c.GetContextValue("infobase-usr", true)
if len(user) == 0 {
return
}
pwd, _ := c.GetContextValue("infobase-pwd", true)
c.client.AuthenticateInfobase(infobase, user.String(), pwd.String())
}
func (c ClientContext) HealthCheck() (bool, error) {
_, err := c.client.GetAgentVersion(c.ctx)
if err != nil {
return false, err
}
return true, nil
}

27
service/clusters.go Normal file
View File

@ -0,0 +1,27 @@
package service
import (
"github.com/khorevaa/kubodin/errors"
"github.com/khorevaa/ras-client/serialize"
)
func (s *service) GetClusters(client ClientContext) ([]*serialize.ClusterInfo, error) {
return s.getClusters(client)
}
func (s *service) GetClusterInfo(client ClientContext) (*serialize.ClusterInfo, error) {
clusterID, ok := client.GetClusterID()
if !ok {
return nil, errors.BadRequest.New("incorrect or not set <cluster-id>")
}
info, err := client.GetClusterInfo(clusterID)
if err != nil {
return nil, err
}
return &info, nil
}

108
service/contextValue.go Normal file
View File

@ -0,0 +1,108 @@
package service
import (
"github.com/gofiber/fiber/v2"
uuid "github.com/satori/go.uuid"
"net/url"
"strconv"
"strings"
)
func GetContextValue(ctx *fiber.Ctx, name string, unescape ...bool) (ContextValue, bool) {
names := strings.Fields(name)
var val string
for _, valName := range names {
val = ctx.Params(valName)
if len(val) > 0 {
break
}
val = ctx.Query(valName)
if len(val) > 0 {
break
}
}
if len(val) == 0 {
return "", false
}
urlUnescape := true
if len(unescape) > 0 {
urlUnescape = unescape[0]
}
if urlUnescape {
val, _ = url.QueryUnescape(val)
}
return ContextValue(val), len(val) > 0
}
func GetContextValueOrNil(ctx *fiber.Ctx, name string, unescape ...bool) ContextValue {
val, _ := GetContextValue(ctx, name, unescape...)
return val
}
func GetClusterID(ctx *fiber.Ctx) (ContextValue, bool) {
return GetContextValue(ctx, "cluster cluster-id", true)
}
func GetInfobaseID(ctx *fiber.Ctx) (ContextValue, bool) {
return GetContextValue(ctx, "infobase infobase-id", true)
}
type ContextValue string
func (val ContextValue) Empty() bool {
return len(val) == 0
}
func (val ContextValue) NotEmpty() bool {
return !val.Empty()
}
func (val ContextValue) String() string {
return string(val)
}
func (val ContextValue) Bool(defaultVal ...bool) bool {
var defVal bool
if len(defaultVal) > 0 {
defVal = defaultVal[0]
}
valB, err := strconv.ParseBool(val.String())
if err != nil {
return defVal
}
return valB
}
func (val ContextValue) UUID() (uuid.UUID, error) {
return uuid.FromString(val.String())
}
func (val ContextValue) NilUUID() bool {
return len(val) > 0 &&
uuid.FromStringOrNil(val.String()) == uuid.Nil
}
func (val ContextValue) NotNilUUID() bool {
return !val.NilUUID()
}

153
service/helpers.go Normal file
View File

@ -0,0 +1,153 @@
package service
import (
"github.com/khorevaa/kubodin/errors"
"github.com/khorevaa/ras-client/serialize"
uuid "github.com/satori/go.uuid"
)
func (s service) findInfobaseInList(list serialize.InfobaseSummaryList, infobaseID string) (*serialize.InfobaseSummaryInfo, bool) {
if id := uuid.FromStringOrNil(infobaseID); id != uuid.Nil {
return list.ByID(id)
}
return list.ByName(infobaseID)
}
func (s *service) getClusterID(client ClientContext, clusterID ...uuid.UUID) (uuid.UUID, error) {
if len(clusterID) > 0 &&
clusterID[0] != uuid.Nil {
return clusterID[0], nil
}
cluster, ok := client.GetClusterID()
if ok {
return cluster, nil
}
clusters, err := s.getClusters(client)
if err != nil {
return uuid.Nil, err
}
if len(clusters) == 1 {
return clusters[0].UUID, nil
}
return uuid.Nil, errors.BadRequest.New("to many clusters. Set <cluster id> value manually")
}
func (s *service) getAnyClusterID(client ClientContext, clusterID ...uuid.UUID) (uuid.UUID, error) {
if len(clusterID) > 0 &&
clusterID[0] != uuid.Nil {
return clusterID[0], nil
}
clusters, err := s.getClusters(client)
if err != nil {
return uuid.Nil, err
}
if len(clusters) > 0 {
return clusters[0].UUID, nil
}
return uuid.Nil, errors.BadRequest.New("no registered clusters. Set <cluster id> value manually")
}
func (s service) findInfobase(client ClientContext, infobaseID string, clusterID ...uuid.UUID) (*serialize.InfobaseSummaryInfo, error) {
clusters, err := s.getClusters(client)
if err != nil {
return nil, err
}
if len(clusterID) == 1 && clusterID[0] != uuid.Nil {
list, err := s.getClusterInfobases(client, clusterID[0])
if err != nil {
return nil, err
}
summaryInfo, ok := s.findInfobaseInList(list, infobaseID)
if !ok {
return nil, errors.BadRequest.Newf("infobase not found by name or uuid <%s> on cluster <%s>",
infobaseID, clusterID[0].String())
}
return summaryInfo, nil
}
var summaryInfoList []*serialize.InfobaseSummaryInfo
for _, cluster := range clusters {
list, err := s.getClusterInfobases(client, cluster.UUID)
if err != nil {
continue
}
summaryInfo, ok := s.findInfobaseInList(list, infobaseID)
if ok {
summaryInfoList = append(summaryInfoList, summaryInfo)
}
}
switch len(summaryInfoList) {
case 1:
return summaryInfoList[0], nil
case 0:
return nil, errors.BadRequest.Newf("infobase not found by name or uuid <%s>", infobaseID)
default:
return nil, errors.BadRequest.Newf("find to many infobases with <%s>."+
" Set <cluster-id> value manually", infobaseID)
}
}
func (s *service) getClusterInfobases(client ClientContext, clusterID uuid.UUID) (serialize.InfobaseSummaryList, error) {
cacheKey := clusterID.String()
if list, ok := s.getCacheInfobases(cacheKey); ok && !client.Force() {
return list, nil
}
list, err := client.GetClusterInfobases(clusterID)
if err != nil {
return nil, err
}
s.setCacheInfobases(cacheKey, list)
return list, nil
}
func (s *service) getClusters(client ClientContext) ([]*serialize.ClusterInfo, error) {
cacheKey := client.App.Name
if list, ok := s.getCacheClusters(cacheKey); ok && !client.Force() {
return list, nil
}
list, err := client.GetClusters()
if err != nil {
return nil, err
}
s.setCacheClusters(cacheKey, list)
return list, nil
}

87
service/infobase.go Normal file
View File

@ -0,0 +1,87 @@
package service
import (
"github.com/khorevaa/kubodin/errors"
"github.com/khorevaa/ras-client/serialize"
uuid "github.com/satori/go.uuid"
)
func (s *service) GetInfobases(client ClientContext) (serialize.InfobaseSummaryList, error) {
cluster, _ := client.GetClusterID()
clusterID, _ := s.getClusterID(client, cluster)
if clusterID != uuid.Nil {
return s.getClusterInfobases(client, clusterID)
}
clusters, err := s.getClusters(client)
if err != nil {
return nil, err
}
var listAll serialize.InfobaseSummaryList
for _, cluster := range clusters {
list, err := s.getClusterInfobases(client, cluster.UUID)
if err != nil {
continue
}
listAll = append(listAll, list...)
}
return listAll, nil
}
func (s *service) CreateInfobase(client ClientContext, info *serialize.InfobaseInfo, createDB bool) (*serialize.InfobaseInfo, error) {
cluster, _ := client.GetClusterID()
clusterID, err := s.getAnyClusterID(client, cluster)
if err != nil {
return nil, err
}
mode := 0
if createDB {
mode = 1
}
infobaseInfo, err := client.CreateInfobase(clusterID, *info, mode)
if err != nil {
return nil, err
}
s.clearCacheInfobases(clusterID.String())
return &infobaseInfo, nil
}
func (s *service) DropInfobase(client ClientContext, dropDB bool) error {
infobaseID, _ := client.GetInfobaseID()
if infobaseID.Empty() {
return errors.BadRequest.New("infobase id or name must be set")
}
summaryInfo, err := s.findInfobase(client, infobaseID.String())
if err != nil {
return err
}
mode := 0
if dropDB {
mode = 1
}
err = client.DropInfobase(summaryInfo.ClusterID, summaryInfo.UUID, mode)
return err
}

18
service/middleware.go Normal file
View File

@ -0,0 +1,18 @@
package service
import (
"github.com/gofiber/fiber/v2"
)
// New creates a new middleware handler
func Middleware(s Service) fiber.Handler {
// Return new handler
return func(c *fiber.Ctx) (err error) {
c.Context().SetUserValue("service", s)
// Return err if exist, else move to next handler
return c.Next()
}
}

62
service/service.go Normal file
View File

@ -0,0 +1,62 @@
package service
import (
db "github.com/khorevaa/kubodin/database"
"github.com/khorevaa/kubodin/models"
"github.com/khorevaa/kubodin/service/cache"
"github.com/khorevaa/ras-client/serialize"
)
const (
clustersTpl = "%s.clusters"
infobasesTpl = "%s.infobases"
)
var _ Service = (*service)(nil)
//Service interface allows us to access the CRUD Operations
type Service interface {
Repository() db.Repository
CreateInfobase(ctt ClientContext, info *serialize.InfobaseInfo, createDB bool) (*serialize.InfobaseInfo, error)
DropInfobase(ctt ClientContext, deleteDB bool) error
GetInfobases(ctt ClientContext) (serialize.InfobaseSummaryList, error)
GetClusters(ctt ClientContext) ([]*serialize.ClusterInfo, error)
GetClusterInfo(ctt ClientContext) (*serialize.ClusterInfo, error)
GetAppServers() (apps []*models.AppServer, err error)
GetAppServer(name string) (*models.AppServer, error)
GetCache() cache.Cache
}
func NewService(cache cache.Cache, repository db.Repository) (Service, error) {
return &service{
cache: cache,
repository: repository,
}, nil
}
type service struct {
repository db.Repository
cache cache.Cache
}
func (s service) GetCache() cache.Cache {
return s.cache
}
func (s *service) Repository() db.Repository {
return s.repository
}
func (s *service) GetAppServers() (apps []*models.AppServer, err error) {
return s.repository.GetAppServers()
}
func (s *service) GetAppServer(name string) (*models.AppServer, error) {
return s.repository.GetAppServer(name)
}