diff --git a/connector/docker.go b/connector/docker.go index 0e30c12..0b3840e 100644 --- a/connector/docker.go +++ b/connector/docker.go @@ -148,6 +148,23 @@ func portsFormat(ports map[api.Port][]api.PortBinding) string { return strings.Join(append(exposed, published...), "\n") } +func webPort(ports map[api.Port][]api.PortBinding) string { + for _, v := range ports { + if len(v) == 0 { + continue + } + for _, binding := range v { + publishedIp := binding.HostIP + if publishedIp == "0.0.0.0" { + publishedIp = "localhost" + } + publishedWebPort := fmt.Sprintf("%s:%s", publishedIp, binding.HostPort) + return publishedWebPort + } + } + return "" +} + func ipsFormat(networks map[string]api.ContainerNetwork) string { var ips []string @@ -173,6 +190,10 @@ func (cm *Docker) refresh(c *container.Container) { c.SetMeta("image", insp.Config.Image) c.SetMeta("IPs", ipsFormat(insp.NetworkSettings.Networks)) c.SetMeta("ports", portsFormat(insp.NetworkSettings.Ports)) + webPort := webPort(insp.NetworkSettings.Ports) + if webPort != "" { + c.SetMeta("Web Port", webPort) + } c.SetMeta("created", insp.Created.Format("Mon Jan 2 15:04:05 2006")) c.SetMeta("health", insp.State.Health.Status) for _, env := range insp.Config.Env { diff --git a/go.mod b/go.mod index 4495847..34f45db 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473 github.com/opencontainers/runc v1.0.0-rc92 + github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.4.0 ) diff --git a/go.sum b/go.sum index 874df58..39ccf96 100644 --- a/go.sum +++ b/go.sum @@ -146,6 +146,8 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6 h1:N github.com/opencontainers/runtime-spec v1.0.3-0.20200728170252-4d89ac9fbff6/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.6.0 h1:+bIAS/Za3q5FTwWym4fTB0vObnfCf3G/NC7K6Jx62mY= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23 h1:dofHuld+js7eKSemxqTVIo8yRlpRw+H1SdpzZxWruBc= +github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/grid.go b/grid.go index e4e0fdb..826f6c7 100644 --- a/grid.go +++ b/grid.go @@ -162,6 +162,9 @@ func Display() bool { menu = ExecShell ui.StopLoop() }) + ui.Handle("/sys/kbd/w", func(ui.Event) { + menu = OpenInBrowser() + }) ui.Handle("/sys/kbd/o", func(ui.Event) { menu = SingleView ui.StopLoop() diff --git a/menus.go b/menus.go index 8176c6f..81e6d01 100644 --- a/menus.go +++ b/menus.go @@ -10,6 +10,7 @@ import ( "github.com/bcicen/ctop/widgets" "github.com/bcicen/ctop/widgets/menu" ui "github.com/gizak/termui" + "github.com/pkg/browser" ) // MenuFn executes a menu window, returning the next menu or nil @@ -27,6 +28,7 @@ var helpDialog = []menu.Item{ {"[o] - open single view", ""}, {"[l] - view container logs ([t] to toggle timestamp when open)", ""}, {"[e] - exec shell", ""}, + {"[w] - open browser (first port is http)", ""}, {"[c] - configure columns", ""}, {"[S] - save current configuration to file", ""}, {"[q] - exit ctop", ""}, @@ -221,6 +223,7 @@ func ContainerMenu() MenuFn { items = append(items, menu.Item{Val: "pause", Label: "[p] pause"}) items = append(items, menu.Item{Val: "restart", Label: "[r] restart"}) items = append(items, menu.Item{Val: "exec", Label: "[e] exec shell"}) + items = append(items, menu.Item{Val: "browser", Label: "[w] open in browser"}) } if c.Meta["state"] == "exited" || c.Meta["state"] == "created" { items = append(items, menu.Item{Val: "start", Label: "[s] start"}) @@ -277,6 +280,9 @@ func ContainerMenu() MenuFn { selected = "restart" ui.StopLoop() }) + ui.Handle("/sys/kbd/w", func(ui.Event) { + selected = "browser" + }) } ui.Handle("/sys/kbd/R", func(ui.Event) { selected = "remove" @@ -303,6 +309,8 @@ func ContainerMenu() MenuFn { nextMenu = LogMenu case "exec": nextMenu = ExecShell + case "browser": + nextMenu = OpenInBrowser case "start": nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start) case "stop": @@ -374,6 +382,21 @@ func ExecShell() MenuFn { return nil } +func OpenInBrowser() MenuFn { + c := cursor.Selected() + if c == nil { + return nil + } + + webPort := c.Meta.Get("Web Port") + if webPort == "" { + return nil + } + link := "http://" + webPort + "/" + browser.OpenURL(link) + return nil +} + // Create a confirmation dialog with a given description string and // func to perform if confirmed func Confirm(txt string, fn func()) MenuFn {