mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-21 23:01:03 +00:00
We want to forward env vars to commands just because it's good practice, and probably enables things that previously weren't possible.
146 lines
4.2 KiB
Go
146 lines
4.2 KiB
Go
package commands
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/sasha-s/go-deadlock"
|
|
|
|
dockerTypes "github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/client"
|
|
"github.com/go-errors/errors"
|
|
"github.com/jesseduffield/lazydocker/pkg/i18n"
|
|
"github.com/jesseduffield/lazydocker/pkg/utils"
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
// Container : A docker Container
|
|
type Container struct {
|
|
Name string
|
|
ServiceName string
|
|
ContainerNumber string // might make this an int in the future if need be
|
|
|
|
// OneOff tells us if the container is just a job container or is actually bound to the service
|
|
OneOff bool
|
|
ProjectName string
|
|
ID string
|
|
Container dockerTypes.Container
|
|
Client *client.Client
|
|
OSCommand *OSCommand
|
|
Log *logrus.Entry
|
|
StatHistory []*RecordedStats
|
|
Details dockerTypes.ContainerJSON
|
|
MonitoringStats bool
|
|
DockerCommand LimitedDockerCommand
|
|
Tr *i18n.TranslationSet
|
|
|
|
StatsMutex deadlock.Mutex
|
|
}
|
|
|
|
// Remove removes the container
|
|
func (c *Container) Remove(options dockerTypes.ContainerRemoveOptions) error {
|
|
c.Log.Warn(fmt.Sprintf("removing container %s", c.Name))
|
|
if err := c.Client.ContainerRemove(context.Background(), c.ID, options); err != nil {
|
|
if strings.Contains(err.Error(), "Stop the container before attempting removal or force remove") {
|
|
return ComplexError{
|
|
Code: MustStopContainer,
|
|
Message: err.Error(),
|
|
frame: xerrors.Caller(1),
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Stop stops the container
|
|
func (c *Container) Stop() error {
|
|
c.Log.Warn(fmt.Sprintf("stopping container %s", c.Name))
|
|
return c.Client.ContainerStop(context.Background(), c.ID, nil)
|
|
}
|
|
|
|
// Pause pauses the container
|
|
func (c *Container) Pause() error {
|
|
c.Log.Warn(fmt.Sprintf("pausing container %s", c.Name))
|
|
return c.Client.ContainerPause(context.Background(), c.ID)
|
|
}
|
|
|
|
// Unpause unpauses the container
|
|
func (c *Container) Unpause() error {
|
|
c.Log.Warn(fmt.Sprintf("unpausing container %s", c.Name))
|
|
return c.Client.ContainerUnpause(context.Background(), c.ID)
|
|
}
|
|
|
|
// Restart restarts the container
|
|
func (c *Container) Restart() error {
|
|
c.Log.Warn(fmt.Sprintf("restarting container %s", c.Name))
|
|
return c.Client.ContainerRestart(context.Background(), c.ID, nil)
|
|
}
|
|
|
|
// Attach attaches the container
|
|
func (c *Container) Attach() (*exec.Cmd, error) {
|
|
if !c.DetailsLoaded() {
|
|
return nil, errors.New(c.Tr.WaitingForContainerInfo)
|
|
}
|
|
|
|
// verify that we can in fact attach to this container
|
|
if !c.Details.Config.OpenStdin {
|
|
return nil, errors.New(c.Tr.UnattachableContainerError)
|
|
}
|
|
|
|
if c.Container.State == "exited" {
|
|
return nil, errors.New(c.Tr.CannotAttachStoppedContainerError)
|
|
}
|
|
|
|
c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
|
|
// TODO: use SDK
|
|
cmd := c.OSCommand.NewCmd("docker", "attach", "--sig-proxy=false", c.ID)
|
|
return cmd, nil
|
|
}
|
|
|
|
// Top returns process information
|
|
func (c *Container) Top(ctx context.Context) (container.ContainerTopOKBody, error) {
|
|
detail, err := c.Inspect()
|
|
if err != nil {
|
|
return container.ContainerTopOKBody{}, err
|
|
}
|
|
|
|
// check container status
|
|
if !detail.State.Running {
|
|
return container.ContainerTopOKBody{}, errors.New("container is not running")
|
|
}
|
|
|
|
return c.Client.ContainerTop(ctx, c.ID, []string{})
|
|
}
|
|
|
|
// PruneContainers prunes containers
|
|
func (c *DockerCommand) PruneContainers() error {
|
|
_, err := c.Client.ContainersPrune(context.Background(), filters.Args{})
|
|
return err
|
|
}
|
|
|
|
// Inspect returns details about the container
|
|
func (c *Container) Inspect() (dockerTypes.ContainerJSON, error) {
|
|
return c.Client.ContainerInspect(context.Background(), c.ID)
|
|
}
|
|
|
|
// RenderTop returns details about the container
|
|
func (c *Container) RenderTop(ctx context.Context) (string, error) {
|
|
result, err := c.Top(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return utils.RenderTable(append([][]string{result.Titles}, result.Processes...))
|
|
}
|
|
|
|
// DetailsLoaded tells us whether we have yet loaded the details for a container. Because this is an asynchronous operation, sometimes we have the container before we have its details.
|
|
func (c *Container) DetailsLoaded() bool {
|
|
return c.Details.ContainerJSONBase != nil
|
|
}
|