1
0
mirror of https://github.com/umputun/reproxy.git synced 2025-11-29 22:08:14 +02:00

Invoke Docker API directly

* Remove third-party docker client dependency
* Simplify code and tests

Might need to run `go mod tidy` and `go mod vendor` afterwards
This commit is contained in:
Timofey
2021-04-15 00:28:57 +03:00
committed by Umputun
parent 93f4d5b975
commit a91fb6816f
5 changed files with 260 additions and 290 deletions

View File

@@ -2,16 +2,18 @@ package provider
import (
"context"
"errors"
"encoding/json"
"fmt"
"net"
"net/http"
"regexp"
"sort"
"strconv"
"strings"
"time"
dc "github.com/fsouza/go-dockerclient"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/umputun/reproxy/app/discovery"
)
@@ -27,41 +29,29 @@ import (
type Docker struct {
DockerClient DockerClient
Excludes []string
Network string
AutoAPI bool
}
// DockerClient defines interface listing containers and subscribing to events
type DockerClient interface {
ListContainers(opts dc.ListContainersOptions) ([]dc.APIContainers, error)
AddEventListenerWithOptions(options dc.EventsOptions, listener chan<- *dc.APIEvents) error
ListContainers() ([]containerInfo, error)
}
// containerInfo is simplified docker.APIEvents for containers only
// containerInfo is simplified view of container metadata
type containerInfo struct {
ID string
Name string
TS time.Time
Labels map[string]string
IP string
Ports []int
ID string `json:"Id"`
Name string `json:"-"`
State string `json:"State"`
Labels map[string]string `json:"Labels"`
TS time.Time `json:"-"`
IP string `json:"-"`
Ports []int `json:"-"`
}
// Events gets eventsCh with all containers-related docker events events
func (d *Docker) Events(ctx context.Context) (res <-chan discovery.ProviderID) {
eventsCh := make(chan discovery.ProviderID)
go func() {
defer close(eventsCh)
// loop over to recover from failed events call
for {
err := d.events(ctx, d.DockerClient, eventsCh) // publish events to eventsCh in a blocking loop
if err == context.Canceled || err == context.DeadlineExceeded {
return
}
log.Printf("[WARN] docker events listener failed (restarted), %v", err)
time.Sleep(1 * time.Second) // prevent busy loop on restart of event listener
}
}()
go d.events(ctx, eventsCh)
return eventsCh
}
@@ -133,7 +123,7 @@ func (d *Docker) List() ([]discovery.URLMapper, error) {
srcRegex, err := regexp.Compile(srcURL)
if err != nil {
return nil, fmt.Errorf("invalid src regex %s: %w", srcURL, err)
return nil, errors.Wrapf(err, "invalid src regex %s", srcURL)
}
// docker server label may have multiple, comma separated servers
@@ -165,7 +155,7 @@ func (d *Docker) matchedPort(c containerInfo) (port int, err error) {
if v, ok := c.Labels["reproxy.port"]; ok {
rp, err := strconv.Atoi(v)
if err != nil {
return 0, fmt.Errorf("invalid reproxy.port %s: %w", v, err)
return 0, errors.Wrapf(err, "invalid reproxy.port %s", v)
}
for _, p := range c.Ports {
// set port to reproxy.port if matched with one of exposed
@@ -178,105 +168,140 @@ func (d *Docker) matchedPort(c containerInfo) (port int, err error) {
return port, nil
}
// activate starts blocking listener for all docker events
// filters everything except "container" type, detects stop/start events and publishes signals to eventsCh
func (d *Docker) events(ctx context.Context, client DockerClient, eventsCh chan discovery.ProviderID) error {
dockerEventsCh := make(chan *dc.APIEvents)
err := client.AddEventListenerWithOptions(dc.EventsOptions{
Filters: map[string][]string{"type": {"container"}, "event": {"start", "die", "destroy", "restart", "pause"}}},
dockerEventsCh)
if err != nil {
return fmt.Errorf("can't add even listener: %w", err)
}
// changed in tests
var dockerPollingInterval = time.Second * 10
// events starts monitoring changes in running containers and sends refresh
// notification to eventsCh when change(s) are detected. Blocks caller
func (d *Docker) events(ctx context.Context, eventsCh chan<- discovery.ProviderID) error {
ticker := time.NewTicker(dockerPollingInterval)
defer ticker.Stop()
eventsCh <- discovery.PIDocker // initial emmit
for {
select {
case <-ctx.Done():
return ctx.Err()
case ev, ok := <-dockerEventsCh:
if !ok {
return errors.New("events closed")
}
log.Printf("[DEBUG] api event %+v", ev)
containerName := strings.TrimPrefix(ev.Actor.Attributes["name"], "/")
if discovery.Contains(containerName, d.Excludes) {
log.Printf("[DEBUG] container %s excluded", containerName)
continue
}
log.Printf("[INFO] new docker event: container %s, status %s", containerName, ev.Status)
case <-ticker.C:
eventsCh <- discovery.PIDocker
}
}
}
func (d *Docker) listContainers() (res []containerInfo, err error) {
portsExposed := func(c dc.APIContainers) (ports []int) {
if len(c.Ports) == 0 {
return nil
}
for _, p := range c.Ports {
ports = append(ports, int(p.PrivatePort))
}
return ports
}
containers, err := d.DockerClient.ListContainers(dc.ListContainersOptions{All: false})
containers, err := d.DockerClient.ListContainers()
if err != nil {
return nil, fmt.Errorf("can't list containers: %w", err)
return nil, errors.Wrap(err, "can't list containers")
}
log.Printf("[DEBUG] total containers = %d", len(containers))
for _, c := range containers {
if c.State != "running" {
log.Printf("[DEBUG] skip container %s due to state %s", c.Names[0], c.State)
log.Printf("[DEBUG] skip container %s due to state %s", c.Name, c.State)
continue
}
containerName := strings.TrimPrefix(c.Names[0], "/")
if discovery.Contains(containerName, d.Excludes) || strings.EqualFold(containerName, "reproxy") {
log.Printf("[DEBUG] container %s excluded", containerName)
if discovery.Contains(c.Name, d.Excludes) || strings.EqualFold(c.Name, "reproxy") {
log.Printf("[DEBUG] container %s excluded", c.Name)
continue
}
if v, ok := c.Labels["reproxy.enabled"]; ok {
if strings.EqualFold(v, "false") || strings.EqualFold(v, "no") || v == "0" {
log.Printf("[DEBUG] skip container %s due to reproxy.enabled=%s", containerName, v)
log.Printf("[DEBUG] skip container %s due to reproxy.enabled=%s", c.Name, v)
continue
}
}
var ip string
for k, v := range c.Networks.Networks {
if d.Network == "" || k == d.Network { // match on network name if defined
ip = v.IPAddress
break
}
}
if ip == "" {
log.Printf("[DEBUG] skip container %s, no ip on %+v", c.Names[0], c.Networks.Networks)
if c.IP == "" {
log.Printf("[DEBUG] skip container %s, no ip on defined networks", c.Name)
continue
}
ports := portsExposed(c)
if len(ports) == 0 {
log.Printf("[DEBUG] skip container %s, no exposed ports", c.Names[0])
if len(c.Ports) == 0 {
log.Printf("[DEBUG] skip container %s, no exposed ports", c.Name)
continue
}
ci := containerInfo{
Name: containerName,
ID: c.ID,
TS: time.Unix(c.Created/1000, 0),
Labels: c.Labels,
IP: ip,
Ports: ports,
}
log.Printf("[DEBUG] running container added, %+v", ci)
res = append(res, ci)
log.Printf("[DEBUG] running container added, %+v", c)
res = append(res, c)
}
log.Print("[DEBUG] completed list")
return res, nil
}
type dockerClient struct {
client http.Client
network string // network for IP selection
}
// NewDockerClient constructs docker client for given host and network
func NewDockerClient(host, network string) DockerClient {
var schemaRegex = regexp.MustCompile("^(?:([a-z0-9]+)://)?(.*)$")
parts := schemaRegex.FindStringSubmatch(host)
proto, addr := parts[1], parts[2]
log.Printf("[DEBUG] Configuring docker client to talk to %s via %s", addr, proto)
client := http.Client{
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial(proto, addr)
},
},
Timeout: time.Second * 5,
}
return &dockerClient{client, network}
}
func (d *dockerClient) ListContainers() ([]containerInfo, error) {
const dockerAPIVersion = "v1.41"
resp, err := d.client.Get(fmt.Sprintf("http://localhost/%s/containers/json", dockerAPIVersion))
if err != nil {
return nil, errors.Wrap(err, "failed connection to docker socket")
}
defer resp.Body.Close()
response := []struct {
containerInfo
Created int64
NetworkSettings struct {
Networks map[string]struct {
IPAddress string
}
}
Names []string
ExposedPorts []struct{ PrivatePort int } `json:"Ports"`
}{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, errors.Wrap(err, "failed to parse response from docker daemon")
}
containers := make([]containerInfo, len(response))
for i, c := range response {
// fill remaining fields
c.TS = time.Unix(c.Created, 0)
for k, v := range c.NetworkSettings.Networks {
if d.network == "" || k == d.network { // match on network name if defined
c.IP = v.IPAddress
break
}
}
c.Name = strings.TrimPrefix(c.Names[0], "/")
for _, p := range c.ExposedPorts {
c.Ports = append(c.Ports, p.PrivatePort)
}
containers[i] = c.containerInfo
}
return containers, nil
}

View File

@@ -5,8 +5,6 @@ package provider
import (
"sync"
dclient "github.com/fsouza/go-dockerclient"
)
// DockerClientMock is a mock implementation of DockerClient.
@@ -15,10 +13,7 @@ import (
//
// // make and configure a mocked DockerClient
// mockedDockerClient := &DockerClientMock{
// AddEventListenerWithOptionsFunc: func(options dclient.EventsOptions, listener chan<- *dclient.APIEvents) error {
// panic("mock out the AddEventListenerWithOptions method")
// },
// ListContainersFunc: func(opts dclient.ListContainersOptions) ([]dclient.APIContainers, error) {
// ListContainersFunc: func() ([]containerInfo, error) {
// panic("mock out the ListContainers method")
// },
// }
@@ -28,90 +23,37 @@ import (
//
// }
type DockerClientMock struct {
// AddEventListenerWithOptionsFunc mocks the AddEventListenerWithOptions method.
AddEventListenerWithOptionsFunc func(options dclient.EventsOptions, listener chan<- *dclient.APIEvents) error
// ListContainersFunc mocks the ListContainers method.
ListContainersFunc func(opts dclient.ListContainersOptions) ([]dclient.APIContainers, error)
ListContainersFunc func() ([]containerInfo, error)
// calls tracks calls to the methods.
calls struct {
// AddEventListenerWithOptions holds details about calls to the AddEventListenerWithOptions method.
AddEventListenerWithOptions []struct {
// Options is the options argument value.
Options dclient.EventsOptions
// Listener is the listener argument value.
Listener chan<- *dclient.APIEvents
}
// ListContainers holds details about calls to the ListContainers method.
ListContainers []struct {
// Opts is the opts argument value.
Opts dclient.ListContainersOptions
}
}
lockAddEventListenerWithOptions sync.RWMutex
lockListContainers sync.RWMutex
}
// AddEventListenerWithOptions calls AddEventListenerWithOptionsFunc.
func (mock *DockerClientMock) AddEventListenerWithOptions(options dclient.EventsOptions, listener chan<- *dclient.APIEvents) error {
if mock.AddEventListenerWithOptionsFunc == nil {
panic("DockerClientMock.AddEventListenerWithOptionsFunc: method is nil but DockerClient.AddEventListenerWithOptions was just called")
}
callInfo := struct {
Options dclient.EventsOptions
Listener chan<- *dclient.APIEvents
}{
Options: options,
Listener: listener,
}
mock.lockAddEventListenerWithOptions.Lock()
mock.calls.AddEventListenerWithOptions = append(mock.calls.AddEventListenerWithOptions, callInfo)
mock.lockAddEventListenerWithOptions.Unlock()
return mock.AddEventListenerWithOptionsFunc(options, listener)
}
// AddEventListenerWithOptionsCalls gets all the calls that were made to AddEventListenerWithOptions.
// Check the length with:
// len(mockedDockerClient.AddEventListenerWithOptionsCalls())
func (mock *DockerClientMock) AddEventListenerWithOptionsCalls() []struct {
Options dclient.EventsOptions
Listener chan<- *dclient.APIEvents
} {
var calls []struct {
Options dclient.EventsOptions
Listener chan<- *dclient.APIEvents
}
mock.lockAddEventListenerWithOptions.RLock()
calls = mock.calls.AddEventListenerWithOptions
mock.lockAddEventListenerWithOptions.RUnlock()
return calls
lockListContainers sync.RWMutex
}
// ListContainers calls ListContainersFunc.
func (mock *DockerClientMock) ListContainers(opts dclient.ListContainersOptions) ([]dclient.APIContainers, error) {
func (mock *DockerClientMock) ListContainers() ([]containerInfo, error) {
if mock.ListContainersFunc == nil {
panic("DockerClientMock.ListContainersFunc: method is nil but DockerClient.ListContainers was just called")
}
callInfo := struct {
Opts dclient.ListContainersOptions
}{
Opts: opts,
}
}{}
mock.lockListContainers.Lock()
mock.calls.ListContainers = append(mock.calls.ListContainers, callInfo)
mock.lockListContainers.Unlock()
return mock.ListContainersFunc(opts)
return mock.ListContainersFunc()
}
// ListContainersCalls gets all the calls that were made to ListContainers.
// Check the length with:
// len(mockedDockerClient.ListContainersCalls())
func (mock *DockerClientMock) ListContainersCalls() []struct {
Opts dclient.ListContainersOptions
} {
var calls []struct {
Opts dclient.ListContainersOptions
}
mock.lockListContainers.RLock()
calls = mock.calls.ListContainers

View File

@@ -1,71 +1,51 @@
package provider
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
dc "github.com/fsouza/go-dockerclient"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDocker_List(t *testing.T) {
dclient := &DockerClientMock{
ListContainersFunc: func(opts dc.ListContainersOptions) ([]dc.APIContainers, error) {
return []dc.APIContainers{
{Names: []string{"c0"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"bridge": {IPAddress: "127.0.0.2"}},
},
Ports: []dc.APIPort{
{PrivatePort: 12348},
},
ListContainersFunc: func() ([]containerInfo, error) {
return []containerInfo{
{
Name: "c0", State: "running", IP: "127.0.0.2", Ports: []int{12348},
Labels: map[string]string{"reproxy.route": "^/a/(.*)", "reproxy.dest": "/a/$1",
"reproxy.server": "example.com", "reproxy.ping": "/ping"},
},
{Names: []string{"c1"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"bridge": {IPAddress: "127.0.0.2"}},
},
Ports: []dc.APIPort{
{PrivatePort: 12345},
},
{
Name: "c1", State: "running", IP: "127.0.0.2", Ports: []int{12345},
Labels: map[string]string{"reproxy.route": "^/api/123/(.*)", "reproxy.dest": "/blah/$1",
"reproxy.server": "example.com", "reproxy.ping": "/ping"},
},
{Names: []string{"c2"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"bridge": {IPAddress: "127.0.0.3"}},
},
Ports: []dc.APIPort{
{PrivatePort: 12346},
},
{
Name: "c2", State: "running", IP: "127.0.0.3", Ports: []int{12346},
Labels: map[string]string{"reproxy.enabled": "y"},
},
{Names: []string{"c3"}, State: "stopped"},
{Names: []string{"c4"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"other": {IPAddress: "127.0.0.2"}},
},
Ports: []dc.APIPort{
{PrivatePort: 12345},
},
{
Name: "c3", State: "stopped",
},
{Names: []string{"c5"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"bridge": {IPAddress: "127.0.0.122"}},
},
Ports: []dc.APIPort{
{PrivatePort: 2345},
},
{
Name: "c4", State: "running", IP: "127.0.0.2", Ports: []int{12345},
},
{
Name: "c5", State: "running", IP: "127.0.0.122", Ports: []int{2345},
Labels: map[string]string{"reproxy.enabled": "false"},
},
}, nil
},
}
d := Docker{DockerClient: dclient, Network: "bridge"}
d := Docker{DockerClient: dclient}
res, err := d.List()
require.NoError(t, err)
require.Equal(t, 3, len(res))
@@ -86,97 +66,32 @@ func TestDocker_List(t *testing.T) {
assert.Equal(t, "*", res[2].Server)
}
func TestDocker_ListWithAutoAPI(t *testing.T) {
dclient := &DockerClientMock{
ListContainersFunc: func(opts dc.ListContainersOptions) ([]dc.APIContainers, error) {
return []dc.APIContainers{
{Names: []string{"c1"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"bridge": {IPAddress: "127.0.0.2"}},
},
Ports: []dc.APIPort{
{PrivatePort: 1345},
{PrivatePort: 12345},
},
Labels: map[string]string{"reproxy.route": "^/api/123/(.*)", "reproxy.dest": "/blah/$1",
"reproxy.port": "12345", "reproxy.server": "example.com, example2.com", "reproxy.ping": "/ping"},
},
{Names: []string{"c2"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"bridge": {IPAddress: "127.0.0.3"}},
},
Ports: []dc.APIPort{
{PrivatePort: 12346},
},
},
{Names: []string{"c3"}, State: "stopped"},
{Names: []string{"c4"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"other": {IPAddress: "127.0.0.2"}},
},
Ports: []dc.APIPort{
{PrivatePort: 12345},
},
},
{Names: []string{"c5"}, State: "running",
Networks: dc.NetworkList{
Networks: map[string]dc.ContainerNetwork{"bridge": {IPAddress: "127.0.0.122"}},
},
Ports: []dc.APIPort{
{PrivatePort: 2345},
},
Labels: map[string]string{"reproxy.enabled": "false"},
},
}, nil
},
}
func TestDockerClient(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/v1.41/containers/json", r.URL.Path)
d := Docker{DockerClient: dclient, Network: "bridge", AutoAPI: true}
res, err := d.List()
require.NoError(t, err)
require.Equal(t, 3, len(res))
// obtained using curl --unix-socket /var/run/docker.sock http://localhost/v1.41/containers/json
resp, err := ioutil.ReadFile("testdata/containers.json")
require.NoError(t, err)
w.Write(resp)
}))
assert.Equal(t, "^/api/123/(.*)", res[0].SrcMatch.String())
assert.Equal(t, "http://127.0.0.2:12345/blah/$1", res[0].Dst)
assert.Equal(t, "example.com", res[0].Server)
assert.Equal(t, "http://127.0.0.2:12345/ping", res[0].PingURL)
defer srv.Close()
addr := fmt.Sprintf("tcp://%s", strings.TrimPrefix(srv.URL, "http://"))
assert.Equal(t, "^/api/123/(.*)", res[1].SrcMatch.String())
assert.Equal(t, "http://127.0.0.2:12345/blah/$1", res[1].Dst)
assert.Equal(t, "example2.com", res[1].Server)
assert.Equal(t, "http://127.0.0.2:12345/ping", res[1].PingURL)
client := NewDockerClient(addr, "bridge")
c, err := client.ListContainers()
require.NoError(t, err, "unexpected error while listing containers")
assert.Equal(t, "^/api/c2/(.*)", res[2].SrcMatch.String())
assert.Equal(t, "http://127.0.0.3:12346/$1", res[2].Dst)
assert.Equal(t, "http://127.0.0.3:12346/ping", res[2].PingURL)
assert.Equal(t, "*", res[2].Server)
}
func TestDocker_Events(t *testing.T) {
dclient := &DockerClientMock{
AddEventListenerWithOptionsFunc: func(options dc.EventsOptions, listener chan<- *dc.APIEvents) error {
go func() {
time.Sleep(30 * time.Millisecond)
listener <- &dc.APIEvents{Type: "container", Status: "start",
Actor: dc.APIActor{Attributes: map[string]string{"name": "/c1"}}}
time.Sleep(30 * time.Millisecond)
listener <- &dc.APIEvents{Type: "container", Status: "start",
Actor: dc.APIActor{Attributes: map[string]string{"name": "/c2"}}}
}()
return nil
},
}
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
d := Docker{DockerClient: dclient}
ch := d.Events(ctx)
events := 0
for range ch {
t.Log("event")
events++
}
assert.Equal(t, 2+1, events, "initial event plus 2 more")
assert.Len(t, c, 2)
assert.NotEmpty(t, c[0].ID)
assert.Equal(t, "nginx", c[0].Name)
assert.Equal(t, "running", c[0].State)
assert.Equal(t, "172.17.0.3", c[0].IP)
assert.Equal(t, "y", c[0].Labels["reproxy.enabled"])
assert.Equal(t, []int{80}, c[0].Ports)
assert.Equal(t, time.Unix(1618417435, 0), c[0].TS)
assert.Equal(t, "", c[1].IP)
}

View File

@@ -0,0 +1,91 @@
[
{
"Id": "6f3c99ca4d83811ae1bf1c3e288ac229288028a8b097565cb93eed85ccc4c9b4",
"Names": [
"/nginx"
],
"Image": "nginx",
"ImageID": "sha256:62d49f9bab67f7c70ac3395855bf01389eb3175b374e621f6f191bf31b54cd5b",
"Command": "/docker-entrypoint.sh nginx -g 'daemon off;'",
"Created": 1618417435,
"Ports": [
{
"PrivatePort": 80,
"Type": "tcp"
}
],
"Labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>",
"reproxy.enabled": "y"
},
"State": "running",
"Status": "Up 2 seconds",
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "7cf54d5b5b0cea56bcd4b5a8d7fd00ec6da9283a77e3cc0bd1252ebab60eca67",
"EndpointID": "481acb2624a95a12826357b1d6e9560ff0225b102ef932bb4dc484770f5b632b",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:42:42:42:03",
"DriverOpts": null
}
}
},
"Mounts": []
},
{
"Id": "d5503b81f356718bb01357daab63acb88eaae682bcb638383fadbac66e0bb830",
"Names": [
"/weather"
],
"Image": "weather:latest",
"ImageID": "sha256:28ceada083a985a36f794904261b0a82d2c3be9de57076acab90ac304f77952e",
"Command": "/usr/local/bin/weather",
"Created": 1618410445,
"Ports": [
{
"PrivatePort": 8000,
"Type": "tcp"
}
],
"Labels": {
"hello": "world"
},
"State": "running",
"Status": "Up 2 hours (healthy)",
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"someOtherNetwork": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "7cf54d5b5b0cea56bcd4b5a8d7fd00ec6da9283a77e3cc0bd1252ebab60eca67",
"EndpointID": "b4a14144a7300b37d4e907fdffa68b5234f67d2e7bb5db869198cf2db78d6280",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:42:42:42:02",
"DriverOpts": null
}
}
},
"Mounts": []
}
]

View File

@@ -13,7 +13,6 @@ import (
"syscall"
"time"
docker "github.com/fsouza/go-dockerclient"
log "github.com/go-pkgz/lgr"
"github.com/umputun/go-flags"
"gopkg.in/natefinch/lumberjack.v2"
@@ -203,15 +202,13 @@ func makeProviders() ([]discovery.Provider, error) {
}
if opts.Docker.Enabled {
client, err := docker.NewClient(opts.Docker.Host)
if err != nil {
return nil, fmt.Errorf("failed to make docker client %w", err)
}
client := provider.NewDockerClient(opts.Docker.Host, opts.Docker.Network)
if opts.Docker.AutoAPI {
log.Printf("[INFO] auto-api enabled for docker")
}
res = append(res, &provider.Docker{DockerClient: client, Excludes: opts.Docker.Excluded,
Network: opts.Docker.Network, AutoAPI: opts.Docker.AutoAPI})
AutoAPI: opts.Docker.AutoAPI})
}
if len(res) == 0 && opts.Assets.Location == "" {