mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-09 18:57:29 +00:00
``` $ gvt delete github.com/weaveworks/weave/common $ gvt fetch --revision 4da998ab4507b6a4852bf341b5df68eb7fca8a57 github.com/weaveworks/weave/common 2018/07/23 17:21:52 Fetching: github.com/weaveworks/weave/common 2018/07/23 17:22:05 · Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/fsouza/go-dockerclient 2018/07/23 17:22:05 ·· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/archive 2018/07/23 17:22:05 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/system 2018/07/23 17:22:05 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/opencontainers/image-spec/specs-go/v1 2018/07/23 17:22:05 ····· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/opencontainers/go-digest 2018/07/23 17:22:05 ····· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/opencontainers/image-spec/specs-go 2018/07/23 17:22:05 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/golang.org/x/sys/unix 2018/07/23 17:22:05 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/go-units 2018/07/23 17:22:05 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/containerd/continuity/pathdriver 2018/07/23 17:22:05 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/Microsoft/go-winio 2018/07/23 17:22:05 ····· Fetching recursive dependency: github.com/weaveworks/weave/vendor/golang.org/x/sys/windows 2018/07/23 17:22:05 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/mount 2018/07/23 17:22:05 ····· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/sirupsen/logrus 2018/07/23 17:22:05 ······ Fetching recursive dependency: github.com/weaveworks/weave/vendor/golang.org/x/crypto/ssh/terminal 2018/07/23 17:22:06 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/pkg/errors 2018/07/23 17:22:06 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/longpath 2018/07/23 17:22:06 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/idtools 2018/07/23 17:22:06 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/opencontainers/runc/libcontainer/user 2018/07/23 17:22:06 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/fileutils 2018/07/23 17:22:06 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/pools 2018/07/23 17:22:06 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/ioutils 2018/07/23 17:22:06 ····· Fetching recursive dependency: github.com/weaveworks/weave/vendor/golang.org/x/net/context 2018/07/23 17:22:06 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/opencontainers/runc/libcontainer/system 2018/07/23 17:22:06 ·· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/opts 2018/07/23 17:22:06 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/api/types 2018/07/23 17:22:06 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/gogo/protobuf/proto 2018/07/23 17:22:06 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/go-connections/nat 2018/07/23 17:22:06 ·· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/stdcopy 2018/07/23 17:22:06 ·· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/homedir 2018/07/23 17:22:06 ·· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/jsonmessage 2018/07/23 17:22:06 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/Nvveen/Gotty 2018/07/23 17:22:06 ··· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/docker/docker/pkg/term 2018/07/23 17:22:06 ···· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/Azure/go-ansiterm/winterm 2018/07/23 17:22:06 ····· Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/Azure/go-ansiterm 2018/07/23 17:22:06 · Fetching recursive dependency: github.com/weaveworks/weave/vendor/github.com/weaveworks/go-odp/odp ```
257 lines
6.6 KiB
Go
257 lines
6.6 KiB
Go
package docker
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
docker "github.com/fsouza/go-dockerclient"
|
|
|
|
"github.com/weaveworks/weave/common"
|
|
)
|
|
|
|
const (
|
|
InitialInterval = 1 * time.Second
|
|
MaxInterval = 20 * time.Second
|
|
)
|
|
|
|
// An observer for container events
|
|
type ContainerObserver interface {
|
|
ContainerStarted(ident string)
|
|
ContainerDied(ident string)
|
|
ContainerDestroyed(ident string)
|
|
}
|
|
|
|
type Client struct {
|
|
*docker.Client
|
|
}
|
|
|
|
type syncPair struct {
|
|
stop chan struct{}
|
|
done chan struct{}
|
|
}
|
|
|
|
type pendingStarts map[string]*syncPair
|
|
|
|
// NewClient creates a new Docker client and checks we can talk to Docker
|
|
func NewClient(apiPath string) (*Client, error) {
|
|
if apiPath != "" && !strings.Contains(apiPath, "://") {
|
|
apiPath = "tcp://" + apiPath
|
|
}
|
|
dc, err := docker.NewClient(apiPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client := &Client{dc}
|
|
|
|
return client, client.checkWorking()
|
|
}
|
|
|
|
func NewVersionedClient(apiPath string, apiVersionString string) (*Client, error) {
|
|
if !strings.Contains(apiPath, "://") {
|
|
apiPath = "tcp://" + apiPath
|
|
}
|
|
dc, err := docker.NewVersionedClient(apiPath, apiVersionString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client := &Client{dc}
|
|
|
|
return client, client.checkWorking()
|
|
}
|
|
|
|
func NewVersionedClientFromEnv(apiVersionString string) (*Client, error) {
|
|
dc, err := docker.NewVersionedClientFromEnv(apiVersionString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client := &Client{dc}
|
|
|
|
return client, client.checkWorking()
|
|
}
|
|
|
|
func (c *Client) checkWorking() error {
|
|
_, err := c.Version()
|
|
return err
|
|
}
|
|
|
|
func (c *Client) Info() string {
|
|
env, err := c.Version()
|
|
if err != nil {
|
|
return fmt.Sprintf("Docker API error: %s", err)
|
|
}
|
|
return fmt.Sprintf("Docker API on %s: %v", c.Endpoint(), env)
|
|
}
|
|
|
|
func (c *Client) DockerVersion() string {
|
|
if env, err := c.Version(); err == nil {
|
|
if v, found := env.Map()["Version"]; found {
|
|
return v
|
|
}
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
// AddObserver adds an observer for docker events
|
|
func (c *Client) AddObserver(ob ContainerObserver) error {
|
|
go func() {
|
|
pending := make(pendingStarts)
|
|
retryInterval := InitialInterval
|
|
for {
|
|
events := make(chan *docker.APIEvents)
|
|
if err := c.AddEventListener(events); err != nil {
|
|
c.errorf("Unable to add listener to Docker API: %s - retrying in %ds", err, retryInterval/time.Second)
|
|
} else {
|
|
start := time.Now()
|
|
for event := range events {
|
|
switch event.Status {
|
|
case "start":
|
|
pending.finish(event.ID)
|
|
pending.start(event.ID, c, ob)
|
|
case "die":
|
|
pending.finish(event.ID)
|
|
ob.ContainerDied(event.ID)
|
|
case "destroy":
|
|
pending.finish(event.ID)
|
|
ob.ContainerDestroyed(event.ID)
|
|
}
|
|
}
|
|
if time.Since(start) > retryInterval {
|
|
retryInterval = InitialInterval
|
|
}
|
|
c.errorf("Event listener channel closed - retrying subscription in %ds", retryInterval/time.Second)
|
|
}
|
|
time.Sleep(retryInterval)
|
|
retryInterval = retryInterval * 3 / 2
|
|
if retryInterval > MaxInterval {
|
|
retryInterval = MaxInterval
|
|
}
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
// Docker sends a 'start' event before it has attempted to start the
|
|
// container. Delay notifying the observer until the container has a
|
|
// pid, or we are told to stop when a 'die' event arrives.
|
|
//
|
|
// Note we always deliver the event, even if the container seems to
|
|
// have gone away.
|
|
func (pending pendingStarts) start(id string, c *Client, ob ContainerObserver) {
|
|
sync := syncPair{make(chan struct{}), make(chan struct{})}
|
|
pending[id] = &sync
|
|
go func() {
|
|
defer close(sync.done)
|
|
defer ob.ContainerStarted(id)
|
|
for {
|
|
if container, err := c.InspectContainer(id); err != nil || container.State.Pid != 0 {
|
|
return
|
|
}
|
|
select {
|
|
case <-sync.stop:
|
|
return
|
|
case <-time.After(200 * time.Millisecond):
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (pending pendingStarts) finish(id string) {
|
|
if sync, found := pending[id]; found {
|
|
close(sync.stop)
|
|
<-sync.done
|
|
delete(pending, id)
|
|
}
|
|
}
|
|
|
|
// AllContainerIDs returns all the IDs of Docker containers,
|
|
// whether they are running or not.
|
|
func (c *Client) AllContainerIDs() ([]string, error) {
|
|
return c.containerIDs(true)
|
|
}
|
|
|
|
// RunningContainerIDs returns all the IDs of the running
|
|
// Docker containers.
|
|
func (c *Client) RunningContainerIDs() ([]string, error) {
|
|
return c.containerIDs(false)
|
|
}
|
|
|
|
func (c *Client) containerIDs(all bool) ([]string, error) {
|
|
containers, err := c.ListContainers(docker.ListContainersOptions{All: all})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var ids []string
|
|
for _, c := range containers {
|
|
ids = append(ids, c.ID)
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
// IsContainerNotRunning returns true if we have checked with Docker that the ID is not running
|
|
func (c *Client) IsContainerNotRunning(idStr string) bool {
|
|
container, err := c.InspectContainer(idStr)
|
|
if err == nil {
|
|
return !container.State.Running || container.State.Restarting
|
|
}
|
|
if _, notThere := err.(*docker.NoSuchContainer); notThere {
|
|
return true
|
|
}
|
|
c.errorf("Could not check container status: %s", err)
|
|
return false
|
|
}
|
|
|
|
// This is intended to find an IP address that we can reach the container on;
|
|
// if it is on the Docker bridge network then that address; if on the host network
|
|
// then localhost
|
|
func (c *Client) GetContainerIP(nameOrID string) (string, error) {
|
|
info, err := c.InspectContainer(nameOrID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if info.NetworkSettings.Networks != nil {
|
|
if bridgeNetwork, ok := info.NetworkSettings.Networks["bridge"]; ok {
|
|
return bridgeNetwork.IPAddress, nil
|
|
} else if _, ok := info.NetworkSettings.Networks["host"]; ok {
|
|
return "127.0.0.1", nil
|
|
}
|
|
} else if info.HostConfig.NetworkMode == "host" {
|
|
return "127.0.0.1", nil
|
|
}
|
|
if info.NetworkSettings.IPAddress == "" {
|
|
return "", errors.New("No IP address found for container " + nameOrID)
|
|
}
|
|
return info.NetworkSettings.IPAddress, nil
|
|
}
|
|
|
|
func (c *Client) EnsureNetwork(networkName, driver, subnet string, options map[string]interface{}) error {
|
|
_, err := c.CreateNetwork(
|
|
docker.CreateNetworkOptions{
|
|
Name: networkName,
|
|
CheckDuplicate: true,
|
|
Driver: driver,
|
|
IPAM: docker.IPAMOptions{
|
|
Driver: driver,
|
|
Config: []docker.IPAMConfig{{Subnet: subnet}},
|
|
},
|
|
Options: options,
|
|
})
|
|
if err != docker.ErrNetworkAlreadyExists && err != nil {
|
|
// Despite appearances to the contrary, CreateNetwork does
|
|
// sometimes(always?) *not* return ErrNetworkAlreadyExists
|
|
// when the network already exists. Hence we need to check for
|
|
// this explicitly.
|
|
if _, err2 := c.NetworkInfo(networkName); err2 != nil {
|
|
return fmt.Errorf("unable to create network: %s", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// logging
|
|
|
|
func (c *Client) errorf(fmt string, args ...interface{}) {
|
|
common.Log.Errorf("[docker] "+fmt, args...)
|
|
}
|