mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 00:21:04 +00:00
Merge pull request #335 from jesseduffield/inspect-via-sdk
This commit is contained in:
commit
b709f9f6b1
5 changed files with 48 additions and 215 deletions
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
|
|
||||||
|
|
@ -40,7 +39,7 @@ type Container struct {
|
||||||
Config *config.AppConfig
|
Config *config.AppConfig
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
StatHistory []*RecordedStats
|
StatHistory []*RecordedStats
|
||||||
Details Details
|
Details types.ContainerJSON
|
||||||
MonitoringStats bool
|
MonitoringStats bool
|
||||||
DockerCommand LimitedDockerCommand
|
DockerCommand LimitedDockerCommand
|
||||||
Tr *i18n.TranslationSet
|
Tr *i18n.TranslationSet
|
||||||
|
|
@ -48,190 +47,6 @@ type Container struct {
|
||||||
StatsMutex sync.Mutex
|
StatsMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Details is a struct containing what we get back from `docker inspect` on a container
|
|
||||||
type Details struct {
|
|
||||||
ID string `json:"Id"`
|
|
||||||
Created time.Time `json:"Created"`
|
|
||||||
Path string `json:"Path"`
|
|
||||||
Args []string `json:"Args"`
|
|
||||||
State struct {
|
|
||||||
Status string `json:"Status"`
|
|
||||||
Running bool `json:"Running"`
|
|
||||||
Paused bool `json:"Paused"`
|
|
||||||
Restarting bool `json:"Restarting"`
|
|
||||||
OOMKilled bool `json:"OOMKilled"`
|
|
||||||
Dead bool `json:"Dead"`
|
|
||||||
Pid int `json:"Pid"`
|
|
||||||
ExitCode int `json:"ExitCode"`
|
|
||||||
Error string `json:"Error"`
|
|
||||||
StartedAt time.Time `json:"StartedAt"`
|
|
||||||
FinishedAt time.Time `json:"FinishedAt"`
|
|
||||||
Health types.Health `json:"Health"`
|
|
||||||
} `json:"State"`
|
|
||||||
Image string `json:"Image"`
|
|
||||||
ResolvConfPath string `json:"ResolvConfPath"`
|
|
||||||
HostnamePath string `json:"HostnamePath"`
|
|
||||||
HostsPath string `json:"HostsPath"`
|
|
||||||
LogPath string `json:"LogPath"`
|
|
||||||
Name string `json:"Name"`
|
|
||||||
RestartCount int `json:"RestartCount"`
|
|
||||||
Driver string `json:"Driver"`
|
|
||||||
Platform string `json:"Platform"`
|
|
||||||
MountLabel string `json:"MountLabel"`
|
|
||||||
ProcessLabel string `json:"ProcessLabel"`
|
|
||||||
AppArmorProfile string `json:"AppArmorProfile"`
|
|
||||||
ExecIDs interface{} `json:"ExecIDs"`
|
|
||||||
HostConfig struct {
|
|
||||||
Binds []string `json:"Binds"`
|
|
||||||
ContainerIDFile string `json:"ContainerIDFile"`
|
|
||||||
LogConfig struct {
|
|
||||||
Type string `json:"Type"`
|
|
||||||
Config struct{} `json:"Config"`
|
|
||||||
} `json:"LogConfig"`
|
|
||||||
NetworkMode string `json:"NetworkMode"`
|
|
||||||
PortBindings struct{} `json:"PortBindings"`
|
|
||||||
RestartPolicy struct {
|
|
||||||
Name string `json:"Name"`
|
|
||||||
MaximumRetryCount int `json:"MaximumRetryCount"`
|
|
||||||
} `json:"RestartPolicy"`
|
|
||||||
AutoRemove bool `json:"AutoRemove"`
|
|
||||||
VolumeDriver string `json:"VolumeDriver"`
|
|
||||||
VolumesFrom []interface{} `json:"VolumesFrom"`
|
|
||||||
CapAdd interface{} `json:"CapAdd"`
|
|
||||||
CapDrop interface{} `json:"CapDrop"`
|
|
||||||
DNS interface{} `json:"Dns"`
|
|
||||||
DNSOptions interface{} `json:"DnsOptions"`
|
|
||||||
DNSSearch interface{} `json:"DnsSearch"`
|
|
||||||
ExtraHosts interface{} `json:"ExtraHosts"`
|
|
||||||
GroupAdd interface{} `json:"GroupAdd"`
|
|
||||||
IpcMode string `json:"IpcMode"`
|
|
||||||
Cgroup string `json:"Cgroup"`
|
|
||||||
Links interface{} `json:"Links"`
|
|
||||||
OomScoreAdj int `json:"OomScoreAdj"`
|
|
||||||
PidMode string `json:"PidMode"`
|
|
||||||
Privileged bool `json:"Privileged"`
|
|
||||||
PublishAllPorts bool `json:"PublishAllPorts"`
|
|
||||||
ReadonlyRootfs bool `json:"ReadonlyRootfs"`
|
|
||||||
SecurityOpt interface{} `json:"SecurityOpt"`
|
|
||||||
UTSMode string `json:"UTSMode"`
|
|
||||||
UsernsMode string `json:"UsernsMode"`
|
|
||||||
ShmSize int `json:"ShmSize"`
|
|
||||||
Runtime string `json:"Runtime"`
|
|
||||||
ConsoleSize []int `json:"ConsoleSize"`
|
|
||||||
Isolation string `json:"Isolation"`
|
|
||||||
CPUShares int `json:"CpuShares"`
|
|
||||||
Memory int `json:"Memory"`
|
|
||||||
NanoCpus int `json:"NanoCpus"`
|
|
||||||
CgroupParent string `json:"CgroupParent"`
|
|
||||||
BlkioWeight int `json:"BlkioWeight"`
|
|
||||||
BlkioWeightDevice interface{} `json:"BlkioWeightDevice"`
|
|
||||||
BlkioDeviceReadBps interface{} `json:"BlkioDeviceReadBps"`
|
|
||||||
BlkioDeviceWriteBps interface{} `json:"BlkioDeviceWriteBps"`
|
|
||||||
BlkioDeviceReadIOps interface{} `json:"BlkioDeviceReadIOps"`
|
|
||||||
BlkioDeviceWriteIOps interface{} `json:"BlkioDeviceWriteIOps"`
|
|
||||||
CPUPeriod int `json:"CpuPeriod"`
|
|
||||||
CPUQuota int `json:"CpuQuota"`
|
|
||||||
CPURealtimePeriod int `json:"CpuRealtimePeriod"`
|
|
||||||
CPURealtimeRuntime int `json:"CpuRealtimeRuntime"`
|
|
||||||
CpusetCpus string `json:"CpusetCpus"`
|
|
||||||
CpusetMems string `json:"CpusetMems"`
|
|
||||||
Devices interface{} `json:"Devices"`
|
|
||||||
DeviceCgroupRules interface{} `json:"DeviceCgroupRules"`
|
|
||||||
DiskQuota int `json:"DiskQuota"`
|
|
||||||
KernelMemory int `json:"KernelMemory"`
|
|
||||||
MemoryReservation int `json:"MemoryReservation"`
|
|
||||||
MemorySwap int `json:"MemorySwap"`
|
|
||||||
MemorySwappiness interface{} `json:"MemorySwappiness"`
|
|
||||||
OomKillDisable bool `json:"OomKillDisable"`
|
|
||||||
PidsLimit int `json:"PidsLimit"`
|
|
||||||
Ulimits interface{} `json:"Ulimits"`
|
|
||||||
CPUCount int `json:"CpuCount"`
|
|
||||||
CPUPercent int `json:"CpuPercent"`
|
|
||||||
IOMaximumIOps int `json:"IOMaximumIOps"`
|
|
||||||
IOMaximumBandwidth int `json:"IOMaximumBandwidth"`
|
|
||||||
MaskedPaths []string `json:"MaskedPaths"`
|
|
||||||
ReadonlyPaths []string `json:"ReadonlyPaths"`
|
|
||||||
} `json:"HostConfig"`
|
|
||||||
GraphDriver struct {
|
|
||||||
Data struct {
|
|
||||||
LowerDir string `json:"LowerDir"`
|
|
||||||
MergedDir string `json:"MergedDir"`
|
|
||||||
UpperDir string `json:"UpperDir"`
|
|
||||||
WorkDir string `json:"WorkDir"`
|
|
||||||
} `json:"Data"`
|
|
||||||
Name string `json:"Name"`
|
|
||||||
} `json:"GraphDriver"`
|
|
||||||
Mounts []struct {
|
|
||||||
Type string `json:"Type"`
|
|
||||||
Name string `json:"Name,omitempty"`
|
|
||||||
Source string `json:"Source"`
|
|
||||||
Destination string `json:"Destination"`
|
|
||||||
Driver string `json:"Driver,omitempty"`
|
|
||||||
Mode string `json:"Mode"`
|
|
||||||
RW bool `json:"RW"`
|
|
||||||
Propagation string `json:"Propagation"`
|
|
||||||
} `json:"Mounts"`
|
|
||||||
Config struct {
|
|
||||||
Hostname string `json:"Hostname"`
|
|
||||||
Domainname string `json:"Domainname"`
|
|
||||||
User string `json:"User"`
|
|
||||||
AttachStdin bool `json:"AttachStdin"`
|
|
||||||
AttachStdout bool `json:"AttachStdout"`
|
|
||||||
AttachStderr bool `json:"AttachStderr"`
|
|
||||||
Tty bool `json:"Tty"`
|
|
||||||
OpenStdin bool `json:"OpenStdin"`
|
|
||||||
StdinOnce bool `json:"StdinOnce"`
|
|
||||||
Env []string `json:"Env"`
|
|
||||||
Cmd []string `json:"Cmd"`
|
|
||||||
Image string `json:"Image"`
|
|
||||||
Volumes struct {
|
|
||||||
APIBundle struct{} `json:"/api-bundle"`
|
|
||||||
App struct{} `json:"/app"`
|
|
||||||
} `json:"Volumes"`
|
|
||||||
WorkingDir string `json:"WorkingDir"`
|
|
||||||
Entrypoint interface{} `json:"Entrypoint"`
|
|
||||||
OnBuild interface{} `json:"OnBuild"`
|
|
||||||
Labels map[string]string `json:"Labels"`
|
|
||||||
} `json:"Config"`
|
|
||||||
NetworkSettings struct {
|
|
||||||
Bridge string `json:"Bridge"`
|
|
||||||
SandboxID string `json:"SandboxID"`
|
|
||||||
HairpinMode bool `json:"HairpinMode"`
|
|
||||||
LinkLocalIPv6Address string `json:"LinkLocalIPv6Address"`
|
|
||||||
LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen"`
|
|
||||||
Ports map[string][]struct {
|
|
||||||
HostIP string `json:"HostIP"`
|
|
||||||
HostPort string `json:"HostPort"`
|
|
||||||
} `json:"Ports"`
|
|
||||||
SandboxKey string `json:"SandboxKey"`
|
|
||||||
SecondaryIPAddresses interface{} `json:"SecondaryIPAddresses"`
|
|
||||||
SecondaryIPv6Addresses interface{} `json:"SecondaryIPv6Addresses"`
|
|
||||||
EndpointID string `json:"EndpointID"`
|
|
||||||
Gateway string `json:"Gateway"`
|
|
||||||
GlobalIPv6Address string `json:"GlobalIPv6Address"`
|
|
||||||
GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen"`
|
|
||||||
IPAddress string `json:"IPAddress"`
|
|
||||||
IPPrefixLen int `json:"IPPrefixLen"`
|
|
||||||
IPv6Gateway string `json:"IPv6Gateway"`
|
|
||||||
MacAddress string `json:"MacAddress"`
|
|
||||||
Networks map[string]struct {
|
|
||||||
IPAMConfig interface{} `json:"IPAMConfig"`
|
|
||||||
Links interface{} `json:"Links"`
|
|
||||||
Aliases []string `json:"Aliases"`
|
|
||||||
NetworkID string `json:"NetworkID"`
|
|
||||||
EndpointID string `json:"EndpointID"`
|
|
||||||
Gateway string `json:"Gateway"`
|
|
||||||
IPAddress string `json:"IPAddress"`
|
|
||||||
IPPrefixLen int `json:"IPPrefixLen"`
|
|
||||||
IPv6Gateway string `json:"IPv6Gateway"`
|
|
||||||
GlobalIPv6Address string `json:"GlobalIPv6Address"`
|
|
||||||
GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen"`
|
|
||||||
MacAddress string `json:"MacAddress"`
|
|
||||||
DriverOpts interface{} `json:"DriverOpts"`
|
|
||||||
} `json:"Networks"`
|
|
||||||
} `json:"NetworkSettings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDisplayStrings returns the dispaly string of Container
|
// GetDisplayStrings returns the dispaly string of Container
|
||||||
func (c *Container) GetDisplayStrings(isFocused bool) []string {
|
func (c *Container) GetDisplayStrings(isFocused bool) []string {
|
||||||
image := strings.TrimPrefix(c.Container.Image, "sha256:")
|
image := strings.TrimPrefix(c.Container.Image, "sha256:")
|
||||||
|
|
@ -246,6 +61,10 @@ func (c *Container) GetDisplayStatus() string {
|
||||||
|
|
||||||
// GetDisplayStatus returns the exit code if the container has exited, and the health status if the container is running (and has a health check)
|
// GetDisplayStatus returns the exit code if the container has exited, and the health status if the container is running (and has a health check)
|
||||||
func (c *Container) GetDisplaySubstatus() string {
|
func (c *Container) GetDisplaySubstatus() string {
|
||||||
|
if !c.DetailsLoaded() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
switch c.Container.State {
|
switch c.Container.State {
|
||||||
case "exited":
|
case "exited":
|
||||||
return utils.ColoredString(
|
return utils.ColoredString(
|
||||||
|
|
@ -259,12 +78,19 @@ func (c *Container) GetDisplaySubstatus() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) getHealthStatus() string {
|
func (c *Container) getHealthStatus() string {
|
||||||
|
if !c.DetailsLoaded() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
healthStatusColorMap := map[string]color.Attribute{
|
healthStatusColorMap := map[string]color.Attribute{
|
||||||
"healthy": color.FgGreen,
|
"healthy": color.FgGreen,
|
||||||
"unhealthy": color.FgRed,
|
"unhealthy": color.FgRed,
|
||||||
"starting": color.FgYellow,
|
"starting": color.FgYellow,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Details.State.Health == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
healthStatus := c.Details.State.Health.Status
|
healthStatus := c.Details.State.Health.Status
|
||||||
if healthStatusColor, ok := healthStatusColorMap[healthStatus]; ok {
|
if healthStatusColor, ok := healthStatusColorMap[healthStatus]; ok {
|
||||||
return utils.ColoredString(fmt.Sprintf("(%s)", healthStatus), healthStatusColor)
|
return utils.ColoredString(fmt.Sprintf("(%s)", healthStatus), healthStatusColor)
|
||||||
|
|
@ -296,14 +122,16 @@ func (c *Container) GetDisplayCPUPerc() string {
|
||||||
|
|
||||||
// ProducingLogs tells us whether we should bother checking a container's logs
|
// ProducingLogs tells us whether we should bother checking a container's logs
|
||||||
func (c *Container) ProducingLogs() bool {
|
func (c *Container) ProducingLogs() bool {
|
||||||
return c.Container.State == "running" && !(c.Details.HostConfig.LogConfig.Type == "none")
|
return c.Container.State == "running" && c.DetailsLoaded() && c.Details.HostConfig.LogConfig.Type != "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetColor Container color
|
// GetColor Container color
|
||||||
func (c *Container) GetColor() color.Attribute {
|
func (c *Container) GetColor() color.Attribute {
|
||||||
switch c.Container.State {
|
switch c.Container.State {
|
||||||
case "exited":
|
case "exited":
|
||||||
if c.Details.State.ExitCode == 0 {
|
// This means the colour may be briefly yellow and then switch to red upon starting
|
||||||
|
// Not sure what a better alternative is.
|
||||||
|
if !c.DetailsLoaded() || c.Details.State.ExitCode == 0 {
|
||||||
return color.FgYellow
|
return color.FgYellow
|
||||||
}
|
}
|
||||||
return color.FgRed
|
return color.FgRed
|
||||||
|
|
@ -355,7 +183,9 @@ func (c *Container) Restart() error {
|
||||||
|
|
||||||
// Attach attaches the container
|
// Attach attaches the container
|
||||||
func (c *Container) Attach() (*exec.Cmd, error) {
|
func (c *Container) Attach() (*exec.Cmd, error) {
|
||||||
c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
|
if !c.DetailsLoaded() {
|
||||||
|
return nil, errors.New(c.Tr.WaitingForContainerInfo)
|
||||||
|
}
|
||||||
|
|
||||||
// verify that we can in fact attach to this container
|
// verify that we can in fact attach to this container
|
||||||
if !c.Details.Config.OpenStdin {
|
if !c.Details.Config.OpenStdin {
|
||||||
|
|
@ -366,6 +196,7 @@ func (c *Container) Attach() (*exec.Cmd, error) {
|
||||||
return nil, errors.New(c.Tr.CannotAttachStoppedContainerError)
|
return nil, errors.New(c.Tr.CannotAttachStoppedContainerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Log.Warn(fmt.Sprintf("attaching to container %s", c.Name))
|
||||||
cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID)
|
cmd := c.OSCommand.PrepareSubProcess("docker", "attach", "--sig-proxy=false", c.ID)
|
||||||
return cmd, nil
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
@ -420,7 +251,7 @@ func (c *Container) RenderTop() (string, error) {
|
||||||
return utils.RenderTable(append([][]string{result.Titles}, result.Processes...))
|
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. Details is a struct, not a pointer to a struct, so it starts off with heaps of zero values. One of which is the container Image, which starts as a blank string. Given that every container should have an image, this is a good proxy to use
|
// 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 {
|
func (c *Container) DetailsLoaded() bool {
|
||||||
return c.Details.Image != ""
|
return c.Details.ContainerJSONBase != nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -382,26 +382,13 @@ func (c *DockerCommand) UpdateContainerDetails() error {
|
||||||
c.ContainerMutex.Lock()
|
c.ContainerMutex.Lock()
|
||||||
defer c.ContainerMutex.Unlock()
|
defer c.ContainerMutex.Unlock()
|
||||||
|
|
||||||
containers := c.Containers
|
for _, container := range c.Containers {
|
||||||
|
details, err := c.Client.ContainerInspect(context.Background(), container.ID)
|
||||||
ids := make([]string, len(containers))
|
if err != nil {
|
||||||
for i, container := range containers {
|
c.Log.Error(err)
|
||||||
ids[i] = container.ID
|
} else {
|
||||||
}
|
container.Details = details
|
||||||
|
}
|
||||||
cmd := c.OSCommand.RunCustomCommand("docker inspect " + strings.Join(ids, " "))
|
|
||||||
output, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var details []*Details
|
|
||||||
if err := json.Unmarshal(output, &details); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, container := range containers {
|
|
||||||
container.Details = *details[i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,12 @@ func (gui *Gui) handleContainerSelect(g *gocui.Gui, v *gocui.View) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderContainerEnv(container *commands.Container) error {
|
func (gui *Gui) renderContainerEnv(container *commands.Container) error {
|
||||||
|
if !container.DetailsLoaded() {
|
||||||
|
return gui.T.NewTask(func(stop chan struct{}) {
|
||||||
|
_ = gui.renderString(gui.g, "main", gui.Tr.WaitingForContainerInfo)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
mainView := gui.getMainView()
|
mainView := gui.getMainView()
|
||||||
mainView.Autoscroll = false
|
mainView.Autoscroll = false
|
||||||
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
|
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
|
||||||
|
|
@ -125,6 +131,12 @@ func (gui *Gui) renderContainerEnv(container *commands.Container) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) renderContainerConfig(container *commands.Container) error {
|
func (gui *Gui) renderContainerConfig(container *commands.Container) error {
|
||||||
|
if !container.DetailsLoaded() {
|
||||||
|
return gui.T.NewTask(func(stop chan struct{}) {
|
||||||
|
_ = gui.renderString(gui.g, "main", gui.Tr.WaitingForContainerInfo)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
mainView := gui.getMainView()
|
mainView := gui.getMainView()
|
||||||
mainView.Autoscroll = false
|
mainView.Autoscroll = false
|
||||||
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
|
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
|
||||||
|
|
@ -142,9 +154,9 @@ func (gui *Gui) renderContainerConfig(container *commands.Container) error {
|
||||||
output += "\n"
|
output += "\n"
|
||||||
for _, mount := range container.Details.Mounts {
|
for _, mount := range container.Details.Mounts {
|
||||||
if mount.Type == "volume" {
|
if mount.Type == "volume" {
|
||||||
output += fmt.Sprintf("%s%s %s\n", strings.Repeat(" ", padding), utils.ColoredString(mount.Type+":", color.FgYellow), mount.Name)
|
output += fmt.Sprintf("%s%s %s\n", strings.Repeat(" ", padding), utils.ColoredString(string(mount.Type)+":", color.FgYellow), mount.Name)
|
||||||
} else {
|
} else {
|
||||||
output += fmt.Sprintf("%s%s %s:%s\n", strings.Repeat(" ", padding), utils.ColoredString(mount.Type+":", color.FgYellow), mount.Source, mount.Destination)
|
output += fmt.Sprintf("%s%s %s:%s\n", strings.Repeat(" ", padding), utils.ColoredString(string(mount.Type)+":", color.FgYellow), mount.Source, mount.Destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,9 @@ func (gui *Gui) getProjectName() string {
|
||||||
projectName := path.Base(gui.Config.ProjectDir)
|
projectName := path.Base(gui.Config.ProjectDir)
|
||||||
if gui.DockerCommand.InDockerComposeProject {
|
if gui.DockerCommand.InDockerComposeProject {
|
||||||
for _, service := range gui.DockerCommand.Services {
|
for _, service := range gui.DockerCommand.Services {
|
||||||
if service.Container != nil {
|
container := service.Container
|
||||||
return service.Container.Details.Config.Labels["com.docker.compose.project"]
|
if container != nil && container.DetailsLoaded() {
|
||||||
|
return container.Details.Config.Labels["com.docker.compose.project"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ type TranslationSet struct {
|
||||||
ErrorOccurred string
|
ErrorOccurred string
|
||||||
ConnectionFailed string
|
ConnectionFailed string
|
||||||
UnattachableContainerError string
|
UnattachableContainerError string
|
||||||
|
WaitingForContainerInfo string
|
||||||
CannotAttachStoppedContainerError string
|
CannotAttachStoppedContainerError string
|
||||||
CannotAccessDockerSocketError string
|
CannotAccessDockerSocketError string
|
||||||
CannotKillChildError string
|
CannotKillChildError string
|
||||||
|
|
@ -115,6 +116,7 @@ func englishSet() TranslationSet {
|
||||||
ErrorOccurred: "An error occurred! Please create an issue at https://github.com/jesseduffield/lazydocker/issues",
|
ErrorOccurred: "An error occurred! Please create an issue at https://github.com/jesseduffield/lazydocker/issues",
|
||||||
ConnectionFailed: "connection to docker client failed. You may need to restart the docker client",
|
ConnectionFailed: "connection to docker client failed. You may need to restart the docker client",
|
||||||
UnattachableContainerError: "Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file",
|
UnattachableContainerError: "Container does not support attaching. You must either run the service with the '-it' flag or use `stdin_open: true, tty: true` in the docker-compose.yml file",
|
||||||
|
WaitingForContainerInfo: "Cannot proceed until docker gives us more information about the container. Please retry in a few moments.",
|
||||||
CannotAttachStoppedContainerError: "You cannot attach to a stopped container, you need to start it first (which you can actually do with the 'r' key) (yes I'm too lazy to do this automatically for you) (pretty cool that I get to communicate one-on-one with you in the form of an error message though)",
|
CannotAttachStoppedContainerError: "You cannot attach to a stopped container, you need to start it first (which you can actually do with the 'r' key) (yes I'm too lazy to do this automatically for you) (pretty cool that I get to communicate one-on-one with you in the form of an error message though)",
|
||||||
CannotAccessDockerSocketError: "Can't access docker socket at: unix:///var/run/docker.sock\nRun lazydocker as root or read https://docs.docker.com/install/linux/linux-postinstall/",
|
CannotAccessDockerSocketError: "Can't access docker socket at: unix:///var/run/docker.sock\nRun lazydocker as root or read https://docs.docker.com/install/linux/linux-postinstall/",
|
||||||
CannotKillChildError: "Waited three seconds for child process to stop. There may be an orphan process that continues to run on your system.",
|
CannotKillChildError: "Waited three seconds for child process to stop. There may be an orphan process that continues to run on your system.",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue