mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-22 15:11:02 +00:00
Correlate Services and Containers by Service Name
The config hash ends up not being a unique identifier of a Service. Additionally, Services may have multiple associated Containers.
``` yaml
# Sample docker-compose.yml
version: "2.2"
services:
named-service:
container_name: 'named-container'
image: 'alpine'
command: 'sh -c "while :; do sleep 1; done"'
anonymous-service:
image: 'alpine'
command: 'sh -c "while :; do sleep 1; done"'
scaled-service:
image: 'alpine'
scale: 3
command: 'sh -c "while :; do sleep 1; done"'
```
``` bash
$ docker-compose config --hash="*"
named-service e6e7c4564dfc435ba96725098c62e6b6cf8da9f0b76b0a08be2baddedad6d007
anonymous-service 254f4815ef36bc9fcc2ce80f82278187c9e233309a4106951551f3692657ce9c
scaled-service 254f4815ef36bc9fcc2ce80f82278187c9e233309a4106951551f3692657ce9c
```
This commit changes how Services are discovered (`docker-compose config --services`), how they're associated (by Container ID, rather than config hash or name), and allows for Services to reference multiple Containers (though only the first is directly used for anything at this time).
This commit is contained in:
parent
b5f64c0a2e
commit
a4bd90ff8a
5 changed files with 74 additions and 65 deletions
|
|
@ -216,20 +216,26 @@ func (c *DockerCommand) RefreshContainersAndServices() error {
|
|||
}
|
||||
}
|
||||
|
||||
c.assignContainersToServices(containers, services)
|
||||
|
||||
var displayContainers = containers
|
||||
if !c.Config.UserConfig.Gui.ShowAllContainers {
|
||||
displayContainers = c.obtainStandaloneContainers(containers, services)
|
||||
|
||||
if c.InDockerComposeProject {
|
||||
var standaloneContainers, err = c.assignContainersToServices(containers, services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !c.Config.UserConfig.Gui.ShowAllContainers {
|
||||
displayContainers = standaloneContainers
|
||||
}
|
||||
}
|
||||
|
||||
// sort services first by whether they have a linked container, and second by alphabetical order
|
||||
sort.Slice(services, func(i, j int) bool {
|
||||
if services[i].Container != nil && services[j].Container == nil {
|
||||
if len(services[i].Containers) > 0 && len(services[j].Containers) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if services[i].Container == nil && services[j].Container != nil {
|
||||
if len(services[i].Containers) == 0 && len(services[j].Containers) > 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -243,17 +249,40 @@ func (c *DockerCommand) RefreshContainersAndServices() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) {
|
||||
L:
|
||||
for _, service := range services {
|
||||
for _, container := range containers {
|
||||
if !container.OneOff && container.ServiceName == service.Name {
|
||||
service.Container = container
|
||||
continue L
|
||||
}
|
||||
}
|
||||
service.Container = nil
|
||||
func (c *DockerCommand) assignContainersToServices(containers []*Container, services []*Service) ([]*Container, error) {
|
||||
// Get a list of the Container IDs for this Docker Compose project.
|
||||
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
|
||||
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s ps -q", composeCommand))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create an index for Container IDs to make lookups less expensive.
|
||||
lines := utils.SplitLines(output)
|
||||
serviceContainerIds := make(map[string]bool)
|
||||
for _, line := range lines {
|
||||
serviceContainerIds[line] = true
|
||||
}
|
||||
|
||||
// Index Services by name.
|
||||
serviceMap := make(map[string]*Service)
|
||||
for _, service := range services {
|
||||
serviceMap[service.Name] = service
|
||||
}
|
||||
|
||||
// Walk the list of Containers, categorizing them as either associated with a
|
||||
// Service or as standalone.
|
||||
standaloneContainers := make([]*Container, 0, len(containers) - len(serviceContainerIds))
|
||||
for _, container := range containers {
|
||||
if !container.OneOff && serviceContainerIds[container.ID] {
|
||||
service := serviceMap[container.ServiceName]
|
||||
service.Containers = append(service.Containers, container)
|
||||
} else {
|
||||
standaloneContainers = append(standaloneContainers, container)
|
||||
}
|
||||
}
|
||||
|
||||
return standaloneContainers, nil
|
||||
}
|
||||
|
||||
// filterOutExited filters out the exited containers if c.ShowExited is false
|
||||
|
|
@ -270,22 +299,6 @@ func (c *DockerCommand) filterOutExited(containers []*Container) []*Container {
|
|||
return toReturn
|
||||
}
|
||||
|
||||
// obtainStandaloneContainers returns standalone containers. Standalone containers are containers which are either one-off containers, or whose service is not part of this docker-compose context
|
||||
func (c *DockerCommand) obtainStandaloneContainers(containers []*Container, services []*Service) []*Container {
|
||||
standaloneContainers := []*Container{}
|
||||
L:
|
||||
for _, container := range containers {
|
||||
for _, service := range services {
|
||||
if !container.OneOff && container.ServiceName != "" && container.ServiceName == service.Name {
|
||||
continue L
|
||||
}
|
||||
}
|
||||
standaloneContainers = append(standaloneContainers, container)
|
||||
}
|
||||
|
||||
return standaloneContainers
|
||||
}
|
||||
|
||||
// GetContainers gets the docker containers
|
||||
func (c *DockerCommand) GetContainers() ([]*Container, error) {
|
||||
c.ContainerMutex.Lock()
|
||||
|
|
@ -349,25 +362,20 @@ func (c *DockerCommand) GetServices() ([]*Service, error) {
|
|||
}
|
||||
|
||||
composeCommand := c.Config.UserConfig.CommandTemplates.DockerCompose
|
||||
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --hash=*", composeCommand))
|
||||
output, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("%s config --services", composeCommand))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// output looks like:
|
||||
// service1 998d6d286b0499e0ff23d66302e720991a2asdkf9c30d0542034f610daf8a971
|
||||
// service2 asdld98asdklasd9bccd02438de0994f8e19cbe691feb3755336ec5ca2c55971
|
||||
|
||||
lines := utils.SplitLines(output)
|
||||
services := make([]*Service, len(lines))
|
||||
for i, str := range lines {
|
||||
arr := strings.Split(str, " ")
|
||||
serviceNames := utils.SplitLines(output)
|
||||
services := make([]*Service, len(serviceNames))
|
||||
for i, name := range serviceNames {
|
||||
services[i] = &Service{
|
||||
Name: arr[0],
|
||||
ID: arr[1],
|
||||
Name: name,
|
||||
OSCommand: c.OSCommand,
|
||||
Log: c.Log,
|
||||
DockerCommand: c,
|
||||
Containers: make([]*Container, 0, 1),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,27 +13,26 @@ import (
|
|||
// Service : A docker Service
|
||||
type Service struct {
|
||||
Name string
|
||||
ID string
|
||||
OSCommand *OSCommand
|
||||
Log *logrus.Entry
|
||||
Container *Container
|
||||
Containers []*Container
|
||||
DockerCommand LimitedDockerCommand
|
||||
}
|
||||
|
||||
// GetDisplayStrings returns the dispaly string of Container
|
||||
func (s *Service) GetDisplayStrings(isFocused bool) []string {
|
||||
|
||||
if s.Container == nil {
|
||||
if len(s.Containers) == 0 {
|
||||
return []string{utils.ColoredString("none", color.FgBlue), s.Name, ""}
|
||||
}
|
||||
|
||||
cont := s.Container
|
||||
cont := s.Containers[0]
|
||||
return []string{cont.GetDisplayStatus(), s.Name, cont.GetDisplayCPUPerc()}
|
||||
}
|
||||
|
||||
// Remove removes the service's containers
|
||||
func (s *Service) Remove(options types.ContainerRemoveOptions) error {
|
||||
return s.Container.Remove(options)
|
||||
return s.Containers[0].Remove(options)
|
||||
}
|
||||
|
||||
// Stop stops the service's containers
|
||||
|
|
@ -58,12 +57,12 @@ func (s *Service) Restart() error {
|
|||
|
||||
// Attach attaches to the service
|
||||
func (s *Service) Attach() (*exec.Cmd, error) {
|
||||
return s.Container.Attach()
|
||||
return s.Containers[0].Attach()
|
||||
}
|
||||
|
||||
// Top returns process information
|
||||
func (s *Service) Top() (container.ContainerTopOKBody, error) {
|
||||
return s.Container.Top()
|
||||
return s.Containers[0].Top()
|
||||
}
|
||||
|
||||
// ViewLogs attaches to a subprocess viewing the service's logs
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ func (gui *Gui) refreshContainersAndServices() error {
|
|||
// see if our selected service has moved
|
||||
if selectedService != nil {
|
||||
for i, service := range gui.DockerCommand.Services {
|
||||
if service.ID == selectedService.ID {
|
||||
if service.Name == selectedService.Name {
|
||||
if i == sl {
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ func (gui *Gui) refreshProject() error {
|
|||
projectName := path.Base(gui.Config.ProjectDir)
|
||||
if gui.DockerCommand.InDockerComposeProject {
|
||||
for _, service := range gui.DockerCommand.Services {
|
||||
if service.Container != nil {
|
||||
projectName = service.Container.Details.Config.Labels["com.docker.compose.project"]
|
||||
if len(service.Containers) > 0 {
|
||||
projectName = service.Containers[0].Details.Config.Labels["com.docker.compose.project"]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,15 +46,17 @@ func (gui *Gui) handleServiceSelect(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
containerID := ""
|
||||
if service.Container != nil {
|
||||
containerID = service.Container.ID
|
||||
containerNum := ""
|
||||
if len(service.Containers) != 0 {
|
||||
containerID = service.Containers[0].ID
|
||||
containerNum = service.Containers[0].ContainerNumber
|
||||
}
|
||||
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Services.SelectedLine, len(gui.DockerCommand.Services), v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key := "services-" + service.ID + "-" + containerID + "-" + gui.getServiceContexts()[gui.State.Panels.Services.ContextIndex]
|
||||
key := "services-" + service.Name + "-" + containerID + "-" + containerNum + "-" + gui.getServiceContexts()[gui.State.Panels.Services.ContextIndex]
|
||||
if !gui.shouldRefresh(key) {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -74,10 +76,10 @@ func (gui *Gui) handleServiceSelect(g *gocui.Gui, v *gocui.View) error {
|
|||
return err
|
||||
}
|
||||
case "container-config":
|
||||
if service.Container == nil {
|
||||
if len(service.Containers) == 0 {
|
||||
return gui.renderString(gui.g, "main", gui.Tr.NoContainer)
|
||||
}
|
||||
if err := gui.renderContainerConfig(service.Container); err != nil {
|
||||
if err := gui.renderContainerConfig(service.Containers[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
case "top":
|
||||
|
|
@ -92,11 +94,11 @@ func (gui *Gui) handleServiceSelect(g *gocui.Gui, v *gocui.View) error {
|
|||
}
|
||||
|
||||
func (gui *Gui) renderServiceStats(service *commands.Service) error {
|
||||
if service.Container == nil {
|
||||
if len(service.Containers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.renderContainerStats(service.Container)
|
||||
return gui.renderContainerStats(service.Containers[0])
|
||||
}
|
||||
|
||||
func (gui *Gui) renderServiceTop(service *commands.Service) error {
|
||||
|
|
@ -120,13 +122,13 @@ func (gui *Gui) renderServiceLogs(service *commands.Service) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if service.Container == nil {
|
||||
if len(service.Containers) == 0 {
|
||||
return gui.T.NewTask(func(stop chan struct{}) {
|
||||
gui.clearMainView()
|
||||
})
|
||||
}
|
||||
|
||||
return gui.renderContainerLogs(service.Container)
|
||||
return gui.renderContainerLogs(service.Containers[0])
|
||||
}
|
||||
|
||||
func (gui *Gui) handleServicesNextLine(g *gocui.Gui, v *gocui.View) error {
|
||||
|
|
@ -252,7 +254,7 @@ func (gui *Gui) handleServiceAttach(g *gocui.Gui, v *gocui.View) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if service.Container == nil {
|
||||
if len(service.Containers) == 0 {
|
||||
return gui.createErrorPanel(gui.g, gui.Tr.NoContainers)
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +376,7 @@ func (gui *Gui) handleServicesCustomCommand(g *gocui.Gui, v *gocui.View) error {
|
|||
|
||||
commandObject := gui.DockerCommand.NewCommandObject(commands.CommandObject{
|
||||
Service: service,
|
||||
Container: service.Container,
|
||||
Container: service.Containers[0],
|
||||
})
|
||||
|
||||
var customCommands []config.CustomCommand
|
||||
|
|
@ -396,7 +398,7 @@ L:
|
|||
}
|
||||
}
|
||||
|
||||
if service.Container != nil {
|
||||
if len(service.Containers) > 0 {
|
||||
customCommands = append(customCommands, gui.Config.UserConfig.CustomCommands.Containers...)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue