refactor, initial work for ssh driver (still wip)

This commit is contained in:
Pavel 2023-11-18 16:14:39 +03:00
parent 7c443887dc
commit d3029d09e7
22 changed files with 461 additions and 128 deletions

View File

@ -36,7 +36,6 @@ var rootCmd = &cobra.Command{
for id, item := range docker.Default.Containers() {
err := server.DefaultManager.ProcessEvent(dto.Event{
Type: dto.EventStart,
ID: id,
Container: item,
})
if err != nil {
@ -54,7 +53,8 @@ var rootCmd = &cobra.Command{
for event := range events {
err := server.DefaultManager.ProcessEvent(event)
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
View File

@ -6,6 +6,7 @@ require (
github.com/docker/docker v24.0.7+incompatible
github.com/docker/go-connections v0.4.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/spf13/cast v1.5.1
github.com/spf13/cobra v1.8.0

2
go.sum
View File

@ -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/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/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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=

View File

@ -10,7 +10,7 @@ import (
"github.com/Neur0toxine/sshpoke/internal/logger"
"github.com/Neur0toxine/sshpoke/internal/server"
"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"
"github.com/Neur0toxine/sshpoke/pkg/plugin/pb"
"google.golang.org/grpc"
@ -44,10 +44,7 @@ func (p *pluginAPI) EventStatus(ctx context.Context, msg *pb.EventStatusMessage)
if pl == nil {
return nil, ErrUnauthorized
}
pl.HandleStatus(dto.EventStatus{
ID: msg.Id,
Error: msg.Error,
})
pl.PushEventStatus(convert.MessageToAppEventStatus(msg))
return &emptypb.Empty{}, nil
}

View File

@ -50,6 +50,21 @@ func (d *Docker) Containers() map[string]dto.Container {
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) {
cli, err := client.NewClientWithOpts(config.Default.Docker.Opts)
if err != nil {
@ -83,7 +98,6 @@ func (d *Docker) Listen() (chan dto.Event, error) {
}
newEvent := dto.Event{
Type: eventType,
ID: event.Actor.ID,
Container: converted,
}
msg := "exposing container"

View File

@ -77,6 +77,8 @@ func dockerContainerToInternal(container types.Container) (result dto.Container,
}
return dto.Container{
ID: container.ID,
Names: container.Names,
IP: ip,
Port: uint16(port),
Server: labels.Server,

View 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
}

View 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
}

View File

@ -1,17 +1,22 @@
package iface
package base
import (
"context"
"github.com/Neur0toxine/sshpoke/internal/config"
"github.com/Neur0toxine/sshpoke/pkg/dto"
"go.uber.org/zap"
)
type DriverConstructor func(ctx context.Context, name string, params config.DriverParams) (Driver, error)
type EventStatusCallback func(status dto.EventStatus)
type Driver interface {
Name() string
SetEventStatusCallback(callback EventStatusCallback)
PushEventStatus(status dto.EventStatus)
Handle(event dto.Event) error
Driver() config.DriverType
Log() *zap.SugaredLogger
WaitForShutdown()
}

View File

@ -4,13 +4,13 @@ import (
"context"
"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/plugin"
"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 {
case config.DriverSSH:
return ssh.New(ctx, name, params)

View File

@ -4,30 +4,40 @@ import (
"context"
"github.com/Neur0toxine/sshpoke/internal/config"
"github.com/Neur0toxine/sshpoke/internal/logger"
"github.com/Neur0toxine/sshpoke/internal/server/driver/iface"
"github.com/Neur0toxine/sshpoke/internal/server/driver/base"
"github.com/Neur0toxine/sshpoke/pkg/dto"
)
// 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?
type Null struct {
name string
base.Base
}
func New(ctx context.Context, name string, params config.DriverParams) (iface.Driver, error) {
return &Null{name: name}, nil
func New(ctx context.Context, name string, params config.DriverParams) (base.Driver, error) {
return &Null{
Base: base.New(ctx, name),
}, nil
}
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
}
func (d *Null) Name() string {
return d.name
}
func (d *Null) Driver() config.DriverType {
return config.DriverNull
}

View File

@ -7,8 +7,7 @@ import (
"sync/atomic"
"github.com/Neur0toxine/sshpoke/internal/config"
"github.com/Neur0toxine/sshpoke/internal/logger"
"github.com/Neur0toxine/sshpoke/internal/server/driver/iface"
"github.com/Neur0toxine/sshpoke/internal/server/driver/base"
"github.com/Neur0toxine/sshpoke/internal/server/driver/util"
"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.
type Driver struct {
ctx context.Context
name string
base.Base
params Params
send *Queue[dto.Event]
listening atomic.Bool
@ -29,16 +27,14 @@ type EventStream interface {
}
type Plugin interface {
iface.Driver
base.Driver
Token() string
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{
name: name,
ctx: ctx,
Base: base.New(ctx, name),
send: NewQueue[dto.Event](),
}
if err := util.UnmarshalParams(params, &drv.params); err != nil {
@ -56,10 +52,6 @@ func (d *Driver) Handle(event dto.Event) error {
return nil
}
func (d *Driver) Name() string {
return d.name
}
func (d *Driver) Driver() config.DriverType {
return config.DriverPlugin
}
@ -88,21 +80,17 @@ func (d *Driver) Listen(ctx context.Context, stream EventStream) error {
return nil
}
if err != nil {
logger.Sugar.Errorw("error writing event to plugin",
"server", d.name, "error", err)
d.Log().Errorw("error writing event to plugin",
"server", d.Name(), "error", 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 {
select {
case <-d.ctx.Done():
case <-d.Context().Done():
return true
default:
return false
@ -110,6 +98,6 @@ func (d *Driver) isDone() bool {
}
func (d *Driver) WaitForShutdown() {
<-d.ctx.Done()
<-d.Context().Done()
return
}

View File

@ -3,20 +3,26 @@ package ssh
import (
"context"
"errors"
"fmt"
"os"
"path"
"strings"
"sync"
"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/proto/sshtun"
"github.com/Neur0toxine/sshpoke/pkg/dto"
"golang.org/x/crypto/ssh"
)
type SSH struct {
ctx context.Context
name string
base.Base
params Params
sessions map[string]conn
keys []ssh.Signer
wg sync.WaitGroup
}
@ -25,21 +31,47 @@ type conn struct {
tun *sshtun.Tunnel
}
func New(ctx context.Context, name string, params config.DriverParams) (iface.Driver, error) {
drv := &SSH{ctx: ctx, name: name, sessions: make(map[string]conn)}
func New(ctx context.Context, name string, params config.DriverParams) (base.Driver, error) {
drv := &SSH{
Base: base.New(ctx, name),
sessions: make(map[string]conn),
}
if err := util.UnmarshalParams(params, &drv.params); err != nil {
return nil, err
}
drv.populateFromSSHConfig()
if err := drv.parseKeys(); err != nil {
return nil, err
}
return drv, nil
}
func (d *SSH) Handle(event dto.Event) error {
// TODO: Implement event handling & connections management.
return errors.New(d.name + " server handler is not implemented yet")
func (d *SSH) populateFromSSHConfig() {
if d.params.Auth.Directory == "" {
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 {
return d.name
func (d *SSH) Handle(event dto.Event) error {
// TODO: Implement event handling & connections management.
return errors.New("server handler is not implemented yet")
}
func (d *SSH) Driver() config.DriverType {
@ -49,3 +81,79 @@ func (d *SSH) Driver() config.DriverType {
func (d *SSH) WaitForShutdown() {
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
}

View File

@ -1,63 +1,24 @@
package ssh
import (
"fmt"
"github.com/Neur0toxine/sshpoke/internal/server/driver/ssh/types"
"github.com/Neur0toxine/sshpoke/internal/server/driver/util"
)
type Params struct {
Address string `mapstructure:"address" validate:"required"`
Auth Auth `mapstructure:"auth"`
KeepAlive KeepAlive `mapstructure:"keepalive"`
Auth types.Auth `mapstructure:"auth"`
KeepAlive types.KeepAlive `mapstructure:"keepalive"`
Domain string `mapstructure:"domain"`
DomainProto string `mapstructure:"domain_proto"`
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"`
}
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 {
if err := util.Validator.Struct(p); err != nil {
return err
}
return p.Auth.validate()
return p.Auth.Validate()
}

View 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))
}

View 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
}

View File

@ -0,0 +1,8 @@
package types
type DomainMode string
const (
DomainModeSingle DomainMode = "single"
DomainModeMulti DomainMode = "multi"
)

View 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"`
}

View File

@ -6,20 +6,28 @@ import (
"sync"
"github.com/Neur0toxine/sshpoke/internal/config"
"github.com/Neur0toxine/sshpoke/internal/docker"
"github.com/Neur0toxine/sshpoke/internal/logger"
"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/pkg/dto"
)
type Manager struct {
rw sync.RWMutex
servers map[string]iface.Driver
servers map[string]base.Driver
plugins map[string]plugin.Plugin
statusMap map[string]serverStatus
statusLock sync.RWMutex
defaultServer string
}
type serverStatus struct {
Name string `json:"name"`
Connections Connections `json:"connections"`
}
var DefaultManager *Manager
var (
ErrNoServer = errors.New("server is not specified")
@ -28,7 +36,7 @@ var (
func NewManager(ctx context.Context, servers []config.Server, defaultServer string) *Manager {
m := &Manager{
servers: make(map[string]iface.Driver),
servers: make(map[string]base.Driver),
plugins: make(map[string]plugin.Plugin),
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)
continue
}
server.SetEventStatusCallback(m.eventStatusCallback(server.Name()))
if server.Driver() == config.DriverPlugin {
pl := server.(plugin.Plugin)
if pl.Token() == "" {
@ -73,6 +82,37 @@ func (m *Manager) ProcessEvent(event dto.Event) error {
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 {
server, ok := m.plugins[token]
if !ok {

View File

@ -9,14 +9,15 @@ import (
func MessageToAppEvent(event *pb.EventMessage) dto.Event {
return dto.Event{
Type: MessageEventTypeToApp(event.Type),
ID: event.Id,
Type: MessageEventTypeToApp(event.GetType()),
Container: dto.Container{
IP: net.ParseIP(event.Container.Ip),
Port: uint16(event.Container.Port),
Server: event.Container.Server,
Prefix: event.Container.Prefix,
Domain: event.Container.Domain,
ID: event.GetContainer().GetId(),
Names: event.GetContainer().GetNames(),
IP: net.ParseIP(event.GetContainer().GetIp()),
Port: uint16(event.GetContainer().GetPort()),
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 {
return &pb.EventMessage{
Type: AppEventTypeToMessage(event.Type),
Id: event.ID,
Container: &pb.Container{
Id: event.Container.ID,
Names: event.Container.Names,
Ip: event.Container.IP.String(),
Port: uint32(event.Container.Port),
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 {
return &pb.EventStatusMessage{
Type: AppEventTypeToMessage(status.Type),
Id: status.ID,
Error: status.Error,
Domain: status.Domain,
@ -58,7 +70,9 @@ func AppEventTypeToMessage(typ dto.EventType) pb.EventType {
case 1:
return pb.EventType_EVENT_STOP
case 2:
fallthrough
return pb.EventType_EVENT_SHUTDOWN
case 3:
return pb.EventType_EVENT_ERROR
default:
return pb.EventType_EVENT_UNKNOWN
}

View File

@ -8,6 +8,7 @@ const (
EventStart EventType = iota
EventStop
EventShutdown
EventError
EventUnknown
)
@ -24,20 +25,22 @@ func TypeFromAction(action string) EventType {
type Event struct {
Type EventType
ID string
Container Container
}
type EventStatus struct {
Type EventType
ID string
Error string
Domain string
}
type Container struct {
IP net.IP
Port uint16
Server string
Prefix string
Domain string
ID string `json:"id"`
Names []string `json:"names"`
IP net.IP `json:"ip"`
Port uint16 `json:"port"`
Server string `json:"-"`
Prefix string `json:"prefix"`
Domain string `json:"domain"`
}

View File

@ -13,15 +13,18 @@ enum EventType {
EVENT_START = 0;
EVENT_STOP = 1;
EVENT_SHUTDOWN = 2;
EVENT_UNKNOWN = 3;
EVENT_ERROR = 3;
EVENT_UNKNOWN = 4;
}
message Container {
string ip = 1;
uint32 port = 2;
string server = 3;
string prefix = 4;
string domain = 5;
string id = 1;
repeated string names = 2;
string ip = 3;
uint32 port = 4;
string server = 5;
string prefix = 6;
string domain = 7;
}
message EventMessage {
@ -31,7 +34,8 @@ message EventMessage {
}
message EventStatusMessage {
string id = 1;
string error = 2;
string domain = 3;
EventType type = 1;
string id = 2;
string error = 3;
string domain = 4;
}