mirror of https://github.com/docker/docs.git
commit
35c6eec263
|
@ -21,6 +21,6 @@ script:
|
|||
- script/validate-dco
|
||||
- script/validate-gofmt
|
||||
- go vet ./...
|
||||
- fgt golint -min_confidence=0.9 ./...
|
||||
- fgt golint ./...
|
||||
- go test -v -race ./...
|
||||
- script/coverage
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/samalba/dockerclient",
|
||||
"Rev": "0689bcd74173c6abd6394b7ad435df46b0df26f8"
|
||||
"Rev": "c37a52f55ab5a9edb9ffd4cf6e78692962b29b8d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/samuel/go-zookeeper/zk",
|
||||
|
|
|
@ -31,6 +31,7 @@ type DockerClient struct {
|
|||
HTTPClient *http.Client
|
||||
TLSConfig *tls.Config
|
||||
monitorEvents int32
|
||||
monitorStats int32
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
|
@ -60,7 +61,7 @@ func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout tim
|
|||
}
|
||||
}
|
||||
httpClient := newHTTPClient(u, tlsConfig, timeout)
|
||||
return &DockerClient{u, httpClient, tlsConfig, 0}, nil
|
||||
return &DockerClient{u, httpClient, tlsConfig, 0, 0}, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) {
|
||||
|
@ -197,6 +198,20 @@ func (client *DockerClient) ContainerLogs(id string, options *LogOptions) (io.Re
|
|||
return resp.Body, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, error) {
|
||||
uri := fmt.Sprintf("/%s/containers/%s/changes", APIVersion, id)
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes := []*ContainerChanges{}
|
||||
err = json.Unmarshal(data, &changes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) StartContainer(id string, config *HostConfig) error {
|
||||
data, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
|
@ -266,6 +281,35 @@ func (client *DockerClient) StopAllMonitorEvents() {
|
|||
atomic.StoreInt32(&client.monitorEvents, 0)
|
||||
}
|
||||
|
||||
func (client *DockerClient) StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
|
||||
atomic.StoreInt32(&client.monitorStats, 1)
|
||||
go client.getStats(id, cb, ec, args...)
|
||||
}
|
||||
|
||||
func (client *DockerClient) getStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
|
||||
uri := fmt.Sprintf("%s/%s/containers/%s/stats", client.URL.String(), APIVersion, id)
|
||||
resp, err := client.HTTPClient.Get(uri)
|
||||
if err != nil {
|
||||
ec <- err
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
for atomic.LoadInt32(&client.monitorStats) > 0 {
|
||||
var stats *Stats
|
||||
if err := dec.Decode(&stats); err != nil {
|
||||
ec <- err
|
||||
return
|
||||
}
|
||||
cb(id, stats, ec, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (client *DockerClient) StopAllMonitorStats() {
|
||||
atomic.StoreInt32(&client.monitorStats, 0)
|
||||
}
|
||||
|
||||
func (client *DockerClient) Version() (*Version, error) {
|
||||
uri := fmt.Sprintf("/%s/version", APIVersion)
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
|
@ -305,6 +349,20 @@ func (client *DockerClient) PullImage(name string, auth *AuthConfig) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) LoadImage(reader io.Reader) error {
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/%s/images/load", APIVersion)
|
||||
_, err = client.doRequest("POST", uri, data, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
|
||||
argForce := 0
|
||||
argVolumes := 0
|
||||
|
|
|
@ -74,6 +74,18 @@ func TestListContainers(t *testing.T) {
|
|||
assertEqual(t, cnt.SizeRw, int64(0), "")
|
||||
}
|
||||
|
||||
func TestContainerChanges(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
changes, err := client.ContainerChanges("foobar")
|
||||
if err != nil {
|
||||
t.Fatal("cannot get container changes: %s", err)
|
||||
}
|
||||
assertEqual(t, len(changes), 3, "unexpected number of changes")
|
||||
c := changes[0]
|
||||
assertEqual(t, c.Path, "/dev", "unexpected")
|
||||
assertEqual(t, c.Kind, 0, "unexpected")
|
||||
}
|
||||
|
||||
func TestListContainersWithSize(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
containers, err := client.ListContainers(true, true, "")
|
||||
|
|
|
@ -27,6 +27,7 @@ func init() {
|
|||
r.HandleFunc(baseURL+"/info", handlerGetInfo).Methods("GET")
|
||||
r.HandleFunc(baseURL+"/containers/json", handlerGetContainers).Methods("GET")
|
||||
r.HandleFunc(baseURL+"/containers/{id}/logs", handleContainerLogs).Methods("GET")
|
||||
r.HandleFunc(baseURL+"/containers/{id}/changes", handleContainerChanges).Methods("GET")
|
||||
r.HandleFunc(baseURL+"/containers/{id}/kill", handleContainerKill).Methods("POST")
|
||||
r.HandleFunc(baseURL+"/images/create", handleImagePull).Methods("POST")
|
||||
testHTTPServer = httptest.NewServer(handlerAccessLog(r))
|
||||
|
@ -107,6 +108,25 @@ func handleContainerLogs(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
func handleContainerChanges(w http.ResponseWriter, r *http.Request) {
|
||||
writeHeaders(w, 200, "changes")
|
||||
body := `[
|
||||
{
|
||||
"Path": "/dev",
|
||||
"Kind": 0
|
||||
},
|
||||
{
|
||||
"Path": "/dev/kmsg",
|
||||
"Kind": 1
|
||||
},
|
||||
{
|
||||
"Path": "/test",
|
||||
"Kind": 1
|
||||
}
|
||||
]`
|
||||
w.Write([]byte(body))
|
||||
}
|
||||
|
||||
func getBoolValue(boolString string) bool {
|
||||
switch boolString {
|
||||
case "1":
|
||||
|
|
43
Godeps/_workspace/src/github.com/samalba/dockerclient/examples/stats/stats.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/samalba/dockerclient/examples/stats/stats.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/samalba/dockerclient"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func statCallback(id string, stat *dockerclient.Stats, ec chan error, args ...interface{}) {
|
||||
log.Println(stat)
|
||||
}
|
||||
|
||||
func waitForInterrupt() {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
for _ = range sigChan {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
docker, err := dockerclient.NewDockerClient(os.Getenv("DOCKER_HOST"), nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
containerConfig := &dockerclient.ContainerConfig{Image: "busybox", Cmd: []string{"sh"}}
|
||||
containerId, err := docker.CreateContainer(containerConfig, "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Start the container
|
||||
err = docker.StartContainer(containerId, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
docker.StartMonitorStats(containerId, statCallback, nil)
|
||||
|
||||
waitForInterrupt()
|
||||
}
|
|
@ -6,12 +6,15 @@ import (
|
|||
|
||||
type Callback func(*Event, chan error, ...interface{})
|
||||
|
||||
type StatCallback func(string, *Stats, chan error, ...interface{})
|
||||
|
||||
type Client interface {
|
||||
Info() (*Info, error)
|
||||
ListContainers(all, size bool, filters string) ([]Container, error)
|
||||
InspectContainer(id string) (*ContainerInfo, error)
|
||||
CreateContainer(config *ContainerConfig, name string) (string, error)
|
||||
ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error)
|
||||
ContainerChanges(id string) ([]*ContainerChanges, error)
|
||||
Exec(config *ExecConfig) (string, error)
|
||||
StartContainer(id string, config *HostConfig) error
|
||||
StopContainer(id string, timeout int) error
|
||||
|
@ -19,8 +22,11 @@ type Client interface {
|
|||
KillContainer(id, signal string) error
|
||||
StartMonitorEvents(cb Callback, ec chan error, args ...interface{})
|
||||
StopAllMonitorEvents()
|
||||
StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{})
|
||||
StopAllMonitorStats()
|
||||
Version() (*Version, error)
|
||||
PullImage(name string, auth *AuthConfig) error
|
||||
LoadImage(reader io.Reader) error
|
||||
RemoveContainer(id string, force, volumes bool) error
|
||||
ListImages() ([]*Image, error)
|
||||
RemoveImage(name string) ([]*ImageDelete, error)
|
||||
|
|
|
@ -40,6 +40,11 @@ func (client *MockClient) ContainerLogs(id string, options *dockerclient.LogOpti
|
|||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) ContainerChanges(id string) ([]*dockerclient.ContainerChanges, error) {
|
||||
args := client.Mock.Called(id)
|
||||
return args.Get(0).([]*dockerclient.ContainerChanges), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) StartContainer(id string, config *dockerclient.HostConfig) error {
|
||||
args := client.Mock.Called(id, config)
|
||||
return args.Error(0)
|
||||
|
@ -68,6 +73,14 @@ func (client *MockClient) StopAllMonitorEvents() {
|
|||
client.Mock.Called()
|
||||
}
|
||||
|
||||
func (client *MockClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) {
|
||||
client.Mock.Called(id, cb, ec, args)
|
||||
}
|
||||
|
||||
func (client *MockClient) StopAllMonitorStats() {
|
||||
client.Mock.Called()
|
||||
}
|
||||
|
||||
func (client *MockClient) Version() (*dockerclient.Version, error) {
|
||||
args := client.Mock.Called()
|
||||
return args.Get(0).(*dockerclient.Version), args.Error(1)
|
||||
|
@ -78,6 +91,11 @@ func (client *MockClient) PullImage(name string, auth *dockerclient.AuthConfig)
|
|||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) LoadImage(reader io.Reader) error {
|
||||
args := client.Mock.Called(reader)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) RemoveContainer(id string, force, volumes bool) error {
|
||||
args := client.Mock.Called(id, force, volumes)
|
||||
return args.Error(0)
|
||||
|
|
|
@ -21,6 +21,7 @@ type ContainerConfig struct {
|
|||
Env []string
|
||||
Cmd []string
|
||||
Image string
|
||||
Labels map[string]string
|
||||
Volumes map[string]struct{}
|
||||
WorkingDir string
|
||||
Entrypoint []string
|
||||
|
@ -42,6 +43,7 @@ type HostConfig struct {
|
|||
Dns []string
|
||||
DnsSearch []string
|
||||
VolumesFrom []string
|
||||
SecurityOpt []string
|
||||
NetworkMode string
|
||||
RestartPolicy RestartPolicy
|
||||
}
|
||||
|
@ -94,8 +96,8 @@ type ContainerInfo struct {
|
|||
}
|
||||
Image string
|
||||
NetworkSettings struct {
|
||||
IpAddress string
|
||||
IpPrefixLen int
|
||||
IPAddress string `json:"IpAddress"`
|
||||
IPPrefixLen int `json:"IpPrefixLen"`
|
||||
Gateway string
|
||||
Bridge string
|
||||
Ports map[string][]PortBinding
|
||||
|
@ -106,6 +108,11 @@ type ContainerInfo struct {
|
|||
HostConfig *HostConfig
|
||||
}
|
||||
|
||||
type ContainerChanges struct {
|
||||
Path string
|
||||
Kind int
|
||||
}
|
||||
|
||||
type Port struct {
|
||||
IP string
|
||||
PrivatePort int
|
||||
|
@ -171,3 +178,80 @@ type ImageDelete struct {
|
|||
Deleted string
|
||||
Untagged string
|
||||
}
|
||||
|
||||
// The following are types for the API stats endpoint
|
||||
type ThrottlingData struct {
|
||||
// Number of periods with throttling active
|
||||
Periods uint64 `json:"periods"`
|
||||
// Number of periods when the container hit its throttling limit.
|
||||
ThrottledPeriods uint64 `json:"throttled_periods"`
|
||||
// Aggregate time the container was throttled for in nanoseconds.
|
||||
ThrottledTime uint64 `json:"throttled_time"`
|
||||
}
|
||||
|
||||
type CpuUsage struct {
|
||||
// Total CPU time consumed.
|
||||
// Units: nanoseconds.
|
||||
TotalUsage uint64 `json:"total_usage"`
|
||||
// Total CPU time consumed per core.
|
||||
// Units: nanoseconds.
|
||||
PercpuUsage []uint64 `json:"percpu_usage"`
|
||||
// Time spent by tasks of the cgroup in kernel mode.
|
||||
// Units: nanoseconds.
|
||||
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||
// Time spent by tasks of the cgroup in user mode.
|
||||
// Units: nanoseconds.
|
||||
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||
}
|
||||
|
||||
type CpuStats struct {
|
||||
CpuUsage CpuUsage `json:"cpu_usage"`
|
||||
SystemUsage uint64 `json:"system_cpu_usage"`
|
||||
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||
}
|
||||
|
||||
type NetworkStats struct {
|
||||
RxBytes uint64 `json:"rx_bytes"`
|
||||
RxPackets uint64 `json:"rx_packets"`
|
||||
RxErrors uint64 `json:"rx_errors"`
|
||||
RxDropped uint64 `json:"rx_dropped"`
|
||||
TxBytes uint64 `json:"tx_bytes"`
|
||||
TxPackets uint64 `json:"tx_packets"`
|
||||
TxErrors uint64 `json:"tx_errors"`
|
||||
TxDropped uint64 `json:"tx_dropped"`
|
||||
}
|
||||
|
||||
type MemoryStats struct {
|
||||
Usage uint64 `json:"usage"`
|
||||
MaxUsage uint64 `json:"max_usage"`
|
||||
Stats map[string]uint64 `json:"stats"`
|
||||
Failcnt uint64 `json:"failcnt"`
|
||||
Limit uint64 `json:"limit"`
|
||||
}
|
||||
|
||||
type BlkioStatEntry struct {
|
||||
Major uint64 `json:"major"`
|
||||
Minor uint64 `json:"minor"`
|
||||
Op string `json:"op"`
|
||||
Value uint64 `json:"value"`
|
||||
}
|
||||
|
||||
type BlkioStats struct {
|
||||
// number of bytes tranferred to and from the block device
|
||||
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"`
|
||||
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"`
|
||||
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"`
|
||||
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"`
|
||||
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"`
|
||||
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"`
|
||||
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"`
|
||||
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
Read time.Time `json:"read"`
|
||||
NetworkStats NetworkStats `json:"network,omitempty"`
|
||||
CpuStats CpuStats `json:"cpu_stats,omitempty"`
|
||||
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ const APIVERSION = "1.16"
|
|||
|
||||
type context struct {
|
||||
cluster cluster.Cluster
|
||||
eventsHandler *eventsHandler
|
||||
eventsHandler *EventsHandler
|
||||
debug bool
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
|
|
@ -9,23 +9,24 @@ import (
|
|||
"github.com/docker/swarm/cluster"
|
||||
)
|
||||
|
||||
type eventsHandler struct {
|
||||
// EventsHandler broadcasts events to multiple client listeners.
|
||||
type EventsHandler struct {
|
||||
sync.RWMutex
|
||||
ws map[string]io.Writer
|
||||
cs map[string]chan struct{}
|
||||
}
|
||||
|
||||
// NewEventsHandler creates a new eventsHandler for a cluster.
|
||||
// NewEventsHandler creates a new EventsHandler for a cluster.
|
||||
// The new eventsHandler is initialized with no writers or channels.
|
||||
func NewEventsHandler() *eventsHandler {
|
||||
return &eventsHandler{
|
||||
func NewEventsHandler() *EventsHandler {
|
||||
return &EventsHandler{
|
||||
ws: make(map[string]io.Writer),
|
||||
cs: make(map[string]chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds the writer and a new channel for the remote address.
|
||||
func (eh *eventsHandler) Add(remoteAddr string, w io.Writer) {
|
||||
func (eh *EventsHandler) Add(remoteAddr string, w io.Writer) {
|
||||
eh.Lock()
|
||||
eh.ws[remoteAddr] = w
|
||||
eh.cs[remoteAddr] = make(chan struct{})
|
||||
|
@ -33,13 +34,13 @@ func (eh *eventsHandler) Add(remoteAddr string, w io.Writer) {
|
|||
}
|
||||
|
||||
// Wait waits on a signal from the remote address.
|
||||
func (eh *eventsHandler) Wait(remoteAddr string) {
|
||||
func (eh *EventsHandler) Wait(remoteAddr string) {
|
||||
<-eh.cs[remoteAddr]
|
||||
}
|
||||
|
||||
// Handle writes information about a cluster event to each remote address in the cluster that has been added to the events handler.
|
||||
// After a successful write to a remote address, the associated channel is closed and the address is removed from the events handler.
|
||||
func (eh *eventsHandler) Handle(e *cluster.Event) error {
|
||||
func (eh *EventsHandler) Handle(e *cluster.Event) error {
|
||||
eh.RLock()
|
||||
|
||||
str := fmt.Sprintf("{%q:%q,%q:%q,%q:%q,%q:%d,%q:{%q:%q,%q:%q,%q:%q,%q:%q}}",
|
||||
|
@ -71,7 +72,7 @@ func (eh *eventsHandler) Handle(e *cluster.Event) error {
|
|||
}
|
||||
|
||||
// Size returns the number of remote addresses that the events handler currently contains.
|
||||
func (eh *eventsHandler) Size() int {
|
||||
func (eh *EventsHandler) Size() int {
|
||||
eh.RLock()
|
||||
defer eh.RUnlock()
|
||||
return len(eh.ws)
|
||||
|
|
|
@ -35,7 +35,7 @@ func newListener(proto, addr string, tlsConfig *tls.Config) (net.Listener, error
|
|||
//
|
||||
// The expected format for a host string is [protocol://]address. The protocol
|
||||
// must be either "tcp" or "unix", with "tcp" used by default if not specified.
|
||||
func ListenAndServe(c cluster.Cluster, hosts []string, enableCors bool, tlsConfig *tls.Config, eventsHandler *eventsHandler) error {
|
||||
func ListenAndServe(c cluster.Cluster, hosts []string, enableCors bool, tlsConfig *tls.Config, eventsHandler *EventsHandler) error {
|
||||
context := &context{
|
||||
cluster: c,
|
||||
eventsHandler: eventsHandler,
|
||||
|
|
|
@ -241,7 +241,7 @@ Contributing a new discovery backend is easy, simply implement this
|
|||
interface:
|
||||
|
||||
```go
|
||||
type DiscoveryService interface {
|
||||
type Discovery interface {
|
||||
Initialize(string, int) error
|
||||
Fetch() ([]string, error)
|
||||
Watch(WatchCallback)
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
consul "github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
// DiscoveryService is exported
|
||||
type DiscoveryService struct {
|
||||
// Discovery is exported
|
||||
type Discovery struct {
|
||||
heartbeat time.Duration
|
||||
client *consul.Client
|
||||
prefix string
|
||||
|
@ -20,11 +20,11 @@ type DiscoveryService struct {
|
|||
}
|
||||
|
||||
func init() {
|
||||
discovery.Register("consul", &DiscoveryService{})
|
||||
discovery.Register("consul", &Discovery{})
|
||||
}
|
||||
|
||||
// Initialize is exported
|
||||
func (s *DiscoveryService) Initialize(uris string, heartbeat uint64) error {
|
||||
func (s *Discovery) Initialize(uris string, heartbeat uint64) error {
|
||||
parts := strings.SplitN(uris, "/", 2)
|
||||
if len(parts) < 2 {
|
||||
return fmt.Errorf("invalid format %q, missing <path>", uris)
|
||||
|
@ -56,7 +56,7 @@ func (s *DiscoveryService) Initialize(uris string, heartbeat uint64) error {
|
|||
}
|
||||
|
||||
// Fetch is exported
|
||||
func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
||||
func (s *Discovery) Fetch() ([]*discovery.Entry, error) {
|
||||
kv := s.client.KV()
|
||||
pairs, _, err := kv.List(s.prefix, nil)
|
||||
if err != nil {
|
||||
|
@ -75,7 +75,7 @@ func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
|||
}
|
||||
|
||||
// Watch is exported
|
||||
func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
||||
func (s *Discovery) Watch(callback discovery.WatchCallback) {
|
||||
for _ = range s.waitForChange() {
|
||||
log.WithField("name", "consul").Debug("Discovery watch triggered")
|
||||
entries, err := s.Fetch()
|
||||
|
@ -86,14 +86,14 @@ func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
|||
}
|
||||
|
||||
// Register is exported
|
||||
func (s *DiscoveryService) Register(addr string) error {
|
||||
func (s *Discovery) Register(addr string) error {
|
||||
kv := s.client.KV()
|
||||
p := &consul.KVPair{Key: path.Join(s.prefix, addr), Value: []byte(addr)}
|
||||
_, err := kv.Put(p, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DiscoveryService) waitForChange() <-chan uint64 {
|
||||
func (s *Discovery) waitForChange() <-chan uint64 {
|
||||
c := make(chan uint64)
|
||||
go func() {
|
||||
for {
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func TestInitialize(t *testing.T) {
|
||||
discovery := &DiscoveryService{}
|
||||
discovery := &Discovery{}
|
||||
|
||||
assert.Equal(t, discovery.Initialize("127.0.0.1", 0).Error(), "invalid format \"127.0.0.1\", missing <path>")
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ func (m Entry) String() string {
|
|||
// WatchCallback is exported
|
||||
type WatchCallback func(entries []*Entry)
|
||||
|
||||
// DiscoveryService is exported
|
||||
type DiscoveryService interface {
|
||||
// Discovery is exported
|
||||
type Discovery interface {
|
||||
Initialize(string, uint64) error
|
||||
Fetch() ([]*Entry, error)
|
||||
Watch(WatchCallback)
|
||||
|
@ -40,7 +40,7 @@ type DiscoveryService interface {
|
|||
}
|
||||
|
||||
var (
|
||||
discoveries map[string]DiscoveryService
|
||||
discoveries map[string]Discovery
|
||||
// ErrNotSupported is exported
|
||||
ErrNotSupported = errors.New("discovery service not supported")
|
||||
// ErrNotImplemented is exported
|
||||
|
@ -48,11 +48,11 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
discoveries = make(map[string]DiscoveryService)
|
||||
discoveries = make(map[string]Discovery)
|
||||
}
|
||||
|
||||
// Register is exported
|
||||
func Register(scheme string, d DiscoveryService) error {
|
||||
func Register(scheme string, d Discovery) error {
|
||||
if _, exists := discoveries[scheme]; exists {
|
||||
return fmt.Errorf("scheme already registered %s", scheme)
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ func parse(rawurl string) (string, string) {
|
|||
}
|
||||
|
||||
// New is exported
|
||||
func New(rawurl string, heartbeat uint64) (DiscoveryService, error) {
|
||||
func New(rawurl string, heartbeat uint64) (Discovery, error) {
|
||||
scheme, uri := parse(rawurl)
|
||||
|
||||
if discovery, exists := discoveries[scheme]; exists {
|
||||
|
|
|
@ -10,19 +10,19 @@ import (
|
|||
"github.com/docker/swarm/discovery"
|
||||
)
|
||||
|
||||
// DiscoveryService is exported
|
||||
type DiscoveryService struct {
|
||||
// Discovery is exported
|
||||
type Discovery struct {
|
||||
ttl uint64
|
||||
client *etcd.Client
|
||||
path string
|
||||
}
|
||||
|
||||
func init() {
|
||||
discovery.Register("etcd", &DiscoveryService{})
|
||||
discovery.Register("etcd", &Discovery{})
|
||||
}
|
||||
|
||||
// Initialize is exported
|
||||
func (s *DiscoveryService) Initialize(uris string, heartbeat uint64) error {
|
||||
func (s *Discovery) Initialize(uris string, heartbeat uint64) error {
|
||||
var (
|
||||
// split here because uris can contain multiples ips
|
||||
// like `etcd://192.168.0.1,192.168.0.2,192.168.0.3/path`
|
||||
|
@ -56,7 +56,7 @@ func (s *DiscoveryService) Initialize(uris string, heartbeat uint64) error {
|
|||
}
|
||||
|
||||
// Fetch is exported
|
||||
func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
||||
func (s *Discovery) Fetch() ([]*discovery.Entry, error) {
|
||||
resp, err := s.client.Get(s.path, true, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -70,7 +70,7 @@ func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
|||
}
|
||||
|
||||
// Watch is exported
|
||||
func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
||||
func (s *Discovery) Watch(callback discovery.WatchCallback) {
|
||||
watchChan := make(chan *etcd.Response)
|
||||
go s.client.Watch(s.path, 0, true, watchChan, nil)
|
||||
for _ = range watchChan {
|
||||
|
@ -83,7 +83,7 @@ func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
|||
}
|
||||
|
||||
// Register is exported
|
||||
func (s *DiscoveryService) Register(addr string) error {
|
||||
func (s *Discovery) Register(addr string) error {
|
||||
_, err := s.client.Set(path.Join(s.path, addr), addr, s.ttl)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func TestInitialize(t *testing.T) {
|
||||
discovery := &DiscoveryService{}
|
||||
discovery := &Discovery{}
|
||||
|
||||
assert.Equal(t, discovery.Initialize("127.0.0.1", 0).Error(), "invalid format \"127.0.0.1\", missing <path>")
|
||||
|
||||
|
|
|
@ -8,18 +8,18 @@ import (
|
|||
"github.com/docker/swarm/discovery"
|
||||
)
|
||||
|
||||
// DiscoveryService is exported
|
||||
type DiscoveryService struct {
|
||||
// Discovery is exported
|
||||
type Discovery struct {
|
||||
heartbeat uint64
|
||||
path string
|
||||
}
|
||||
|
||||
func init() {
|
||||
discovery.Register("file", &DiscoveryService{})
|
||||
discovery.Register("file", &Discovery{})
|
||||
}
|
||||
|
||||
// Initialize is exported
|
||||
func (s *DiscoveryService) Initialize(path string, heartbeat uint64) error {
|
||||
func (s *Discovery) Initialize(path string, heartbeat uint64) error {
|
||||
s.path = path
|
||||
s.heartbeat = heartbeat
|
||||
return nil
|
||||
|
@ -47,7 +47,7 @@ func parseFileContent(content []byte) []string {
|
|||
}
|
||||
|
||||
// Fetch is exported
|
||||
func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
||||
func (s *Discovery) Fetch() ([]*discovery.Entry, error) {
|
||||
fileContent, err := ioutil.ReadFile(s.path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -56,7 +56,7 @@ func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
|||
}
|
||||
|
||||
// Watch is exported
|
||||
func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
||||
func (s *Discovery) Watch(callback discovery.WatchCallback) {
|
||||
for _ = range time.Tick(time.Duration(s.heartbeat) * time.Second) {
|
||||
entries, err := s.Fetch()
|
||||
if err == nil {
|
||||
|
@ -66,6 +66,6 @@ func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
|||
}
|
||||
|
||||
// Register is exported
|
||||
func (s *DiscoveryService) Register(addr string) error {
|
||||
func (s *Discovery) Register(addr string) error {
|
||||
return discovery.ErrNotImplemented
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func TestInitialize(t *testing.T) {
|
||||
discovery := &DiscoveryService{}
|
||||
discovery := &Discovery{}
|
||||
discovery.Initialize("/path/to/file", 0)
|
||||
assert.Equal(t, discovery.path, "/path/to/file")
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ func TestContent(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
discovery := &DiscoveryService{path: "/path/to/file"}
|
||||
discovery := &Discovery{path: "/path/to/file"}
|
||||
assert.Error(t, discovery.Register("0.0.0.0"))
|
||||
}
|
||||
|
||||
|
|
|
@ -6,17 +6,17 @@ import (
|
|||
"github.com/docker/swarm/discovery"
|
||||
)
|
||||
|
||||
// DiscoveryService is exported
|
||||
type DiscoveryService struct {
|
||||
// Discovery is exported
|
||||
type Discovery struct {
|
||||
entries []*discovery.Entry
|
||||
}
|
||||
|
||||
func init() {
|
||||
discovery.Register("nodes", &DiscoveryService{})
|
||||
discovery.Register("nodes", &Discovery{})
|
||||
}
|
||||
|
||||
// Initialize is exported
|
||||
func (s *DiscoveryService) Initialize(uris string, _ uint64) error {
|
||||
func (s *Discovery) Initialize(uris string, _ uint64) error {
|
||||
for _, input := range strings.Split(uris, ",") {
|
||||
for _, ip := range discovery.Generate(input) {
|
||||
entry, err := discovery.NewEntry(ip)
|
||||
|
@ -31,15 +31,15 @@ func (s *DiscoveryService) Initialize(uris string, _ uint64) error {
|
|||
}
|
||||
|
||||
// Fetch is exported
|
||||
func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
||||
func (s *Discovery) Fetch() ([]*discovery.Entry, error) {
|
||||
return s.entries, nil
|
||||
}
|
||||
|
||||
// Watch is exported
|
||||
func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
||||
func (s *Discovery) Watch(callback discovery.WatchCallback) {
|
||||
}
|
||||
|
||||
// Register is exported
|
||||
func (s *DiscoveryService) Register(addr string) error {
|
||||
func (s *Discovery) Register(addr string) error {
|
||||
return discovery.ErrNotImplemented
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func TestInitialise(t *testing.T) {
|
||||
discovery := &DiscoveryService{}
|
||||
discovery := &Discovery{}
|
||||
discovery.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0)
|
||||
assert.Equal(t, len(discovery.entries), 2)
|
||||
assert.Equal(t, discovery.entries[0].String(), "1.1.1.1:1111")
|
||||
|
@ -15,7 +15,7 @@ func TestInitialise(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInitialiseWithPattern(t *testing.T) {
|
||||
discovery := &DiscoveryService{}
|
||||
discovery := &Discovery{}
|
||||
discovery.Initialize("1.1.1.[1:2]:1111,2.2.2.[2:4]:2222", 0)
|
||||
assert.Equal(t, len(discovery.entries), 5)
|
||||
assert.Equal(t, discovery.entries[0].String(), "1.1.1.1:1111")
|
||||
|
@ -26,6 +26,6 @@ func TestInitialiseWithPattern(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
discovery := &DiscoveryService{}
|
||||
discovery := &Discovery{}
|
||||
assert.Error(t, discovery.Register("0.0.0.0"))
|
||||
}
|
||||
|
|
|
@ -15,19 +15,19 @@ import (
|
|||
// DiscoveryUrl is exported
|
||||
const DiscoveryURL = "https://discovery-stage.hub.docker.com/v1"
|
||||
|
||||
// DiscoveryService is exported
|
||||
type DiscoveryService struct {
|
||||
// Discovery is exported
|
||||
type Discovery struct {
|
||||
heartbeat uint64
|
||||
url string
|
||||
token string
|
||||
}
|
||||
|
||||
func init() {
|
||||
discovery.Register("token", &DiscoveryService{})
|
||||
discovery.Register("token", &Discovery{})
|
||||
}
|
||||
|
||||
// Initialize is exported
|
||||
func (s *DiscoveryService) Initialize(urltoken string, heartbeat uint64) error {
|
||||
func (s *Discovery) Initialize(urltoken string, heartbeat uint64) error {
|
||||
if i := strings.LastIndex(urltoken, "/"); i != -1 {
|
||||
s.url = "https://" + urltoken[:i]
|
||||
s.token = urltoken[i+1:]
|
||||
|
@ -45,7 +45,7 @@ func (s *DiscoveryService) Initialize(urltoken string, heartbeat uint64) error {
|
|||
}
|
||||
|
||||
// Fetch returns the list of entries for the discovery service at the specified endpoint
|
||||
func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
||||
func (s *Discovery) Fetch() ([]*discovery.Entry, error) {
|
||||
|
||||
resp, err := http.Get(fmt.Sprintf("%s/%s/%s", s.url, "clusters", s.token))
|
||||
if err != nil {
|
||||
|
@ -67,7 +67,7 @@ func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
|||
}
|
||||
|
||||
// Watch is exported
|
||||
func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
||||
func (s *Discovery) Watch(callback discovery.WatchCallback) {
|
||||
for _ = range time.Tick(time.Duration(s.heartbeat) * time.Second) {
|
||||
entries, err := s.Fetch()
|
||||
if err == nil {
|
||||
|
@ -77,7 +77,7 @@ func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
|||
}
|
||||
|
||||
// Register adds a new entry identified by the into the discovery service
|
||||
func (s *DiscoveryService) Register(addr string) error {
|
||||
func (s *Discovery) Register(addr string) error {
|
||||
buf := strings.NewReader(addr)
|
||||
|
||||
resp, err := http.Post(fmt.Sprintf("%s/%s/%s", s.url,
|
||||
|
@ -92,7 +92,7 @@ func (s *DiscoveryService) Register(addr string) error {
|
|||
}
|
||||
|
||||
// CreateCluster returns a unique cluster token
|
||||
func (s *DiscoveryService) CreateCluster() (string, error) {
|
||||
func (s *Discovery) CreateCluster() (string, error) {
|
||||
resp, err := http.Post(fmt.Sprintf("%s/%s", s.url, "clusters"), "", nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func TestInitialize(t *testing.T) {
|
||||
discovery := &DiscoveryService{}
|
||||
discovery := &Discovery{}
|
||||
err := discovery.Initialize("token", 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, discovery.token, "token")
|
||||
|
@ -23,7 +23,7 @@ func TestInitialize(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
discovery := &DiscoveryService{token: "TEST_TOKEN", url: DiscoveryURL}
|
||||
discovery := &Discovery{token: "TEST_TOKEN", url: DiscoveryURL}
|
||||
expected := "127.0.0.1:2675"
|
||||
assert.NoError(t, discovery.Register(expected))
|
||||
|
||||
|
|
|
@ -11,22 +11,22 @@ import (
|
|||
"github.com/samuel/go-zookeeper/zk"
|
||||
)
|
||||
|
||||
// DiscoveryService is exported
|
||||
type DiscoveryService struct {
|
||||
// Discovery is exported
|
||||
type Discovery struct {
|
||||
conn *zk.Conn
|
||||
path []string
|
||||
heartbeat uint64
|
||||
}
|
||||
|
||||
func init() {
|
||||
discovery.Register("zk", &DiscoveryService{})
|
||||
discovery.Register("zk", &Discovery{})
|
||||
}
|
||||
|
||||
func (s *DiscoveryService) fullpath() string {
|
||||
func (s *Discovery) fullpath() string {
|
||||
return "/" + strings.Join(s.path, "/")
|
||||
}
|
||||
|
||||
func (s *DiscoveryService) createFullpath() error {
|
||||
func (s *Discovery) createFullpath() error {
|
||||
for i := 1; i <= len(s.path); i++ {
|
||||
newpath := "/" + strings.Join(s.path[:i], "/")
|
||||
_, err := s.conn.Create(newpath, []byte{1}, 0, zk.WorldACL(zk.PermAll))
|
||||
|
@ -41,7 +41,7 @@ func (s *DiscoveryService) createFullpath() error {
|
|||
}
|
||||
|
||||
// Initialize is exported
|
||||
func (s *DiscoveryService) Initialize(uris string, heartbeat uint64) error {
|
||||
func (s *Discovery) Initialize(uris string, heartbeat uint64) error {
|
||||
var (
|
||||
// split here because uris can contain multiples ips
|
||||
// like `zk://192.168.0.1,192.168.0.2,192.168.0.3/path`
|
||||
|
@ -75,7 +75,7 @@ func (s *DiscoveryService) Initialize(uris string, heartbeat uint64) error {
|
|||
}
|
||||
|
||||
// Fetch is exported
|
||||
func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
||||
func (s *Discovery) Fetch() ([]*discovery.Entry, error) {
|
||||
addrs, _, err := s.conn.Children(s.fullpath())
|
||||
|
||||
if err != nil {
|
||||
|
@ -86,7 +86,7 @@ func (s *DiscoveryService) Fetch() ([]*discovery.Entry, error) {
|
|||
}
|
||||
|
||||
// Watch is exported
|
||||
func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
||||
func (s *Discovery) Watch(callback discovery.WatchCallback) {
|
||||
|
||||
addrs, _, eventChan, err := s.conn.ChildrenW(s.fullpath())
|
||||
if err != nil {
|
||||
|
@ -112,7 +112,7 @@ func (s *DiscoveryService) Watch(callback discovery.WatchCallback) {
|
|||
}
|
||||
|
||||
// Register is exported
|
||||
func (s *DiscoveryService) Register(addr string) error {
|
||||
func (s *Discovery) Register(addr string) error {
|
||||
nodePath := path.Join(s.fullpath(), addr)
|
||||
|
||||
// check existing for the parent path first
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func TestInitialize(t *testing.T) {
|
||||
service := &DiscoveryService{}
|
||||
service := &Discovery{}
|
||||
|
||||
assert.Equal(t, service.Initialize("127.0.0.1", 0).Error(), "invalid format \"127.0.0.1\", missing <path>")
|
||||
|
||||
|
|
2
main.go
2
main.go
|
@ -64,7 +64,7 @@ func main() {
|
|||
ShortName: "c",
|
||||
Usage: "create a cluster",
|
||||
Action: func(c *cli.Context) {
|
||||
discovery := &token.DiscoveryService{}
|
||||
discovery := &token.Discovery{}
|
||||
discovery.Initialize("", 0)
|
||||
token, err := discovery.CreateCluster()
|
||||
if len(c.Args()) != 0 {
|
||||
|
|
|
@ -236,8 +236,8 @@ func TestPortFilterRandomAssignment(t *testing.T) {
|
|||
},
|
||||
}},
|
||||
NetworkSettings: struct {
|
||||
IpAddress string
|
||||
IpPrefixLen int
|
||||
IPAddress string `json:"IpAddress"`
|
||||
IPPrefixLen int `json:"IpPrefixLen"`
|
||||
Gateway string
|
||||
Bridge string
|
||||
Ports map[string][]dockerclient.PortBinding
|
||||
|
|
Loading…
Reference in New Issue