Skip to content

Commit

Permalink
add: start and stop container buttons and functionalities.
Browse files Browse the repository at this point in the history
  • Loading branch information
arshamalh committed Nov 8, 2023
1 parent eaf329a commit 228a852
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 24 deletions.
8 changes: 5 additions & 3 deletions 2do.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
- [x] ChatID whitelisting (has a TODO, middleware whitelisting) for security concerns
- [x] Define semi-hardcoded buttons! (button unique identifiers all totally hard coded in two places which lead to confusion, one when defining and one when putting in the handler, it's better to define button somehow else)
- [x] Remove session which is used as a one size fits all!
- [x] Debug container logs, also what to do in case of high amount of logs?
- [x] Zero stats problem
- [x] Container Start Stop handlers and functionality
- [x] Welcome message should have button and better message
- [x] Add image handlers, next, prev, back
- [x] Image size should be in human readable units
- [ ] Image ID should be shorted (trimmed)
Expand All @@ -14,10 +18,8 @@
- [ ] Image run, rename, remove commands
- [ ] Error on line 78 if log file about ">" character should be resolved
- [ ] Add ability to run containers out of images
- [x] Debug container logs, also what to do in case of high amount of logs?
- [ ] Debug container stats as it sounds to have problem in some cases
- [x] Zero stats problem
- [x] Welcome message should have button and better message
- [ ] Ability to filter Containers and Images
- [ ] Remove previous messages buttons (when they are not required)
- [ ] Handle callback queries to show messages, e.g. for remove.
- Helper: &telebot.CallbackResponse{Text: fmt.Sprint(ctx.Data(), "removed!")}
Expand Down
6 changes: 5 additions & 1 deletion contracts/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import (
)

type Docker interface {
GetContainer(containerID string) (*models.Container, error)
ContainersList() []*models.Container
ImagesList() []*models.Image
ContainerLogs(containerID string) (io.ReadCloser, error)
ContainerStats(containerID string) (io.ReadCloser, error)
ContainerStart(containerID string) error
ContainerStop(containerID string) error

ImagesList() []*models.Image
}
39 changes: 31 additions & 8 deletions docker/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,40 @@ import (
"github.com/docker/docker/api/types"
)

func (d docker) ContainersList() (containers []*models.Container) {
func (d *docker) ContainersList() (containers []*models.Container) {
raw_containers, _ := d.cli.ContainerList(context.TODO(), types.ContainerListOptions{All: true})
for _, rcont := range raw_containers {
for _, raw_cont := range raw_containers {
containers = append(containers, &models.Container{
ID: rcont.ID,
Name: rcont.Names[0],
Image: rcont.Image,
Status: rcont.Status,
ID: raw_cont.ID,
Name: raw_cont.Names[0],
Image: raw_cont.Image,
Status: raw_cont.Status,
State: models.ContainerState(raw_cont.State),
})
}
return
}

func (d docker) ContainerStats(containerID string) (io.ReadCloser, error) {
func (d *docker) GetContainer(containerID string) (*models.Container, error) {
container, err := d.cli.ContainerInspect(context.TODO(), containerID)
if err != nil {
return nil, err
}
return &models.Container{
ID: container.ID,
Name: container.Name,
Image: container.Image,
Status: container.State.Status,
State: models.ContainerState(container.State.Status),
}, nil
}

func (d *docker) ContainerStats(containerID string) (io.ReadCloser, error) {
stats, err := d.cli.ContainerStats(context.TODO(), containerID, true)
return stats.Body, err
}

func (d docker) ContainerLogs(containerID string) (io.ReadCloser, error) {
func (d *docker) ContainerLogs(containerID string) (io.ReadCloser, error) {
// TODO: Interesting options about logs are available, you can get them from user settings
return d.cli.ContainerLogs(context.TODO(), containerID, types.ContainerLogsOptions{
ShowStdout: true,
Expand All @@ -35,3 +50,11 @@ func (d docker) ContainerLogs(containerID string) (io.ReadCloser, error) {
Details: false,
})
}

func (d *docker) ContainerStart(containerID string) error {
return d.cli.ContainerStart(context.TODO(), containerID, types.ContainerStartOptions{})
}

func (d *docker) ContainerStop(containerID string) error {
return d.cli.ContainerStop(context.TODO(), containerID, nil)
}
13 changes: 13 additions & 0 deletions models/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ package models

import "fmt"

type ContainerState string

const (
ContainerStateCreated ContainerState = "created"
ContainerStateRunning ContainerState = "running"
ContainerStatePaused ContainerState = "paused"
ContainerStateRestarting ContainerState = "restarting"
ContainerStateRemoving ContainerState = "removing"
ContainerStateExited ContainerState = "exited"
ContainerStateDead ContainerState = "dead"
)

type Container struct {
ID string
Name string
Image string
Status string
State ContainerState
}

func (c Container) String() string {
Expand Down
83 changes: 78 additions & 5 deletions telegram/handlers/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,19 @@ func (h *handler) ContainersNavBtn(ctx telebot.Context) error {
}
conts := h.session.GetContainers(userID)
if len(conts) == 0 {
return ctx.Respond(&telebot.CallbackResponse{Text: "There is either no containers or you should run /containers again!"})
return ctx.Respond(
&telebot.CallbackResponse{
Text: "There is either no containers or you should run /containers again!",
},
)
}
index = tools.Indexer(index, len(conts))
current := conts[index]

containerIsOn := current.State == models.ContainerStateRunning
err = ctx.Edit(
msgs.FmtContainer(current),
keyboards.ContainersList(index, false),
keyboards.ContainersList(index, containerIsOn),
telebot.ModeMarkdownV2,
)
if err != nil {
Expand All @@ -46,10 +52,11 @@ func (h *handler) ContainersBackBtn(ctx telebot.Context) error {
log.Gl.Error(err.Error())
}
current := h.session.GetContainers(userID)[index]

containerIsOn := current.State == models.ContainerStateRunning
return ctx.Edit(
msgs.FmtContainer(current),
// TODO: false and true passed for making keyboards are hardcoded but should be changed soon.
keyboards.ContainersList(index, false),
keyboards.ContainersList(index, containerIsOn),
telebot.ModeMarkdownV2,
)
}
Expand All @@ -59,14 +66,17 @@ func (h *handler) ContainersList(ctx telebot.Context) error {
containers := h.docker.ContainersList()
h.session.SetContainers(userID, containers)
current := containers[0]

containerIsOn := current.State == models.ContainerStateRunning
return ctx.Send(
msgs.FmtContainer(current),
keyboards.ContainersList(0, false),
keyboards.ContainersList(0, containerIsOn),
telebot.ModeMarkdownV2,
)
}

func (h *handler) Logs(ctx telebot.Context) error {
// TODO: A list (queue) of logs, append to the end and remove from the beginning
userID := ctx.Chat().ID
index, err := strconv.Atoi(ctx.Data())
if err != nil {
Expand Down Expand Up @@ -145,3 +155,66 @@ func (h *handler) Stats(ctx telebot.Context) error {
}
return ctx.Respond()
}

func (h *handler) ContainerStart(ctx telebot.Context) error {
userID := ctx.Chat().ID
index, err := strconv.Atoi(ctx.Data())
if err != nil {
log.Gl.Error(err.Error())
}
current := h.session.GetContainers(userID)[index]
if err := h.docker.ContainerStart(current.ID); err != nil {
log.Gl.Error(err.Error())
return ctx.Respond(
&telebot.CallbackResponse{
Text: "We cannot start the container!",
},
)
}

current, err = h.docker.GetContainer(current.ID)
if err != nil {
return ctx.Respond(
&telebot.CallbackResponse{
Text: "Container started, but we're not able to show current state.",
},
)
}
return ctx.Edit(
msgs.FmtContainer(current),
keyboards.ContainersList(index, true),
telebot.ModeMarkdownV2,
)
}

func (h *handler) ContainerStop(ctx telebot.Context) error {
userID := ctx.Chat().ID
index, err := strconv.Atoi(ctx.Data())
if err != nil {
log.Gl.Error(err.Error())
}
current := h.session.GetContainers(userID)[index]
if err := h.docker.ContainerStop(current.ID); err != nil {
log.Gl.Error(err.Error())
return ctx.Respond(
&telebot.CallbackResponse{
Text: "We cannot stop the container!",
},
)
}

current, err = h.docker.GetContainer(current.ID)
if err != nil {
return ctx.Respond(
&telebot.CallbackResponse{
Text: "Container stopped, but we're not able to show current state.",
},
)
}

return ctx.Edit(
msgs.FmtContainer(current),
keyboards.ContainersList(index, false),
telebot.ModeMarkdownV2,
)
}
3 changes: 3 additions & 0 deletions telegram/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@ func Register(bot *telebot.Bot, docker contracts.Docker, session repo.Session) {
h.bot.Handle(btns.ContLogs.Key(), h.Logs)
h.bot.Handle(btns.ContStats.Key(), h.Stats)
h.bot.Handle(btns.ContBack.Key(), h.ContainersBackBtn)
h.bot.Handle(btns.ContStop.Key(), h.ContainerStop)
h.bot.Handle(btns.ContStart.Key(), h.ContainerStart)

/// Images
h.bot.Handle(btns.Images.Key(), h.ImagesList)

h.bot.Handle(btns.ImgBack.Key(), h.ImagesBackBtn)
h.bot.Handle(btns.ImgPrev.Key(), h.ImagesNavBtn)
h.bot.Handle(btns.ImgNext.Key(), h.ImagesNavBtn)

}
14 changes: 7 additions & 7 deletions telegram/keyboards/keyboards.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ func ContainersList(index int, containerIsOn bool) *telebot.ReplyMarkup {
keyboard.Data("➡", btns.ContNext.String(), fmt.Sprint(index+1)),
},
telebot.Row{
switchBtn(keyboard, containerIsOn),
keyboard.Data("Remove", btns.ContRemove.String()),
keyboard.Data("Rename", btns.ContRename.String()),
switchBtn(keyboard, index, containerIsOn),
keyboard.Data("Remove", btns.ContRemove.String(), fmt.Sprint(index)),
keyboard.Data("Rename", btns.ContRename.String(), fmt.Sprint(index)),
},
telebot.Row{
keyboard.Data("Logs", btns.ContLogs.String(), fmt.Sprint(index)),
Expand Down Expand Up @@ -65,17 +65,17 @@ func Back(index int, containerIsOn bool) *telebot.ReplyMarkup {
keyboard.Inline(
telebot.Row{
keyboard.Data("⬅", btns.ContBack.String(), fmt.Sprint(index)),
switchBtn(keyboard, containerIsOn),
switchBtn(keyboard, index, containerIsOn),
},
)

return keyboard
}

func switchBtn(keyboard *telebot.ReplyMarkup, containerIsOn bool) telebot.Btn {
func switchBtn(keyboard *telebot.ReplyMarkup, index int, containerIsOn bool) telebot.Btn {
if containerIsOn {
return keyboard.Data("Stop", btns.ContStop.String())
return keyboard.Data("Stop", btns.ContStop.String(), fmt.Sprint(index))
} else {
return keyboard.Data("Start", btns.ContStart.String())
return keyboard.Data("Start", btns.ContStart.String(), fmt.Sprint(index))
}
}

0 comments on commit 228a852

Please sign in to comment.