diff --git a/cmd/start.go b/cmd/start.go index 7617363..8279098 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -6,7 +6,7 @@ import ( "github.com/arshamalh/dockeroller/docker" "github.com/arshamalh/dockeroller/log" - "github.com/arshamalh/dockeroller/repo/ephemeral" + "github.com/arshamalh/dockeroller/session" tpkg "github.com/arshamalh/dockeroller/telegram" "github.com/arshamalh/dockeroller/telegram/handlers" "github.com/joho/godotenv" @@ -60,7 +60,7 @@ func startTelegram(docker docker.Docker, token string, whitelistedIDs []int64) { if err != nil { log.Gl.Error(err.Error()) } - session := ephemeral.New() + session := session.New() handlers.Register(bot, docker, session) // Middlewares bot.Use(middleware.Whitelist(whitelistedIDs...)) diff --git a/docker/containers.go b/docker/containers.go index 173724a..afdf7e5 100644 --- a/docker/containers.go +++ b/docker/containers.go @@ -4,35 +4,35 @@ import ( "context" "io" - "github.com/arshamalh/dockeroller/models" + "github.com/arshamalh/dockeroller/entities" "github.com/docker/docker/api/types" ) -func (d *docker) ContainersList() (containers []*models.Container) { +func (d *docker) ContainersList() (containers []*entities.Container) { raw_containers, _ := d.cli.ContainerList(context.TODO(), types.ContainerListOptions{All: true}) for _, raw_cont := range raw_containers { - containers = append(containers, &models.Container{ + containers = append(containers, &entities.Container{ ID: raw_cont.ID, Name: raw_cont.Names[0], Image: raw_cont.Image, Status: raw_cont.Status, - State: models.ContainerState(raw_cont.State), + State: entities.ContainerState(raw_cont.State), }) } return } -func (d *docker) GetContainer(containerID string) (*models.Container, error) { +func (d *docker) GetContainer(containerID string) (*entities.Container, error) { container, err := d.cli.ContainerInspect(context.TODO(), containerID) if err != nil { return nil, err } - return &models.Container{ + return &entities.Container{ ID: container.ID, Name: container.Name, Image: container.Image, Status: container.State.Status, - State: models.ContainerState(container.State.Status), + State: entities.ContainerState(container.State.Status), }, nil } @@ -59,7 +59,7 @@ func (d *docker) ContainerStop(containerID string) error { return d.cli.ContainerStop(context.TODO(), containerID, nil) } -func (d *docker) ContainerRemove(containerID string, removeForm *models.ContainerRemoveForm) error { +func (d *docker) ContainerRemove(containerID string, removeForm *entities.ContainerRemoveForm) error { return d.cli.ContainerRemove(context.TODO(), containerID, types.ContainerRemoveOptions{ RemoveVolumes: removeForm.RemoveVolumes, Force: removeForm.Force, diff --git a/docker/docker.go b/docker/docker.go index 0d0b669..a7be743 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -4,22 +4,22 @@ import ( "context" "io" + "github.com/arshamalh/dockeroller/entities" "github.com/arshamalh/dockeroller/log" - "github.com/arshamalh/dockeroller/models" "github.com/moby/moby/client" ) type Docker interface { - GetContainer(containerID string) (*models.Container, error) - ContainersList() []*models.Container + GetContainer(containerID string) (*entities.Container, error) + ContainersList() []*entities.Container ContainerLogs(containerID string) (io.ReadCloser, error) ContainerStats(containerID string) (io.ReadCloser, error) ContainerStart(containerID string) error ContainerStop(containerID string) error - ContainerRemove(containerID string, removeForm *models.ContainerRemoveForm) error + ContainerRemove(containerID string, removeForm *entities.ContainerRemoveForm) error ContainerRename(containerID, newName string) error - ImagesList() []*models.Image + ImagesList() []*entities.Image ImageTag(ctx context.Context, imageID, newTag string) error ImageRemove(ctx context.Context, imageID string, force, pruneChildren bool) error } diff --git a/docker/images.go b/docker/images.go index bc3be15..c74ab53 100644 --- a/docker/images.go +++ b/docker/images.go @@ -5,19 +5,19 @@ import ( "fmt" "time" - "github.com/arshamalh/dockeroller/models" + "github.com/arshamalh/dockeroller/entities" "github.com/docker/docker/api/types" ) -func (d *docker) ImagesList() (images []*models.Image) { +func (d *docker) ImagesList() (images []*entities.Image) { raw_images, _ := d.cli.ImageList(context.TODO(), types.ImageListOptions{All: true}) for _, raw_img := range raw_images { status := d.getImageStatus(context.TODO(), raw_img) - images = append(images, &models.Image{ + images = append(images, &entities.Image{ ID: raw_img.ID, Size: raw_img.Size, Tags: raw_img.RepoTags, - Status: models.ImageStatus(status), + Status: entities.ImageStatus(status), CreatedAt: fmt.Sprint(time.Unix(raw_img.Created, 0).Format("2006-01-02 15:04:05")), }) } @@ -40,14 +40,14 @@ func (d *docker) ImageRemove(ctx context.Context, imageID string, force, pruneCh func (d *docker) getImageStatus(ctx context.Context, image types.ImageSummary) (status string) { if len(image.RepoTags) == 0 { - status = string(models.ImageStatusUnUsedDangling) + status = string(entities.ImageStatusUnUsedDangling) } containers, _ := d.cli.ContainerList(ctx, types.ContainerListOptions{}) newImgs := make(map[string][]string) for _, cont := range containers { if cont.ImageID != image.ID { - status = string(models.ImageStatusUnUsed) + status = string(entities.ImageStatusUnUsed) } else { newSlice := newImgs[image.ID] if newSlice == nil { @@ -56,7 +56,7 @@ func (d *docker) getImageStatus(ctx context.Context, image types.ImageSummary) ( newSlice = append(newSlice, cont.ID) newImgs[image.ID] = newSlice - status = string(models.ImageStatusInUse) + status = string(entities.ImageStatusInUse) } } diff --git a/models/container.go b/entities/container.go similarity index 97% rename from models/container.go rename to entities/container.go index 3ced08a..b560679 100644 --- a/models/container.go +++ b/entities/container.go @@ -1,4 +1,4 @@ -package models +package entities import "fmt" diff --git a/models/userdata.go b/entities/forms.go similarity index 56% rename from models/userdata.go rename to entities/forms.go index ecc3113..e616b40 100644 --- a/models/userdata.go +++ b/entities/forms.go @@ -1,8 +1,8 @@ -package models +package entities -type UserData struct { - ContainerRemoveForm *ContainerRemoveForm - ImageRemoveForm *ImageRemoveForm +type Forms struct { + ContainerRemove *ContainerRemoveForm + ImageRemove *ImageRemoveForm } type ContainerRemoveForm struct { diff --git a/models/image.go b/entities/image.go similarity index 94% rename from models/image.go rename to entities/image.go index a09172e..d1805b8 100644 --- a/models/image.go +++ b/entities/image.go @@ -1,4 +1,4 @@ -package models +package entities type ImageStatus string diff --git a/models/queue.go b/entities/queue.go similarity index 72% rename from models/queue.go rename to entities/queue.go index 4f7a479..82e42c3 100644 --- a/models/queue.go +++ b/entities/queue.go @@ -1,4 +1,4 @@ -package models +package entities import "fmt" @@ -25,19 +25,6 @@ func NewQueue() *Queue { return &Queue{} } -func (q *Queue) isZeroOrOneNode() (bool, *Node) { - if q.Length == 0 { - return true, nil - } else if q.Length == 1 { - value := q.head - q.head = nil - q.tail = nil - q.Length-- - return true, value - } - return false, nil -} - func (q *Queue) Push(value string) *Queue { new_node := newNode(value, nil) if q.Length == 0 { @@ -50,14 +37,23 @@ func (q *Queue) Push(value string) *Queue { return q } -func (q *Queue) Pop() *Node { - if ok, node := q.isZeroOrOneNode(); ok { - return node +// Removes a Node from the queue and returns it, +// Returns an error if there is no available node +func (q *Queue) Pop() (string, error) { + if q.Length == 0 { + return "", fmt.Errorf("no node available to pop") } + node := q.head - q.head = q.head.Next + if q.Length == 1 { + q.head = nil + q.tail = nil + } else { + q.head = q.head.Next + } + q.Length-- - return node + return node.Value, nil } func (q *Queue) String() string { diff --git a/models/scene.go b/entities/scene.go similarity index 93% rename from models/scene.go rename to entities/scene.go index 0892869..82b7a5e 100644 --- a/models/scene.go +++ b/entities/scene.go @@ -1,4 +1,4 @@ -package models +package entities type Scene int diff --git a/models/stats.go b/entities/stats.go similarity index 97% rename from models/stats.go rename to entities/stats.go index 075ac57..e8b858a 100644 --- a/models/stats.go +++ b/entities/stats.go @@ -1,4 +1,4 @@ -package models +package entities import "time" diff --git a/go.mod b/go.mod index 1c466cc..69a724a 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/spf13/cobra v1.7.0 + github.com/stretchr/testify v1.8.4 // indirect go.uber.org/zap v1.26.0 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect golang.org/x/sys v0.1.0 // indirect diff --git a/go.sum b/go.sum index 1b5d2d7..82c1034 100644 --- a/go.sum +++ b/go.sum @@ -62,7 +62,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= diff --git a/repo/ephemeral/ephemeral.go b/repo/ephemeral/ephemeral.go deleted file mode 100644 index 0677da7..0000000 --- a/repo/ephemeral/ephemeral.go +++ /dev/null @@ -1,152 +0,0 @@ -package ephemeral - -import ( - "github.com/arshamalh/dockeroller/models" -) - -type data struct { - Scene models.Scene - CurrentQuestion models.Question - Containers []*models.Container - Images []*models.Image - CurrentContainer *models.Container - CurrentImage *models.Image - UserData *models.UserData - QuitChan chan struct{} -} - -type ephemeral struct { - data map[int64]data -} - -func New() *ephemeral { - return &ephemeral{ - data: make(map[int64]data), - } -} - -func (e *ephemeral) SetScene(userID int64, scene models.Scene) { - uData := e.data[userID] - uData.Scene = scene - switch scene { - case models.SceneRenameContainer: - uData.CurrentQuestion = models.QNewContainerName - case models.SceneRenameImage: - uData.CurrentQuestion = models.QNewImageName - } - e.data[userID] = uData -} - -func (e *ephemeral) GetScene(userID int64) models.Scene { - return e.data[userID].Scene -} - -func (e *ephemeral) SetCurrentQuestion(userID int64, question models.Question) { - uData := e.data[userID] - uData.CurrentQuestion = question - e.data[userID] = uData -} - -func (e *ephemeral) GetCurrentQuestion(userID int64) models.Question { - return e.data[userID].CurrentQuestion -} - -func (e *ephemeral) SetCurrentContainer(userID int64, container *models.Container) { - uData := e.data[userID] - uData.CurrentContainer = container - e.data[userID] = uData -} - -func (e *ephemeral) GetCurrentContainer(userID int64) *models.Container { - return e.data[userID].CurrentContainer -} - -func (e *ephemeral) SetCurrentImage(userID int64, image *models.Image) { - uData := e.data[userID] - uData.CurrentImage = image - e.data[userID] = uData -} - -func (e *ephemeral) GetCurrentImage(userID int64) *models.Image { - return e.data[userID].CurrentImage -} - -func (e *ephemeral) GetContainers(userID int64) []*models.Container { - return e.data[userID].Containers -} - -func (e *ephemeral) SetContainers(userID int64, containers []*models.Container) { - uData := e.data[userID] - uData.Containers = containers - e.data[userID] = uData -} - -func (e *ephemeral) SetQuitChan(userID int64, quitChan chan struct{}) { - uData := e.data[userID] - uData.QuitChan = quitChan - e.data[userID] = uData -} - -func (e *ephemeral) GetQuitChan(userID int64) chan<- struct{} { - return e.data[userID].QuitChan -} - -func (e *ephemeral) GetImages(userID int64) []*models.Image { - return e.data[userID].Images // TODO: Not safe!! what if userID was not valid? -} - -func (e *ephemeral) SetImages(userID int64, images []*models.Image) { - uData := e.data[userID] - uData.Images = images - e.data[userID] = uData -} - -func (e *ephemeral) GetUserData(userID int64) *models.UserData { - return e.data[userID].UserData -} - -func (e *ephemeral) SetUserData(userID int64, userData *models.UserData) { - uData := e.data[userID] - uData.UserData = userData - e.data[userID] = uData -} - -func (e *ephemeral) SetContainerRemoveForm(userID int64, force, removeVolumes bool) *models.ContainerRemoveForm { - uData := e.data[userID] - if uData.UserData == nil { - uData.UserData = &models.UserData{ - ContainerRemoveForm: &models.ContainerRemoveForm{}, - } - } - uData.UserData.ContainerRemoveForm.Force = force - uData.UserData.ContainerRemoveForm.RemoveVolumes = removeVolumes - e.data[userID] = uData - return uData.UserData.ContainerRemoveForm -} - -func (e *ephemeral) GetContainerRemoveForm(userID int64) *models.ContainerRemoveForm { - if e.data[userID].UserData != nil && e.data[userID].UserData.ContainerRemoveForm != nil { - return e.data[userID].UserData.ContainerRemoveForm - } - return nil -} - -func (e *ephemeral) SetImageRemoveForm(userID int64, force, pruneChildren bool) *models.ImageRemoveForm { - uData := e.data[userID] - if uData.UserData == nil { - uData.UserData = &models.UserData{ - ImageRemoveForm: &models.ImageRemoveForm{}, - } - } - uData.UserData.ImageRemoveForm.Force = force - uData.UserData.ImageRemoveForm.PruneChildren = pruneChildren - e.data[userID] = uData - return uData.UserData.ImageRemoveForm -} - -func (e *ephemeral) GetImageRemoveForm(userID int64) *models.ImageRemoveForm { - if e.data[userID].UserData != nil && e.data[userID].UserData.ImageRemoveForm != nil { - return e.data[userID].UserData.ImageRemoveForm - } - return nil -} diff --git a/repo/repo.go b/repo/repo.go deleted file mode 100644 index 88ee6bf..0000000 --- a/repo/repo.go +++ /dev/null @@ -1,35 +0,0 @@ -package repo - -import "github.com/arshamalh/dockeroller/models" - -type Session interface { - SetScene(userID int64, scene models.Scene) - GetScene(userID int64) models.Scene - - SetCurrentQuestion(userID int64, question models.Question) - GetCurrentQuestion(userID int64) models.Question - - SetCurrentContainer(userID int64, container *models.Container) - GetCurrentContainer(userID int64) *models.Container - - SetCurrentImage(userID int64, image *models.Image) - GetCurrentImage(userID int64) *models.Image - - SetContainers(userID int64, containers []*models.Container) - GetContainers(userID int64) []*models.Container - - SetQuitChan(userID int64, quitChan chan struct{}) - GetQuitChan(userID int64) chan<- struct{} - - SetImages(userID int64, images []*models.Image) - GetImages(userID int64) []*models.Image - - SetUserData(userID int64, userData *models.UserData) - GetUserData(userID int64) *models.UserData - - SetContainerRemoveForm(userID int64, force, removeVolumes bool) *models.ContainerRemoveForm - GetContainerRemoveForm(userID int64) *models.ContainerRemoveForm - - SetImageRemoveForm(userID int64, force, removeVolumes bool) *models.ImageRemoveForm - GetImageRemoveForm(userID int64) *models.ImageRemoveForm -} diff --git a/session/session.go b/session/session.go new file mode 100644 index 0000000..fc98151 --- /dev/null +++ b/session/session.go @@ -0,0 +1,34 @@ +package session + +import "github.com/arshamalh/dockeroller/entities" + +type Session interface { + Get(userID int64) *UserData +} + +type session struct { + userData map[int64]*UserData +} + +func New() *session { + return &session{ + userData: make(map[int64]*UserData), + } +} + +func (e *session) Get(userID int64) *UserData { + if ud := e.userData[userID]; ud == nil { + e.init(userID) + } + return e.userData[userID] +} + +func (e *session) init(userID int64) { + e.userData[userID] = &UserData{ + UserID: userID, + Scene: 0, + CurrentQuestion: 0, + Containers: make([]*entities.Container, 0), + Images: make([]*entities.Image, 0), + } +} diff --git a/session/userdata.go b/session/userdata.go new file mode 100644 index 0000000..7007120 --- /dev/null +++ b/session/userdata.go @@ -0,0 +1,121 @@ +package session + +import "github.com/arshamalh/dockeroller/entities" + +type UserData struct { + UserID int64 + Scene entities.Scene + CurrentQuestion entities.Question + Containers []*entities.Container + Images []*entities.Image + CurrentContainer *entities.Container + CurrentImage *entities.Image + Forms *entities.Forms + QuitChan chan struct{} +} + +func (d *UserData) SetScene(scene entities.Scene) { + d.Scene = scene + switch scene { + case entities.SceneRenameContainer: + d.CurrentQuestion = entities.QNewContainerName + case entities.SceneRenameImage: + d.CurrentQuestion = entities.QNewImageName + } +} + +func (d *UserData) GetScene() entities.Scene { + return d.Scene +} + +func (d *UserData) SetCurrentQuestion(question entities.Question) { + d.CurrentQuestion = question +} + +func (d *UserData) GetCurrentQuestion() entities.Question { + return d.CurrentQuestion +} + +func (d *UserData) SetCurrentContainer(container *entities.Container) { + d.CurrentContainer = container +} + +func (d *UserData) GetCurrentContainer() *entities.Container { + return d.CurrentContainer +} + +func (d *UserData) SetCurrentImage(image *entities.Image) { + d.CurrentImage = image +} + +func (d *UserData) GetCurrentImage() *entities.Image { + return d.CurrentImage +} + +func (d *UserData) GetContainers() []*entities.Container { + return d.Containers +} + +func (d *UserData) SetContainers(containers []*entities.Container) { + d.Containers = containers +} + +func (d *UserData) SetQuitChan(quitChan chan struct{}) { + d.QuitChan = quitChan +} + +func (d *UserData) GetQuitChan() chan<- struct{} { + return d.QuitChan +} + +func (d *UserData) GetImages() []*entities.Image { + return d.Images +} + +func (d *UserData) SetImages(images []*entities.Image) { + d.Images = images +} + +func (d *UserData) GetForms() *entities.Forms { + return d.Forms +} + +func (d *UserData) SetForms(forms *entities.Forms) { + d.Forms = forms +} + +func (d *UserData) SetContainerRemoveForm(force, removeVolumes bool) *entities.ContainerRemoveForm { + if d.Forms == nil { + d.Forms = &entities.Forms{ + ContainerRemove: &entities.ContainerRemoveForm{}, + } + } + d.Forms.ContainerRemove.Force = force + d.Forms.ContainerRemove.RemoveVolumes = removeVolumes + return d.Forms.ContainerRemove +} + +func (d *UserData) GetContainerRemoveForm() *entities.ContainerRemoveForm { + if d.Forms != nil && d.Forms.ContainerRemove != nil { + return d.Forms.ContainerRemove + } + return nil +} + +func (d *UserData) SetImageRemoveForm(force, pruneChildren bool) *entities.ImageRemoveForm { + if d.Forms == nil { + d.Forms = &entities.Forms{ + ImageRemove: &entities.ImageRemoveForm{}, + } + } + d.Forms.ImageRemove.Force = force + d.Forms.ImageRemove.PruneChildren = pruneChildren + return d.Forms.ImageRemove +} + +func (d *UserData) GetImageRemoveForm() *entities.ImageRemoveForm { + if d.Forms != nil && d.Forms.ImageRemove != nil { + return d.Forms.ImageRemove + } + return nil +} diff --git a/telegram/handlers/containers.go b/telegram/handlers/containers.go index c3adb2d..b4a6f70 100644 --- a/telegram/handlers/containers.go +++ b/telegram/handlers/containers.go @@ -6,8 +6,8 @@ import ( "strconv" "time" + "github.com/arshamalh/dockeroller/entities" "github.com/arshamalh/dockeroller/log" - "github.com/arshamalh/dockeroller/models" "github.com/arshamalh/dockeroller/telegram/keyboards" "github.com/arshamalh/dockeroller/telegram/msgs" "github.com/arshamalh/dockeroller/tools" @@ -17,11 +17,12 @@ import ( func (h *handler) ContainersNavBtn(ctx telebot.Context) error { userID := ctx.Chat().ID + session := h.session.Get(userID) index, err := strconv.Atoi(ctx.Data()) if err != nil { log.Gl.Error(err.Error()) } - conts := h.session.GetContainers(userID) + conts := session.GetContainers() if len(conts) == 0 { return ctx.Respond( &telebot.CallbackResponse{ @@ -32,7 +33,7 @@ func (h *handler) ContainersNavBtn(ctx telebot.Context) error { index = tools.Indexer(index, len(conts)) current := conts[index] - containerIsOn := current.State == models.ContainerStateRunning + containerIsOn := current.State == entities.ContainerStateRunning err = ctx.Edit( msgs.FmtContainer(current), keyboards.ContainersList(index, containerIsOn), @@ -46,16 +47,17 @@ func (h *handler) ContainersNavBtn(ctx telebot.Context) error { func (h *handler) ContainersBackBtn(ctx telebot.Context) error { userID := ctx.Chat().ID - if quitChan := h.session.GetQuitChan(userID); quitChan != nil { + session := h.session.Get(userID) + if quitChan := session.GetQuitChan(); quitChan != nil { quitChan <- struct{}{} } index, err := strconv.Atoi(ctx.Data()) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetContainers(userID)[index] + current := session.GetContainers()[index] - containerIsOn := current.State == models.ContainerStateRunning + containerIsOn := current.State == entities.ContainerStateRunning return ctx.Edit( msgs.FmtContainer(current), keyboards.ContainersList(index, containerIsOn), @@ -71,7 +73,7 @@ func (h *handler) ContainersList(ctx telebot.Context) error { return ctx.Send("there is no container") } current := containers[0] - containerIsOn := current.State == models.ContainerStateRunning + containerIsOn := current.State == entities.ContainerStateRunning return ctx.Send( msgs.FmtContainer(current), keyboards.ContainersList(0, containerIsOn), @@ -82,20 +84,21 @@ func (h *handler) ContainersList(ctx telebot.Context) error { func (h *handler) ContainerLogs(ctx telebot.Context) error { // TODO: Starting from the beginning might cause confusion in long stream of errors, we should have a navigate till to the end button. userID := ctx.Chat().ID + session := h.session.Get(userID) index, err := strconv.Atoi(ctx.Data()) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetContainers(userID)[index] + current := session.GetContainers()[index] quit := make(chan struct{}) - h.session.SetQuitChan(userID, quit) + session.SetQuitChan(quit) stream, err := h.docker.ContainerLogs(current.ID) if err != nil { log.Gl.Error(err.Error()) } streamer := bufio.NewScanner(stream) - queue := models.NewQueue() + queue := entities.NewQueue() for streamer.Scan() { select { case <-quit: @@ -122,12 +125,13 @@ func (h *handler) ContainerLogs(ctx telebot.Context) error { func (h *handler) ContainerStats(ctx telebot.Context) error { userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetContainers(userID)[index] + current := session.GetContainers()[index] quit := make(chan struct{}) - h.session.SetQuitChan(userID, quit) + session.SetQuitChan(quit) stream, err := h.docker.ContainerStats(current.ID) if err != nil { log.Gl.Error(err.Error()) @@ -140,7 +144,7 @@ func (h *handler) ContainerStats(ctx telebot.Context) error { log.Gl.Debug("end of streaming stats for user", zap.Int64("used_id", userID)) return nil default: - stats := models.Stats{} + stats := entities.Stats{} err := json.Unmarshal(streamer.Bytes(), &stats) if err != nil { log.Gl.Error(err.Error()) @@ -166,10 +170,11 @@ func (h *handler) ContainerStats(ctx telebot.Context) error { func (h *handler) ContainerStart(ctx telebot.Context) error { userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetContainers(userID)[index] + current := session.GetContainers()[index] if err := h.docker.ContainerStart(current.ID); err != nil { log.Gl.Error(err.Error()) return ctx.Respond( @@ -197,10 +202,11 @@ func (h *handler) ContainerStart(ctx telebot.Context) error { func (h *handler) ContainerStop(ctx telebot.Context) error { userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetContainers(userID)[index] + current := session.GetContainers()[index] if err := h.docker.ContainerStop(current.ID); err != nil { log.Gl.Error(err.Error()) return ctx.Respond( @@ -232,13 +238,14 @@ func (h *handler) ContainerRemoveForm(ctx telebot.Context) error { ctx.Respond(&telebot.CallbackResponse{Text: "Please fill the form and press done"}) userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetContainers(userID)[index] - cRemoveForm := h.session.GetContainerRemoveForm(userID) + current := session.GetContainers()[index] + cRemoveForm := session.GetContainerRemoveForm() if cRemoveForm == nil { - cRemoveForm = h.session.SetContainerRemoveForm(userID, false, false) + cRemoveForm = session.SetContainerRemoveForm(false, false) } return ctx.Edit( @@ -252,11 +259,12 @@ func (h *handler) ContainerRemoveForm(ctx telebot.Context) error { func (h *handler) ContainerRemoveDone(ctx telebot.Context) error { userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetContainers(userID)[index] - cRemoveForm := h.session.GetContainerRemoveForm(userID) + current := session.GetContainers()[index] + cRemoveForm := session.GetContainerRemoveForm() if err := h.docker.ContainerRemove(current.ID, cRemoveForm); err != nil { log.Gl.Error(err.Error()) @@ -271,7 +279,7 @@ func (h *handler) ContainerRemoveDone(ctx telebot.Context) error { } current = containers[0] - containerIsOn := current.State == models.ContainerStateRunning + containerIsOn := current.State == entities.ContainerStateRunning return ctx.Edit( msgs.FmtContainer(current), keyboards.ContainersList(0, containerIsOn), @@ -282,6 +290,7 @@ func (h *handler) ContainerRemoveDone(ctx telebot.Context) error { func (h *handler) ContainerRemoveForce(ctx telebot.Context) error { userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) return ctx.Respond(&telebot.CallbackResponse{ @@ -289,10 +298,10 @@ func (h *handler) ContainerRemoveForce(ctx telebot.Context) error { }) } - current := h.session.GetContainers(userID)[index] - cRemoveForm := h.session.GetContainerRemoveForm(userID) + current := session.GetContainers()[index] + cRemoveForm := session.GetContainerRemoveForm() cRemoveForm.Force = !cRemoveForm.Force - h.session.SetContainerRemoveForm(userID, cRemoveForm.Force, cRemoveForm.RemoveVolumes) + session.SetContainerRemoveForm(cRemoveForm.Force, cRemoveForm.RemoveVolumes) return ctx.Edit( msgs.FmtContainer(current), @@ -303,6 +312,7 @@ func (h *handler) ContainerRemoveForce(ctx telebot.Context) error { func (h *handler) ContainerRemoveVolumes(ctx telebot.Context) error { userID := ctx.Chat().ID + session := h.session.Get(userID) index, err := strconv.Atoi(ctx.Data()) if err != nil { log.Gl.Error(err.Error()) @@ -311,10 +321,10 @@ func (h *handler) ContainerRemoveVolumes(ctx telebot.Context) error { }) } - current := h.session.GetContainers(userID)[index] - cRemoveForm := h.session.GetContainerRemoveForm(userID) + current := session.GetContainers()[index] + cRemoveForm := session.GetContainerRemoveForm() cRemoveForm.RemoveVolumes = !cRemoveForm.RemoveVolumes - h.session.SetContainerRemoveForm(userID, cRemoveForm.Force, cRemoveForm.RemoveVolumes) + session.SetContainerRemoveForm(cRemoveForm.Force, cRemoveForm.RemoveVolumes) return ctx.Edit( msgs.FmtContainer(current), @@ -329,15 +339,16 @@ func (h *handler) ContainerRename(ctx telebot.Context) error { } userID := ctx.Chat().ID currentContainerIndex := ctx.Data() + session := h.session.Get(userID) index, err := strconv.Atoi(currentContainerIndex) if err != nil { log.Gl.Error(err.Error()) return ctx.Send("wrong button clicked!") } - containers := h.session.GetContainers(userID) + containers := session.GetContainers() current := containers[index] - h.session.SetScene(userID, models.SceneRenameContainer) - h.session.SetCurrentContainer(userID, current) + session.SetScene(entities.SceneRenameContainer) + session.SetCurrentContainer(current) return ctx.Edit( msgs.ContainerNewNameInput, @@ -348,7 +359,8 @@ func (h *handler) ContainerRename(ctx telebot.Context) error { func (h *handler) ContainerRenameTextHandler(ctx telebot.Context) error { userID := ctx.Chat().ID - container := h.session.GetCurrentContainer(userID) + session := h.session.Get(userID) + container := session.GetCurrentContainer() if container == nil { return ctx.Edit( "you're lost!, please /start again", @@ -376,8 +388,9 @@ func (h *handler) ContainerRenameTextHandler(ctx telebot.Context) error { ) } -func (h *handler) updateContainersList(userID int64) []*models.Container { +func (h *handler) updateContainersList(userID int64) []*entities.Container { containers := h.docker.ContainersList() - h.session.SetContainers(userID, containers) + session := h.session.Get(userID) + session.SetContainers(containers) return containers } diff --git a/telegram/handlers/general.go b/telegram/handlers/general.go index 6e02383..808f833 100644 --- a/telegram/handlers/general.go +++ b/telegram/handlers/general.go @@ -3,29 +3,30 @@ package handlers import ( "strings" + "github.com/arshamalh/dockeroller/entities" "github.com/arshamalh/dockeroller/log" - "github.com/arshamalh/dockeroller/models" "github.com/arshamalh/dockeroller/telegram/keyboards" "github.com/arshamalh/dockeroller/telegram/msgs" "go.uber.org/zap" "gopkg.in/telebot.v3" ) -func StartHandler(ctx telebot.Context) error { +func (h *handler) StartHandler(ctx telebot.Context) error { newWelcomeMsg := strings.Replace(msgs.WelcomeMessage, "{name}", ctx.Message().Sender.FirstName, -1) return ctx.Send(newWelcomeMsg, keyboards.Welcome()) } func (h *handler) General(ctx telebot.Context) error { userID := ctx.Chat().ID - scene := h.session.GetScene(userID) + session := h.session.Get(userID) + scene := session.GetScene() switch scene { - case models.SceneRenameContainer: + case entities.SceneRenameContainer: return h.ContainerRenameTextHandler(ctx) - case models.SceneRenameImage: + case entities.SceneRenameImage: return h.ImageTagTextHandler(ctx) } log.Gl.Error("orphan scene", zap.Int64("user id", userID), zap.Int("current scene", int(scene))) - return StartHandler(ctx) + return h.StartHandler(ctx) } diff --git a/telegram/handlers/handlers.go b/telegram/handlers/handlers.go index dbdb961..ee55562 100644 --- a/telegram/handlers/handlers.go +++ b/telegram/handlers/handlers.go @@ -2,8 +2,7 @@ package handlers import ( "github.com/arshamalh/dockeroller/docker" - "github.com/arshamalh/dockeroller/models" - "github.com/arshamalh/dockeroller/repo" + sessionPkg "github.com/arshamalh/dockeroller/session" "github.com/arshamalh/dockeroller/telegram/btns" "gopkg.in/telebot.v3" ) @@ -11,11 +10,11 @@ import ( type handler struct { docker docker.Docker bot *telebot.Bot - session repo.Session + session sessionPkg.Session // log } -func Register(bot *telebot.Bot, docker docker.Docker, session repo.Session) { +func Register(bot *telebot.Bot, docker docker.Docker, session sessionPkg.Session) { h := &handler{ docker: docker, bot: bot, @@ -23,7 +22,7 @@ func Register(bot *telebot.Bot, docker docker.Docker, session repo.Session) { } // Command handlers - h.bot.Handle("/start", StartHandler) + h.bot.Handle("/start", h.StartHandler) h.bot.Handle("/containers", h.ContainersList) h.bot.Handle("/images", h.ImagesList) @@ -57,13 +56,3 @@ func Register(bot *telebot.Bot, docker docker.Docker, session repo.Session) { h.bot.Handle(btns.ImgRmDone.Key(), h.ImageRemoveDone) h.bot.Handle(btns.ImgTag.Key(), h.ImageTag) } - -// Set a scene for the specified user -func (h *handler) EnterScene(userID int64, scene models.Scene) { - h.session.SetScene(userID, scene) -} - -// Returns the current scene for the specified user -func (h *handler) Scene(userID int64) models.Scene { - return h.session.GetScene(userID) -} diff --git a/telegram/handlers/image_remove.go b/telegram/handlers/image_remove.go index 28546a2..8bda8aa 100644 --- a/telegram/handlers/image_remove.go +++ b/telegram/handlers/image_remove.go @@ -4,8 +4,8 @@ import ( "context" "strconv" + "github.com/arshamalh/dockeroller/entities" "github.com/arshamalh/dockeroller/log" - "github.com/arshamalh/dockeroller/models" "github.com/arshamalh/dockeroller/telegram/keyboards" "github.com/arshamalh/dockeroller/telegram/msgs" "gopkg.in/telebot.v3" @@ -15,6 +15,7 @@ import ( // it's just a form for setting the options of removing a image in the later "Done" step. func (h *handler) ImageRemoveForm(ctx telebot.Context) error { userID := ctx.Chat().ID + session := h.session.Get(userID) index, err := strconv.Atoi(ctx.Data()) if err != nil { log.Gl.Error(err.Error()) @@ -23,10 +24,10 @@ func (h *handler) ImageRemoveForm(ctx telebot.Context) error { // Informational Response ctx.Respond(&telebot.CallbackResponse{Text: "Please fill the form and press done"}) - current := h.session.GetImages(userID)[index] - imgRmForm := h.session.GetImageRemoveForm(userID) + current := session.GetImages()[index] + imgRmForm := session.GetImageRemoveForm() if imgRmForm == nil { - imgRmForm = h.session.SetImageRemoveForm(userID, false, false) + imgRmForm = session.SetImageRemoveForm(false, false) } return ctx.Edit( @@ -39,12 +40,13 @@ func (h *handler) ImageRemoveForm(ctx telebot.Context) error { // Removes the image with specified options func (h *handler) ImageRemoveDone(ctx telebot.Context) error { userID := ctx.Chat().ID + session := h.session.Get(userID) index, err := strconv.Atoi(ctx.Data()) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetImages(userID)[index] - imgRmForm := h.session.GetImageRemoveForm(userID) + current := session.GetImages()[index] + imgRmForm := session.GetImageRemoveForm() if err := h.docker.ImageRemove(context.TODO(), current.ID, imgRmForm.Force, imgRmForm.PruneChildren); err != nil { log.Gl.Error(err.Error()) @@ -69,6 +71,7 @@ func (h *handler) ImageRemoveDone(ctx telebot.Context) error { func (h *handler) ImageRemoveForce(ctx telebot.Context) error { userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) return ctx.Respond(&telebot.CallbackResponse{ @@ -76,10 +79,10 @@ func (h *handler) ImageRemoveForce(ctx telebot.Context) error { }) } - current := h.session.GetImages(userID)[index] - imgRmForm := h.session.GetImageRemoveForm(userID) + current := session.GetImages()[index] + imgRmForm := session.GetImageRemoveForm() imgRmForm.Force = !imgRmForm.Force - h.session.SetImageRemoveForm(userID, imgRmForm.Force, imgRmForm.PruneChildren) + session.SetImageRemoveForm(imgRmForm.Force, imgRmForm.PruneChildren) return ctx.Edit( msgs.FmtImage(current), @@ -91,6 +94,7 @@ func (h *handler) ImageRemoveForce(ctx telebot.Context) error { func (h *handler) ImageRemovePruneChildren(ctx telebot.Context) error { userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) return ctx.Respond(&telebot.CallbackResponse{ @@ -98,10 +102,10 @@ func (h *handler) ImageRemovePruneChildren(ctx telebot.Context) error { }) } - current := h.session.GetImages(userID)[index] - imgRmForm := h.session.GetImageRemoveForm(userID) + current := session.GetImages()[index] + imgRmForm := session.GetImageRemoveForm() imgRmForm.PruneChildren = !imgRmForm.PruneChildren - h.session.SetImageRemoveForm(userID, imgRmForm.Force, imgRmForm.PruneChildren) + session.SetImageRemoveForm(imgRmForm.Force, imgRmForm.PruneChildren) return ctx.Edit( msgs.FmtImage(current), @@ -110,8 +114,9 @@ func (h *handler) ImageRemovePruneChildren(ctx telebot.Context) error { ) } -func (h *handler) updateImagesList(userID int64) []*models.Image { +func (h *handler) updateImagesList(userID int64) []*entities.Image { images := h.docker.ImagesList() - h.session.SetImages(userID, images) + session := h.session.Get(userID) + session.SetImages(images) return images } diff --git a/telegram/handlers/image_tag.go b/telegram/handlers/image_tag.go index 85a6031..07f3595 100644 --- a/telegram/handlers/image_tag.go +++ b/telegram/handlers/image_tag.go @@ -4,8 +4,8 @@ import ( "context" "strconv" + "github.com/arshamalh/dockeroller/entities" "github.com/arshamalh/dockeroller/log" - "github.com/arshamalh/dockeroller/models" "github.com/arshamalh/dockeroller/telegram/keyboards" "github.com/arshamalh/dockeroller/telegram/msgs" "gopkg.in/telebot.v3" @@ -16,16 +16,17 @@ func (h *handler) ImageTag(ctx telebot.Context) error { log.Gl.Error(err.Error()) } userID := ctx.Chat().ID + session := h.session.Get(userID) currentImageIndex := ctx.Data() index, err := strconv.Atoi(currentImageIndex) if err != nil { log.Gl.Error(err.Error()) return ctx.Send("wrong button clicked!") } - images := h.session.GetImages(userID) + images := session.GetImages() current := images[index] - h.EnterScene(userID, models.SceneRenameImage) - h.session.SetCurrentImage(userID, current) + session.SetScene(entities.SceneRenameImage) + session.SetCurrentImage(current) return ctx.Edit( msgs.ImageNewNameInput, @@ -36,7 +37,8 @@ func (h *handler) ImageTag(ctx telebot.Context) error { func (h *handler) ImageTagTextHandler(ctx telebot.Context) error { userID := ctx.Chat().ID - image := h.session.GetCurrentImage(userID) + session := h.session.Get(userID) + image := session.GetCurrentImage() if image == nil { return ctx.Edit( "you're lost!, please /start again", diff --git a/telegram/handlers/images.go b/telegram/handlers/images.go index 3c582e4..83b0d51 100644 --- a/telegram/handlers/images.go +++ b/telegram/handlers/images.go @@ -13,8 +13,9 @@ import ( func (h *handler) ImagesList(ctx telebot.Context) error { ctx.Respond() userID := ctx.Chat().ID + session := h.session.Get(userID) images := h.docker.ImagesList() - h.session.SetImages(userID, images) + session.SetImages(images) current := images[0] return ctx.Send( msgs.FmtImage(current), @@ -26,10 +27,11 @@ func (h *handler) ImagesList(ctx telebot.Context) error { func (h *handler) ImagesNavBtn(ctx telebot.Context) error { userID := ctx.Chat().ID index, err := strconv.Atoi(ctx.Data()) + session := h.session.Get(userID) if err != nil { log.Gl.Error(err.Error()) } - images := h.session.GetImages(userID) + images := session.GetImages() if len(images) == 0 { return ctx.Respond(&telebot.CallbackResponse{Text: "There is either no images or you should run /images again!"}) } @@ -48,14 +50,15 @@ func (h *handler) ImagesNavBtn(ctx telebot.Context) error { func (h *handler) ImagesBackBtn(ctx telebot.Context) error { userID := ctx.Chat().ID - if quitChan := h.session.GetQuitChan(userID); quitChan != nil { + session := h.session.Get(userID) + if quitChan := session.GetQuitChan(); quitChan != nil { quitChan <- struct{}{} } index, err := strconv.Atoi(ctx.Data()) if err != nil { log.Gl.Error(err.Error()) } - current := h.session.GetImages(userID)[index] + current := session.GetImages()[index] return ctx.Edit( msgs.FmtImage(current), keyboards.ImagesList(index), diff --git a/telegram/msgs/format.go b/telegram/msgs/format.go index 1d28011..548f64d 100644 --- a/telegram/msgs/format.go +++ b/telegram/msgs/format.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/arshamalh/dockeroller/models" + "github.com/arshamalh/dockeroller/entities" "github.com/arshamalh/dockeroller/tools" ) @@ -25,7 +25,7 @@ func FmtMono(input string) string { ).Replace(input) } -func FmtContainer(container *models.Container) string { +func FmtContainer(container *entities.Container) string { response := strings.NewReplacer( "{name}", container.Name, "{image}", container.Image, @@ -35,7 +35,7 @@ func FmtContainer(container *models.Container) string { return response } -func FmtImage(image *models.Image) string { +func FmtImage(image *entities.Image) string { response := strings.NewReplacer( "{id}", image.ID, "{size}", tools.SizeToHumanReadable(image.Size), @@ -47,7 +47,7 @@ func FmtImage(image *models.Image) string { return response } -func FmtStats(stat models.Stats) string { +func FmtStats(stat entities.Stats) string { cpu_usage, memory_usage_percent := tools.StatsCalculator(stat) response := strings.NewReplacer( "{cpu_usage}", fmt.Sprintf("%.2f", cpu_usage), diff --git a/tools/stats_calculator.go b/tools/stats_calculator.go index a1f8687..bc1ff68 100644 --- a/tools/stats_calculator.go +++ b/tools/stats_calculator.go @@ -1,8 +1,6 @@ package tools -import ( - "github.com/arshamalh/dockeroller/models" -) +import "github.com/arshamalh/dockeroller/entities" // These are formulas to calculate stats // cpu_delta = cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage @@ -16,7 +14,7 @@ import ( // // CPU usage are based on the host, not VM, so in Windows devices, you may see different cpu usages in the Bot and Docker desktop and docker cli, // but, that's calculable. both are correct anyway. -func StatsCalculator(stat models.Stats) (cpu_usage float64, memory_usage float64) { +func StatsCalculator(stat entities.Stats) (cpu_usage float64, memory_usage float64) { // CPU usage calculation cpu_delta := stat.CPU.Usage.Total - stat.PerCPU.Usage.Total system_cpu_delta := stat.CPU.SystemCPUUsage - stat.PerCPU.SystemCPUUsage