restructure container,connectors in subpackage

This commit is contained in:
Bradley Cicenas
2017-06-08 14:51:02 +00:00
parent 8fb7a8988f
commit b85ca680f0
9 changed files with 50 additions and 32 deletions

182
connector/dockersource.go Normal file
View File

@@ -0,0 +1,182 @@
package connector
import (
"fmt"
"sort"
"strings"
"sync"
"github.com/bcicen/ctop/container"
"github.com/bcicen/ctop/logging"
"github.com/bcicen/ctop/metrics"
"github.com/fsouza/go-dockerclient"
)
var (
log = logging.Init()
)
type ContainerSource interface {
All() container.Containers
Get(string) (*container.Container, bool)
}
type DockerContainerSource struct {
client *docker.Client
containers map[string]*container.Container
needsRefresh chan string // container IDs requiring refresh
lock sync.RWMutex
}
func NewDockerContainerSource() *DockerContainerSource {
// init docker client
client, err := docker.NewClientFromEnv()
if err != nil {
panic(err)
}
cm := &DockerContainerSource{
client: client,
containers: make(map[string]*container.Container),
needsRefresh: make(chan string, 60),
lock: sync.RWMutex{},
}
go cm.Loop()
cm.refreshAll()
go cm.watchEvents()
return cm
}
// Docker events watcher
func (cm *DockerContainerSource) watchEvents() {
log.Info("docker event listener starting")
events := make(chan *docker.APIEvents)
cm.client.AddEventListener(events)
for e := range events {
if e.Type != "container" {
continue
}
switch e.Action {
case "start", "die", "pause", "unpause":
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
cm.needsRefresh <- e.ID
case "destroy":
log.Debugf("handling docker event: action=%s id=%s", e.Action, e.ID)
cm.delByID(e.ID)
}
}
}
func portsFormat(ports map[docker.Port][]docker.PortBinding) string {
var exposed []string
var published []string
for k, v := range ports {
if len(v) == 0 {
exposed = append(exposed, string(k))
continue
}
for _, binding := range v {
s := fmt.Sprintf("%s -> %s:%s", k, binding.HostIP, binding.HostPort)
published = append(published, s)
}
}
return strings.Join(append(exposed, published...), "\n")
}
func (cm *DockerContainerSource) refresh(c *container.Container) {
insp := cm.inspect(c.Id)
// remove container if no longer exists
if insp == nil {
cm.delByID(c.Id)
return
}
c.SetMeta("name", shortName(insp.Name))
c.SetMeta("image", insp.Config.Image)
c.SetMeta("ports", portsFormat(insp.NetworkSettings.Ports))
c.SetMeta("created", insp.Created.Format("Mon Jan 2 15:04:05 2006"))
c.SetState(insp.State.Status)
}
func (cm *DockerContainerSource) inspect(id string) *docker.Container {
c, err := cm.client.InspectContainer(id)
if err != nil {
if _, ok := err.(*docker.NoSuchContainer); ok == false {
log.Errorf(err.Error())
}
}
return c
}
// Mark all container IDs for refresh
func (cm *DockerContainerSource) refreshAll() {
opts := docker.ListContainersOptions{All: true}
allContainers, err := cm.client.ListContainers(opts)
if err != nil {
panic(err)
}
for _, i := range allContainers {
c := cm.MustGet(i.ID)
c.SetMeta("name", shortName(i.Names[0]))
c.SetState(i.State)
cm.needsRefresh <- c.Id
}
}
func (cm *DockerContainerSource) Loop() {
for id := range cm.needsRefresh {
c := cm.MustGet(id)
cm.refresh(c)
}
}
// Get a single container, creating one anew if not existing
func (cm *DockerContainerSource) MustGet(id string) *container.Container {
c, ok := cm.Get(id)
// append container struct for new containers
if !ok {
// create collector
collector := metrics.NewDocker(cm.client, id)
// create container
c = container.New(id, collector)
cm.lock.Lock()
cm.containers[id] = c
cm.lock.Unlock()
}
return c
}
// Get a single container, by ID
func (cm *DockerContainerSource) Get(id string) (*container.Container, bool) {
cm.lock.Lock()
c, ok := cm.containers[id]
cm.lock.Unlock()
return c, ok
}
// Remove containers by ID
func (cm *DockerContainerSource) delByID(id string) {
cm.lock.Lock()
delete(cm.containers, id)
cm.lock.Unlock()
log.Infof("removed dead container: %s", id)
}
// Return array of all containers, sorted by field
func (cm *DockerContainerSource) All() (containers container.Containers) {
cm.lock.Lock()
for _, c := range cm.containers {
containers = append(containers, c)
}
cm.lock.Unlock()
sort.Sort(containers)
containers.Filter()
return containers
}
// use primary container name
func shortName(name string) string {
return strings.Replace(name, "/", "", 1)
}

126
connector/mocksource.go Normal file
View File

@@ -0,0 +1,126 @@
// +build !release
package connector
import (
"math/rand"
"sort"
"strings"
"time"
"github.com/bcicen/ctop/container"
"github.com/bcicen/ctop/metrics"
"github.com/jgautheron/codename-generator"
"github.com/nu7hatch/gouuid"
)
type MockContainerSource struct {
containers container.Containers
}
func NewMockContainerSource() *MockContainerSource {
cs := &MockContainerSource{}
go cs.Init()
go cs.Loop()
return cs
}
// Create Mock containers
func (cs *MockContainerSource) Init() {
rand.Seed(int64(time.Now().Nanosecond()))
for i := 0; i < 4; i++ {
cs.makeContainer(3)
}
for i := 0; i < 16; i++ {
cs.makeContainer(1)
}
}
func (cs *MockContainerSource) makeContainer(aggression int64) {
collector := metrics.NewMock(aggression)
c := container.New(makeID(), collector)
c.SetMeta("name", makeName())
c.SetState(makeState())
cs.containers = append(cs.containers, c)
}
func (cs *MockContainerSource) Loop() {
iter := 0
for {
// Change state for random container
if iter%5 == 0 && len(cs.containers) > 0 {
randC := cs.containers[rand.Intn(len(cs.containers))]
randC.SetState(makeState())
}
iter++
time.Sleep(3 * time.Second)
}
}
// Get a single container, by ID
func (cs *MockContainerSource) Get(id string) (*container.Container, bool) {
for _, c := range cs.containers {
if c.Id == id {
return c, true
}
}
return nil, false
}
// Return array of all containers, sorted by field
func (cs *MockContainerSource) All() container.Containers {
sort.Sort(cs.containers)
cs.containers.Filter()
return cs.containers
}
// Remove containers by ID
func (cs *MockContainerSource) delByID(id string) {
for n, c := range cs.containers {
if c.Id == id {
cs.del(n)
return
}
}
}
// Remove one or more containers by index
func (cs *MockContainerSource) del(idx ...int) {
for _, i := range idx {
cs.containers = append(cs.containers[:i], cs.containers[i+1:]...)
}
log.Infof("removed %d dead containers", len(idx))
}
func makeID() string {
u, err := uuid.NewV4()
if err != nil {
panic(err)
}
return strings.Replace(u.String(), "-", "", -1)[:12]
}
func makeName() string {
n, err := codename.Get(codename.Sanitized)
nsp := strings.Split(n, "-")
if len(nsp) > 2 {
n = strings.Join(nsp[:2], "-")
}
if err != nil {
panic(err)
}
return strings.Replace(n, "-", "_", -1)
}
func makeState() string {
switch rand.Intn(10) {
case 0, 1, 2:
return "exited"
case 3:
return "paused"
}
return "running"
}