From e5ccc673d902811886d7280777d67f4f4efd9ad6 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 1 Jun 2019 18:30:55 +1000 Subject: [PATCH] more progress --- pkg/commands/container.go | 194 ++++++++++++++++++++++++++++++++++++ pkg/commands/docker.go | 19 ++++ pkg/gui/containers_panel.go | 22 ++-- 3 files changed, 222 insertions(+), 13 deletions(-) diff --git a/pkg/commands/container.go b/pkg/commands/container.go index d1e32f50..d29412a1 100644 --- a/pkg/commands/container.go +++ b/pkg/commands/container.go @@ -4,6 +4,7 @@ import ( "context" "os/exec" "strings" + "time" "github.com/docker/docker/api/types" "github.com/docker/docker/client" @@ -26,6 +27,199 @@ type Container struct { OSCommand *OSCommand Log *logrus.Entry Stats ContainerCliStat + Details Details +} + +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"` + } `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 struct { + ComDockerComposeOneoff string `json:"com.docker.compose.oneoff"` + ComDockerComposeProject string `json:"com.docker.compose.project"` + ComDockerComposeService string `json:"com.docker.compose.service"` + ComDockerComposeSlug string `json:"com.docker.compose.slug"` + ComDockerComposeVersion string `json:"com.docker.compose.version"` + } `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 struct { + } `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 struct { + ApdevDefault 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:"apdev_default"` + } `json:"Networks"` + } `json:"NetworkSettings"` } // ContainerCliStat is a stat object returned by the CLI docker stat command diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go index d386235d..b55eccb8 100644 --- a/pkg/commands/docker.go +++ b/pkg/commands/docker.go @@ -81,7 +81,11 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) { ownContainers := make([]*Container, len(containers)) + ids := []string{} + for i, container := range containers { + ids = append(ids, container.ID) + ownContainers[i] = &Container{ ID: container.ID, Name: strings.TrimLeft(container.Names[0], "/"), @@ -95,6 +99,21 @@ func (c *DockerCommand) GetContainers() ([]*Container, error) { } } + cmd := c.OSCommand.RunCustomCommand("docker inspect " + strings.Join(ids, " ")) + output, err := cmd.CombinedOutput() + if err != nil { + return nil, err + } + + var details []*Details + if err := json.Unmarshal(output, &details); err != nil { + return nil, err + } + + for i, container := range ownContainers { + container.Details = *details[i] + } + // ownContainers, err = c.UpdateContainerStats(ownContainers) // if err != nil { // return nil, err diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index 15fc96b7..dd4c2ecd 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -174,13 +174,18 @@ func (gui *Gui) renderContainerLogs(mainView *gocui.View, container *commands.Co mainView.Autoscroll = true mainView.Title = "Logs" + if container.Details.Config.AttachStdin { + logs, err := gui.OSCommand.RunCommandWithOutput("docker logs --since=60m " + container.ID) + if err != nil { + return err + } + return gui.renderString(gui.g, "main", logs) + } + var cmd *exec.Cmd cmd = gui.OSCommand.RunCustomCommand("docker logs --since=60m --timestamps --follow " + container.ID) - r, err := cmd.StdoutPipe() - if err != nil { - return err - } + cmd.Stdout = mainView go func() { gui.State.MainProcessChan <- struct{}{} @@ -192,15 +197,6 @@ func (gui *Gui) renderContainerLogs(mainView *gocui.View, container *commands.Co cmd.Process.Kill() }() - go func() { - for { - s := bufio.NewScanner(r) - s.Split(bufio.ScanLines) - for s.Scan() { - mainView.Write(append(s.Bytes(), '\n')) - } - } - }() cmd.Wait() gui.State.MainProcessMutex.Unlock() }()