refactor, initial work for ssh driver (still wip)
This commit is contained in:
parent
7c443887dc
commit
d3029d09e7
@ -36,7 +36,6 @@ var rootCmd = &cobra.Command{
|
|||||||
for id, item := range docker.Default.Containers() {
|
for id, item := range docker.Default.Containers() {
|
||||||
err := server.DefaultManager.ProcessEvent(dto.Event{
|
err := server.DefaultManager.ProcessEvent(dto.Event{
|
||||||
Type: dto.EventStart,
|
Type: dto.EventStart,
|
||||||
ID: id,
|
|
||||||
Container: item,
|
Container: item,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,7 +53,8 @@ var rootCmd = &cobra.Command{
|
|||||||
for event := range events {
|
for event := range events {
|
||||||
err := server.DefaultManager.ProcessEvent(event)
|
err := server.DefaultManager.ProcessEvent(event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Sugar.Errorw("cannot expose container", "id", event.ID, "error", err)
|
logger.Sugar.Errorw("cannot expose container",
|
||||||
|
"id", event.Container.ID, "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ require (
|
|||||||
github.com/docker/docker v24.0.7+incompatible
|
github.com/docker/docker v24.0.7+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/go-playground/validator/v10 v10.16.0
|
github.com/go-playground/validator/v10 v10.16.0
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/spf13/cast v1.5.1
|
github.com/spf13/cast v1.5.1
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
|
2
go.sum
2
go.sum
@ -162,6 +162,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
|||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||||
|
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/Neur0toxine/sshpoke/internal/logger"
|
"github.com/Neur0toxine/sshpoke/internal/logger"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server"
|
"github.com/Neur0toxine/sshpoke/internal/server"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/plugin"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/plugin"
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
"github.com/Neur0toxine/sshpoke/pkg/convert"
|
||||||
plugin2 "github.com/Neur0toxine/sshpoke/pkg/plugin"
|
plugin2 "github.com/Neur0toxine/sshpoke/pkg/plugin"
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/plugin/pb"
|
"github.com/Neur0toxine/sshpoke/pkg/plugin/pb"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@ -44,10 +44,7 @@ func (p *pluginAPI) EventStatus(ctx context.Context, msg *pb.EventStatusMessage)
|
|||||||
if pl == nil {
|
if pl == nil {
|
||||||
return nil, ErrUnauthorized
|
return nil, ErrUnauthorized
|
||||||
}
|
}
|
||||||
pl.HandleStatus(dto.EventStatus{
|
pl.PushEventStatus(convert.MessageToAppEventStatus(msg))
|
||||||
ID: msg.Id,
|
|
||||||
Error: msg.Error,
|
|
||||||
})
|
|
||||||
return &emptypb.Empty{}, nil
|
return &emptypb.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,21 @@ func (d *Docker) Containers() map[string]dto.Container {
|
|||||||
return containers
|
return containers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Docker) GetContainer(id string) (dto.Container, bool) {
|
||||||
|
container, err := d.cli.ContainerList(d.ctx, types.ContainerListOptions{
|
||||||
|
Filters: filters.NewArgs(filters.Arg("id", id)),
|
||||||
|
All: true,
|
||||||
|
})
|
||||||
|
if err != nil || len(container) != 1 {
|
||||||
|
return dto.Container{}, false
|
||||||
|
}
|
||||||
|
converted, ok := dockerContainerToInternal(container[0])
|
||||||
|
if !ok {
|
||||||
|
return dto.Container{}, false
|
||||||
|
}
|
||||||
|
return converted, true
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Docker) Listen() (chan dto.Event, error) {
|
func (d *Docker) Listen() (chan dto.Event, error) {
|
||||||
cli, err := client.NewClientWithOpts(config.Default.Docker.Opts)
|
cli, err := client.NewClientWithOpts(config.Default.Docker.Opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -83,7 +98,6 @@ func (d *Docker) Listen() (chan dto.Event, error) {
|
|||||||
}
|
}
|
||||||
newEvent := dto.Event{
|
newEvent := dto.Event{
|
||||||
Type: eventType,
|
Type: eventType,
|
||||||
ID: event.Actor.ID,
|
|
||||||
Container: converted,
|
Container: converted,
|
||||||
}
|
}
|
||||||
msg := "exposing container"
|
msg := "exposing container"
|
||||||
|
@ -77,6 +77,8 @@ func dockerContainerToInternal(container types.Container) (result dto.Container,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return dto.Container{
|
return dto.Container{
|
||||||
|
ID: container.ID,
|
||||||
|
Names: container.Names,
|
||||||
IP: ip,
|
IP: ip,
|
||||||
Port: uint16(port),
|
Port: uint16(port),
|
||||||
Server: labels.Server,
|
Server: labels.Server,
|
||||||
|
30
internal/server/container_list.go
Normal file
30
internal/server/container_list.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Connections map[string]dto.Container
|
||||||
|
|
||||||
|
func (c Connections) MarshalJSON() ([]byte, error) {
|
||||||
|
items := []dto.Container{}
|
||||||
|
for _, item := range c {
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
return json.Marshal(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Connections) UnmarshalJSON(data []byte) error {
|
||||||
|
var items []dto.Container
|
||||||
|
if err := json.Unmarshal(data, &items); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m := make(map[string]dto.Container, len(items))
|
||||||
|
for _, item := range items {
|
||||||
|
m[item.ID] = item
|
||||||
|
}
|
||||||
|
*c = m
|
||||||
|
return nil
|
||||||
|
}
|
46
internal/server/driver/base/base.go
Normal file
46
internal/server/driver/base/base.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/Neur0toxine/sshpoke/internal/logger"
|
||||||
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Base struct {
|
||||||
|
ctx context.Context
|
||||||
|
name string
|
||||||
|
log *zap.SugaredLogger
|
||||||
|
eventStatusCb EventStatusCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, name string) Base {
|
||||||
|
return Base{
|
||||||
|
ctx: ctx,
|
||||||
|
name: name,
|
||||||
|
log: logger.Default.With(zap.String("serverName", name)).Sugar(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) SetEventStatusCallback(callback EventStatusCallback) {
|
||||||
|
b.eventStatusCb = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) PushEventStatus(status dto.EventStatus) {
|
||||||
|
if b.eventStatusCb != nil {
|
||||||
|
b.eventStatusCb(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) Context() context.Context {
|
||||||
|
return b.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) Name() string {
|
||||||
|
return b.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Base) Log() *zap.SugaredLogger {
|
||||||
|
return b.log
|
||||||
|
}
|
@ -1,17 +1,22 @@
|
|||||||
package iface
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/Neur0toxine/sshpoke/internal/config"
|
"github.com/Neur0toxine/sshpoke/internal/config"
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DriverConstructor func(ctx context.Context, name string, params config.DriverParams) (Driver, error)
|
type DriverConstructor func(ctx context.Context, name string, params config.DriverParams) (Driver, error)
|
||||||
|
type EventStatusCallback func(status dto.EventStatus)
|
||||||
|
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
Name() string
|
Name() string
|
||||||
|
SetEventStatusCallback(callback EventStatusCallback)
|
||||||
|
PushEventStatus(status dto.EventStatus)
|
||||||
Handle(event dto.Event) error
|
Handle(event dto.Event) error
|
||||||
Driver() config.DriverType
|
Driver() config.DriverType
|
||||||
|
Log() *zap.SugaredLogger
|
||||||
WaitForShutdown()
|
WaitForShutdown()
|
||||||
}
|
}
|
@ -4,13 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/Neur0toxine/sshpoke/internal/config"
|
"github.com/Neur0toxine/sshpoke/internal/config"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/iface"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/base"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/null"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/null"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/plugin"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/plugin"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/ssh"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(ctx context.Context, name string, driver config.DriverType, params config.DriverParams) (iface.Driver, error) {
|
func New(ctx context.Context, name string, driver config.DriverType, params config.DriverParams) (base.Driver, error) {
|
||||||
switch driver {
|
switch driver {
|
||||||
case config.DriverSSH:
|
case config.DriverSSH:
|
||||||
return ssh.New(ctx, name, params)
|
return ssh.New(ctx, name, params)
|
||||||
|
@ -4,30 +4,40 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/Neur0toxine/sshpoke/internal/config"
|
"github.com/Neur0toxine/sshpoke/internal/config"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/logger"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/base"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/iface"
|
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Null driver only logs container events to debug log. It is used when user provides invalid driver type.
|
// Null driver only logs container events to debug log. It is used when user provides invalid driver type.
|
||||||
// You can use it directly, but it won't do anything, so... why bother?
|
// You can use it directly, but it won't do anything, so... why bother?
|
||||||
type Null struct {
|
type Null struct {
|
||||||
name string
|
base.Base
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, name string, params config.DriverParams) (iface.Driver, error) {
|
func New(ctx context.Context, name string, params config.DriverParams) (base.Driver, error) {
|
||||||
return &Null{name: name}, nil
|
return &Null{
|
||||||
|
Base: base.New(ctx, name),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Null) Handle(event dto.Event) error {
|
func (d *Null) Handle(event dto.Event) error {
|
||||||
logger.Sugar.Debugw("handling event with null driver", "serverName", d.name, "event", event)
|
d.Log().Debugw("handling event with null driver", "serverName", d.Name(), "event", event)
|
||||||
|
switch event.Type {
|
||||||
|
case dto.EventStart:
|
||||||
|
d.PushEventStatus(dto.EventStatus{
|
||||||
|
Type: dto.EventStart,
|
||||||
|
ID: event.Container.ID,
|
||||||
|
Domain: "https://" + event.Container.ID + "null.dev",
|
||||||
|
})
|
||||||
|
case dto.EventStop, dto.EventShutdown:
|
||||||
|
d.PushEventStatus(dto.EventStatus{
|
||||||
|
Type: event.Type,
|
||||||
|
ID: event.Container.ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Null) Name() string {
|
|
||||||
return d.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Null) Driver() config.DriverType {
|
func (d *Null) Driver() config.DriverType {
|
||||||
return config.DriverNull
|
return config.DriverNull
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/Neur0toxine/sshpoke/internal/config"
|
"github.com/Neur0toxine/sshpoke/internal/config"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/logger"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/base"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/iface"
|
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/util"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/util"
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
||||||
)
|
)
|
||||||
@ -17,8 +16,7 @@ var ErrAlreadyConnected = errors.New("already connected")
|
|||||||
|
|
||||||
// Driver plugin uses RPC to communicate with external plugin.
|
// Driver plugin uses RPC to communicate with external plugin.
|
||||||
type Driver struct {
|
type Driver struct {
|
||||||
ctx context.Context
|
base.Base
|
||||||
name string
|
|
||||||
params Params
|
params Params
|
||||||
send *Queue[dto.Event]
|
send *Queue[dto.Event]
|
||||||
listening atomic.Bool
|
listening atomic.Bool
|
||||||
@ -29,16 +27,14 @@ type EventStream interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Plugin interface {
|
type Plugin interface {
|
||||||
iface.Driver
|
base.Driver
|
||||||
Token() string
|
Token() string
|
||||||
Listen(ctx context.Context, stream EventStream) error
|
Listen(ctx context.Context, stream EventStream) error
|
||||||
HandleStatus(event dto.EventStatus)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, name string, params config.DriverParams) (iface.Driver, error) {
|
func New(ctx context.Context, name string, params config.DriverParams) (base.Driver, error) {
|
||||||
drv := &Driver{
|
drv := &Driver{
|
||||||
name: name,
|
Base: base.New(ctx, name),
|
||||||
ctx: ctx,
|
|
||||||
send: NewQueue[dto.Event](),
|
send: NewQueue[dto.Event](),
|
||||||
}
|
}
|
||||||
if err := util.UnmarshalParams(params, &drv.params); err != nil {
|
if err := util.UnmarshalParams(params, &drv.params); err != nil {
|
||||||
@ -56,10 +52,6 @@ func (d *Driver) Handle(event dto.Event) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) Name() string {
|
|
||||||
return d.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Driver) Driver() config.DriverType {
|
func (d *Driver) Driver() config.DriverType {
|
||||||
return config.DriverPlugin
|
return config.DriverPlugin
|
||||||
}
|
}
|
||||||
@ -88,21 +80,17 @@ func (d *Driver) Listen(ctx context.Context, stream EventStream) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Sugar.Errorw("error writing event to plugin",
|
d.Log().Errorw("error writing event to plugin",
|
||||||
"server", d.name, "error", err)
|
"server", d.Name(), "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) HandleStatus(event dto.EventStatus) {
|
|
||||||
logger.Sugar.Errorw("plugin error", "serverName", d.name, "id", event.ID, "error", event.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Driver) isDone() bool {
|
func (d *Driver) isDone() bool {
|
||||||
select {
|
select {
|
||||||
case <-d.ctx.Done():
|
case <-d.Context().Done():
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@ -110,6 +98,6 @@ func (d *Driver) isDone() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) WaitForShutdown() {
|
func (d *Driver) WaitForShutdown() {
|
||||||
<-d.ctx.Done()
|
<-d.Context().Done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,26 @@ package ssh
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Neur0toxine/sshpoke/internal/config"
|
"github.com/Neur0toxine/sshpoke/internal/config"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/iface"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/base"
|
||||||
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/ssh/types"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/util"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/util"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/proto/sshtun"
|
"github.com/Neur0toxine/sshpoke/internal/server/proto/sshtun"
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SSH struct {
|
type SSH struct {
|
||||||
ctx context.Context
|
base.Base
|
||||||
name string
|
|
||||||
params Params
|
params Params
|
||||||
sessions map[string]conn
|
sessions map[string]conn
|
||||||
|
keys []ssh.Signer
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,21 +31,47 @@ type conn struct {
|
|||||||
tun *sshtun.Tunnel
|
tun *sshtun.Tunnel
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, name string, params config.DriverParams) (iface.Driver, error) {
|
func New(ctx context.Context, name string, params config.DriverParams) (base.Driver, error) {
|
||||||
drv := &SSH{ctx: ctx, name: name, sessions: make(map[string]conn)}
|
drv := &SSH{
|
||||||
|
Base: base.New(ctx, name),
|
||||||
|
sessions: make(map[string]conn),
|
||||||
|
}
|
||||||
if err := util.UnmarshalParams(params, &drv.params); err != nil {
|
if err := util.UnmarshalParams(params, &drv.params); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
drv.populateFromSSHConfig()
|
||||||
|
if err := drv.parseKeys(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return drv, nil
|
return drv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SSH) Handle(event dto.Event) error {
|
func (d *SSH) populateFromSSHConfig() {
|
||||||
// TODO: Implement event handling & connections management.
|
if d.params.Auth.Directory == "" {
|
||||||
return errors.New(d.name + " server handler is not implemented yet")
|
return
|
||||||
|
}
|
||||||
|
cfg, err := parseSSHConfig(types.SmartPath(path.Join(string(d.params.Auth.Directory), "config")))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user, err := cfg.Get(d.params.Address, "User"); err == nil && user != "" {
|
||||||
|
d.params.Auth.User = user
|
||||||
|
}
|
||||||
|
if usePass, err := cfg.Get(d.params.Address, "PasswordAuthentication"); err == nil && usePass == "yes" {
|
||||||
|
d.params.Auth.Type = types.AuthTypePassword
|
||||||
|
}
|
||||||
|
if keyfile, err := cfg.Get(d.params.Address, "IdentityFile"); err == nil && keyfile != "" {
|
||||||
|
resolvedKeyFile, err := types.SmartPath(keyfile).Resolve(false)
|
||||||
|
if err == nil {
|
||||||
|
d.params.Auth.Type = types.AuthTypeKey
|
||||||
|
d.params.Auth.Keyfile = resolvedKeyFile
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SSH) Name() string {
|
func (d *SSH) Handle(event dto.Event) error {
|
||||||
return d.name
|
// TODO: Implement event handling & connections management.
|
||||||
|
return errors.New("server handler is not implemented yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SSH) Driver() config.DriverType {
|
func (d *SSH) Driver() config.DriverType {
|
||||||
@ -49,3 +81,79 @@ func (d *SSH) Driver() config.DriverType {
|
|||||||
func (d *SSH) WaitForShutdown() {
|
func (d *SSH) WaitForShutdown() {
|
||||||
d.wg.Wait()
|
d.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *SSH) parseKeys() error {
|
||||||
|
if d.params.Auth.Type != types.AuthTypeKey {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dir, err := d.params.Auth.Directory.Resolve(true)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot parse keys: %s", err)
|
||||||
|
}
|
||||||
|
if d.params.Auth.Keyfile != "" {
|
||||||
|
key, err := parseKey(path.Join(dir, d.params.Auth.Keyfile))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.keys = []ssh.Signer{key}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot read key directory: %s", err)
|
||||||
|
}
|
||||||
|
keys := []ssh.Signer{}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
d.Log().Debugf("skipping '%s' because it's a directory", entry.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
info, err := entry.Info()
|
||||||
|
if err != nil {
|
||||||
|
d.Log().Debugf("skipping '%s' because stat failed: %s", entry.Name(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(entry.Name(), ".pub") {
|
||||||
|
d.Log().Debugf("skipping '%s' because it's probably a public key", entry.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if entry.Name() == "config" {
|
||||||
|
d.Log().Debugf("skipping '%s' because it's probably a ssh-config file", entry.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if entry.Name() == "known_hosts" {
|
||||||
|
d.Log().Debugf(
|
||||||
|
"skipping '%s' because it's probably a list of hosts generated by OpenSSH", entry.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// this file is too small to be a private key
|
||||||
|
if info.Size() < 256 {
|
||||||
|
d.Log().Debugf("skipping '%s' because the file is smaller than 256 bytes", entry.Name())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key, err := parseKey(path.Join(dir, entry.Name()))
|
||||||
|
if err != nil {
|
||||||
|
d.Log().Debugf("skipping '%s' because it's probably not a key: %s", entry.Name(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.Log().Debugf("loading key '%s', type: %s", entry.Name(), key.PublicKey().Type())
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return errors.New("no keys in the provided directory")
|
||||||
|
}
|
||||||
|
d.keys = keys
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseKey(keyFile string) (ssh.Signer, error) {
|
||||||
|
keyData, err := os.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key, err := ssh.ParsePrivateKey(keyData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
@ -1,63 +1,24 @@
|
|||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/ssh/types"
|
||||||
|
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/util"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Params struct {
|
type Params struct {
|
||||||
Address string `mapstructure:"address" validate:"required"`
|
Address string `mapstructure:"address" validate:"required"`
|
||||||
Auth Auth `mapstructure:"auth"`
|
Auth types.Auth `mapstructure:"auth"`
|
||||||
KeepAlive KeepAlive `mapstructure:"keepalive"`
|
KeepAlive types.KeepAlive `mapstructure:"keepalive"`
|
||||||
Domain string `mapstructure:"domain"`
|
Domain string `mapstructure:"domain"`
|
||||||
DomainProto string `mapstructure:"domain_proto"`
|
DomainProto string `mapstructure:"domain_proto"`
|
||||||
DomainExtractRegex string `mapstructure:"domain_extract_regex" validate:"validregexp"`
|
DomainExtractRegex string `mapstructure:"domain_extract_regex" validate:"validregexp"`
|
||||||
Mode DomainMode `mapstructure:"mode" validate:"required,oneof=single multi"`
|
Mode types.DomainMode `mapstructure:"mode" validate:"required,oneof=single multi"`
|
||||||
Prefix bool `mapstructure:"prefix"`
|
Prefix bool `mapstructure:"prefix"`
|
||||||
}
|
|
||||||
|
|
||||||
type AuthType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
AuthTypePasswordless AuthType = "passwordless"
|
|
||||||
AuthTypePassword AuthType = "password"
|
|
||||||
AuthTypeKey AuthType = "key"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DomainMode string
|
|
||||||
|
|
||||||
const (
|
|
||||||
DomainModeSingle DomainMode = "single"
|
|
||||||
DomainModeMulti DomainMode = "multi"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Auth struct {
|
|
||||||
Type AuthType `mapstructure:"type" validate:"required,oneof=passwordless password key"`
|
|
||||||
User string `mapstructure:"user"`
|
|
||||||
Password string `mapstructure:"password"`
|
|
||||||
Directory string `mapstructure:"directory"`
|
|
||||||
Keyfile string `mapstructure:"keyfile"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Auth) validate() error {
|
|
||||||
if a.Type == AuthTypePassword && a.Password == "" {
|
|
||||||
return fmt.Errorf("password must be provided for authentication type '%s'", AuthTypePassword)
|
|
||||||
}
|
|
||||||
if a.Type == AuthTypeKey && a.Directory == "" {
|
|
||||||
return fmt.Errorf("password must be provided for authentication type '%s'", AuthTypePassword)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type KeepAlive struct {
|
|
||||||
Interval int `mapstructure:"interval" validate:"gte=0"`
|
|
||||||
MaxAttempts int `mapstructure:"max_attempts" validate:"gte=1"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Params) Validate() error {
|
func (p *Params) Validate() error {
|
||||||
if err := util.Validator.Struct(p); err != nil {
|
if err := util.Validator.Struct(p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p.Auth.validate()
|
return p.Auth.Validate()
|
||||||
}
|
}
|
||||||
|
21
internal/server/driver/ssh/sshconfig.go
Normal file
21
internal/server/driver/ssh/sshconfig.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/ssh/types"
|
||||||
|
"github.com/kevinburke/ssh_config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseSSHConfig(filePath types.SmartPath) (*ssh_config.Config, error) {
|
||||||
|
fileName, err := filePath.Resolve(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
file, err := os.ReadFile(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ssh_config.Decode(bytes.NewReader(file))
|
||||||
|
}
|
73
internal/server/driver/ssh/types/auth.go
Normal file
73
internal/server/driver/ssh/types/auth.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var envMatcherRegExp = regexp.MustCompile(`\$[\w\d\_]`)
|
||||||
|
|
||||||
|
type (
|
||||||
|
AuthType string
|
||||||
|
SmartPath string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AuthTypePasswordless AuthType = "passwordless"
|
||||||
|
AuthTypePassword AuthType = "password"
|
||||||
|
AuthTypeKey AuthType = "key"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
Type AuthType `mapstructure:"type" validate:"required,oneof=passwordless password key"`
|
||||||
|
User string `mapstructure:"user"`
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
Directory SmartPath `mapstructure:"directory"`
|
||||||
|
Keyfile string `mapstructure:"keyfile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k SmartPath) Resolve(shouldBeDirectory bool) (result string, err error) {
|
||||||
|
result = strings.TrimSpace(string(k))
|
||||||
|
if strings.HasPrefix(result, "~/") {
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
result = path.Join(homeDir, result[2:])
|
||||||
|
}
|
||||||
|
for _, match := range envMatcherRegExp.FindAllString(string(k), -1) {
|
||||||
|
envVar := match[1:]
|
||||||
|
if envVar == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
envVar = os.Getenv(envVar)
|
||||||
|
result = strings.ReplaceAll(result, match, envVar)
|
||||||
|
}
|
||||||
|
result, err = filepath.Abs(result)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stat, err := os.Stat(result)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !stat.IsDir() && shouldBeDirectory {
|
||||||
|
err = errors.New("is not a directory")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Auth) Validate() error {
|
||||||
|
if a.Type == AuthTypePassword && a.Password == "" {
|
||||||
|
return fmt.Errorf("password must be provided for authentication type '%s'", AuthTypePassword)
|
||||||
|
}
|
||||||
|
if a.Type == AuthTypeKey && a.Directory == "" {
|
||||||
|
return fmt.Errorf("password must be provided for authentication type '%s'", AuthTypePassword)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
8
internal/server/driver/ssh/types/domain_mode.go
Normal file
8
internal/server/driver/ssh/types/domain_mode.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type DomainMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
DomainModeSingle DomainMode = "single"
|
||||||
|
DomainModeMulti DomainMode = "multi"
|
||||||
|
)
|
6
internal/server/driver/ssh/types/keep_alive.go
Normal file
6
internal/server/driver/ssh/types/keep_alive.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type KeepAlive struct {
|
||||||
|
Interval int `mapstructure:"interval" validate:"gte=0"`
|
||||||
|
MaxAttempts int `mapstructure:"max_attempts" validate:"gte=1"`
|
||||||
|
}
|
@ -6,20 +6,28 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Neur0toxine/sshpoke/internal/config"
|
"github.com/Neur0toxine/sshpoke/internal/config"
|
||||||
|
"github.com/Neur0toxine/sshpoke/internal/docker"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/logger"
|
"github.com/Neur0toxine/sshpoke/internal/logger"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/iface"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/base"
|
||||||
"github.com/Neur0toxine/sshpoke/internal/server/driver/plugin"
|
"github.com/Neur0toxine/sshpoke/internal/server/driver/plugin"
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
rw sync.RWMutex
|
rw sync.RWMutex
|
||||||
servers map[string]iface.Driver
|
servers map[string]base.Driver
|
||||||
plugins map[string]plugin.Plugin
|
plugins map[string]plugin.Plugin
|
||||||
|
statusMap map[string]serverStatus
|
||||||
|
statusLock sync.RWMutex
|
||||||
defaultServer string
|
defaultServer string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverStatus struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Connections Connections `json:"connections"`
|
||||||
|
}
|
||||||
|
|
||||||
var DefaultManager *Manager
|
var DefaultManager *Manager
|
||||||
var (
|
var (
|
||||||
ErrNoServer = errors.New("server is not specified")
|
ErrNoServer = errors.New("server is not specified")
|
||||||
@ -28,7 +36,7 @@ var (
|
|||||||
|
|
||||||
func NewManager(ctx context.Context, servers []config.Server, defaultServer string) *Manager {
|
func NewManager(ctx context.Context, servers []config.Server, defaultServer string) *Manager {
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
servers: make(map[string]iface.Driver),
|
servers: make(map[string]base.Driver),
|
||||||
plugins: make(map[string]plugin.Plugin),
|
plugins: make(map[string]plugin.Plugin),
|
||||||
defaultServer: defaultServer,
|
defaultServer: defaultServer,
|
||||||
}
|
}
|
||||||
@ -38,6 +46,7 @@ func NewManager(ctx context.Context, servers []config.Server, defaultServer stri
|
|||||||
logger.Sugar.Errorf("cannot initialize server '%s': %s", serverConfig.Name, err)
|
logger.Sugar.Errorf("cannot initialize server '%s': %s", serverConfig.Name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
server.SetEventStatusCallback(m.eventStatusCallback(server.Name()))
|
||||||
if server.Driver() == config.DriverPlugin {
|
if server.Driver() == config.DriverPlugin {
|
||||||
pl := server.(plugin.Plugin)
|
pl := server.(plugin.Plugin)
|
||||||
if pl.Token() == "" {
|
if pl.Token() == "" {
|
||||||
@ -73,6 +82,37 @@ func (m *Manager) ProcessEvent(event dto.Event) error {
|
|||||||
return srv.Handle(event)
|
return srv.Handle(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) eventStatusCallback(serverName string) base.EventStatusCallback {
|
||||||
|
return func(status dto.EventStatus) {
|
||||||
|
m.processEventStatus(serverName, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) processEventStatus(serverName string, event dto.EventStatus) {
|
||||||
|
m.statusLock.RLock()
|
||||||
|
_, exists := m.statusMap[serverName]
|
||||||
|
if !exists {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.statusLock.RUnlock()
|
||||||
|
defer m.statusLock.Unlock()
|
||||||
|
m.statusLock.Lock()
|
||||||
|
item, found := docker.Default.GetContainer(event.ID)
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch event.Type {
|
||||||
|
case dto.EventStart:
|
||||||
|
item.Domain = event.Domain
|
||||||
|
case dto.EventStop, dto.EventShutdown, dto.EventError:
|
||||||
|
item.Domain = ""
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.statusMap[serverName].Connections[item.ID] = item
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) PluginByToken(token string) plugin.Plugin {
|
func (m *Manager) PluginByToken(token string) plugin.Plugin {
|
||||||
server, ok := m.plugins[token]
|
server, ok := m.plugins[token]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -9,14 +9,15 @@ import (
|
|||||||
|
|
||||||
func MessageToAppEvent(event *pb.EventMessage) dto.Event {
|
func MessageToAppEvent(event *pb.EventMessage) dto.Event {
|
||||||
return dto.Event{
|
return dto.Event{
|
||||||
Type: MessageEventTypeToApp(event.Type),
|
Type: MessageEventTypeToApp(event.GetType()),
|
||||||
ID: event.Id,
|
|
||||||
Container: dto.Container{
|
Container: dto.Container{
|
||||||
IP: net.ParseIP(event.Container.Ip),
|
ID: event.GetContainer().GetId(),
|
||||||
Port: uint16(event.Container.Port),
|
Names: event.GetContainer().GetNames(),
|
||||||
Server: event.Container.Server,
|
IP: net.ParseIP(event.GetContainer().GetIp()),
|
||||||
Prefix: event.Container.Prefix,
|
Port: uint16(event.GetContainer().GetPort()),
|
||||||
Domain: event.Container.Domain,
|
Server: event.GetContainer().GetServer(),
|
||||||
|
Prefix: event.GetContainer().GetPrefix(),
|
||||||
|
Domain: event.GetContainer().GetDomain(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,8 +25,9 @@ func MessageToAppEvent(event *pb.EventMessage) dto.Event {
|
|||||||
func AppEventToMessage(event dto.Event) *pb.EventMessage {
|
func AppEventToMessage(event dto.Event) *pb.EventMessage {
|
||||||
return &pb.EventMessage{
|
return &pb.EventMessage{
|
||||||
Type: AppEventTypeToMessage(event.Type),
|
Type: AppEventTypeToMessage(event.Type),
|
||||||
Id: event.ID,
|
|
||||||
Container: &pb.Container{
|
Container: &pb.Container{
|
||||||
|
Id: event.Container.ID,
|
||||||
|
Names: event.Container.Names,
|
||||||
Ip: event.Container.IP.String(),
|
Ip: event.Container.IP.String(),
|
||||||
Port: uint32(event.Container.Port),
|
Port: uint32(event.Container.Port),
|
||||||
Server: event.Container.Server,
|
Server: event.Container.Server,
|
||||||
@ -35,8 +37,18 @@ func AppEventToMessage(event dto.Event) *pb.EventMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MessageToAppEventStatus(val *pb.EventStatusMessage) dto.EventStatus {
|
||||||
|
return dto.EventStatus{
|
||||||
|
Type: MessageEventTypeToApp(val.GetType()),
|
||||||
|
ID: val.GetId(),
|
||||||
|
Error: val.GetError(),
|
||||||
|
Domain: val.GetDomain(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func AppEventStatusToMessage(status dto.EventStatus) *pb.EventStatusMessage {
|
func AppEventStatusToMessage(status dto.EventStatus) *pb.EventStatusMessage {
|
||||||
return &pb.EventStatusMessage{
|
return &pb.EventStatusMessage{
|
||||||
|
Type: AppEventTypeToMessage(status.Type),
|
||||||
Id: status.ID,
|
Id: status.ID,
|
||||||
Error: status.Error,
|
Error: status.Error,
|
||||||
Domain: status.Domain,
|
Domain: status.Domain,
|
||||||
@ -58,7 +70,9 @@ func AppEventTypeToMessage(typ dto.EventType) pb.EventType {
|
|||||||
case 1:
|
case 1:
|
||||||
return pb.EventType_EVENT_STOP
|
return pb.EventType_EVENT_STOP
|
||||||
case 2:
|
case 2:
|
||||||
fallthrough
|
return pb.EventType_EVENT_SHUTDOWN
|
||||||
|
case 3:
|
||||||
|
return pb.EventType_EVENT_ERROR
|
||||||
default:
|
default:
|
||||||
return pb.EventType_EVENT_UNKNOWN
|
return pb.EventType_EVENT_UNKNOWN
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ const (
|
|||||||
EventStart EventType = iota
|
EventStart EventType = iota
|
||||||
EventStop
|
EventStop
|
||||||
EventShutdown
|
EventShutdown
|
||||||
|
EventError
|
||||||
EventUnknown
|
EventUnknown
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,20 +25,22 @@ func TypeFromAction(action string) EventType {
|
|||||||
|
|
||||||
type Event struct {
|
type Event struct {
|
||||||
Type EventType
|
Type EventType
|
||||||
ID string
|
|
||||||
Container Container
|
Container Container
|
||||||
}
|
}
|
||||||
|
|
||||||
type EventStatus struct {
|
type EventStatus struct {
|
||||||
|
Type EventType
|
||||||
ID string
|
ID string
|
||||||
Error string
|
Error string
|
||||||
Domain string
|
Domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
IP net.IP
|
ID string `json:"id"`
|
||||||
Port uint16
|
Names []string `json:"names"`
|
||||||
Server string
|
IP net.IP `json:"ip"`
|
||||||
Prefix string
|
Port uint16 `json:"port"`
|
||||||
Domain string
|
Server string `json:"-"`
|
||||||
|
Prefix string `json:"prefix"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
}
|
}
|
||||||
|
@ -13,15 +13,18 @@ enum EventType {
|
|||||||
EVENT_START = 0;
|
EVENT_START = 0;
|
||||||
EVENT_STOP = 1;
|
EVENT_STOP = 1;
|
||||||
EVENT_SHUTDOWN = 2;
|
EVENT_SHUTDOWN = 2;
|
||||||
EVENT_UNKNOWN = 3;
|
EVENT_ERROR = 3;
|
||||||
|
EVENT_UNKNOWN = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Container {
|
message Container {
|
||||||
string ip = 1;
|
string id = 1;
|
||||||
uint32 port = 2;
|
repeated string names = 2;
|
||||||
string server = 3;
|
string ip = 3;
|
||||||
string prefix = 4;
|
uint32 port = 4;
|
||||||
string domain = 5;
|
string server = 5;
|
||||||
|
string prefix = 6;
|
||||||
|
string domain = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message EventMessage {
|
message EventMessage {
|
||||||
@ -31,7 +34,8 @@ message EventMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message EventStatusMessage {
|
message EventStatusMessage {
|
||||||
string id = 1;
|
EventType type = 1;
|
||||||
string error = 2;
|
string id = 2;
|
||||||
string domain = 3;
|
string error = 3;
|
||||||
|
string domain = 4;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user