Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged changes from master to k8s branch #176

Open
wants to merge 32 commits into
base: k8s
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f27de1c
Exec Sh Feature
fr05t1k Oct 7, 2018
e68f7ba
fix: handlers used to work after "exec sh" command
fr05t1k Oct 12, 2018
967a87a
Exec using API
fr05t1k Oct 13, 2018
a26fc91
Ability to change Shell
fr05t1k Oct 25, 2018
d59c91a
Do not allow to close /dev/stdin
fr05t1k Oct 26, 2018
ca35ef2
Unnecessary loop stopping
fr05t1k Oct 28, 2018
101ddad
Fixed a problem with rendering
fr05t1k Oct 28, 2018
70bd2ae
v0.7.2
bcicen Jan 24, 2019
7b4d4db
add status indicator doc
bcicen Jan 24, 2019
2f7bc2a
Update status.md
bcicen Jan 24, 2019
c49939f
Update status.md
bcicen Jan 24, 2019
29fa8cf
Update status.md
bcicen Jan 24, 2019
9592de8
add keyboard shortcuts to container menu
bcicen Jan 24, 2019
b401e7b
Fix function comments based on best practices from Effective Go
CodeLingoBot Mar 7, 2019
0479d42
Merge pull request #168 from CodeLingoBot/rewrite
bcicen Mar 7, 2019
1b441db
Switches to a read-only socket mount
captn3m0 Apr 16, 2019
d7384db
Merge branch 'exec' of https://github.com/fr05t1k/ctop into fr05t1k-exec
bcicen May 12, 2019
b8c38d0
add exec shortcut key to container menu
bcicen May 12, 2019
d187e8c
drop potentially empty initial frames during exec attach
bcicen May 12, 2019
c1d4615
Merge branch 'fr05t1k-exec'
bcicen May 12, 2019
73986d2
Enable cursor
fr05t1k May 13, 2019
42f095c
Merge pull request #170 from fr05t1k/exec
bcicen May 13, 2019
98fcfe8
refactor connectors for retry logic, add error view
bcicen May 22, 2019
0a5a4c9
add multi-line scrolling support, timestamps to error view
bcicen May 22, 2019
c8ac331
fix timer
bcicen May 22, 2019
4c4f041
improve health check visibility
bcicen Jun 22, 2019
331f50f
update status doc image
bcicen Jun 22, 2019
8946c4b
Merge pull request #169 from captn3m0/patch-1
bcicen Jun 29, 2019
b7d8148
update dockerclient, runc deps
bcicen Jun 23, 2019
d8c7dd4
move image to go 1.12
bcicen Jun 24, 2019
31bc33d
Mreger 'base/master' into 'base/k8s'
sah4ez Jul 12, 2019
ddd80b4
Added script for instaling minikube
sah4ez Jul 12, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM quay.io/vektorcloud/go:1.11
FROM quay.io/vektorcloud/go:1.12

RUN apk add --no-cache make

Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Fetch the [latest release](https://github.com/bcicen/ctop/releases) for your pla
#### Linux

```bash
sudo wget https://github.com/bcicen/ctop/releases/download/v0.7.1/ctop-0.7.1-linux-amd64 -O /usr/local/bin/ctop
sudo wget https://github.com/bcicen/ctop/releases/download/v0.7.2/ctop-0.7.2-linux-amd64 -O /usr/local/bin/ctop
sudo chmod +x /usr/local/bin/ctop
```

Expand All @@ -31,7 +31,7 @@ brew install ctop
```
or
```bash
sudo curl -Lo /usr/local/bin/ctop https://github.com/bcicen/ctop/releases/download/v0.7.1/ctop-0.7.1-darwin-amd64
sudo curl -Lo /usr/local/bin/ctop https://github.com/bcicen/ctop/releases/download/v0.7.2/ctop-0.7.2-darwin-amd64
sudo chmod +x /usr/local/bin/ctop
```

Expand All @@ -40,7 +40,7 @@ sudo chmod +x /usr/local/bin/ctop
```bash
docker run --rm -ti \
--name=ctop \
-v /var/run/docker.sock:/var/run/docker.sock \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
quay.io/vektorlab/ctop:latest
```

Expand Down Expand Up @@ -70,6 +70,7 @@ Option | Description
-s | select initial container sort field
-scale-cpu | show cpu as % of system total
-v | output version information and exit
-shell | specify shell (default: sh)

### Keybindings

Expand All @@ -84,6 +85,7 @@ s | Select container sort field
r | Reverse container sort order
o | Open single view
l | View container logs (`t` to toggle timestamp when open)
e | Exec Shell
S | Save current configuration to file
q | Quit ctop

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.7.1
0.7.2
2 changes: 1 addition & 1 deletion _docs/connectors.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# connectors
# Connectors

`ctop` comes with the below native connectors, enabled via the `--connector` option.

Expand Down
Binary file added _docs/img/status.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions _docs/status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Status Indicator

The `ctop` grid view provides a compact status indicator to convey container state

<img width="200px" src="img/status.png" alt="ctop"/>

### Status

<span align="center">

Appearance | Description
--- | ---
red | container is stopped
green | container is running
▮▮ | container is paused

</span>

### Health
If the container is configured with a health check, a `+` will appear next to the indicator

<span align="center">

Appearance | Description
--- | ---
red | health check in failed state
yellow | health check in starting state
green | health check in OK state

</span>
7 changes: 6 additions & 1 deletion config/param.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ var params = []*Param{
Val: "state",
Label: "Kubernetes namespace for monitoring",
},
&Param{
Key: "shell",
Val: "sh",
Label: "Shell",
},
}

type Param struct {
Expand All @@ -35,7 +40,7 @@ func Get(k string) *Param {
return &Param{} // default
}

// Get Param value by key
// GetVal gets Param value by key
func GetVal(k string) string {
return Get(k).Val
}
Expand Down
4 changes: 2 additions & 2 deletions config/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type Switch struct {
Label string
}

// Return Switch by key
// GetSwitch returns Switch by key
func GetSwitch(k string) *Switch {
for _, sw := range GlobalSwitches {
if sw.Key == k {
Expand All @@ -45,7 +45,7 @@ func GetSwitch(k string) *Switch {
return &Switch{} // default
}

// Return Switch value by key
// GetSwitchVal returns Switch value by key
func GetSwitchVal(k string) bool {
return GetSwitch(k).Val
}
Expand Down
48 changes: 37 additions & 11 deletions connector/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,45 @@ type Docker struct {
client *api.Client
containers map[string]*container.Container
needsRefresh chan string // container IDs requiring refresh
closed chan struct{}
lock sync.RWMutex
}

func NewDocker() Connector {
func NewDocker() (Connector, error) {
// init docker client
client, err := api.NewClientFromEnv()
if err != nil {
panic(err)
return nil, err
}
cm := &Docker{
client: client,
containers: make(map[string]*container.Container),
needsRefresh: make(chan string, 60),
closed: make(chan struct{}),
lock: sync.RWMutex{},
}

// query info as pre-flight healthcheck
info, err := client.Info()
if err != nil {
return nil, err
}

log.Debugf("docker-connector ID: %s", info.ID)
log.Debugf("docker-connector Driver: %s", info.Driver)
log.Debugf("docker-connector Images: %d", info.Images)
log.Debugf("docker-connector Name: %s", info.Name)
log.Debugf("docker-connector ServerVersion: %s", info.ServerVersion)

go cm.Loop()
cm.refreshAll()
go cm.watchEvents()
return cm
return cm, nil
}

// Docker implements Connector
func (cm *Docker) Wait() struct{} { return <-cm.closed }

// Docker events watcher
func (cm *Docker) watchEvents() {
log.Info("docker event listener starting")
Expand All @@ -60,6 +78,8 @@ func (cm *Docker) watchEvents() {
cm.delByID(e.ID)
}
}
log.Info("docker event listener exited")
close(cm.closed)
}

func portsFormat(ports map[api.Port][]api.PortBinding) string {
Expand Down Expand Up @@ -114,7 +134,7 @@ func (cm *Docker) inspect(id string) *api.Container {
c, err := cm.client.InspectContainer(id)
if err != nil {
if _, ok := err.(*api.NoSuchContainer); !ok {
log.Errorf(err.Error())
log.Errorf("%s (%T)", err.Error(), err)
}
}
return c
Expand All @@ -125,7 +145,8 @@ func (cm *Docker) refreshAll() {
opts := api.ListContainersOptions{All: true}
allContainers, err := cm.client.ListContainers(opts)
if err != nil {
panic(err)
log.Errorf("%s (%T)", err.Error(), err)
return
}

for _, i := range allContainers {
Expand All @@ -137,13 +158,18 @@ func (cm *Docker) refreshAll() {
}

func (cm *Docker) Loop() {
for id := range cm.needsRefresh {
c := cm.MustGet(id)
cm.refresh(c)
for {
select {
case id := <-cm.needsRefresh:
c := cm.MustGet(id)
cm.refresh(c)
case <-cm.closed:
return
}
}
}

// Get a single container, creating one anew if not existing
// MustGet gets a single container, creating one anew if not existing
func (cm *Docker) MustGet(id string) *container.Container {
c, ok := cm.Get(id)
// append container struct for new containers
Expand All @@ -161,7 +187,7 @@ func (cm *Docker) MustGet(id string) *container.Container {
return c
}

// Get a single container, by ID
// Docker implements Connector
func (cm *Docker) Get(id string) (*container.Container, bool) {
cm.lock.Lock()
c, ok := cm.containers[id]
Expand All @@ -177,7 +203,7 @@ func (cm *Docker) delByID(id string) {
log.Infof("removed dead container: %s", id)
}

// Return array of all containers, sorted by field
// Docker implements Connector
func (cm *Docker) All() (containers container.Containers) {
cm.lock.Lock()
for _, c := range cm.containers {
Expand Down
87 changes: 78 additions & 9 deletions connector/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,89 @@ package connector
import (
"fmt"
"sort"
"sync"
"time"

"github.com/bcicen/ctop/container"
"github.com/bcicen/ctop/logging"
)

var (
log = logging.Init()
enabled = make(map[string]func() Connector)
enabled = make(map[string]ConnectorFn)
)

// return names for all enabled connectors on the current platform
type ConnectorFn func() (Connector, error)

type Connector interface {
// All returns a pre-sorted container.Containers of all discovered containers
All() container.Containers
// Get returns a single container.Container by ID
Get(string) (*container.Container, bool)
// Wait waits for the underlying connection to be lost before returning
Wait() struct{}
}

// ConnectorSuper provides initial connection and retry on failure for
// an undlerying Connector type
type ConnectorSuper struct {
conn Connector
connFn ConnectorFn
err error
lock sync.RWMutex
}

func NewConnectorSuper(connFn ConnectorFn) *ConnectorSuper {
cs := &ConnectorSuper{
connFn: connFn,
err: fmt.Errorf("connecting..."),
}
go cs.loop()
return cs
}

// Get returns the underlying Connector, or nil and an error
// if the Connector is not yet initialized or is disconnected.
func (cs *ConnectorSuper) Get() (Connector, error) {
cs.lock.RLock()
defer cs.lock.RUnlock()
if cs.err != nil {
return nil, cs.err
}
return cs.conn, nil
}

func (cs *ConnectorSuper) setError(err error) {
cs.lock.Lock()
defer cs.lock.Unlock()
cs.err = err
}

func (cs *ConnectorSuper) loop() {
const interval = 3
for {
log.Infof("initializing connector")

conn, err := cs.connFn()
if err != nil {
cs.setError(err)
log.Errorf("failed to initialize connector: %s (%T)", err, err)
log.Errorf("retrying in %ds", interval)
time.Sleep(interval * time.Second)
} else {
cs.conn = conn
cs.setError(nil)
log.Infof("successfully initialized connector")

// wait until connection closed
cs.conn.Wait()
cs.setError(fmt.Errorf("attempting to reconnect..."))
log.Infof("connector closed")
}
}
}

// Enabled returns names for all enabled connectors on the current platform
func Enabled() (a []string) {
for k, _ := range enabled {
a = append(a, k)
Expand All @@ -22,14 +94,11 @@ func Enabled() (a []string) {
return a
}

func ByName(s string) (Connector, error) {
// ByName returns a ConnectorSuper for a given name, or error if the connector
// does not exists on the current platform
func ByName(s string) (*ConnectorSuper, error) {
if cfn, ok := enabled[s]; ok {
return cfn(), nil
return NewConnectorSuper(cfn), nil
}
return nil, fmt.Errorf("invalid connector type \"%s\"", s)
}

type Connector interface {
All() container.Containers
Get(string) (*container.Container, bool)
}