whalewatcher

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 5, 2021 License: Apache-2.0 Imports: 3 Imported by: 2

README

Whalewatcher

PkgGoDev GitHub build and test Go Report Card

whalewatcher is a simple Golang module that watches Docker and plain containerd containers becoming alive with processes and later die, keeping track of only the "alive" containers. On purpose, this module focuses solely on running and paused containers, so those only that have at least an initial container process running (and thus a PID).

Envisioned use cases are container-aware tools – such as lxkns – that seemingly randomly need the current state of running container affairs. Tools that yet do not want to do the ugly lifting of container engine event tracking, engine state resynchronization after reconnects, et cetera. Here, the whalewatcher module reduces system load especially when state is requested in bursts, as it offers a load-optimized kind of "cache". Yet this cache is always closely synchronized to the container engine state.

🛈 If your application requires immediate action upon container lifecycle events then our whalewatcher isn't the right module for it.

Features

  • supports different container engines:
  • composer project-aware:
    • docker-compose
    • nerdctl composer-project support currently is blocked due to nerdctl issue #241, so project-related containers appear as standalone containers.
  • documentation ... PkgGoDev

Example Usage

From example/main.go:

package main

import (
  "context"
  "fmt"
  "sort"

  "github.com/thediveo/whalewatcher/watcher/moby"
)

func main() {
    whalewatcher, err := moby.NewWatcher("unix:///var/run/docker.sock")
    if err != nil {
        panic(err)
    }
    ctx, cancel := context.WithCancel(context.Background())
    fmt.Printf("watching engine ID: %s\n", whalewatcher.ID(ctx))

    // run the watch in a separate go routine.
    go whalewatcher.Watch(ctx)

    // depending on application you don't need to wait for the first results to
    // become ready; in this example we want to wait for results.
    <-whalewatcher.Ready()

    // get list of projects; we add the unnamed "" project which automatically
    // contains all non-project (standalone) containers.
    projectnames := append(whalewatcher.Portfolio().Names(), "")
    sort.Strings(projectnames)
    for _, projectname := range projectnames {
        containers := whalewatcher.Portfolio().Project(projectname)
        if containers == nil {
            continue // doh ... gone!
        }
        fmt.Printf("project %q:\n", projectname)
        for _, container := range containers.Containers() {
            fmt.Printf("  container %q with PID %d\n", container.Name, container.PID)
        }
        fmt.Println()
    }

    // finally stop the watch
    cancel()
    whalewatcher.Close()
}

Hacking It

This project comes with comprehensive unit tests, including (limited) mocking of Docker clients to the small extend required for whale watching.

Copyright 2021 Harald Albrecht, licensed under the Apache License, Version 2.0.

Documentation

Overview

Package whalewatcher watches Docker and containerd containers as they come and go from the perspective of containers that are "alive", that is, only those containers with actual processes. "Dead" (that is, stopped) containers without any processes are not tracked. Additionally, this package understands how such containers optionally are organized into composer projects (Docker composer, https://github.com/docker/compose).

As the focus is on containers that are either in running or paused states, the envisioned use cases are thus tools that somehow interact with processes of these "alive" containers, especially via various elements of the proc filesystem.

In order to cause as low system load as possible this module monitors the container engine's container lifecycle-related events instead of stupid polling.

Watcher

A Watcher watches the containers of a single container engine instance when running its Watch method in a separate go routine. Cancel its passed context to stop watching.

Watchers return information about alive containers (and optionally their organization into projects) via a Portfolio. Please do not keep the Portfolio reference for long periods of time, as might change in case the watcher needs to reconnect to a container engine after losing API contact.

Please refer to example/main.go as an example:

package main

import (
	"context"
	"fmt"
	"sort"

	"github.com/thediveo/whalewatcher/watcher/moby"
)

whalewatcher, err := moby.NewWatcher("unix:///var/run/docker.sock")
if err != nil {
	panic(err)
}
ctx, cancel := context.WithCancel(context.Background())
fmt.Printf("watching engine ID: %s\n", whalewatcher.ID(ctx))

// run the watch in a separate go routine.
go whalewatcher.Watch(ctx)

// depending on application you don't need to wait for the first results to
// become ready; in this example we want to wait for results.
<-whalewatcher.Ready()

// get list of projects; we add the unnamed "" project which automatically
// contains all non-project (standalone) containers.
projectnames := append(whalewatcher.Portfolio().Names(), "")
sort.Strings(projectnames)
for _, projectname := range projectnames {
	containers := whalewatcher.Portfolio().Project(projectname)
	if containers == nil {
		continue // doh ... gone!
	}
	fmt.Printf("project %q:\n", projectname)
	for _, container := range containers.Containers() {
		fmt.Printf("  container %q with PID %d\n", container.Name, container.PID)
	}
	fmt.Println()
}

// finally stop the watch
cancel()
whalewatcher.Close()

Note: if an application needs to watch both Docker and "pure" containerd containers, then it needs to create two watchers, one for the Docker engine and another one for the containerd instance. The containerd watcher doesn't watch any Docker-managed containers (it cannot as Docker does not attach all information at the containerd level, especially not the container name).

Portfolio

The container information model starts with the Portfolio: the Portfolio knows about the currently available projects (ComposerProjects).

ComposerProject

Composer projects are either explicitly named or the "zero" project that has no name (empty name). Projects then know the Containers associated to them.

Container

Containers store limited aspects about individual containers, such as their names, IDs, and PIDs.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ComposerProject

type ComposerProject struct {
	Name string // composer project name, guaranteed to be constant.
	// contains filtered or unexported fields
}

ComposerProject represents a set of (running or paused, yet somehow alive) containers belonging to a specific Docker Compose/Composer project.

As composer projects are artefacts above the first-level elements of the Docker container engine we can only reconstruct them in an extremely limited fashion from the live container information available to us. Yet that's fine in our context, as we just want to understand the concrete relationships between projects and their containers.

func (*ComposerProject) Container

func (p *ComposerProject) Container(nameorid string) *Container

Container returns container information about the container with the specified name or ID. If the name or ID wasn't found in this project, then nil is returned instead.

func (*ComposerProject) ContainerNames

func (p *ComposerProject) ContainerNames() []string

ContainerNames returns the current list of container names belonging to this composer project.

func (*ComposerProject) Containers

func (p *ComposerProject) Containers() []*Container

Containers returns the current list of containers in this composer project.

func (*ComposerProject) String

func (p *ComposerProject) String() string

String returns a textual representation of a composer project with its containers (rendering names, but not IDs).

type Container

type Container struct {
	ID      string            // unique identifier of this container.
	Name    string            // user-friendly name of this container.
	Labels  map[string]string // labels assigned to this container.
	PID     int               // PID of container's initial ("ealdorman") process.
	Project string            // optional composer project name, or zero.
}

Container is a deliberately limited fake view on containers, dealing with only those few bits of data we're interested in for watching alive containers and how they optionally are organized into composer projects.

We consider containers to be alive when they have an initial process (which might be frozen) and thus a PID corresponding with that initial process. In contrast, we don't care about containers which are either dead without any container process(es) or are just yet created and thus still without any container process(es).

func (Container) ProjectName

func (c Container) ProjectName() string

ProjectName returns the name of the composer project for this container, if any; otherwise, it returns "" if a container isn't associated with a composer project.

func (Container) String

func (c Container) String() string

String renders a textual representation of the information kept about a specific container, such as its name, ID, and PID.

type Portfolio

type Portfolio struct {
	// contains filtered or unexported fields
}

Portfolio represents all known composer projects, including the "zero" (unnamed) project. The "zero" project has the zero name and contains all containers that are not part of any named composer project. The Portfolio manages projects implicitly when adding and removing containers belonging to projects. Thus, there is no need to explicitly add or delete composer projects.

func NewPortfolio added in v0.2.0

func NewPortfolio() *Portfolio

NewPortfolio returns a new Portfolio.

func (*Portfolio) Add added in v0.2.0

func (pf *Portfolio) Add(cntr *Container)

Add a container to the portfolio, creating also its composer project if that is not yet known.

func (*Portfolio) ContainerTotal

func (pf *Portfolio) ContainerTotal() (total int)

ContainerTotal returns the total number of containers over all projects, including non-project "standalone" containers.

func (*Portfolio) Names

func (pf *Portfolio) Names() []string

Names returns the names of all composer projects sans the "zero" project.

func (*Portfolio) Project

func (pf *Portfolio) Project(name string) *ComposerProject

Project returns the project with the specified name (including the zero project name), or nil if no project with the specified name currently exists.

func (*Portfolio) Remove added in v0.2.0

func (pf *Portfolio) Remove(nameorid string, project string)

Remove a container identified by its ID or name as well as its composer project name from the portfolio, removing its composer project if it was the only container left in the project.

It's fine (no error) trying to to Remove a non-existing container.

Directories

Path Synopsis
Package engineclient provides container engine-specific EngineClient implementations in its sub-packages.
Package engineclient provides container engine-specific EngineClient implementations in its sub-packages.
containerd
Package containerd implements the containerd EngineClient.
Package containerd implements the containerd EngineClient.
moby
Package moby implements the Docker/Moby EngineClient.
Package moby implements the Docker/Moby EngineClient.
test
mockingmoby
Package mockingmoby is a very minimalist Docker mock client designed for simple unit tests in the whalewatcher package.
Package mockingmoby is a very minimalist Docker mock client designed for simple unit tests in the whalewatcher package.
Package watcher allows keeping track of the currently alive containers of a container engine.
Package watcher allows keeping track of the currently alive containers of a container engine.
containerd
Package containerd provides a container Watcher for containerd engines.
Package containerd provides a container Watcher for containerd engines.
moby
Package moby provides a container Watcher for Docker/Moby engines.
Package moby provides a container Watcher for Docker/Moby engines.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL